aws-sdk-s3 1.75.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/aws-sdk-s3.rb +73 -0
- data/lib/aws-sdk-s3/bucket.rb +861 -0
- data/lib/aws-sdk-s3/bucket_acl.rb +277 -0
- data/lib/aws-sdk-s3/bucket_cors.rb +262 -0
- data/lib/aws-sdk-s3/bucket_lifecycle.rb +264 -0
- data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +283 -0
- data/lib/aws-sdk-s3/bucket_logging.rb +251 -0
- data/lib/aws-sdk-s3/bucket_notification.rb +293 -0
- data/lib/aws-sdk-s3/bucket_policy.rb +242 -0
- data/lib/aws-sdk-s3/bucket_region_cache.rb +81 -0
- data/lib/aws-sdk-s3/bucket_request_payment.rb +236 -0
- data/lib/aws-sdk-s3/bucket_tagging.rb +251 -0
- data/lib/aws-sdk-s3/bucket_versioning.rb +312 -0
- data/lib/aws-sdk-s3/bucket_website.rb +292 -0
- data/lib/aws-sdk-s3/client.rb +11818 -0
- data/lib/aws-sdk-s3/client_api.rb +3014 -0
- data/lib/aws-sdk-s3/customizations.rb +34 -0
- data/lib/aws-sdk-s3/customizations/bucket.rb +162 -0
- data/lib/aws-sdk-s3/customizations/multipart_upload.rb +44 -0
- data/lib/aws-sdk-s3/customizations/object.rb +389 -0
- data/lib/aws-sdk-s3/customizations/object_summary.rb +85 -0
- data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +13 -0
- data/lib/aws-sdk-s3/encryption.rb +21 -0
- data/lib/aws-sdk-s3/encryption/client.rb +375 -0
- data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +190 -0
- data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +65 -0
- data/lib/aws-sdk-s3/encryption/default_key_provider.rb +40 -0
- data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +61 -0
- data/lib/aws-sdk-s3/encryption/errors.rb +15 -0
- data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +58 -0
- data/lib/aws-sdk-s3/encryption/io_decrypter.rb +36 -0
- data/lib/aws-sdk-s3/encryption/io_encrypter.rb +71 -0
- data/lib/aws-sdk-s3/encryption/key_provider.rb +31 -0
- data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +75 -0
- data/lib/aws-sdk-s3/encryption/materials.rb +60 -0
- data/lib/aws-sdk-s3/encryption/utils.rb +81 -0
- data/lib/aws-sdk-s3/encryptionV2/client.rb +388 -0
- data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +198 -0
- data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +103 -0
- data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +38 -0
- data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +66 -0
- data/lib/aws-sdk-s3/encryptionV2/errors.rb +13 -0
- data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +56 -0
- data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +35 -0
- data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +71 -0
- data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +29 -0
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +99 -0
- data/lib/aws-sdk-s3/encryptionV2/materials.rb +58 -0
- data/lib/aws-sdk-s3/encryptionV2/utils.rb +116 -0
- data/lib/aws-sdk-s3/encryption_v2.rb +20 -0
- data/lib/aws-sdk-s3/errors.rb +115 -0
- data/lib/aws-sdk-s3/event_streams.rb +69 -0
- data/lib/aws-sdk-s3/file_downloader.rb +142 -0
- data/lib/aws-sdk-s3/file_part.rb +78 -0
- data/lib/aws-sdk-s3/file_uploader.rb +70 -0
- data/lib/aws-sdk-s3/legacy_signer.rb +189 -0
- data/lib/aws-sdk-s3/multipart_file_uploader.rb +227 -0
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +173 -0
- data/lib/aws-sdk-s3/multipart_upload.rb +401 -0
- data/lib/aws-sdk-s3/multipart_upload_error.rb +18 -0
- data/lib/aws-sdk-s3/multipart_upload_part.rb +423 -0
- data/lib/aws-sdk-s3/object.rb +1422 -0
- data/lib/aws-sdk-s3/object_acl.rb +333 -0
- data/lib/aws-sdk-s3/object_copier.rb +101 -0
- data/lib/aws-sdk-s3/object_multipart_copier.rb +182 -0
- data/lib/aws-sdk-s3/object_summary.rb +1181 -0
- data/lib/aws-sdk-s3/object_version.rb +550 -0
- data/lib/aws-sdk-s3/plugins/accelerate.rb +87 -0
- data/lib/aws-sdk-s3/plugins/bucket_arn.rb +212 -0
- data/lib/aws-sdk-s3/plugins/bucket_dns.rb +91 -0
- data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +45 -0
- data/lib/aws-sdk-s3/plugins/dualstack.rb +74 -0
- data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +28 -0
- data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +25 -0
- data/lib/aws-sdk-s3/plugins/http_200_errors.rb +55 -0
- data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +62 -0
- data/lib/aws-sdk-s3/plugins/location_constraint.rb +35 -0
- data/lib/aws-sdk-s3/plugins/md5s.rb +84 -0
- data/lib/aws-sdk-s3/plugins/redirects.rb +45 -0
- data/lib/aws-sdk-s3/plugins/s3_host_id.rb +30 -0
- data/lib/aws-sdk-s3/plugins/s3_signer.rb +222 -0
- data/lib/aws-sdk-s3/plugins/sse_cpk.rb +70 -0
- data/lib/aws-sdk-s3/plugins/streaming_retry.rb +118 -0
- data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +97 -0
- data/lib/aws-sdk-s3/presigned_post.rb +686 -0
- data/lib/aws-sdk-s3/presigner.rb +253 -0
- data/lib/aws-sdk-s3/resource.rb +117 -0
- data/lib/aws-sdk-s3/types.rb +13154 -0
- data/lib/aws-sdk-s3/waiters.rb +243 -0
- metadata +184 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module S3
|
5
|
+
module Plugins
|
6
|
+
class Expect100Continue < Seahorse::Client::Plugin
|
7
|
+
|
8
|
+
def add_handlers(handlers, config)
|
9
|
+
if config.http_continue_timeout && config.http_continue_timeout > 0
|
10
|
+
handlers.add(Handler)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
class Handler < Seahorse::Client::Handler
|
16
|
+
|
17
|
+
def call(context)
|
18
|
+
if context.http_request.body && context.http_request.body.size > 0
|
19
|
+
context.http_request.headers['expect'] = '100-continue'
|
20
|
+
end
|
21
|
+
@handler.call(context)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module S3
|
5
|
+
module Plugins
|
6
|
+
class GetBucketLocationFix < Seahorse::Client::Plugin
|
7
|
+
|
8
|
+
class Handler < Seahorse::Client::Handler
|
9
|
+
|
10
|
+
def call(context)
|
11
|
+
@handler.call(context).on(200) do |response|
|
12
|
+
response.data = S3::Types::GetBucketLocationOutput.new
|
13
|
+
xml = context.http_response.body_contents
|
14
|
+
matches = xml.match(/>(.+?)<\/LocationConstraint>/)
|
15
|
+
response.data[:location_constraint] = matches ? matches[1] : ''
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
handler(Handler, priority: 60, operations: [:get_bucket_location])
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module S3
|
5
|
+
module Plugins
|
6
|
+
|
7
|
+
# A handful of Amazon S3 operations will respond with a 200 status
|
8
|
+
# code but will send an error in the response body. This plugin
|
9
|
+
# injects a handler that will parse 200 response bodies for potential
|
10
|
+
# errors, allowing them to be retried.
|
11
|
+
# @api private
|
12
|
+
class Http200Errors < Seahorse::Client::Plugin
|
13
|
+
|
14
|
+
class Handler < Seahorse::Client::Handler
|
15
|
+
|
16
|
+
def call(context)
|
17
|
+
@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
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
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
|
34
|
+
Seahorse::Client::NetworkingError.new(
|
35
|
+
S3::Errors
|
36
|
+
.error_class('InternalError')
|
37
|
+
.new(context, 'Empty or incomplete response body')
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
handler(
|
44
|
+
Handler,
|
45
|
+
step: :sign,
|
46
|
+
operations: [
|
47
|
+
:complete_multipart_upload,
|
48
|
+
:copy_object,
|
49
|
+
:upload_part_copy,
|
50
|
+
]
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module S3
|
5
|
+
module Plugins
|
6
|
+
|
7
|
+
class IADRegionalEndpoint < Seahorse::Client::Plugin
|
8
|
+
|
9
|
+
option(:s3_us_east_1_regional_endpoint,
|
10
|
+
default: 'legacy',
|
11
|
+
doc_type: String,
|
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.
|
15
|
+
DOCS
|
16
|
+
resolve_iad_regional_endpoint(cfg)
|
17
|
+
end
|
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
|
+
private
|
44
|
+
|
45
|
+
def self.resolve_iad_regional_endpoint(cfg)
|
46
|
+
mode = ENV['AWS_S3_US_EAST_1_REGIONAL_ENDPOINT'] ||
|
47
|
+
Aws.shared_config.s3_us_east_1_regional_endpoint(profile: cfg.profile) ||
|
48
|
+
'legacy'
|
49
|
+
mode = mode.downcase
|
50
|
+
unless %w(legacy regional).include?(mode)
|
51
|
+
raise ArgumentError, "expected :s3_us_east_1_regional_endpoint or"\
|
52
|
+
" ENV['AWS_S3_US_EAST_1_REGIONAL_ENDPOINT'] to be `legacy` or"\
|
53
|
+
" `regional`."
|
54
|
+
end
|
55
|
+
mode
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module S3
|
5
|
+
module Plugins
|
6
|
+
|
7
|
+
# When making calls to {S3::Client#create_bucket} outside the
|
8
|
+
# "classic" region, the bucket location constraint must be specified.
|
9
|
+
# This plugin auto populates the constraint to the configured region.
|
10
|
+
class LocationConstraint < Seahorse::Client::Plugin
|
11
|
+
|
12
|
+
class Handler < Seahorse::Client::Handler
|
13
|
+
|
14
|
+
def call(context)
|
15
|
+
unless context.config.region == 'us-east-1'
|
16
|
+
populate_location_constraint(context.params, context.config.region)
|
17
|
+
end
|
18
|
+
@handler.call(context)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def populate_location_constraint(params, region)
|
24
|
+
params[:create_bucket_configuration] ||= {}
|
25
|
+
params[:create_bucket_configuration][:location_constraint] ||= region
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
handler(Handler, step: :initialize, operations: [:create_bucket])
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Aws
|
6
|
+
module S3
|
7
|
+
module Plugins
|
8
|
+
# @api private
|
9
|
+
# This plugin is effectively deprecated in favor of modeled
|
10
|
+
# httpChecksumRequired traits.
|
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
|
+
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
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module S3
|
5
|
+
module Plugins
|
6
|
+
class Redirects < Seahorse::Client::Plugin
|
7
|
+
|
8
|
+
option(:follow_redirects,
|
9
|
+
default: true,
|
10
|
+
doc_type: 'Boolean',
|
11
|
+
docstring: <<-DOCS)
|
12
|
+
When `true`, this client will follow 307 redirects returned
|
13
|
+
by Amazon S3.
|
14
|
+
DOCS
|
15
|
+
|
16
|
+
# @api private
|
17
|
+
class Handler < Seahorse::Client::Handler
|
18
|
+
|
19
|
+
def call(context)
|
20
|
+
response = @handler.call(context)
|
21
|
+
if context.http_response.status_code == 307
|
22
|
+
endpoint = context.http_response.headers['location']
|
23
|
+
unless context.http_request.endpoint.host.include?('fips')
|
24
|
+
context.http_request.endpoint = endpoint
|
25
|
+
end
|
26
|
+
context.http_response.body.truncate(0)
|
27
|
+
@handler.call(context)
|
28
|
+
else
|
29
|
+
response
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_handlers(handlers, config)
|
36
|
+
if config.follow_redirects
|
37
|
+
# we want to re-trigger request signing
|
38
|
+
handlers.add(Handler, step: :sign, priority: 90)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module S3
|
5
|
+
module Plugins
|
6
|
+
|
7
|
+
# Support S3 host id, more information, see:
|
8
|
+
# http://docs.aws.amazon.com/AmazonS3/latest/dev/troubleshooting.html#sdk-request-ids
|
9
|
+
#
|
10
|
+
# This plugin adds :host_id for s3 responses when available
|
11
|
+
# @api private
|
12
|
+
class S3HostId < Seahorse::Client::Plugin
|
13
|
+
|
14
|
+
class Handler < Seahorse::Client::Handler
|
15
|
+
|
16
|
+
def call(context)
|
17
|
+
response = @handler.call(context)
|
18
|
+
h = context.http_response.headers
|
19
|
+
context[:s3_host_id] = h['x-amz-id-2']
|
20
|
+
response
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
handler(Handler, step: :sign)
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aws-sigv4'
|
4
|
+
|
5
|
+
module Aws
|
6
|
+
module S3
|
7
|
+
module Plugins
|
8
|
+
# This plugin is an implementation detail and may be modified.
|
9
|
+
# @api private
|
10
|
+
class S3Signer < Seahorse::Client::Plugin
|
11
|
+
option(:signature_version, 'v4')
|
12
|
+
|
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
|
+
def add_handlers(handlers, cfg)
|
27
|
+
case cfg.signature_version
|
28
|
+
when 'v4' then add_v4_handlers(handlers)
|
29
|
+
when 's3' then add_legacy_handler(handlers)
|
30
|
+
else
|
31
|
+
msg = "unsupported signature version `#{cfg.signature_version}'"
|
32
|
+
raise ArgumentError, msg
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_v4_handlers(handlers)
|
37
|
+
handlers.add(CachedBucketRegionHandler, step: :sign, priority: 60)
|
38
|
+
handlers.add(V4Handler, step: :sign)
|
39
|
+
handlers.add(BucketRegionErrorHandler, step: :sign, priority: 40)
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_legacy_handler(handlers)
|
43
|
+
handlers.add(LegacyHandler, step: :sign)
|
44
|
+
end
|
45
|
+
|
46
|
+
class LegacyHandler < Seahorse::Client::Handler
|
47
|
+
def call(context)
|
48
|
+
LegacySigner.sign(context)
|
49
|
+
@handler.call(context)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
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
|
+
# This handler will update the http endpoint when the bucket region
|
80
|
+
# is known/cached.
|
81
|
+
class CachedBucketRegionHandler < Seahorse::Client::Handler
|
82
|
+
def call(context)
|
83
|
+
bucket = context.params[:bucket]
|
84
|
+
check_for_cached_region(context, bucket) if bucket
|
85
|
+
@handler.call(context)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
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
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# This handler detects when a request fails because of a mismatched bucket
|
100
|
+
# region. It follows up by making a request to determine the correct
|
101
|
+
# region, then finally a version 4 signed request against the correct
|
102
|
+
# regional endpoint.
|
103
|
+
class BucketRegionErrorHandler < Seahorse::Client::Handler
|
104
|
+
def call(context)
|
105
|
+
response = @handler.call(context)
|
106
|
+
handle_region_errors(response)
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def handle_region_errors(response)
|
112
|
+
if wrong_sigv4_region?(response) &&
|
113
|
+
!fips_region?(response) &&
|
114
|
+
!custom_endpoint?(response)
|
115
|
+
get_region_and_retry(response.context)
|
116
|
+
else
|
117
|
+
response
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_region_and_retry(context)
|
122
|
+
actual_region = context.http_response.headers['x-amz-bucket-region']
|
123
|
+
actual_region ||= region_from_body(context.http_response.body_contents)
|
124
|
+
update_bucket_cache(context, actual_region)
|
125
|
+
log_warning(context, actual_region)
|
126
|
+
resign_with_new_region(context, actual_region)
|
127
|
+
@handler.call(context)
|
128
|
+
end
|
129
|
+
|
130
|
+
def update_bucket_cache(context, actual_region)
|
131
|
+
S3::BUCKET_REGIONS[context.params[:bucket]] = actual_region
|
132
|
+
end
|
133
|
+
|
134
|
+
def fips_region?(resp)
|
135
|
+
resp.context.http_request.endpoint.host.include?('fips')
|
136
|
+
end
|
137
|
+
|
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)
|
143
|
+
end
|
144
|
+
|
145
|
+
def wrong_sigv4_region?(resp)
|
146
|
+
resp.context.http_response.status_code == 400 &&
|
147
|
+
(resp.context.http_response.headers['x-amz-bucket-region'] ||
|
148
|
+
resp.context.http_response.body_contents.match(/<Region>.+?<\/Region>/))
|
149
|
+
end
|
150
|
+
|
151
|
+
def resign_with_new_region(context, actual_region)
|
152
|
+
context.http_response.body.truncate(0)
|
153
|
+
context.http_request.endpoint.host = S3Signer.new_hostname(context, actual_region)
|
154
|
+
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
|
+
)
|
161
|
+
)
|
162
|
+
end
|
163
|
+
|
164
|
+
def region_from_body(body)
|
165
|
+
region = body.match(/<Region>(.+?)<\/Region>/)[1]
|
166
|
+
if region.nil? || region == ''
|
167
|
+
raise "couldn't get region from body: #{body}"
|
168
|
+
else
|
169
|
+
region
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def log_warning(context, actual_region)
|
174
|
+
msg = "S3 client configured for #{context.config.region.inspect} " \
|
175
|
+
"but the bucket #{context.params[:bucket].inspect} is in " \
|
176
|
+
"#{actual_region.inspect}; Please configure the proper region " \
|
177
|
+
"to avoid multiple unnecessary redirects and signing attempts\n"
|
178
|
+
if (logger = context.config.logger)
|
179
|
+
logger.warn(msg)
|
180
|
+
else
|
181
|
+
warn(msg)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
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
|
+
)
|
198
|
+
end
|
199
|
+
|
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
|
+
)
|
211
|
+
|
212
|
+
if arn
|
213
|
+
BucketARN.resolve_url!(uri, arn).host
|
214
|
+
else
|
215
|
+
resolved_bucket + '.' + uri.host
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|