aws-sdk-s3 1.220.0 → 1.232.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +89 -0
  3. data/VERSION +1 -1
  4. data/lib/aws-sdk-s3/bucket.rb +26 -2
  5. data/lib/aws-sdk-s3/bucket_notification.rb +3 -3
  6. data/lib/aws-sdk-s3/client.rb +869 -62
  7. data/lib/aws-sdk-s3/client_api.rb +301 -0
  8. data/lib/aws-sdk-s3/customizations/object.rb +55 -25
  9. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +14 -1
  10. data/lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb +13 -1
  11. data/lib/aws-sdk-s3/endpoint_provider.rb +6 -3
  12. data/lib/aws-sdk-s3/endpoints.rb +68 -0
  13. data/lib/aws-sdk-s3/errors.rb +66 -0
  14. data/lib/aws-sdk-s3/file_downloader.rb +3 -1
  15. data/lib/aws-sdk-s3/multipart_file_uploader.rb +1 -1
  16. data/lib/aws-sdk-s3/object.rb +150 -8
  17. data/lib/aws-sdk-s3/object_copier.rb +11 -22
  18. data/lib/aws-sdk-s3/object_multipart_copier.rb +177 -84
  19. data/lib/aws-sdk-s3/object_summary.rb +117 -8
  20. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +1 -1
  21. data/lib/aws-sdk-s3/transfer_manager.rb +44 -31
  22. data/lib/aws-sdk-s3/types.rb +1162 -46
  23. data/lib/aws-sdk-s3.rb +1 -1
  24. data/sig/bucket.rbs +7 -4
  25. data/sig/bucket_acl.rbs +1 -1
  26. data/sig/bucket_cors.rbs +1 -1
  27. data/sig/bucket_lifecycle.rbs +1 -1
  28. data/sig/bucket_lifecycle_configuration.rbs +4 -4
  29. data/sig/bucket_logging.rbs +1 -1
  30. data/sig/bucket_notification.rbs +9 -9
  31. data/sig/bucket_tagging.rbs +1 -1
  32. data/sig/bucket_website.rbs +1 -1
  33. data/sig/client.rbs +246 -198
  34. data/sig/errors.rbs +12 -0
  35. data/sig/multipart_upload.rbs +2 -2
  36. data/sig/object.rbs +32 -13
  37. data/sig/object_acl.rbs +1 -1
  38. data/sig/object_summary.rbs +22 -12
  39. data/sig/params.rbs +78 -0
  40. data/sig/resource.rbs +1 -1
  41. data/sig/types.rbs +249 -39
  42. metadata +4 -3
@@ -54,6 +54,27 @@ module Aws
54
54
  # and any checksums will not change. This is especially useful if the
55
55
  # source object has parts with varied sizes.
56
56
  #
57
+ # @option options [String] :tags_directive Only used when
58
+ # `:multipart_copy` is `true`. When set to `'COPY'`, source object
59
+ # tags are fetched and applied to the destination via PutObjectTagging.
60
+ # When set to `'REPLACE'`, the provided `:tagging` value is parsed and
61
+ # applied via PutObjectTagging. When not set, `:tagging` (if provided)
62
+ # is passed to CreateMultipartUpload directly. Works with or without
63
+ # `:content_length` — tags are fetched from source regardless of
64
+ # whether HeadObject is skipped.
65
+ #
66
+ # @option options [String] :annotations_directive Only used when
67
+ # `:multipart_copy` is `true`. When set to `'COPY'`, source object
68
+ # annotations are fetched and applied to the destination after the
69
+ # multipart upload completes. Works with or without `:content_length`.
70
+ #
71
+ # @option options [String] :metadata_directive Only used when
72
+ # `:multipart_copy` is `true`. When set to `'REPLACE'`, source metadata
73
+ # from HeadObject is not merged into CreateMultipartUpload — only
74
+ # caller-supplied values (e.g. `:metadata`, `:content_type`) are used.
75
+ # Has no effect when `:content_length` is provided since HeadObject
76
+ # is already skipped.
77
+ #
57
78
  # @example Basic object copy
58
79
  #
59
80
  # bucket = Aws::S3::Bucket.new('target-bucket')
@@ -385,17 +406,20 @@ module Aws
385
406
  def upload_stream(options = {}, &block)
386
407
  upload_opts = options.merge(bucket: bucket_name, key: key)
387
408
  executor = DefaultExecutor.new(max_threads: upload_opts.delete(:thread_count))
388
- uploader = MultipartStreamUploader.new(
389
- client: client,
390
- executor: executor,
391
- tempfile: upload_opts.delete(:tempfile),
392
- part_size: upload_opts.delete(:part_size)
393
- )
394
- Aws::Plugins::UserAgent.metric('RESOURCE_MODEL') do
395
- uploader.upload(upload_opts, &block)
409
+ begin
410
+ uploader = MultipartStreamUploader.new(
411
+ client: client,
412
+ executor: executor,
413
+ tempfile: upload_opts.delete(:tempfile),
414
+ part_size: upload_opts.delete(:part_size)
415
+ )
416
+ Aws::Plugins::UserAgent.metric('RESOURCE_MODEL') do
417
+ uploader.upload(upload_opts, &block)
418
+ end
419
+ true
420
+ ensure
421
+ executor.shutdown
396
422
  end
397
- executor.shutdown
398
- true
399
423
  end
400
424
  deprecated(:upload_stream, use: 'Aws::S3::TransferManager#upload_stream', version: 'next major version')
401
425
 
@@ -459,17 +483,20 @@ module Aws
459
483
  def upload_file(source, options = {})
460
484
  upload_opts = options.merge(bucket: bucket_name, key: key)
461
485
  executor = DefaultExecutor.new(max_threads: upload_opts.delete(:thread_count))
462
- uploader = FileUploader.new(
463
- client: client,
464
- executor: executor,
465
- multipart_threshold: upload_opts.delete(:multipart_threshold)
466
- )
467
- response = Aws::Plugins::UserAgent.metric('RESOURCE_MODEL') do
468
- uploader.upload(source, upload_opts)
486
+ begin
487
+ uploader = FileUploader.new(
488
+ client: client,
489
+ executor: executor,
490
+ multipart_threshold: upload_opts.delete(:multipart_threshold)
491
+ )
492
+ response = Aws::Plugins::UserAgent.metric('RESOURCE_MODEL') do
493
+ uploader.upload(source, upload_opts)
494
+ end
495
+ yield response if block_given?
496
+ true
497
+ ensure
498
+ executor.shutdown
469
499
  end
470
- yield response if block_given?
471
- executor.shutdown
472
- true
473
500
  end
474
501
  deprecated(:upload_file, use: 'Aws::S3::TransferManager#upload_file', version: 'next major version')
475
502
 
@@ -541,12 +568,15 @@ module Aws
541
568
  def download_file(destination, options = {})
542
569
  download_opts = options.merge(bucket: bucket_name, key: key)
543
570
  executor = DefaultExecutor.new(max_threads: download_opts.delete([:thread_count]))
544
- downloader = FileDownloader.new(client: client, executor: executor)
545
- Aws::Plugins::UserAgent.metric('RESOURCE_MODEL') do
546
- downloader.download(destination, download_opts)
571
+ begin
572
+ downloader = FileDownloader.new(client: client, executor: executor)
573
+ Aws::Plugins::UserAgent.metric('RESOURCE_MODEL') do
574
+ downloader.download(destination, download_opts)
575
+ end
576
+ true
577
+ ensure
578
+ executor.shutdown
547
579
  end
548
- executor.shutdown
549
- true
550
580
  end
551
581
  deprecated(:download_file, use: 'Aws::S3::TransferManager#download_file', version: 'next major version')
552
582
 
@@ -50,7 +50,7 @@ module Aws
50
50
  # @return [Cipher] Given an encryption envelope, returns a
51
51
  # decryption cipher.
52
52
  def decryption_cipher(envelope, options = {})
53
- encryption_context = Json.load(envelope['x-amz-matdesc'])
53
+ encryption_context = extract_encryption_context(envelope['x-amz-matdesc'])
54
54
  cek_alg = envelope['x-amz-cek-alg']
55
55
 
56
56
  case envelope['x-amz-wrap-alg']
@@ -115,6 +115,19 @@ module Aws
115
115
 
116
116
  private
117
117
 
118
+ # Raise a decryption error for a malformed material description. A
119
+ # material description must be a JSON object.
120
+ def extract_encryption_context(matdesc)
121
+ context = Json.load(matdesc) if matdesc.is_a?(String)
122
+ unless context.is_a?(Hash)
123
+ raise Errors::DecryptionError, 'Malformed material description'
124
+ end
125
+
126
+ context
127
+ rescue Aws::Json::ParseError, EncodingError
128
+ raise Errors::DecryptionError, 'Malformed material description'
129
+ end
130
+
118
131
  def validate_key_wrap(key_wrap_schema)
119
132
  case key_wrap_schema
120
133
  when :kms_context then 'kms+context'
@@ -54,7 +54,7 @@ module Aws
54
54
  cek_alg = envelope['x-amz-c']
55
55
  encryption_context =
56
56
  if !envelope['x-amz-t'].nil?
57
- Json.load(envelope['x-amz-t'])
57
+ extract_encryption_context(envelope['x-amz-t'])
58
58
  else
59
59
  ##= ../specification/s3-encryption/data-format/content-metadata.md#v3-only
60
60
  ##% If the mapkey x-amz-t is not present, the default Material Description value MUST be set to an empty map (`{}`).
@@ -102,6 +102,18 @@ module Aws
102
102
 
103
103
  private
104
104
 
105
+ # Raise a decryption error for a malformed material description.
106
+ def extract_encryption_context(matdesc)
107
+ context = Json.load(matdesc) if matdesc.is_a?(String)
108
+ unless context.is_a?(Hash)
109
+ raise Errors::DecryptionError, 'Malformed material description'
110
+ end
111
+
112
+ context
113
+ rescue Aws::Json::ParseError, EncodingError
114
+ raise Errors::DecryptionError, 'Malformed material description'
115
+ end
116
+
105
117
  def validate_key_wrap(key_wrap_schema)
106
118
  case key_wrap_schema
107
119
  when :kms_context then '12'
@@ -710,10 +710,13 @@ module Aws::S3
710
710
  if (outpost_type = Aws::Endpoints::Matchers.attr(bucket_arn, "resourceId[2]"))
711
711
  if (access_point_name = Aws::Endpoints::Matchers.attr(bucket_arn, "resourceId[3]"))
712
712
  if Aws::Endpoints::Matchers.string_equals?(outpost_type, "accesspoint")
713
- if Aws::Endpoints::Matchers.set?(parameters.endpoint) && (url = Aws::Endpoints::Matchers.parse_url(parameters.endpoint))
714
- return Aws::Endpoints::Endpoint.new(url: "https://#{access_point_name}-#{bucket_arn['accountId']}.#{outpost_id}.#{url['authority']}", headers: {}, properties: {"authSchemes" => [{"disableDoubleEncoding" => true, "name" => "sigv4a", "signingName" => "s3-outposts", "signingRegionSet" => ["*"]}, {"disableDoubleEncoding" => true, "name" => "sigv4", "signingName" => "s3-outposts", "signingRegion" => "#{bucket_arn['region']}"}]})
713
+ if Aws::Endpoints::Matchers.valid_host_label?(access_point_name, false)
714
+ if Aws::Endpoints::Matchers.set?(parameters.endpoint) && (url = Aws::Endpoints::Matchers.parse_url(parameters.endpoint))
715
+ return Aws::Endpoints::Endpoint.new(url: "https://#{access_point_name}-#{bucket_arn['accountId']}.#{outpost_id}.#{url['authority']}", headers: {}, properties: {"authSchemes" => [{"disableDoubleEncoding" => true, "name" => "sigv4a", "signingName" => "s3-outposts", "signingRegionSet" => ["*"]}, {"disableDoubleEncoding" => true, "name" => "sigv4", "signingName" => "s3-outposts", "signingRegion" => "#{bucket_arn['region']}"}]})
716
+ end
717
+ return Aws::Endpoints::Endpoint.new(url: "https://#{access_point_name}-#{bucket_arn['accountId']}.#{outpost_id}.s3-outposts.#{bucket_arn['region']}.#{bucket_partition['dnsSuffix']}", headers: {}, properties: {"authSchemes" => [{"disableDoubleEncoding" => true, "name" => "sigv4a", "signingName" => "s3-outposts", "signingRegionSet" => ["*"]}, {"disableDoubleEncoding" => true, "name" => "sigv4", "signingName" => "s3-outposts", "signingRegion" => "#{bucket_arn['region']}"}]})
715
718
  end
716
- return Aws::Endpoints::Endpoint.new(url: "https://#{access_point_name}-#{bucket_arn['accountId']}.#{outpost_id}.s3-outposts.#{bucket_arn['region']}.#{bucket_partition['dnsSuffix']}", headers: {}, properties: {"authSchemes" => [{"disableDoubleEncoding" => true, "name" => "sigv4a", "signingName" => "s3-outposts", "signingRegionSet" => ["*"]}, {"disableDoubleEncoding" => true, "name" => "sigv4", "signingName" => "s3-outposts", "signingRegion" => "#{bucket_arn['region']}"}]})
719
+ raise ArgumentError, "Invalid ARN: The access point name may only contain a-z, A-Z, 0-9 and `-`. Found: `#{access_point_name}`"
717
720
  end
718
721
  raise ArgumentError, "Expected an outpost type `accesspoint`, found #{outpost_type}"
719
722
  end
@@ -303,6 +303,17 @@ module Aws::S3
303
303
  end
304
304
  end
305
305
 
306
+ class DeleteObjectAnnotation
307
+ def self.build(context)
308
+ Aws::S3::EndpointParameters.create(
309
+ context.config,
310
+ bucket: context.params[:bucket],
311
+ use_dual_stack: context[:use_dualstack_endpoint],
312
+ accelerate: context[:use_accelerate_endpoint],
313
+ )
314
+ end
315
+ end
316
+
306
317
  class DeleteObjectTagging
307
318
  def self.build(context)
308
319
  Aws::S3::EndpointParameters.create(
@@ -660,6 +671,18 @@ module Aws::S3
660
671
  end
661
672
  end
662
673
 
674
+ class GetObjectAnnotation
675
+ def self.build(context)
676
+ Aws::S3::EndpointParameters.create(
677
+ context.config,
678
+ bucket: context.params[:bucket],
679
+ use_dual_stack: context[:use_dualstack_endpoint],
680
+ accelerate: context[:use_accelerate_endpoint],
681
+ key: context.params[:key],
682
+ )
683
+ end
684
+ end
685
+
663
686
  class GetObjectAttributes
664
687
  def self.build(context)
665
688
  Aws::S3::EndpointParameters.create(
@@ -842,6 +865,17 @@ module Aws::S3
842
865
  end
843
866
  end
844
867
 
868
+ class ListObjectAnnotations
869
+ def self.build(context)
870
+ Aws::S3::EndpointParameters.create(
871
+ context.config,
872
+ bucket: context.params[:bucket],
873
+ use_dual_stack: context[:use_dualstack_endpoint],
874
+ accelerate: context[:use_accelerate_endpoint],
875
+ )
876
+ end
877
+ end
878
+
845
879
  class ListObjectVersions
846
880
  def self.build(context)
847
881
  Aws::S3::EndpointParameters.create(
@@ -1165,6 +1199,18 @@ module Aws::S3
1165
1199
  end
1166
1200
  end
1167
1201
 
1202
+ class PutObjectAnnotation
1203
+ def self.build(context)
1204
+ Aws::S3::EndpointParameters.create(
1205
+ context.config,
1206
+ bucket: context.params[:bucket],
1207
+ use_dual_stack: context[:use_dualstack_endpoint],
1208
+ accelerate: context[:use_accelerate_endpoint],
1209
+ key: context.params[:key],
1210
+ )
1211
+ end
1212
+ end
1213
+
1168
1214
  class PutObjectLegalHold
1169
1215
  def self.build(context)
1170
1216
  Aws::S3::EndpointParameters.create(
@@ -1255,6 +1301,18 @@ module Aws::S3
1255
1301
  end
1256
1302
  end
1257
1303
 
1304
+ class UpdateBucketMetadataAnnotationTableConfiguration
1305
+ def self.build(context)
1306
+ Aws::S3::EndpointParameters.create(
1307
+ context.config,
1308
+ bucket: context.params[:bucket],
1309
+ use_dual_stack: context[:use_dualstack_endpoint],
1310
+ accelerate: context[:use_accelerate_endpoint],
1311
+ use_s3_express_control_endpoint: true,
1312
+ )
1313
+ end
1314
+ end
1315
+
1258
1316
  class UpdateBucketMetadataInventoryTableConfiguration
1259
1317
  def self.build(context)
1260
1318
  Aws::S3::EndpointParameters.create(
@@ -1376,6 +1434,8 @@ module Aws::S3
1376
1434
  DeleteBucketWebsite.build(context)
1377
1435
  when :delete_object
1378
1436
  DeleteObject.build(context)
1437
+ when :delete_object_annotation
1438
+ DeleteObjectAnnotation.build(context)
1379
1439
  when :delete_object_tagging
1380
1440
  DeleteObjectTagging.build(context)
1381
1441
  when :delete_objects
@@ -1436,6 +1496,8 @@ module Aws::S3
1436
1496
  GetObject.build(context)
1437
1497
  when :get_object_acl
1438
1498
  GetObjectAcl.build(context)
1499
+ when :get_object_annotation
1500
+ GetObjectAnnotation.build(context)
1439
1501
  when :get_object_attributes
1440
1502
  GetObjectAttributes.build(context)
1441
1503
  when :get_object_legal_hold
@@ -1468,6 +1530,8 @@ module Aws::S3
1468
1530
  ListDirectoryBuckets.build(context)
1469
1531
  when :list_multipart_uploads
1470
1532
  ListMultipartUploads.build(context)
1533
+ when :list_object_annotations
1534
+ ListObjectAnnotations.build(context)
1471
1535
  when :list_object_versions
1472
1536
  ListObjectVersions.build(context)
1473
1537
  when :list_objects
@@ -1522,6 +1586,8 @@ module Aws::S3
1522
1586
  PutObject.build(context)
1523
1587
  when :put_object_acl
1524
1588
  PutObjectAcl.build(context)
1589
+ when :put_object_annotation
1590
+ PutObjectAnnotation.build(context)
1525
1591
  when :put_object_legal_hold
1526
1592
  PutObjectLegalHold.build(context)
1527
1593
  when :put_object_lock_configuration
@@ -1538,6 +1604,8 @@ module Aws::S3
1538
1604
  RestoreObject.build(context)
1539
1605
  when :select_object_content
1540
1606
  SelectObjectContent.build(context)
1607
+ when :update_bucket_metadata_annotation_table_configuration
1608
+ UpdateBucketMetadataAnnotationTableConfiguration.build(context)
1541
1609
  when :update_bucket_metadata_inventory_table_configuration
1542
1610
  UpdateBucketMetadataInventoryTableConfiguration.build(context)
1543
1611
  when :update_bucket_metadata_journal_table_configuration
@@ -28,19 +28,25 @@ module Aws::S3
28
28
  #
29
29
  # ## Error Classes
30
30
  # * {AccessDenied}
31
+ # * {AnnotationLimitExceeded}
32
+ # * {AnnotationNameTooLong}
31
33
  # * {BucketAlreadyExists}
32
34
  # * {BucketAlreadyOwnedByYou}
33
35
  # * {EncryptionTypeMismatch}
34
36
  # * {IdempotencyParameterMismatch}
37
+ # * {InvalidAnnotationName}
35
38
  # * {InvalidObjectState}
39
+ # * {InvalidPrefix}
36
40
  # * {InvalidRequest}
37
41
  # * {InvalidWriteOffset}
42
+ # * {NoSuchAnnotation}
38
43
  # * {NoSuchBucket}
39
44
  # * {NoSuchKey}
40
45
  # * {NoSuchUpload}
41
46
  # * {ObjectAlreadyInActiveTierError}
42
47
  # * {ObjectNotInActiveTierError}
43
48
  # * {TooManyParts}
49
+ # * {UnsupportedMediaType}
44
50
  #
45
51
  # Additionally, error classes are dynamically generated for service errors based on the error code
46
52
  # if they are not defined above.
@@ -58,6 +64,26 @@ module Aws::S3
58
64
  end
59
65
  end
60
66
 
67
+ class AnnotationLimitExceeded < ServiceError
68
+
69
+ # @param [Seahorse::Client::RequestContext] context
70
+ # @param [String] message
71
+ # @param [Aws::S3::Types::AnnotationLimitExceeded] data
72
+ def initialize(context, message, data = Aws::EmptyStructure.new)
73
+ super(context, message, data)
74
+ end
75
+ end
76
+
77
+ class AnnotationNameTooLong < ServiceError
78
+
79
+ # @param [Seahorse::Client::RequestContext] context
80
+ # @param [String] message
81
+ # @param [Aws::S3::Types::AnnotationNameTooLong] data
82
+ def initialize(context, message, data = Aws::EmptyStructure.new)
83
+ super(context, message, data)
84
+ end
85
+ end
86
+
61
87
  class BucketAlreadyExists < ServiceError
62
88
 
63
89
  # @param [Seahorse::Client::RequestContext] context
@@ -98,6 +124,16 @@ module Aws::S3
98
124
  end
99
125
  end
100
126
 
127
+ class InvalidAnnotationName < ServiceError
128
+
129
+ # @param [Seahorse::Client::RequestContext] context
130
+ # @param [String] message
131
+ # @param [Aws::S3::Types::InvalidAnnotationName] data
132
+ def initialize(context, message, data = Aws::EmptyStructure.new)
133
+ super(context, message, data)
134
+ end
135
+ end
136
+
101
137
  class InvalidObjectState < ServiceError
102
138
 
103
139
  # @param [Seahorse::Client::RequestContext] context
@@ -118,6 +154,16 @@ module Aws::S3
118
154
  end
119
155
  end
120
156
 
157
+ class InvalidPrefix < ServiceError
158
+
159
+ # @param [Seahorse::Client::RequestContext] context
160
+ # @param [String] message
161
+ # @param [Aws::S3::Types::InvalidPrefix] data
162
+ def initialize(context, message, data = Aws::EmptyStructure.new)
163
+ super(context, message, data)
164
+ end
165
+ end
166
+
121
167
  class InvalidRequest < ServiceError
122
168
 
123
169
  # @param [Seahorse::Client::RequestContext] context
@@ -138,6 +184,16 @@ module Aws::S3
138
184
  end
139
185
  end
140
186
 
187
+ class NoSuchAnnotation < ServiceError
188
+
189
+ # @param [Seahorse::Client::RequestContext] context
190
+ # @param [String] message
191
+ # @param [Aws::S3::Types::NoSuchAnnotation] data
192
+ def initialize(context, message, data = Aws::EmptyStructure.new)
193
+ super(context, message, data)
194
+ end
195
+ end
196
+
141
197
  class NoSuchBucket < ServiceError
142
198
 
143
199
  # @param [Seahorse::Client::RequestContext] context
@@ -198,6 +254,16 @@ module Aws::S3
198
254
  end
199
255
  end
200
256
 
257
+ class UnsupportedMediaType < ServiceError
258
+
259
+ # @param [Seahorse::Client::RequestContext] context
260
+ # @param [String] message
261
+ # @param [Aws::S3::Types::UnsupportedMediaType] data
262
+ def initialize(context, message, data = Aws::EmptyStructure.new)
263
+ super(context, message, data)
264
+ end
265
+ end
266
+
201
267
  end
202
268
  end
203
269
 
@@ -196,7 +196,9 @@ module Aws
196
196
  end
197
197
 
198
198
  def single_request(opts)
199
- params = get_opts(opts[:params]).merge(response_target: opts[:destination])
199
+ resolve_temp_path(opts)
200
+ target = opts[:temp_path] || opts[:destination]
201
+ params = get_opts(opts[:params]).merge(response_target: target)
200
202
  params[:on_chunk_received] = single_part_progress(opts) if opts[:progress_callback]
201
203
  resp = @client.get_object(params)
202
204
  return resp unless opts[:on_checksum_validated]
@@ -154,7 +154,6 @@ module Aws
154
154
  Thread.current[:net_http_override_body_stream_chunk] = @http_chunk_size if @http_chunk_size
155
155
  update_progress(progress, p)
156
156
  resp = @client.upload_part(p)
157
- p[:body].close
158
157
  completed_part = { etag: resp.etag, part_number: p[:part_number] }
159
158
  apply_part_checksum(resp, completed_part)
160
159
  completed.push(completed_part)
@@ -162,6 +161,7 @@ module Aws
162
161
  abort_upload = true
163
162
  errors << e
164
163
  ensure
164
+ p[:body].close
165
165
  Thread.current[:net_http_override_body_stream_chunk] = nil if @http_chunk_size
166
166
  completion_queue << :done
167
167
  end