aws-sdk-s3 1.79.1 → 1.212.0

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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1548 -0
  3. data/LICENSE.txt +202 -0
  4. data/VERSION +1 -0
  5. data/lib/aws-sdk-s3/access_grants_credentials.rb +57 -0
  6. data/lib/aws-sdk-s3/access_grants_credentials_provider.rb +250 -0
  7. data/lib/aws-sdk-s3/bucket.rb +900 -98
  8. data/lib/aws-sdk-s3/bucket_acl.rb +44 -10
  9. data/lib/aws-sdk-s3/bucket_cors.rb +51 -11
  10. data/lib/aws-sdk-s3/bucket_lifecycle.rb +53 -8
  11. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +107 -9
  12. data/lib/aws-sdk-s3/bucket_logging.rb +43 -6
  13. data/lib/aws-sdk-s3/bucket_notification.rb +32 -9
  14. data/lib/aws-sdk-s3/bucket_policy.rb +90 -6
  15. data/lib/aws-sdk-s3/bucket_region_cache.rb +9 -5
  16. data/lib/aws-sdk-s3/bucket_request_payment.rb +38 -8
  17. data/lib/aws-sdk-s3/bucket_tagging.rb +46 -7
  18. data/lib/aws-sdk-s3/bucket_versioning.rb +127 -9
  19. data/lib/aws-sdk-s3/bucket_website.rb +46 -7
  20. data/lib/aws-sdk-s3/client.rb +13729 -3146
  21. data/lib/aws-sdk-s3/client_api.rb +1604 -277
  22. data/lib/aws-sdk-s3/customizations/bucket.rb +31 -47
  23. data/lib/aws-sdk-s3/customizations/errors.rb +40 -0
  24. data/lib/aws-sdk-s3/customizations/object.rb +253 -82
  25. data/lib/aws-sdk-s3/customizations/object_summary.rb +5 -0
  26. data/lib/aws-sdk-s3/customizations/object_version.rb +13 -0
  27. data/lib/aws-sdk-s3/customizations/types/permanent_redirect.rb +26 -0
  28. data/lib/aws-sdk-s3/customizations.rb +28 -29
  29. data/lib/aws-sdk-s3/default_executor.rb +103 -0
  30. data/lib/aws-sdk-s3/encryption/client.rb +9 -5
  31. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +0 -4
  32. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +2 -0
  33. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +2 -0
  34. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +15 -9
  35. data/lib/aws-sdk-s3/encryptionV2/client.rb +105 -26
  36. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +7 -165
  37. data/lib/aws-sdk-s3/encryptionV2/decryption.rb +205 -0
  38. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +20 -3
  39. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +2 -4
  40. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +2 -0
  41. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +18 -6
  42. data/lib/aws-sdk-s3/encryptionV2/utils.rb +5 -0
  43. data/lib/aws-sdk-s3/encryptionV3/client.rb +885 -0
  44. data/lib/aws-sdk-s3/encryptionV3/decrypt_handler.rb +98 -0
  45. data/lib/aws-sdk-s3/encryptionV3/decryption.rb +244 -0
  46. data/lib/aws-sdk-s3/encryptionV3/default_cipher_provider.rb +159 -0
  47. data/lib/aws-sdk-s3/encryptionV3/default_key_provider.rb +35 -0
  48. data/lib/aws-sdk-s3/encryptionV3/encrypt_handler.rb +98 -0
  49. data/lib/aws-sdk-s3/encryptionV3/errors.rb +47 -0
  50. data/lib/aws-sdk-s3/encryptionV3/io_auth_decrypter.rb +60 -0
  51. data/lib/aws-sdk-s3/encryptionV3/io_decrypter.rb +35 -0
  52. data/lib/aws-sdk-s3/encryptionV3/io_encrypter.rb +84 -0
  53. data/lib/aws-sdk-s3/encryptionV3/key_provider.rb +28 -0
  54. data/lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb +159 -0
  55. data/lib/aws-sdk-s3/encryptionV3/materials.rb +58 -0
  56. data/lib/aws-sdk-s3/encryptionV3/utils.rb +321 -0
  57. data/lib/aws-sdk-s3/encryption_v2.rb +1 -0
  58. data/lib/aws-sdk-s3/encryption_v3.rb +24 -0
  59. data/lib/aws-sdk-s3/endpoint_parameters.rb +181 -0
  60. data/lib/aws-sdk-s3/endpoint_provider.rb +889 -0
  61. data/lib/aws-sdk-s3/endpoints.rb +1544 -0
  62. data/lib/aws-sdk-s3/errors.rb +80 -1
  63. data/lib/aws-sdk-s3/event_streams.rb +1 -1
  64. data/lib/aws-sdk-s3/express_credentials.rb +55 -0
  65. data/lib/aws-sdk-s3/express_credentials_provider.rb +59 -0
  66. data/lib/aws-sdk-s3/file_downloader.rb +258 -82
  67. data/lib/aws-sdk-s3/file_uploader.rb +25 -14
  68. data/lib/aws-sdk-s3/legacy_signer.rb +17 -26
  69. data/lib/aws-sdk-s3/multipart_download_error.rb +8 -0
  70. data/lib/aws-sdk-s3/multipart_file_uploader.rb +111 -86
  71. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +110 -92
  72. data/lib/aws-sdk-s3/multipart_upload.rb +304 -14
  73. data/lib/aws-sdk-s3/multipart_upload_error.rb +3 -4
  74. data/lib/aws-sdk-s3/multipart_upload_part.rb +344 -20
  75. data/lib/aws-sdk-s3/object.rb +2457 -225
  76. data/lib/aws-sdk-s3/object_acl.rb +76 -15
  77. data/lib/aws-sdk-s3/object_copier.rb +7 -5
  78. data/lib/aws-sdk-s3/object_multipart_copier.rb +48 -23
  79. data/lib/aws-sdk-s3/object_summary.rb +2033 -169
  80. data/lib/aws-sdk-s3/object_version.rb +470 -53
  81. data/lib/aws-sdk-s3/plugins/accelerate.rb +1 -39
  82. data/lib/aws-sdk-s3/plugins/access_grants.rb +178 -0
  83. data/lib/aws-sdk-s3/plugins/arn.rb +70 -0
  84. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +3 -41
  85. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +1 -6
  86. data/lib/aws-sdk-s3/plugins/checksum_algorithm.rb +44 -0
  87. data/lib/aws-sdk-s3/plugins/dualstack.rb +2 -49
  88. data/lib/aws-sdk-s3/plugins/endpoints.rb +86 -0
  89. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +3 -1
  90. data/lib/aws-sdk-s3/plugins/express_session_auth.rb +88 -0
  91. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +1 -1
  92. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +87 -26
  93. data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +8 -26
  94. data/lib/aws-sdk-s3/plugins/location_constraint.rb +3 -1
  95. data/lib/aws-sdk-s3/plugins/md5s.rb +10 -68
  96. data/lib/aws-sdk-s3/plugins/s3_signer.rb +48 -88
  97. data/lib/aws-sdk-s3/plugins/streaming_retry.rb +28 -9
  98. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -1
  99. data/lib/aws-sdk-s3/presigned_post.rb +99 -78
  100. data/lib/aws-sdk-s3/presigner.rb +50 -42
  101. data/lib/aws-sdk-s3/resource.rb +144 -15
  102. data/lib/aws-sdk-s3/transfer_manager.rb +321 -0
  103. data/lib/aws-sdk-s3/types.rb +12223 -4723
  104. data/lib/aws-sdk-s3/waiters.rb +1 -1
  105. data/lib/aws-sdk-s3.rb +37 -28
  106. data/sig/bucket.rbs +231 -0
  107. data/sig/bucket_acl.rbs +78 -0
  108. data/sig/bucket_cors.rbs +69 -0
  109. data/sig/bucket_lifecycle.rbs +88 -0
  110. data/sig/bucket_lifecycle_configuration.rbs +115 -0
  111. data/sig/bucket_logging.rbs +76 -0
  112. data/sig/bucket_notification.rbs +114 -0
  113. data/sig/bucket_policy.rbs +59 -0
  114. data/sig/bucket_request_payment.rbs +54 -0
  115. data/sig/bucket_tagging.rbs +65 -0
  116. data/sig/bucket_versioning.rbs +77 -0
  117. data/sig/bucket_website.rbs +93 -0
  118. data/sig/client.rbs +2612 -0
  119. data/sig/customizations/bucket.rbs +19 -0
  120. data/sig/customizations/object.rbs +38 -0
  121. data/sig/customizations/object_summary.rbs +35 -0
  122. data/sig/errors.rbs +44 -0
  123. data/sig/multipart_upload.rbs +120 -0
  124. data/sig/multipart_upload_part.rbs +109 -0
  125. data/sig/object.rbs +464 -0
  126. data/sig/object_acl.rbs +86 -0
  127. data/sig/object_summary.rbs +347 -0
  128. data/sig/object_version.rbs +143 -0
  129. data/sig/resource.rbs +141 -0
  130. data/sig/types.rbs +2899 -0
  131. data/sig/waiters.rbs +95 -0
  132. metadata +74 -16
  133. data/lib/aws-sdk-s3/plugins/bucket_arn.rb +0 -212
@@ -11,7 +11,7 @@ module Aws
11
11
  @handler.call(context).on(200) do |response|
12
12
  response.data = S3::Types::GetBucketLocationOutput.new
13
13
  xml = context.http_response.body_contents
14
- matches = xml.match(/>(.+?)<\/LocationConstraint>/)
14
+ matches = xml.match(/<LocationConstraint.*?>(.+?)<\/LocationConstraint>/)
15
15
  response.data[:location_constraint] = matches ? matches[1] : ''
16
16
  end
17
17
  end
@@ -3,52 +3,113 @@
3
3
  module Aws
4
4
  module S3
5
5
  module Plugins
6
-
7
6
  # A handful of Amazon S3 operations will respond with a 200 status
8
7
  # code but will send an error in the response body. This plugin
9
8
  # injects a handler that will parse 200 response bodies for potential
10
9
  # errors, allowing them to be retried.
11
10
  # @api private
12
11
  class Http200Errors < Seahorse::Client::Plugin
13
-
14
12
  class Handler < Seahorse::Client::Handler
13
+ # A regular expression to match error codes in the response body
14
+ CODE_PATTERN = %r{<Code>(.+?)</Code>}.freeze
15
+ private_constant :CODE_PATTERN
16
+
17
+ # A list of encodings we force into UTF-8
18
+ ENCODINGS_TO_FIX = [Encoding::US_ASCII, Encoding::ASCII_8BIT].freeze
19
+ private_constant :ENCODINGS_TO_FIX
20
+
21
+ # A regular expression to match detect errors in the response body
22
+ ERROR_PATTERN = /<\?xml\s[^>]*\?>\s*<Error>/.freeze
23
+ private_constant :ERROR_PATTERN
24
+
25
+ # A regular expression to match an error message in the response body
26
+ MESSAGE_PATTERN = %r{<Message>(.+?)</Message>}.freeze
27
+ private_constant :MESSAGE_PATTERN
15
28
 
16
29
  def call(context)
17
30
  @handler.call(context).on(200) do |response|
18
- if error = check_for_error(context)
19
- context.http_response.status_code = 500
20
- response.data = nil
21
- response.error = error
22
- end
31
+ return response if streaming_output?(context.operation.output)
32
+
33
+ error = check_for_error(context)
34
+ return response unless error
35
+
36
+ context.http_response.status_code = 500
37
+ response.data = nil
38
+ response.error = error
23
39
  end
24
40
  end
25
41
 
42
+ private
43
+
44
+ def build_error(context, code, message)
45
+ S3::Errors.error_class(code).new(context, message)
46
+ end
47
+
26
48
  def check_for_error(context)
27
- xml = context.http_response.body_contents
28
- if xml.match(/<Error>/)
29
- error_code = xml.match(/<Code>(.+?)<\/Code>/)[1]
30
- error_message = xml.match(/<Message>(.+?)<\/Message>/)[1]
31
- S3::Errors.error_class(error_code).new(context, error_message)
32
- elsif !xml.match(/<\w/) # Must have the start of an XML Tag
33
- # Other incomplete xml bodies will result in XML ParsingError
49
+ xml = normalize_encoding(context.http_response.body_contents)
50
+
51
+ if xml.match?(ERROR_PATTERN)
52
+ error_code = xml.match(CODE_PATTERN)[1]
53
+ error_message = xml.match(MESSAGE_PATTERN)[1]
54
+ build_error(context, error_code, error_message)
55
+ elsif incomplete_xml_body?(xml, context.operation.output)
34
56
  Seahorse::Client::NetworkingError.new(
35
- S3::Errors
36
- .error_class('InternalError')
37
- .new(context, 'Empty or incomplete response body')
57
+ build_error(context, 'InternalError', 'Empty or incomplete response body')
38
58
  )
39
59
  end
40
60
  end
61
+
62
+ # Must have a member in the body and have the start of an XML Tag.
63
+ # Other incomplete xml bodies will result in an XML ParsingError.
64
+ def incomplete_xml_body?(xml, output)
65
+ members_in_body?(output) && !xml.match(/<\w/)
66
+ end
67
+
68
+ # Checks if the output shape is a structure shape and has members that
69
+ # are in the body for the case of a payload and a normal structure. A
70
+ # non-structure shape will not have members in the body. In the case
71
+ # of a string or blob, the body contents would have been checked first
72
+ # before this method is called in incomplete_xml_body?.
73
+ def members_in_body?(output)
74
+ shape = resolve_shape(output)
75
+
76
+ if structure_shape?(shape)
77
+ shape.members.any? { |_, k| k.location.nil? }
78
+ else
79
+ false
80
+ end
81
+ end
82
+
83
+ # Fixes encoding issues when S3 returns UTF-8 content with missing charset in Content-Type header or omits
84
+ # Content-Type header entirely. Net::HTTP defaults to US-ASCII or ASCII-8BIT when charset is unspecified.
85
+ def normalize_encoding(xml)
86
+ return xml unless xml.is_a?(String) && ENCODINGS_TO_FIX.include?(xml.encoding)
87
+
88
+ xml.force_encoding('UTF-8')
89
+ end
90
+
91
+ def resolve_shape(output)
92
+ return output.shape unless output[:payload_member]
93
+
94
+ output[:payload_member].shape
95
+ end
96
+
97
+ # Streaming outputs are not subject to 200 errors.
98
+ def streaming_output?(output)
99
+ if (payload = output[:payload_member])
100
+ # checking ref and shape
101
+ payload['streaming'] || payload.shape['streaming'] || payload.eventstream
102
+ else
103
+ false
104
+ end
105
+ end
106
+
107
+ def structure_shape?(shape)
108
+ shape.is_a?(Seahorse::Model::Shapes::StructureShape)
109
+ end
41
110
  end
42
111
 
43
- handler(
44
- Handler,
45
- step: :sign,
46
- operations: [
47
- :complete_multipart_upload,
48
- :copy_object,
49
- :upload_part_copy,
50
- ]
51
- )
112
+ handler(Handler, step: :sign)
52
113
  end
53
114
  end
54
115
  end
@@ -10,41 +10,23 @@ module Aws
10
10
  default: 'legacy',
11
11
  doc_type: String,
12
12
  docstring: <<-DOCS) do |cfg|
13
- Passing in `regional` to enable regional endpoint for S3's `us-east-1`
14
- region. Defaults to `legacy` mode using global endpoint.
13
+ Pass in `regional` to enable the `us-east-1` regional endpoint.
14
+ Defaults to `legacy` mode which uses the global endpoint.
15
15
  DOCS
16
16
  resolve_iad_regional_endpoint(cfg)
17
17
  end
18
18
 
19
- def add_handlers(handlers, config)
20
- if config.region == 'us-east-1'
21
- handlers.add(Handler)
22
- end
23
- end
24
-
25
- # @api private
26
- class Handler < Seahorse::Client::Handler
27
-
28
- def call(context)
29
- # keep legacy global endpoint pattern by default
30
- if context.config.s3_us_east_1_regional_endpoint == 'legacy'
31
- context.http_request.endpoint.host = IADRegionalEndpoint.legacy_host(
32
- context.http_request.endpoint.host)
33
- end
34
- @handler.call(context)
35
- end
36
-
37
- end
38
-
39
- def self.legacy_host(host)
40
- host.sub(".us-east-1", '')
41
- end
42
-
43
19
  private
44
20
 
45
21
  def self.resolve_iad_regional_endpoint(cfg)
22
+ default_mode_value =
23
+ if cfg.respond_to?(:defaults_mode_config_resolver)
24
+ cfg.defaults_mode_config_resolver.resolve(:s3_us_east_1_regional_endpoint)
25
+ end
26
+
46
27
  mode = ENV['AWS_S3_US_EAST_1_REGIONAL_ENDPOINT'] ||
47
28
  Aws.shared_config.s3_us_east_1_regional_endpoint(profile: cfg.profile) ||
29
+ default_mode_value ||
48
30
  'legacy'
49
31
  mode = mode.downcase
50
32
  unless %w(legacy regional).include?(mode)
@@ -22,7 +22,9 @@ module Aws
22
22
 
23
23
  def populate_location_constraint(params, region)
24
24
  params[:create_bucket_configuration] ||= {}
25
- params[:create_bucket_configuration][:location_constraint] ||= region
25
+ unless params[:create_bucket_configuration][:location]
26
+ params[:create_bucket_configuration][:location_constraint] ||= region
27
+ end
26
28
  end
27
29
 
28
30
  end
@@ -6,78 +6,20 @@ module Aws
6
6
  module S3
7
7
  module Plugins
8
8
  # @api private
9
- # This plugin is effectively deprecated in favor of modeled
9
+ # This plugin is deprecated in favor of modeled
10
10
  # httpChecksumRequired traits.
11
11
  class Md5s < Seahorse::Client::Plugin
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
17
- ]
18
-
19
- # @api private
20
- class Handler < Seahorse::Client::Handler
21
-
22
- CHUNK_SIZE = 1 * 1024 * 1024 # one MB
23
-
24
- def call(context)
25
- body = context.http_request.body
26
- if body.size > 0
27
- context.http_request.headers['Content-Md5'] ||= md5(body)
28
- end
29
- @handler.call(context)
30
- end
31
-
32
- private
33
-
34
- # @param [File, Tempfile, IO#read, String] value
35
- # @return [String<MD5>]
36
- def md5(value)
37
- if (File === value || Tempfile === value) && !value.path.nil? && File.exist?(value.path)
38
- OpenSSL::Digest::MD5.file(value).base64digest
39
- elsif value.respond_to?(:read)
40
- md5 = OpenSSL::Digest::MD5.new
41
- update_in_chunks(md5, value)
42
- md5.base64digest
43
- else
44
- OpenSSL::Digest::MD5.digest(value).base64digest
45
- end
46
- end
47
-
48
- def update_in_chunks(digest, io)
49
- loop do
50
- chunk = io.read(CHUNK_SIZE)
51
- break unless chunk
52
- digest.update(chunk)
53
- end
54
- io.rewind
55
- end
56
-
57
- end
58
-
59
12
  option(:compute_checksums,
60
- default: true,
61
- doc_type: 'Boolean',
62
- docstring: <<-DOCS)
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.
68
- DOCS
69
-
70
- def add_handlers(handlers, config)
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
13
+ default: true,
14
+ doc_type: 'Boolean',
15
+ docstring: <<~DOCS)
16
+ This option is deprecated. Please use `:request_checksum_calculation` instead.
17
+ When `false`, `request_checksum_calculation` is overridden to `when_required`.
18
+ DOCS
19
+
20
+ def after_initialize(client)
21
+ client.config.request_checksum_calculation = 'when_required' unless client.config.compute_checksums
79
22
  end
80
-
81
23
  end
82
24
  end
83
25
  end
@@ -4,25 +4,19 @@ require 'aws-sigv4'
4
4
 
5
5
  module Aws
6
6
  module S3
7
+ # @api private
8
+ def self.bucket_region_cache
9
+ @bucket_region_cache ||= BucketRegionCache.new
10
+ end
11
+
7
12
  module Plugins
8
- # This plugin is an implementation detail and may be modified.
13
+ # This plugin used to have a V4 signer but it was removed in favor of
14
+ # generic Sign plugin that uses endpoint auth scheme.
15
+ #
9
16
  # @api private
10
17
  class S3Signer < Seahorse::Client::Plugin
11
18
  option(:signature_version, 'v4')
12
19
 
13
- option(:sigv4_signer) do |cfg|
14
- S3Signer.build_v4_signer(
15
- region: cfg.sigv4_region,
16
- credentials: cfg.credentials
17
- )
18
- end
19
-
20
- option(:sigv4_region) do |cfg|
21
- raise Aws::Errors::MissingRegionError if cfg.region.nil?
22
-
23
- Aws::Partitions::EndpointProvider.signing_region(cfg.region, 's3')
24
- end
25
-
26
20
  def add_handlers(handlers, cfg)
27
21
  case cfg.signature_version
28
22
  when 'v4' then add_v4_handlers(handlers)
@@ -35,11 +29,11 @@ module Aws
35
29
 
36
30
  def add_v4_handlers(handlers)
37
31
  handlers.add(CachedBucketRegionHandler, step: :sign, priority: 60)
38
- handlers.add(V4Handler, step: :sign)
39
32
  handlers.add(BucketRegionErrorHandler, step: :sign, priority: 40)
40
33
  end
41
34
 
42
35
  def add_legacy_handler(handlers)
36
+ # generic Sign plugin will be skipped if it sees sigv2
43
37
  handlers.add(LegacyHandler, step: :sign)
44
38
  end
45
39
 
@@ -50,32 +44,6 @@ module Aws
50
44
  end
51
45
  end
52
46
 
53
- class V4Handler < Seahorse::Client::Handler
54
- def call(context)
55
- Aws::Plugins::SignatureV4.apply_signature(
56
- context: context,
57
- signer: sigv4_signer(context)
58
- )
59
- @handler.call(context)
60
- end
61
-
62
- private
63
-
64
- def sigv4_signer(context)
65
- # If the client was configured with the wrong region,
66
- # we have to build a new signer.
67
- if context[:cached_sigv4_region] &&
68
- context[:cached_sigv4_region] != context.config.sigv4_signer.region
69
- S3Signer.build_v4_signer(
70
- region: context[:cached_sigv4_region],
71
- credentials: context.config.credentials
72
- )
73
- else
74
- context.config.sigv4_signer
75
- end
76
- end
77
- end
78
-
79
47
  # This handler will update the http endpoint when the bucket region
80
48
  # is known/cached.
81
49
  class CachedBucketRegionHandler < Seahorse::Client::Handler
@@ -88,10 +56,14 @@ module Aws
88
56
  private
89
57
 
90
58
  def check_for_cached_region(context, bucket)
91
- cached_region = S3::BUCKET_REGIONS[bucket]
92
- if cached_region && cached_region != context.config.region
93
- context.http_request.endpoint.host = S3Signer.new_hostname(context, cached_region)
94
- context[:cached_sigv4_region] = cached_region
59
+ cached_region = Aws::S3.bucket_region_cache[bucket]
60
+ if cached_region &&
61
+ cached_region != context.config.region &&
62
+ !S3Signer.custom_endpoint?(context)
63
+ context.http_request.endpoint.host = S3Signer.new_hostname(
64
+ context, cached_region
65
+ )
66
+ context[:sigv4_region] = cached_region # Sign plugin will use this
95
67
  end
96
68
  end
97
69
  end
@@ -99,7 +71,8 @@ module Aws
99
71
  # This handler detects when a request fails because of a mismatched bucket
100
72
  # region. It follows up by making a request to determine the correct
101
73
  # region, then finally a version 4 signed request against the correct
102
- # regional endpoint.
74
+ # regional endpoint. This is intended for s3's global endpoint which
75
+ # will return 400 if the bucket is not in region.
103
76
  class BucketRegionErrorHandler < Seahorse::Client::Handler
104
77
  def call(context)
105
78
  response = @handler.call(context)
@@ -111,7 +84,8 @@ module Aws
111
84
  def handle_region_errors(response)
112
85
  if wrong_sigv4_region?(response) &&
113
86
  !fips_region?(response) &&
114
- !custom_endpoint?(response)
87
+ !S3Signer.custom_endpoint?(response.context) &&
88
+ !expired_credentials?(response)
115
89
  get_region_and_retry(response.context)
116
90
  else
117
91
  response
@@ -128,18 +102,15 @@ module Aws
128
102
  end
129
103
 
130
104
  def update_bucket_cache(context, actual_region)
131
- S3::BUCKET_REGIONS[context.params[:bucket]] = actual_region
105
+ Aws::S3.bucket_region_cache[context.params[:bucket]] = actual_region
132
106
  end
133
107
 
134
108
  def fips_region?(resp)
135
- resp.context.http_request.endpoint.host.include?('fips')
109
+ resp.context.http_request.endpoint.host.include?('s3-fips.')
136
110
  end
137
111
 
138
- def custom_endpoint?(resp)
139
- resolved_suffix = Aws::Partitions::EndpointProvider.dns_suffix_for(
140
- resp.context.config.region
141
- )
142
- !resp.context.http_request.endpoint.hostname.include?(resolved_suffix)
112
+ def expired_credentials?(resp)
113
+ resp.context.http_response.body_contents.match(/<Code>ExpiredToken<\/Code>/)
143
114
  end
144
115
 
145
116
  def wrong_sigv4_region?(resp)
@@ -150,15 +121,18 @@ module Aws
150
121
 
151
122
  def resign_with_new_region(context, actual_region)
152
123
  context.http_response.body.truncate(0)
153
- context.http_request.endpoint.host = S3Signer.new_hostname(context, actual_region)
124
+ context.http_request.endpoint.host = S3Signer.new_hostname(
125
+ context, actual_region
126
+ )
154
127
  context.metadata[:redirect_region] = actual_region
155
- Aws::Plugins::SignatureV4.apply_signature(
156
- context: context,
157
- signer: S3Signer.build_v4_signer(
158
- region: actual_region,
159
- credentials: context.config.credentials
160
- )
128
+
129
+ signer = Aws::Plugins::Sign.signer_for(
130
+ context[:auth_scheme],
131
+ context.config,
132
+ actual_region
161
133
  )
134
+
135
+ signer.sign(context)
162
136
  end
163
137
 
164
138
  def region_from_body(body)
@@ -184,36 +158,22 @@ module Aws
184
158
  end
185
159
 
186
160
  class << self
187
- # @option options [required, String] :region
188
- # @option options [required, #credentials] :credentials
189
- # @api private
190
- def build_v4_signer(options = {})
191
- Aws::Sigv4::Signer.new(
192
- service: 's3',
193
- region: options[:region],
194
- credentials_provider: options[:credentials],
195
- uri_escape_path: false,
196
- unsigned_headers: ['content-length', 'x-amzn-trace-id']
197
- )
161
+ def new_hostname(context, region)
162
+ endpoint_params = context[:endpoint_params].dup
163
+ endpoint_params.region = region
164
+ endpoint_params.endpoint = nil
165
+ endpoint =
166
+ context.config.endpoint_provider.resolve_endpoint(endpoint_params)
167
+ URI(endpoint.url).host
198
168
  end
199
169
 
200
- def new_hostname(context, region)
201
- # Check to see if the bucket is actually an ARN and resolve it
202
- # Otherwise it will retry with the ARN as the bucket name.
203
- resolved_bucket, resolved_region, arn = BucketARN.resolve_arn!(
204
- context.params[:bucket],
205
- region,
206
- context.config.s3_use_arn_region
207
- )
208
- uri = URI.parse(
209
- Aws::Partitions::EndpointProvider.resolve(resolved_region, 's3')
210
- )
170
+ def custom_endpoint?(context)
171
+ region = context.config.region
172
+ partition = Aws::Endpoints::Matchers.aws_partition(region)
173
+ endpoint = context.http_request.endpoint
211
174
 
212
- if arn
213
- BucketARN.resolve_url!(uri, arn).host
214
- else
215
- resolved_bucket + '.' + uri.host
216
- end
175
+ !endpoint.hostname.include?(partition['dnsSuffix']) &&
176
+ !endpoint.hostname.include?(partition['dualStackDnsSuffix'])
217
177
  end
218
178
  end
219
179
  end
@@ -36,6 +36,17 @@ module Aws
36
36
  def rewind; end
37
37
  end
38
38
 
39
+ class NonRetryableStreamingError < StandardError
40
+
41
+ def initialize(error)
42
+ super('Unable to retry request - retry could result in processing duplicated chunks.')
43
+ set_backtrace(error.backtrace)
44
+ @original_error = error
45
+ end
46
+
47
+ attr_reader :original_error
48
+ end
49
+
39
50
  # This handler works with the ResponseTarget plugin to provide smart
40
51
  # retries of S3 streaming operations that support the range parameter
41
52
  # (currently only: get_object). When a 200 OK with a TruncatedBodyError
@@ -51,18 +62,16 @@ module Aws
51
62
  class Handler < Seahorse::Client::Handler
52
63
 
53
64
  def call(context)
54
- target = context.params[:response_target] || context[:response_target]
55
-
56
65
  # 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)
66
+ if supported_target?(context) && !context.params[:range]
67
+ add_event_listeners(context)
59
68
  end
60
69
  @handler.call(context)
61
70
  end
62
71
 
63
72
  private
64
73
 
65
- def add_event_listeners(context, target)
74
+ def add_event_listeners(context)
66
75
  context.http_response.on_headers(200..299) do
67
76
  case context.http_response.body
68
77
  when Seahorse::Client::BlockIO then
@@ -84,8 +93,18 @@ module Aws
84
93
  end
85
94
 
86
95
  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}-"
96
+ if retryable_body?(context)
97
+ if truncated_body?(error)
98
+ context.http_request.headers[:range] = "bytes=#{context.http_response.body.size}-"
99
+ else
100
+ case context.http_response.body
101
+ when RetryableManagedFile
102
+ # call rewind on the underlying file
103
+ context.http_response.body.instance_variable_get(:@file).rewind
104
+ else
105
+ raise NonRetryableStreamingError, error
106
+ end
107
+ end
89
108
  end
90
109
  end
91
110
  end
@@ -102,8 +121,8 @@ module Aws
102
121
  context.http_response.body.is_a?(RetryableManagedFile)
103
122
  end
104
123
 
105
- def supported_target?(target)
106
- case target
124
+ def supported_target?(context)
125
+ case context[:response_target]
107
126
  when Proc, String, Pathname then true
108
127
  else false
109
128
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'uri'
4
- require 'cgi'
4
+ require "cgi/escape"
5
+ require "cgi/util" if RUBY_VERSION < "3.5"
5
6
 
6
7
  module Aws
7
8
  module S3