aws-sdk-s3 1.31.0 → 1.111.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +868 -0
  3. data/LICENSE.txt +202 -0
  4. data/VERSION +1 -0
  5. data/lib/aws-sdk-s3/arn/access_point_arn.rb +69 -0
  6. data/lib/aws-sdk-s3/arn/multi_region_access_point_arn.rb +68 -0
  7. data/lib/aws-sdk-s3/arn/object_lambda_arn.rb +69 -0
  8. data/lib/aws-sdk-s3/arn/outpost_access_point_arn.rb +74 -0
  9. data/lib/aws-sdk-s3/bucket.rb +298 -78
  10. data/lib/aws-sdk-s3/bucket_acl.rb +41 -15
  11. data/lib/aws-sdk-s3/bucket_cors.rb +51 -14
  12. data/lib/aws-sdk-s3/bucket_lifecycle.rb +38 -16
  13. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +40 -16
  14. data/lib/aws-sdk-s3/bucket_logging.rb +36 -15
  15. data/lib/aws-sdk-s3/bucket_notification.rb +44 -18
  16. data/lib/aws-sdk-s3/bucket_policy.rb +35 -13
  17. data/lib/aws-sdk-s3/bucket_region_cache.rb +2 -0
  18. data/lib/aws-sdk-s3/bucket_request_payment.rb +35 -12
  19. data/lib/aws-sdk-s3/bucket_tagging.rb +43 -14
  20. data/lib/aws-sdk-s3/bucket_versioning.rb +70 -12
  21. data/lib/aws-sdk-s3/bucket_website.rb +50 -17
  22. data/lib/aws-sdk-s3/client.rb +7851 -628
  23. data/lib/aws-sdk-s3/client_api.rb +436 -2
  24. data/lib/aws-sdk-s3/customizations/bucket.rb +59 -16
  25. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
  26. data/lib/aws-sdk-s3/customizations/object.rb +200 -62
  27. data/lib/aws-sdk-s3/customizations/object_summary.rb +5 -0
  28. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +2 -0
  29. data/lib/aws-sdk-s3/customizations.rb +4 -1
  30. data/lib/aws-sdk-s3/encryption/client.rb +23 -6
  31. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +71 -29
  32. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +43 -5
  33. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
  34. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +13 -2
  35. data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
  36. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +2 -0
  37. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +11 -3
  38. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +2 -0
  39. data/lib/aws-sdk-s3/encryption/key_provider.rb +2 -0
  40. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +34 -3
  41. data/lib/aws-sdk-s3/encryption/materials.rb +8 -6
  42. data/lib/aws-sdk-s3/encryption/utils.rb +25 -0
  43. data/lib/aws-sdk-s3/encryption.rb +4 -0
  44. data/lib/aws-sdk-s3/encryptionV2/client.rb +566 -0
  45. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +222 -0
  46. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +170 -0
  47. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +40 -0
  48. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +65 -0
  49. data/lib/aws-sdk-s3/encryptionV2/errors.rb +37 -0
  50. data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +58 -0
  51. data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +37 -0
  52. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +73 -0
  53. data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +31 -0
  54. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +169 -0
  55. data/lib/aws-sdk-s3/encryptionV2/materials.rb +60 -0
  56. data/lib/aws-sdk-s3/encryptionV2/utils.rb +103 -0
  57. data/lib/aws-sdk-s3/encryption_v2.rb +23 -0
  58. data/lib/aws-sdk-s3/errors.rb +123 -1
  59. data/lib/aws-sdk-s3/event_streams.rb +20 -7
  60. data/lib/aws-sdk-s3/file_downloader.rb +16 -9
  61. data/lib/aws-sdk-s3/file_part.rb +11 -6
  62. data/lib/aws-sdk-s3/file_uploader.rb +33 -14
  63. data/lib/aws-sdk-s3/legacy_signer.rb +17 -25
  64. data/lib/aws-sdk-s3/multipart_file_uploader.rb +53 -13
  65. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +20 -7
  66. data/lib/aws-sdk-s3/multipart_upload.rb +64 -29
  67. data/lib/aws-sdk-s3/multipart_upload_error.rb +2 -0
  68. data/lib/aws-sdk-s3/multipart_upload_part.rb +116 -42
  69. data/lib/aws-sdk-s3/object.rb +656 -156
  70. data/lib/aws-sdk-s3/object_acl.rb +65 -21
  71. data/lib/aws-sdk-s3/object_copier.rb +2 -0
  72. data/lib/aws-sdk-s3/object_multipart_copier.rb +2 -0
  73. data/lib/aws-sdk-s3/object_summary.rb +485 -143
  74. data/lib/aws-sdk-s3/object_version.rb +117 -62
  75. data/lib/aws-sdk-s3/plugins/accelerate.rb +38 -38
  76. data/lib/aws-sdk-s3/plugins/arn.rb +254 -0
  77. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +8 -8
  78. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +25 -3
  79. data/lib/aws-sdk-s3/plugins/dualstack.rb +38 -33
  80. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +4 -4
  81. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +3 -1
  82. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +11 -3
  83. data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +73 -0
  84. data/lib/aws-sdk-s3/plugins/location_constraint.rb +2 -0
  85. data/lib/aws-sdk-s3/plugins/md5s.rb +30 -28
  86. data/lib/aws-sdk-s3/plugins/object_lambda_endpoint.rb +25 -0
  87. data/lib/aws-sdk-s3/plugins/redirects.rb +2 -0
  88. data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
  89. data/lib/aws-sdk-s3/plugins/s3_signer.rb +89 -36
  90. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +3 -1
  91. data/lib/aws-sdk-s3/plugins/streaming_retry.rb +118 -0
  92. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -0
  93. data/lib/aws-sdk-s3/presigned_post.rb +72 -32
  94. data/lib/aws-sdk-s3/presigner.rb +168 -66
  95. data/lib/aws-sdk-s3/resource.rb +41 -5
  96. data/lib/aws-sdk-s3/types.rb +6758 -1027
  97. data/lib/aws-sdk-s3/waiters.rb +67 -1
  98. data/lib/aws-sdk-s3.rb +12 -6
  99. metadata +37 -13
@@ -1,21 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
- require 'base64'
3
4
 
4
5
  module Aws
5
6
  module S3
6
7
  module Plugins
8
+ # @api private
9
+ # This plugin is effectively deprecated in favor of modeled
10
+ # httpChecksumRequired traits.
7
11
  class Md5s < Seahorse::Client::Plugin
8
-
9
- # Amazon S3 requires these operations to have an MD5 checksum
10
- REQUIRED_OPERATIONS = [
11
- :delete_objects,
12
- :put_bucket_cors,
13
- :put_bucket_lifecycle,
14
- :put_bucket_policy,
15
- :put_bucket_tagging,
16
- :put_object_legal_hold,
17
- :put_object_lock_configuration,
18
- :put_object_retention
12
+ # These operations allow Content MD5 but are not required by
13
+ # httpChecksumRequired. This list should not grow.
14
+ OPTIONAL_OPERATIONS = [
15
+ :put_object,
16
+ :upload_part
19
17
  ]
20
18
 
21
19
  # @api private
@@ -25,7 +23,7 @@ module Aws
25
23
 
26
24
  def call(context)
27
25
  body = context.http_request.body
28
- if body.size > 0
26
+ if body.respond_to?(:size) && body.size > 0
29
27
  context.http_request.headers['Content-Md5'] ||= md5(body)
30
28
  end
31
29
  @handler.call(context)
@@ -37,18 +35,20 @@ module Aws
37
35
  # @return [String<MD5>]
38
36
  def md5(value)
39
37
  if (File === value || Tempfile === value) && !value.path.nil? && File.exist?(value.path)
40
- Base64.encode64(OpenSSL::Digest::MD5.file(value).digest).strip
38
+ OpenSSL::Digest::MD5.file(value).base64digest
41
39
  elsif value.respond_to?(:read)
42
40
  md5 = OpenSSL::Digest::MD5.new
43
41
  update_in_chunks(md5, value)
44
- Base64.encode64(md5.digest).strip
42
+ md5.base64digest
45
43
  else
46
- Base64.encode64(OpenSSL::Digest::MD5.digest(value)).strip
44
+ OpenSSL::Digest::MD5.digest(value).base64digest
47
45
  end
48
46
  end
49
47
 
50
48
  def update_in_chunks(digest, io)
51
- while chunk = io.read(CHUNK_SIZE, buffer ||= "")
49
+ loop do
50
+ chunk = io.read(CHUNK_SIZE)
51
+ break unless chunk
52
52
  digest.update(chunk)
53
53
  end
54
54
  io.rewind
@@ -60,20 +60,22 @@ module Aws
60
60
  default: true,
61
61
  doc_type: 'Boolean',
62
62
  docstring: <<-DOCS)
63
- When `true` a MD5 checksum will be computed for every request that
64
- sends a body. When `false`, MD5 checksums will only be computed
65
- for operations that require them. Checksum errors returned by Amazon
66
- S3 are automatically retried up to `:retry_limit` times.
63
+ When `true` a MD5 checksum will be computed and sent in the Content Md5
64
+ header for :put_object and :upload_part. When `false`, MD5 checksums
65
+ will not be computed for these operations. Checksums are still computed
66
+ for operations requiring them. Checksum errors returned by Amazon S3 are
67
+ automatically retried up to `:retry_limit` times.
67
68
  DOCS
68
69
 
69
70
  def add_handlers(handlers, config)
70
- # priority set low to ensure md5 is computed AFTER the request is
71
- # built but before it is signed
72
- handlers.add(Handler, {
73
- priority: 10,
74
- step: :build,
75
- operations: config.compute_checksums ? nil : REQUIRED_OPERATIONS,
76
- })
71
+ if config.compute_checksums
72
+ # priority set low to ensure md5 is computed AFTER the request is
73
+ # built but before it is signed
74
+ handlers.add(
75
+ Handler,
76
+ priority: 10, step: :build, operations: OPTIONAL_OPERATIONS
77
+ )
78
+ end
77
79
  end
78
80
 
79
81
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module S3
5
+ module Plugins
6
+ # WriteGetObjectResponse is called from Lambda after a data transform.
7
+ # If there is no custom endpoint, we change the endpoint from s3 to
8
+ # s3-object-lambda just for this operation.
9
+ class ObjectLambdaEndpoint < Seahorse::Client::Plugin
10
+ class Handler < Seahorse::Client::Handler
11
+ def call(context)
12
+ if context.config.regional_endpoint
13
+ host = context.http_request.endpoint.host
14
+ host = host.sub('s3.', 's3-object-lambda.')
15
+ context.http_request.endpoint.host = host
16
+ end
17
+ @handler.call(context)
18
+ end
19
+ end
20
+
21
+ handler(Handler, operations: [:write_get_object_response])
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Plugins
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Plugins
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'aws-sigv4'
2
4
 
3
5
  module Aws
@@ -6,18 +8,23 @@ module Aws
6
8
  # This plugin is an implementation detail and may be modified.
7
9
  # @api private
8
10
  class S3Signer < Seahorse::Client::Plugin
9
-
10
11
  option(:signature_version, 'v4')
11
12
 
12
13
  option(:sigv4_signer) do |cfg|
13
14
  S3Signer.build_v4_signer(
15
+ service: 's3',
14
16
  region: cfg.sigv4_region,
15
17
  credentials: cfg.credentials
16
18
  )
17
19
  end
18
20
 
19
21
  option(:sigv4_region) do |cfg|
20
- Aws::Partitions::EndpointProvider.signing_region(cfg.region, 's3')
22
+ # S3 removes core's signature_v4 plugin that checks for this
23
+ raise Aws::Errors::MissingRegionError if cfg.region.nil?
24
+
25
+ Aws::Partitions::EndpointProvider.signing_region(
26
+ cfg.region, 's3'
27
+ )
21
28
  end
22
29
 
23
30
  def add_handlers(handlers, cfg)
@@ -48,7 +55,6 @@ module Aws
48
55
  end
49
56
 
50
57
  class V4Handler < Seahorse::Client::Handler
51
-
52
58
  def call(context)
53
59
  Aws::Plugins::SignatureV4.apply_signature(
54
60
  context: context,
@@ -62,25 +68,42 @@ module Aws
62
68
  def sigv4_signer(context)
63
69
  # If the client was configured with the wrong region,
64
70
  # we have to build a new signer.
65
- if
66
- context[:cached_sigv4_region] &&
67
- context[:cached_sigv4_region] != context.config.sigv4_signer.region
68
- then
71
+ if context[:cached_sigv4_region] &&
72
+ context[:cached_sigv4_region] != context.config.sigv4_signer.region
69
73
  S3Signer.build_v4_signer(
74
+ service: 's3',
70
75
  region: context[:cached_sigv4_region],
71
76
  credentials: context.config.credentials
72
77
  )
78
+ elsif (arn = context.metadata[:s3_arn])
79
+ if arn[:arn].is_a?(MultiRegionAccessPointARN)
80
+ signing_region = '*'
81
+ signing_algorithm = :sigv4a
82
+ else
83
+ signing_region = arn[:resolved_region]
84
+ signing_algorithm = :sigv4
85
+ end
86
+ S3Signer.build_v4_signer(
87
+ service: arn[:arn].service,
88
+ signing_algorithm: signing_algorithm,
89
+ region: signing_region,
90
+ credentials: context.config.credentials
91
+ )
92
+ elsif context.operation.name == 'WriteGetObjectResponse'
93
+ S3Signer.build_v4_signer(
94
+ service: 's3-object-lambda',
95
+ region: context.config.sigv4_region,
96
+ credentials: context.config.credentials
97
+ )
73
98
  else
74
99
  context.config.sigv4_signer
75
100
  end
76
101
  end
77
-
78
102
  end
79
103
 
80
104
  # This handler will update the http endpoint when the bucket region
81
105
  # is known/cached.
82
106
  class CachedBucketRegionHandler < Seahorse::Client::Handler
83
-
84
107
  def call(context)
85
108
  bucket = context.params[:bucket]
86
109
  check_for_cached_region(context, bucket) if bucket
@@ -92,11 +115,12 @@ module Aws
92
115
  def check_for_cached_region(context, bucket)
93
116
  cached_region = S3::BUCKET_REGIONS[bucket]
94
117
  if cached_region && cached_region != context.config.region
95
- context.http_request.endpoint.host = S3Signer.new_hostname(context, cached_region)
118
+ context.http_request.endpoint.host = S3Signer.new_hostname(
119
+ context, cached_region
120
+ )
96
121
  context[:cached_sigv4_region] = cached_region
97
122
  end
98
123
  end
99
-
100
124
  end
101
125
 
102
126
  # This handler detects when a request fails because of a mismatched bucket
@@ -104,7 +128,6 @@ module Aws
104
128
  # region, then finally a version 4 signed request against the correct
105
129
  # regional endpoint.
106
130
  class BucketRegionErrorHandler < Seahorse::Client::Handler
107
-
108
131
  def call(context)
109
132
  response = @handler.call(context)
110
133
  handle_region_errors(response)
@@ -113,7 +136,9 @@ module Aws
113
136
  private
114
137
 
115
138
  def handle_region_errors(response)
116
- if wrong_sigv4_region?(response) && !fips_region?(response)
139
+ if wrong_sigv4_region?(response) &&
140
+ !fips_region?(response) &&
141
+ !custom_endpoint?(response)
117
142
  get_region_and_retry(response.context)
118
143
  else
119
144
  response
@@ -137,21 +162,38 @@ module Aws
137
162
  resp.context.http_request.endpoint.host.include?('fips')
138
163
  end
139
164
 
165
+ def custom_endpoint?(resp)
166
+ resolved_suffix = Aws::Partitions::EndpointProvider.dns_suffix_for(
167
+ resp.context.config.region,
168
+ 's3',
169
+ {
170
+ dualstack: resp.context[:use_dualstack_endpoint],
171
+ fips: resp.context.config.use_fips_endpoint
172
+ }
173
+ )
174
+ !resp.context.http_request.endpoint.hostname.include?(resolved_suffix)
175
+ end
176
+
140
177
  def wrong_sigv4_region?(resp)
141
178
  resp.context.http_response.status_code == 400 &&
142
- (
143
- resp.context.http_response.headers['x-amz-bucket-region'] ||
144
- resp.context.http_response.body_contents.match(/<Region>.+?<\/Region>/)
145
- )
179
+ (resp.context.http_response.headers['x-amz-bucket-region'] ||
180
+ resp.context.http_response.body_contents.match(/<Region>.+?<\/Region>/))
146
181
  end
147
182
 
148
183
  def resign_with_new_region(context, actual_region)
149
184
  context.http_response.body.truncate(0)
150
- context.http_request.endpoint.host = S3Signer.new_hostname(context, actual_region)
185
+ context.http_request.endpoint.host = S3Signer.new_hostname(
186
+ context, actual_region
187
+ )
151
188
  context.metadata[:redirect_region] = actual_region
189
+ # if it's an ARN, use the service in the ARN
190
+ if (arn = context.metadata[:s3_arn])
191
+ service = arn[:arn].service
192
+ end
152
193
  Aws::Plugins::SignatureV4.apply_signature(
153
194
  context: context,
154
195
  signer: S3Signer.build_v4_signer(
196
+ service: service || 's3',
155
197
  region: actual_region,
156
198
  credentials: context.config.credentials
157
199
  )
@@ -160,7 +202,7 @@ module Aws
160
202
 
161
203
  def region_from_body(body)
162
204
  region = body.match(/<Region>(.+?)<\/Region>/)[1]
163
- if region.nil? || region == ""
205
+ if region.nil? || region == ''
164
206
  raise "couldn't get region from body: #{body}"
165
207
  else
166
208
  region
@@ -168,44 +210,55 @@ module Aws
168
210
  end
169
211
 
170
212
  def log_warning(context, actual_region)
171
- msg = "S3 client configured for #{context.config.region.inspect} " +
172
- "but the bucket #{context.params[:bucket].inspect} is in " +
173
- "#{actual_region.inspect}; Please configure the proper region " +
174
- "to avoid multiple unnecessary redirects and signing attempts\n"
175
- if logger = context.config.logger
213
+ msg = "S3 client configured for #{context.config.region.inspect} " \
214
+ "but the bucket #{context.params[:bucket].inspect} is in " \
215
+ "#{actual_region.inspect}; Please configure the proper region " \
216
+ "to avoid multiple unnecessary redirects and signing attempts\n"
217
+ if (logger = context.config.logger)
176
218
  logger.warn(msg)
177
219
  else
178
220
  warn(msg)
179
221
  end
180
222
  end
181
-
182
223
  end
183
224
 
184
225
  class << self
185
-
186
226
  # @option options [required, String] :region
187
227
  # @option options [required, #credentials] :credentials
188
228
  # @api private
189
229
  def build_v4_signer(options = {})
190
- Aws::Sigv4::Signer.new({
191
- service: 's3',
230
+ Aws::Sigv4::Signer.new(
231
+ service: options[:service],
192
232
  region: options[:region],
193
233
  credentials_provider: options[:credentials],
234
+ signing_algorithm: options.fetch(:signing_algorithm, :sigv4),
194
235
  uri_escape_path: false,
195
- unsigned_headers: ['content-length', 'x-amzn-trace-id'],
196
- })
236
+ unsigned_headers: ['content-length', 'x-amzn-trace-id']
237
+ )
197
238
  end
198
239
 
240
+ # Check to see if the bucket is actually an ARN
241
+ # Otherwise it will retry with the ARN as the bucket name.
199
242
  def new_hostname(context, region)
200
- bucket = context.params[:bucket]
201
- if region == 'us-east-1'
202
- "#{bucket}.s3.amazonaws.com"
243
+ uri = URI.parse(
244
+ Aws::Partitions::EndpointProvider.resolve(
245
+ region, 's3', 'regional',
246
+ {
247
+ dualstack: context[:use_dualstack_endpoint],
248
+ fips: context.config.use_fips_endpoint
249
+ }
250
+ )
251
+ )
252
+
253
+ if (arn = context.metadata[:s3_arn])
254
+ # Retry with the response region and not the ARN resolved one
255
+ ARN.resolve_url!(
256
+ uri, arn[:arn], region, arn[:fips], arn[:dualstack]
257
+ ).host
203
258
  else
204
- endpoint = Aws::Partitions::EndpointProvider.resolve(region, 's3')
205
- bucket + '.' + URI.parse(endpoint).host
259
+ "#{context.params[:bucket]}.#{uri.host}"
206
260
  end
207
261
  end
208
-
209
262
  end
210
263
  end
211
264
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
  require 'openssl'
3
5
 
@@ -18,7 +20,7 @@ This should only be disabled for local testing.
18
20
  class Handler < Seahorse::Client::Handler
19
21
 
20
22
  def call(context)
21
- compute_key_md5(context)
23
+ compute_key_md5(context) if context.params.is_a?(Hash)
22
24
  @handler.call(context)
23
25
  end
24
26
 
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Aws
6
+ module S3
7
+ module Plugins
8
+
9
+ # A wrapper around BlockIO that adds no-ops for truncate and rewind
10
+ # @api private
11
+ class RetryableBlockIO
12
+ extend Forwardable
13
+ def_delegators :@block_io, :write, :read, :size
14
+
15
+ def initialize(block_io)
16
+ @block_io = block_io
17
+ end
18
+
19
+ def truncate(_integer); end
20
+
21
+ def rewind; end
22
+ end
23
+
24
+ # A wrapper around ManagedFile that adds no-ops for truncate and rewind
25
+ # @api private
26
+ class RetryableManagedFile
27
+ extend Forwardable
28
+ def_delegators :@file, :write, :read, :size, :open?, :close
29
+
30
+ def initialize(managed_file)
31
+ @file = managed_file
32
+ end
33
+
34
+ def truncate(_integer); end
35
+
36
+ def rewind; end
37
+ end
38
+
39
+ # This handler works with the ResponseTarget plugin to provide smart
40
+ # retries of S3 streaming operations that support the range parameter
41
+ # (currently only: get_object). When a 200 OK with a TruncatedBodyError
42
+ # is received this handler will add a range header that excludes the
43
+ # data that has already been processed (written to file or sent to
44
+ # the target Proc).
45
+ # It is important to not write data to the custom target in the case of
46
+ # a non-success response. We do not want to write an XML error
47
+ # message to someone's file or pass it to a user's Proc.
48
+ # @api private
49
+ class StreamingRetry < Seahorse::Client::Plugin
50
+
51
+ class Handler < Seahorse::Client::Handler
52
+
53
+ def call(context)
54
+ target = context.params[:response_target] || context[:response_target]
55
+
56
+ # retry is only supported when range is NOT set on the initial request
57
+ if supported_target?(target) && !context.params[:range]
58
+ add_event_listeners(context, target)
59
+ end
60
+ @handler.call(context)
61
+ end
62
+
63
+ private
64
+
65
+ def add_event_listeners(context, target)
66
+ context.http_response.on_headers(200..299) do
67
+ case context.http_response.body
68
+ when Seahorse::Client::BlockIO then
69
+ context.http_response.body = RetryableBlockIO.new(context.http_response.body)
70
+ when Seahorse::Client::ManagedFile then
71
+ context.http_response.body = RetryableManagedFile.new(context.http_response.body)
72
+ end
73
+ end
74
+
75
+ context.http_response.on_headers(400..599) do
76
+ context.http_response.body = StringIO.new # something to write the error to
77
+ end
78
+
79
+ context.http_response.on_success(200..299) do
80
+ body = context.http_response.body
81
+ if body.is_a?(RetryableManagedFile) && body.open?
82
+ body.close
83
+ end
84
+ end
85
+
86
+ context.http_response.on_error do |error|
87
+ if retryable_body?(context) && truncated_body?(error)
88
+ context.http_request.headers[:range] = "bytes=#{context.http_response.body.size}-"
89
+ end
90
+ end
91
+ end
92
+
93
+ def truncated_body?(error)
94
+ error.is_a?(Seahorse::Client::NetworkingError) &&
95
+ error.original_error.is_a?(
96
+ Seahorse::Client::NetHttp::Handler::TruncatedBodyError
97
+ )
98
+ end
99
+
100
+ def retryable_body?(context)
101
+ context.http_response.body.is_a?(RetryableBlockIO) ||
102
+ context.http_response.body.is_a?(RetryableManagedFile)
103
+ end
104
+
105
+ def supported_target?(target)
106
+ case target
107
+ when Proc, String, Pathname then true
108
+ else false
109
+ end
110
+ end
111
+ end
112
+
113
+ handler(Handler, step: :sign, operations: [:get_object], priority: 10)
114
+
115
+ end
116
+ end
117
+ end
118
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
  require 'cgi'
3
5