aws-sdk-s3 1.128.0 → 1.199.1

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +450 -1
  3. data/VERSION +1 -1
  4. data/lib/aws-sdk-s3/access_grants_credentials.rb +57 -0
  5. data/lib/aws-sdk-s3/access_grants_credentials_provider.rb +250 -0
  6. data/lib/aws-sdk-s3/bucket.rb +720 -128
  7. data/lib/aws-sdk-s3/bucket_acl.rb +18 -17
  8. data/lib/aws-sdk-s3/bucket_cors.rb +22 -21
  9. data/lib/aws-sdk-s3/bucket_lifecycle.rb +23 -18
  10. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +76 -19
  11. data/lib/aws-sdk-s3/bucket_logging.rb +21 -14
  12. data/lib/aws-sdk-s3/bucket_notification.rb +6 -6
  13. data/lib/aws-sdk-s3/bucket_policy.rb +65 -20
  14. data/lib/aws-sdk-s3/bucket_region_cache.rb +9 -5
  15. data/lib/aws-sdk-s3/bucket_request_payment.rb +15 -15
  16. data/lib/aws-sdk-s3/bucket_tagging.rb +19 -19
  17. data/lib/aws-sdk-s3/bucket_versioning.rb +41 -41
  18. data/lib/aws-sdk-s3/bucket_website.rb +19 -19
  19. data/lib/aws-sdk-s3/client.rb +9352 -3264
  20. data/lib/aws-sdk-s3/client_api.rb +697 -164
  21. data/lib/aws-sdk-s3/customizations/bucket.rb +1 -1
  22. data/lib/aws-sdk-s3/customizations/errors.rb +16 -3
  23. data/lib/aws-sdk-s3/customizations/object.rb +112 -56
  24. data/lib/aws-sdk-s3/customizations/object_summary.rb +5 -0
  25. data/lib/aws-sdk-s3/customizations/object_version.rb +13 -0
  26. data/lib/aws-sdk-s3/customizations.rb +26 -31
  27. data/lib/aws-sdk-s3/encryption/client.rb +2 -2
  28. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +2 -2
  29. data/lib/aws-sdk-s3/encryptionV2/client.rb +2 -2
  30. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +2 -2
  31. data/lib/aws-sdk-s3/endpoint_parameters.rb +54 -15
  32. data/lib/aws-sdk-s3/endpoint_provider.rb +439 -456
  33. data/lib/aws-sdk-s3/endpoints.rb +629 -1261
  34. data/lib/aws-sdk-s3/errors.rb +58 -0
  35. data/lib/aws-sdk-s3/express_credentials.rb +55 -0
  36. data/lib/aws-sdk-s3/express_credentials_provider.rb +59 -0
  37. data/lib/aws-sdk-s3/file_downloader.rb +156 -69
  38. data/lib/aws-sdk-s3/file_uploader.rb +4 -6
  39. data/lib/aws-sdk-s3/legacy_signer.rb +2 -1
  40. data/lib/aws-sdk-s3/multipart_download_error.rb +8 -0
  41. data/lib/aws-sdk-s3/multipart_file_uploader.rb +56 -69
  42. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +84 -91
  43. data/lib/aws-sdk-s3/multipart_upload.rb +179 -26
  44. data/lib/aws-sdk-s3/multipart_upload_error.rb +3 -4
  45. data/lib/aws-sdk-s3/multipart_upload_part.rb +201 -60
  46. data/lib/aws-sdk-s3/object.rb +2007 -281
  47. data/lib/aws-sdk-s3/object_acl.rb +43 -23
  48. data/lib/aws-sdk-s3/object_copier.rb +1 -1
  49. data/lib/aws-sdk-s3/object_multipart_copier.rb +44 -25
  50. data/lib/aws-sdk-s3/object_summary.rb +1735 -232
  51. data/lib/aws-sdk-s3/object_version.rb +394 -52
  52. data/lib/aws-sdk-s3/plugins/access_grants.rb +178 -0
  53. data/lib/aws-sdk-s3/plugins/checksum_algorithm.rb +31 -0
  54. data/lib/aws-sdk-s3/plugins/endpoints.rb +32 -208
  55. data/lib/aws-sdk-s3/plugins/express_session_auth.rb +88 -0
  56. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +55 -18
  57. data/lib/aws-sdk-s3/plugins/location_constraint.rb +3 -1
  58. data/lib/aws-sdk-s3/plugins/md5s.rb +10 -70
  59. data/lib/aws-sdk-s3/plugins/s3_signer.rb +7 -2
  60. data/lib/aws-sdk-s3/plugins/streaming_retry.rb +5 -7
  61. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -1
  62. data/lib/aws-sdk-s3/presigned_post.rb +52 -43
  63. data/lib/aws-sdk-s3/presigner.rb +9 -7
  64. data/lib/aws-sdk-s3/resource.rb +127 -22
  65. data/lib/aws-sdk-s3/transfer_manager.rb +252 -0
  66. data/lib/aws-sdk-s3/types.rb +8068 -1887
  67. data/lib/aws-sdk-s3.rb +35 -31
  68. data/sig/bucket.rbs +231 -0
  69. data/sig/bucket_acl.rbs +78 -0
  70. data/sig/bucket_cors.rbs +69 -0
  71. data/sig/bucket_lifecycle.rbs +88 -0
  72. data/sig/bucket_lifecycle_configuration.rbs +115 -0
  73. data/sig/bucket_logging.rbs +76 -0
  74. data/sig/bucket_notification.rbs +114 -0
  75. data/sig/bucket_policy.rbs +59 -0
  76. data/sig/bucket_request_payment.rbs +54 -0
  77. data/sig/bucket_tagging.rbs +65 -0
  78. data/sig/bucket_versioning.rbs +77 -0
  79. data/sig/bucket_website.rbs +93 -0
  80. data/sig/client.rbs +2584 -0
  81. data/sig/customizations/bucket.rbs +19 -0
  82. data/sig/customizations/object.rbs +38 -0
  83. data/sig/customizations/object_summary.rbs +35 -0
  84. data/sig/errors.rbs +44 -0
  85. data/sig/multipart_upload.rbs +120 -0
  86. data/sig/multipart_upload_part.rbs +109 -0
  87. data/sig/object.rbs +462 -0
  88. data/sig/object_acl.rbs +86 -0
  89. data/sig/object_summary.rbs +345 -0
  90. data/sig/object_version.rbs +143 -0
  91. data/sig/resource.rbs +141 -0
  92. data/sig/types.rbs +2866 -0
  93. data/sig/waiters.rbs +95 -0
  94. metadata +44 -12
  95. data/lib/aws-sdk-s3/plugins/skip_whole_multipart_get_checksums.rb +0 -31
@@ -9,32 +9,22 @@ module Aws
9
9
  class MultipartFileUploader
10
10
 
11
11
  MIN_PART_SIZE = 5 * 1024 * 1024 # 5MB
12
-
13
- FILE_TOO_SMALL = "unable to multipart upload files smaller than 5MB"
14
-
15
12
  MAX_PARTS = 10_000
16
-
17
- THREAD_COUNT = 10
18
-
19
- # @api private
20
- CREATE_OPTIONS = Set.new(
21
- Client.api.operation(:create_multipart_upload).input.shape.member_names
22
- )
23
-
24
- COMPLETE_OPTIONS = Set.new(
25
- Client.api.operation(:complete_multipart_upload).input.shape.member_names
26
- )
27
-
28
- # @api private
29
- UPLOAD_PART_OPTIONS = Set.new(
30
- Client.api.operation(:upload_part).input.shape.member_names
13
+ DEFAULT_THREAD_COUNT = 10
14
+ CREATE_OPTIONS = Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names)
15
+ COMPLETE_OPTIONS = Set.new(Client.api.operation(:complete_multipart_upload).input.shape.member_names)
16
+ UPLOAD_PART_OPTIONS = Set.new(Client.api.operation(:upload_part).input.shape.member_names)
17
+ CHECKSUM_KEYS = Set.new(
18
+ Client.api.operation(:upload_part).input.shape.members.map do |n, s|
19
+ n if s.location == 'header' && s.location_name.start_with?('x-amz-checksum-')
20
+ end.compact
31
21
  )
32
22
 
33
23
  # @option options [Client] :client
34
- # @option options [Integer] :thread_count (THREAD_COUNT)
24
+ # @option options [Integer] :thread_count (DEFAULT_THREAD_COUNT)
35
25
  def initialize(options = {})
36
26
  @client = options[:client] || Client.new
37
- @thread_count = options[:thread_count] || THREAD_COUNT
27
+ @thread_count = options[:thread_count] || DEFAULT_THREAD_COUNT
38
28
  end
39
29
 
40
30
  # @return [Client]
@@ -48,13 +38,11 @@ module Aws
48
38
  # It will be invoked with [bytes_read], [total_sizes]
49
39
  # @return [Seahorse::Client::Response] - the CompleteMultipartUploadResponse
50
40
  def upload(source, options = {})
51
- if File.size(source) < MIN_PART_SIZE
52
- raise ArgumentError, FILE_TOO_SMALL
53
- else
54
- upload_id = initiate_upload(options)
55
- parts = upload_parts(upload_id, source, options)
56
- complete_upload(upload_id, parts, options)
57
- end
41
+ raise ArgumentError, 'unable to multipart upload files smaller than 5MB' if File.size(source) < MIN_PART_SIZE
42
+
43
+ upload_id = initiate_upload(options)
44
+ parts = upload_parts(upload_id, source, options)
45
+ complete_upload(upload_id, parts, source, options)
58
46
  end
59
47
 
60
48
  private
@@ -63,18 +51,21 @@ module Aws
63
51
  @client.create_multipart_upload(create_opts(options)).upload_id
64
52
  end
65
53
 
66
- def complete_upload(upload_id, parts, options)
54
+ def complete_upload(upload_id, parts, source, options)
67
55
  @client.complete_multipart_upload(
68
56
  **complete_opts(options).merge(
69
57
  upload_id: upload_id,
70
- multipart_upload: { parts: parts }
58
+ multipart_upload: { parts: parts },
59
+ mpu_object_size: File.size(source)
71
60
  )
72
61
  )
62
+ rescue StandardError => e
63
+ abort_upload(upload_id, options, [e])
73
64
  end
74
65
 
75
66
  def upload_parts(upload_id, source, options)
76
- pending = PartList.new(compute_parts(upload_id, source, options))
77
67
  completed = PartList.new
68
+ pending = PartList.new(compute_parts(upload_id, source, options))
78
69
  errors = upload_in_threads(pending, completed, options)
79
70
  if errors.empty?
80
71
  completed.to_a.sort_by { |part| part[:part_number] }
@@ -84,18 +75,15 @@ module Aws
84
75
  end
85
76
 
86
77
  def abort_upload(upload_id, options, errors)
87
- @client.abort_multipart_upload(
88
- bucket: options[:bucket],
89
- key: options[:key],
90
- upload_id: upload_id
91
- )
92
- msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
78
+ @client.abort_multipart_upload(bucket: options[:bucket], key: options[:key], upload_id: upload_id)
79
+ msg = "multipart upload failed: #{errors.map(&:message).join('; ')}"
93
80
  raise MultipartUploadError.new(msg, errors)
94
- rescue MultipartUploadError => error
95
- raise error
96
- rescue => error
97
- msg = "failed to abort multipart upload: #{error.message}"
98
- raise MultipartUploadError.new(msg, errors + [error])
81
+ rescue MultipartUploadError => e
82
+ raise e
83
+ rescue StandardError => e
84
+ msg = "failed to abort multipart upload: #{e.message}. "\
85
+ "Multipart upload failed: #{errors.map(&:message).join('; ')}"
86
+ raise MultipartUploadError.new(msg, errors + [e])
99
87
  end
100
88
 
101
89
  def compute_parts(upload_id, source, options)
@@ -108,11 +96,7 @@ module Aws
108
96
  parts << upload_part_opts(options).merge(
109
97
  upload_id: upload_id,
110
98
  part_number: part_number,
111
- body: FilePart.new(
112
- source: source,
113
- offset: offset,
114
- size: part_size(size, default_part_size, offset)
115
- )
99
+ body: FilePart.new(source: source, offset: offset, size: part_size(size, default_part_size, offset))
116
100
  )
117
101
  part_number += 1
118
102
  offset += default_part_size
@@ -120,24 +104,34 @@ module Aws
120
104
  parts
121
105
  end
122
106
 
107
+ def checksum_key?(key)
108
+ CHECKSUM_KEYS.include?(key)
109
+ end
110
+
111
+ def has_checksum_key?(keys)
112
+ keys.any? { |key| checksum_key?(key) }
113
+ end
114
+
123
115
  def create_opts(options)
124
- CREATE_OPTIONS.inject({}) do |hash, key|
116
+ opts = { checksum_algorithm: Aws::Plugins::ChecksumAlgorithm::DEFAULT_CHECKSUM }
117
+ opts[:checksum_type] = 'FULL_OBJECT' if has_checksum_key?(options.keys)
118
+ CREATE_OPTIONS.each_with_object(opts) do |key, hash|
125
119
  hash[key] = options[key] if options.key?(key)
126
- hash
127
120
  end
128
121
  end
129
122
 
130
123
  def complete_opts(options)
131
- COMPLETE_OPTIONS.inject({}) do |hash, key|
124
+ opts = {}
125
+ opts[:checksum_type] = 'FULL_OBJECT' if has_checksum_key?(options.keys)
126
+ COMPLETE_OPTIONS.each_with_object(opts) do |key, hash|
132
127
  hash[key] = options[key] if options.key?(key)
133
- hash
134
128
  end
135
129
  end
136
130
 
137
131
  def upload_part_opts(options)
138
- UPLOAD_PART_OPTIONS.inject({}) do |hash, key|
139
- hash[key] = options[key] if options.key?(key)
140
- hash
132
+ UPLOAD_PART_OPTIONS.each_with_object({}) do |key, hash|
133
+ # don't pass through checksum calculations
134
+ hash[key] = options[key] if options.key?(key) && !checksum_key?(key)
141
135
  end
142
136
  end
143
137
 
@@ -146,10 +140,10 @@ module Aws
146
140
  if (callback = options[:progress_callback])
147
141
  progress = MultipartProgress.new(pending, callback)
148
142
  end
149
- @thread_count.times do
143
+ options.fetch(:thread_count, @thread_count).times do
150
144
  thread = Thread.new do
151
145
  begin
152
- while part = pending.shift
146
+ while (part = pending.shift)
153
147
  if progress
154
148
  part[:on_chunk_sent] =
155
149
  proc do |_chunk, bytes, _total|
@@ -158,24 +152,19 @@ module Aws
158
152
  end
159
153
  resp = @client.upload_part(part)
160
154
  part[:body].close
161
- completed_part = {etag: resp.etag, part_number: part[:part_number]}
162
-
163
- # get the requested checksum from the response
164
- if part[:checksum_algorithm]
165
- k = "checksum_#{part[:checksum_algorithm].downcase}".to_sym
166
- completed_part[k] = resp[k]
167
- end
168
-
155
+ completed_part = { etag: resp.etag, part_number: part[:part_number] }
156
+ algorithm = resp.context.params[:checksum_algorithm]
157
+ k = "checksum_#{algorithm.downcase}".to_sym
158
+ completed_part[k] = resp.send(k)
169
159
  completed.push(completed_part)
170
160
  end
171
161
  nil
172
- rescue => error
162
+ rescue StandardError => e
173
163
  # keep other threads from uploading other parts
174
164
  pending.clear!
175
- error
165
+ e
176
166
  end
177
167
  end
178
- thread.abort_on_exception = true
179
168
  threads << thread
180
169
  end
181
170
  threads.map(&:value).compact
@@ -195,7 +184,6 @@ module Aws
195
184
 
196
185
  # @api private
197
186
  class PartList
198
-
199
187
  def initialize(parts = [])
200
188
  @parts = parts
201
189
  @mutex = Mutex.new
@@ -224,7 +212,6 @@ module Aws
224
212
  def to_a
225
213
  @mutex.synchronize { @parts.dup }
226
214
  end
227
-
228
215
  end
229
216
 
230
217
  # @api private
@@ -243,4 +230,4 @@ module Aws
243
230
  end
244
231
  end
245
232
  end
246
- end
233
+ end
@@ -9,33 +9,19 @@ module Aws
9
9
  module S3
10
10
  # @api private
11
11
  class MultipartStreamUploader
12
- # api private
13
- PART_SIZE = 5 * 1024 * 1024 # 5MB
14
12
 
15
- # api private
16
- THREAD_COUNT = 10
17
-
18
- # api private
19
- TEMPFILE_PREIX = 'aws-sdk-s3-upload_stream'.freeze
20
-
21
- # @api private
22
- CREATE_OPTIONS =
23
- Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names)
24
-
25
- # @api private
26
- UPLOAD_PART_OPTIONS =
27
- Set.new(Client.api.operation(:upload_part).input.shape.member_names)
28
-
29
- # @api private
30
- COMPLETE_UPLOAD_OPTIONS =
31
- Set.new(Client.api.operation(:complete_multipart_upload).input.shape.member_names)
13
+ DEFAULT_PART_SIZE = 5 * 1024 * 1024 # 5MB
14
+ DEFAULT_THREAD_COUNT = 10
15
+ CREATE_OPTIONS = Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names)
16
+ UPLOAD_PART_OPTIONS = Set.new(Client.api.operation(:upload_part).input.shape.member_names)
17
+ COMPLETE_UPLOAD_OPTIONS = Set.new(Client.api.operation(:complete_multipart_upload).input.shape.member_names)
32
18
 
33
19
  # @option options [Client] :client
34
20
  def initialize(options = {})
35
21
  @client = options[:client] || Client.new
36
22
  @tempfile = options[:tempfile]
37
- @part_size = options[:part_size] || PART_SIZE
38
- @thread_count = options[:thread_count] || THREAD_COUNT
23
+ @part_size = options[:part_size] || DEFAULT_PART_SIZE
24
+ @thread_count = options[:thread_count] || DEFAULT_THREAD_COUNT
39
25
  end
40
26
 
41
27
  # @return [Client]
@@ -43,9 +29,10 @@ module Aws
43
29
 
44
30
  # @option options [required,String] :bucket
45
31
  # @option options [required,String] :key
32
+ # @option options [Integer] :thread_count (DEFAULT_THREAD_COUNT)
46
33
  # @return [Seahorse::Client::Response] - the CompleteMultipartUploadResponse
47
34
  def upload(options = {}, &block)
48
- Aws::Plugins::UserAgent.feature('s3-transfer') do
35
+ Aws::Plugins::UserAgent.metric('S3_TRANSFER') do
49
36
  upload_id = initiate_upload(options)
50
37
  parts = upload_parts(upload_id, options, &block)
51
38
  complete_upload(upload_id, parts, options)
@@ -60,11 +47,10 @@ module Aws
60
47
 
61
48
  def complete_upload(upload_id, parts, options)
62
49
  @client.complete_multipart_upload(
63
- **complete_opts(options).merge(
64
- upload_id: upload_id,
65
- multipart_upload: { parts: parts }
66
- )
50
+ **complete_opts(options).merge(upload_id: upload_id, multipart_upload: { parts: parts })
67
51
  )
52
+ rescue StandardError => e
53
+ abort_upload(upload_id, options, [e])
68
54
  end
69
55
 
70
56
  def upload_parts(upload_id, options, &block)
@@ -73,9 +59,11 @@ module Aws
73
59
  errors = begin
74
60
  IO.pipe do |read_pipe, write_pipe|
75
61
  threads = upload_in_threads(
76
- read_pipe, completed,
62
+ read_pipe,
63
+ completed,
77
64
  upload_part_opts(options).merge(upload_id: upload_id),
78
- thread_errors)
65
+ thread_errors
66
+ )
79
67
  begin
80
68
  block.call(write_pipe)
81
69
  ensure
@@ -84,61 +72,53 @@ module Aws
84
72
  end
85
73
  threads.map(&:value).compact
86
74
  end
87
- rescue => e
75
+ rescue StandardError => e
88
76
  thread_errors + [e]
89
77
  end
78
+ return ordered_parts(completed) if errors.empty?
90
79
 
91
- if errors.empty?
92
- Array.new(completed.size) { completed.pop }.sort_by { |part| part[:part_number] }
93
- else
94
- abort_upload(upload_id, options, errors)
95
- end
80
+ abort_upload(upload_id, options, errors)
96
81
  end
97
82
 
98
83
  def abort_upload(upload_id, options, errors)
99
- @client.abort_multipart_upload(
100
- bucket: options[:bucket],
101
- key: options[:key],
102
- upload_id: upload_id
103
- )
104
- msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
84
+ @client.abort_multipart_upload(bucket: options[:bucket], key: options[:key], upload_id: upload_id)
85
+ msg = "multipart upload failed: #{errors.map(&:message).join('; ')}"
105
86
  raise MultipartUploadError.new(msg, errors)
106
- rescue MultipartUploadError => error
107
- raise error
108
- rescue => error
109
- msg = "failed to abort multipart upload: #{error.message}"
110
- raise MultipartUploadError.new(msg, errors + [error])
87
+ rescue MultipartUploadError => e
88
+ raise e
89
+ rescue StandardError => e
90
+ msg = "failed to abort multipart upload: #{e.message}. "\
91
+ "Multipart upload failed: #{errors.map(&:message).join('; ')}"
92
+ raise MultipartUploadError.new(msg, errors + [e])
111
93
  end
112
94
 
113
95
  def create_opts(options)
114
- CREATE_OPTIONS.inject({}) do |hash, key|
96
+ CREATE_OPTIONS.each_with_object({}) do |key, hash|
115
97
  hash[key] = options[key] if options.key?(key)
116
- hash
117
98
  end
118
99
  end
119
100
 
120
101
  def upload_part_opts(options)
121
- UPLOAD_PART_OPTIONS.inject({}) do |hash, key|
102
+ UPLOAD_PART_OPTIONS.each_with_object({}) do |key, hash|
122
103
  hash[key] = options[key] if options.key?(key)
123
- hash
124
104
  end
125
105
  end
126
106
 
127
107
  def complete_opts(options)
128
- COMPLETE_UPLOAD_OPTIONS.inject({}) do |hash, key|
108
+ COMPLETE_UPLOAD_OPTIONS.each_with_object({}) do |key, hash|
129
109
  hash[key] = options[key] if options.key?(key)
130
- hash
131
110
  end
132
111
  end
133
112
 
134
113
  def read_to_part_body(read_pipe)
135
114
  return if read_pipe.closed?
136
- temp_io = @tempfile ? Tempfile.new(TEMPFILE_PREIX) : StringIO.new(String.new)
115
+
116
+ temp_io = @tempfile ? Tempfile.new('aws-sdk-s3-upload_stream') : StringIO.new(String.new)
137
117
  temp_io.binmode
138
118
  bytes_copied = IO.copy_stream(read_pipe, temp_io, @part_size)
139
119
  temp_io.rewind
140
- if bytes_copied == 0
141
- if Tempfile === temp_io
120
+ if bytes_copied.zero?
121
+ if temp_io.is_a?(Tempfile)
142
122
  temp_io.close
143
123
  temp_io.unlink
144
124
  end
@@ -151,51 +131,64 @@ module Aws
151
131
  def upload_in_threads(read_pipe, completed, options, thread_errors)
152
132
  mutex = Mutex.new
153
133
  part_number = 0
154
- @thread_count.times.map do
134
+ options.fetch(:thread_count, @thread_count).times.map do
155
135
  thread = Thread.new do
156
- begin
157
- loop do
158
- body, thread_part_number = mutex.synchronize do
159
- [read_to_part_body(read_pipe), part_number += 1]
160
- end
161
- break unless (body || thread_part_number == 1)
162
- begin
163
- part = options.merge(
164
- body: body,
165
- part_number: thread_part_number,
166
- )
167
- resp = @client.upload_part(part)
168
- completed_part = {etag: resp.etag, part_number: part[:part_number]}
169
-
170
- # get the requested checksum from the response
171
- if part[:checksum_algorithm]
172
- k = "checksum_#{part[:checksum_algorithm].downcase}".to_sym
173
- completed_part[k] = resp[k]
174
- end
175
- completed.push(completed_part)
176
- ensure
177
- if Tempfile === body
178
- body.close
179
- body.unlink
180
- elsif StringIO === body
181
- body.string.clear
182
- end
183
- end
136
+ loop do
137
+ body, thread_part_number = mutex.synchronize do
138
+ [read_to_part_body(read_pipe), part_number += 1]
184
139
  end
185
- nil
186
- rescue => error
187
- # keep other threads from uploading other parts
188
- mutex.synchronize do
189
- thread_errors.push(error)
190
- read_pipe.close_read unless read_pipe.closed?
140
+ break unless body || thread_part_number == 1
141
+
142
+ begin
143
+ part = options.merge(body: body, part_number: thread_part_number)
144
+ resp = @client.upload_part(part)
145
+ completed_part = create_completed_part(resp, part)
146
+ completed.push(completed_part)
147
+ ensure
148
+ clear_body(body)
191
149
  end
192
- error
193
150
  end
151
+ nil
152
+ rescue StandardError => e
153
+ # keep other threads from uploading other parts
154
+ mutex.synchronize do
155
+ thread_errors.push(e)
156
+ read_pipe.close_read unless read_pipe.closed?
157
+ end
158
+ e
194
159
  end
195
- thread.abort_on_exception = true
196
160
  thread
197
161
  end
198
162
  end
163
+
164
+ def create_completed_part(resp, part)
165
+ completed_part = { etag: resp.etag, part_number: part[:part_number] }
166
+ return completed_part unless part[:checksum_algorithm]
167
+
168
+ # get the requested checksum from the response
169
+ k = "checksum_#{part[:checksum_algorithm].downcase}".to_sym
170
+ completed_part[k] = resp[k]
171
+ completed_part
172
+ end
173
+
174
+ def ordered_parts(parts)
175
+ sorted = []
176
+ until parts.empty?
177
+ part = parts.pop
178
+ index = sorted.bsearch_index { |p| p[:part_number] >= part[:part_number] } || sorted.size
179
+ sorted.insert(index, part)
180
+ end
181
+ sorted
182
+ end
183
+
184
+ def clear_body(body)
185
+ if body.is_a?(Tempfile)
186
+ body.close
187
+ body.unlink
188
+ elsif body.is_a?(StringIO)
189
+ body.string.clear
190
+ end
191
+ end
199
192
  end
200
193
  end
201
194
  end