aws-sdk-s3 1.75.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.
- 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
|