aws-sdk-core 3.186.0 → 3.241.3
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 +4 -4
- data/CHANGELOG.md +715 -0
- data/VERSION +1 -1
- data/lib/aws-defaults.rb +4 -1
- data/lib/aws-sdk-core/arn.rb +1 -3
- data/lib/aws-sdk-core/assume_role_credentials.rb +21 -13
- data/lib/aws-sdk-core/assume_role_web_identity_credentials.rb +16 -9
- data/lib/aws-sdk-core/binary/decode_handler.rb +3 -9
- data/lib/aws-sdk-core/binary/encode_handler.rb +1 -1
- data/lib/aws-sdk-core/binary/event_builder.rb +34 -37
- data/lib/aws-sdk-core/binary/event_stream_decoder.rb +1 -0
- data/lib/aws-sdk-core/binary/event_stream_encoder.rb +4 -3
- data/lib/aws-sdk-core/cbor/decoder.rb +308 -0
- data/lib/aws-sdk-core/cbor/encoder.rb +243 -0
- data/lib/aws-sdk-core/cbor.rb +53 -0
- data/lib/aws-sdk-core/client_side_monitoring.rb +9 -0
- data/lib/aws-sdk-core/client_stubs.rb +39 -55
- data/lib/aws-sdk-core/credential_provider.rb +5 -1
- data/lib/aws-sdk-core/credential_provider_chain.rb +101 -25
- data/lib/aws-sdk-core/credentials.rb +19 -6
- data/lib/aws-sdk-core/ec2_metadata.rb +1 -1
- data/lib/aws-sdk-core/ecs_credentials.rb +92 -24
- data/lib/aws-sdk-core/endpoints/endpoint.rb +3 -1
- data/lib/aws-sdk-core/endpoints/matchers.rb +8 -10
- data/lib/aws-sdk-core/endpoints.rb +101 -21
- data/lib/aws-sdk-core/error_handler.rb +46 -0
- data/lib/aws-sdk-core/errors.rb +16 -4
- data/lib/aws-sdk-core/event_emitter.rb +1 -17
- data/lib/aws-sdk-core/instance_profile_credentials.rb +148 -157
- data/lib/aws-sdk-core/json/builder.rb +8 -1
- data/lib/aws-sdk-core/json/error_handler.rb +29 -13
- data/lib/aws-sdk-core/json/handler.rb +13 -6
- data/lib/aws-sdk-core/json/json_engine.rb +3 -1
- data/lib/aws-sdk-core/json/oj_engine.rb +7 -1
- data/lib/aws-sdk-core/json/parser.rb +32 -2
- data/lib/aws-sdk-core/json.rb +43 -14
- data/lib/aws-sdk-core/log/param_filter.rb +2 -2
- data/lib/aws-sdk-core/log/param_formatter.rb +7 -3
- data/lib/aws-sdk-core/log.rb +10 -0
- data/lib/aws-sdk-core/login_credentials.rb +229 -0
- data/lib/aws-sdk-core/lru_cache.rb +75 -0
- data/lib/aws-sdk-core/pageable_response.rb +1 -1
- data/lib/aws-sdk-core/param_validator.rb +7 -2
- data/lib/aws-sdk-core/plugins/bearer_authorization.rb +2 -0
- data/lib/aws-sdk-core/plugins/checksum_algorithm.rb +436 -201
- data/lib/aws-sdk-core/plugins/client_metrics_plugin.rb +1 -1
- data/lib/aws-sdk-core/plugins/client_metrics_send_plugin.rb +14 -2
- data/lib/aws-sdk-core/plugins/credentials_configuration.rb +78 -56
- data/lib/aws-sdk-core/plugins/endpoint_pattern.rb +40 -32
- data/lib/aws-sdk-core/plugins/global_configuration.rb +8 -9
- data/lib/aws-sdk-core/plugins/http_checksum.rb +3 -8
- data/lib/aws-sdk-core/plugins/invocation_id.rb +1 -11
- data/lib/aws-sdk-core/plugins/logging.rb +2 -0
- data/lib/aws-sdk-core/plugins/protocols/api_gateway.rb +3 -1
- data/lib/aws-sdk-core/plugins/protocols/ec2.rb +2 -24
- data/lib/aws-sdk-core/plugins/protocols/json_rpc.rb +6 -8
- data/lib/aws-sdk-core/plugins/protocols/query.rb +4 -2
- data/lib/aws-sdk-core/plugins/protocols/rest_json.rb +3 -15
- data/lib/aws-sdk-core/plugins/protocols/rest_xml.rb +3 -0
- data/lib/aws-sdk-core/plugins/protocols/rpc_v2.rb +17 -0
- data/lib/aws-sdk-core/plugins/regional_endpoint.rb +74 -25
- data/lib/aws-sdk-core/plugins/request_compression.rb +11 -2
- data/lib/aws-sdk-core/plugins/retries/clock_skew.rb +28 -16
- data/lib/aws-sdk-core/plugins/retry_errors.rb +12 -3
- data/lib/aws-sdk-core/plugins/sign.rb +55 -34
- data/lib/aws-sdk-core/plugins/signature_v2.rb +2 -1
- data/lib/aws-sdk-core/plugins/signature_v4.rb +2 -1
- data/lib/aws-sdk-core/plugins/stub_responses.rb +59 -9
- data/lib/aws-sdk-core/plugins/telemetry.rb +75 -0
- data/lib/aws-sdk-core/plugins/transfer_encoding.rb +16 -9
- data/lib/aws-sdk-core/plugins/user_agent.rb +103 -26
- data/lib/aws-sdk-core/plugins.rb +39 -0
- data/lib/aws-sdk-core/process_credentials.rb +48 -29
- data/lib/aws-sdk-core/query/ec2_handler.rb +27 -0
- data/lib/aws-sdk-core/query/ec2_param_builder.rb +5 -7
- data/lib/aws-sdk-core/query/handler.rb +4 -4
- data/lib/aws-sdk-core/query/param_builder.rb +2 -2
- data/lib/aws-sdk-core/query.rb +2 -1
- data/lib/aws-sdk-core/refreshing_credentials.rb +20 -17
- data/lib/aws-sdk-core/resources.rb +8 -0
- data/lib/aws-sdk-core/rest/content_type_handler.rb +60 -0
- data/lib/aws-sdk-core/rest/handler.rb +3 -4
- data/lib/aws-sdk-core/rest/request/body.rb +32 -5
- data/lib/aws-sdk-core/rest/request/endpoint.rb +24 -4
- data/lib/aws-sdk-core/rest/request/headers.rb +15 -7
- data/lib/aws-sdk-core/rest/request/querystring_builder.rb +23 -11
- data/lib/aws-sdk-core/rest/response/body.rb +15 -1
- data/lib/aws-sdk-core/rest/response/header_list_parser.rb +79 -0
- data/lib/aws-sdk-core/rest/response/headers.rb +8 -3
- data/lib/aws-sdk-core/rest.rb +1 -0
- data/lib/aws-sdk-core/rpc_v2/builder.rb +62 -0
- data/lib/aws-sdk-core/rpc_v2/cbor_engine.rb +18 -0
- data/lib/aws-sdk-core/rpc_v2/content_type_handler.rb +47 -0
- data/lib/aws-sdk-core/rpc_v2/error_handler.rb +95 -0
- data/lib/aws-sdk-core/rpc_v2/handler.rb +79 -0
- data/lib/aws-sdk-core/rpc_v2/parser.rb +98 -0
- data/lib/aws-sdk-core/rpc_v2.rb +69 -0
- data/lib/aws-sdk-core/shared_config.rb +109 -22
- data/lib/aws-sdk-core/shared_credentials.rb +1 -7
- data/lib/aws-sdk-core/sso_credentials.rb +5 -2
- data/lib/aws-sdk-core/static_token_provider.rb +1 -2
- data/lib/aws-sdk-core/stubbing/protocols/ec2.rb +12 -11
- data/lib/aws-sdk-core/stubbing/protocols/json.rb +11 -10
- data/lib/aws-sdk-core/stubbing/protocols/query.rb +7 -6
- data/lib/aws-sdk-core/stubbing/protocols/rest.rb +2 -1
- data/lib/aws-sdk-core/stubbing/protocols/rest_json.rb +9 -8
- data/lib/aws-sdk-core/stubbing/protocols/rest_xml.rb +6 -5
- data/lib/aws-sdk-core/stubbing/protocols/rpc_v2.rb +39 -0
- data/lib/aws-sdk-core/stubbing.rb +22 -0
- data/lib/aws-sdk-core/telemetry/base.rb +177 -0
- data/lib/aws-sdk-core/telemetry/no_op.rb +70 -0
- data/lib/aws-sdk-core/telemetry/otel.rb +235 -0
- data/lib/aws-sdk-core/telemetry/span_kind.rb +22 -0
- data/lib/aws-sdk-core/telemetry/span_status.rb +59 -0
- data/lib/aws-sdk-core/telemetry.rb +78 -0
- data/lib/aws-sdk-core/token.rb +3 -3
- data/lib/aws-sdk-core/token_provider.rb +4 -0
- data/lib/aws-sdk-core/token_provider_chain.rb +2 -6
- data/lib/aws-sdk-core/util.rb +41 -1
- data/lib/aws-sdk-core/waiters/poller.rb +10 -5
- data/lib/aws-sdk-core/xml/builder.rb +17 -9
- data/lib/aws-sdk-core/xml/error_handler.rb +35 -43
- data/lib/aws-sdk-core/xml/parser/frame.rb +4 -20
- data/lib/aws-sdk-core/xml/parser/stack.rb +2 -0
- data/lib/aws-sdk-core/xml/parser.rb +2 -6
- data/lib/aws-sdk-core.rb +86 -107
- data/lib/aws-sdk-signin/client.rb +604 -0
- data/lib/aws-sdk-signin/client_api.rb +119 -0
- data/lib/aws-sdk-signin/customizations.rb +1 -0
- data/lib/aws-sdk-signin/endpoint_parameters.rb +69 -0
- data/lib/aws-sdk-signin/endpoint_provider.rb +59 -0
- data/lib/aws-sdk-signin/endpoints.rb +20 -0
- data/lib/aws-sdk-signin/errors.rb +122 -0
- data/lib/aws-sdk-signin/plugins/endpoints.rb +77 -0
- data/lib/aws-sdk-signin/resource.rb +26 -0
- data/lib/aws-sdk-signin/types.rb +299 -0
- data/lib/aws-sdk-signin.rb +63 -0
- data/lib/aws-sdk-sso/client.rb +189 -96
- data/lib/aws-sdk-sso/client_api.rb +7 -0
- data/lib/aws-sdk-sso/endpoint_parameters.rb +13 -10
- data/lib/aws-sdk-sso/endpoint_provider.rb +16 -20
- data/lib/aws-sdk-sso/endpoints.rb +2 -54
- data/lib/aws-sdk-sso/plugins/endpoints.rb +23 -22
- data/lib/aws-sdk-sso/types.rb +1 -0
- data/lib/aws-sdk-sso.rb +15 -11
- data/lib/aws-sdk-ssooidc/client.rb +609 -129
- data/lib/aws-sdk-ssooidc/client_api.rb +94 -1
- data/lib/aws-sdk-ssooidc/endpoint_parameters.rb +13 -10
- data/lib/aws-sdk-ssooidc/endpoint_provider.rb +16 -20
- data/lib/aws-sdk-ssooidc/endpoints.rb +2 -40
- data/lib/aws-sdk-ssooidc/errors.rb +62 -0
- data/lib/aws-sdk-ssooidc/plugins/endpoints.rb +23 -20
- data/lib/aws-sdk-ssooidc/types.rb +419 -53
- data/lib/aws-sdk-ssooidc.rb +15 -11
- data/lib/aws-sdk-sts/client.rb +537 -156
- data/lib/aws-sdk-sts/client_api.rb +108 -8
- data/lib/aws-sdk-sts/customizations.rb +5 -2
- data/lib/aws-sdk-sts/endpoint_parameters.rb +15 -14
- data/lib/aws-sdk-sts/endpoint_provider.rb +52 -57
- data/lib/aws-sdk-sts/endpoints.rb +2 -118
- data/lib/aws-sdk-sts/errors.rb +79 -0
- data/lib/aws-sdk-sts/plugins/endpoints.rb +23 -30
- data/lib/aws-sdk-sts/presigner.rb +3 -7
- data/lib/aws-sdk-sts/types.rb +361 -35
- data/lib/aws-sdk-sts.rb +15 -11
- data/lib/seahorse/client/async_base.rb +4 -5
- data/lib/seahorse/client/async_response.rb +19 -0
- data/lib/seahorse/client/base.rb +18 -21
- data/lib/seahorse/client/h2/connection.rb +18 -28
- data/lib/seahorse/client/h2/handler.rb +19 -3
- data/lib/seahorse/client/handler.rb +1 -1
- data/lib/seahorse/client/http/response.rb +1 -1
- data/lib/seahorse/client/net_http/connection_pool.rb +15 -12
- data/lib/seahorse/client/net_http/handler.rb +21 -9
- data/lib/seahorse/client/net_http/patches.rb +44 -14
- data/lib/seahorse/client/networking_error.rb +1 -1
- data/lib/seahorse/client/plugin.rb +9 -0
- data/lib/seahorse/client/plugins/endpoint.rb +0 -1
- data/lib/seahorse/client/plugins/h2.rb +4 -4
- data/lib/seahorse/client/plugins/net_http.rb +57 -16
- data/lib/seahorse/client/request_context.rb +9 -2
- data/lib/seahorse/client/response.rb +2 -0
- data/lib/seahorse/model/shapes.rb +2 -2
- data/lib/seahorse/util.rb +2 -1
- data/sig/aws-sdk-core/async_client_stubs.rbs +21 -0
- data/sig/aws-sdk-core/client_stubs.rbs +10 -0
- data/sig/aws-sdk-core/errors.rbs +22 -0
- data/sig/aws-sdk-core/resources/collection.rbs +21 -0
- data/sig/aws-sdk-core/structure.rbs +4 -0
- data/sig/aws-sdk-core/telemetry/base.rbs +46 -0
- data/sig/aws-sdk-core/telemetry/otel.rbs +22 -0
- data/sig/aws-sdk-core/telemetry/span_kind.rbs +15 -0
- data/sig/aws-sdk-core/telemetry/span_status.rbs +24 -0
- data/sig/aws-sdk-core/waiters/errors.rbs +20 -0
- data/sig/aws-sdk-core.rbs +7 -0
- data/sig/seahorse/client/async_base.rbs +18 -0
- data/sig/seahorse/client/base.rbs +25 -0
- data/sig/seahorse/client/handler_builder.rbs +16 -0
- data/sig/seahorse/client/response.rbs +61 -0
- metadata +117 -23
- /data/lib/aws-sdk-core/xml/parser/{engines/libxml.rb → libxml_engine.rb} +0 -0
- /data/lib/aws-sdk-core/xml/parser/{engines/nokogiri.rb → nokogiri_engine.rb} +0 -0
- /data/lib/aws-sdk-core/xml/parser/{engines/oga.rb → oga_engine.rb} +0 -0
- /data/lib/aws-sdk-core/xml/parser/{engines/ox.rb → ox_engine.rb} +0 -0
- /data/lib/aws-sdk-core/xml/parser/{engines/rexml.rb → rexml_engine.rb} +0 -0
|
@@ -4,7 +4,8 @@ module Aws
|
|
|
4
4
|
module Plugins
|
|
5
5
|
# @api private
|
|
6
6
|
class ChecksumAlgorithm < Seahorse::Client::Plugin
|
|
7
|
-
|
|
7
|
+
CHECKSUM_CHUNK_SIZE = 1 * 1024 * 1024 # one MB
|
|
8
|
+
DEFAULT_TRAILER_CHUNK_SIZE = 16_384 # 16 KB
|
|
8
9
|
|
|
9
10
|
# determine the set of supported client side checksum algorithms
|
|
10
11
|
# CRC32c requires aws-crt (optional sdk dependency) for support
|
|
@@ -13,34 +14,130 @@ module Aws
|
|
|
13
14
|
begin
|
|
14
15
|
require 'aws-crt'
|
|
15
16
|
supported << 'CRC32C'
|
|
17
|
+
supported << 'CRC64NVME' if Aws::Crt::GEM_VERSION >= '0.3.0'
|
|
16
18
|
rescue LoadError
|
|
19
|
+
# Ignored
|
|
17
20
|
end
|
|
18
21
|
supported
|
|
19
22
|
end.freeze
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
CRT_ALGORITHMS = %w[CRC32C CRC64NVME].freeze
|
|
25
|
+
DEFAULT_CHECKSUM = 'CRC32'
|
|
26
|
+
|
|
27
|
+
# Priority order of checksum algorithms to validate responses against.
|
|
28
|
+
# Remove any algorithms not supported by client (ie, depending on CRT availability).
|
|
29
|
+
# This list was chosen based on average performance.
|
|
30
|
+
CHECKSUM_ALGORITHM_PRIORITIES = %w[CRC32 CRC32C CRC64NVME SHA1 SHA256] & CLIENT_ALGORITHMS
|
|
24
31
|
|
|
25
32
|
# byte size of checksums, used in computing the trailer length
|
|
26
33
|
CHECKSUM_SIZE = {
|
|
27
|
-
'CRC32' =>
|
|
28
|
-
'CRC32C' =>
|
|
29
|
-
'
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
'CRC32' => 9,
|
|
35
|
+
'CRC32C' => 9,
|
|
36
|
+
'CRC64NVME' => 13,
|
|
37
|
+
# SHA functions need 1 byte padding because of how they are encoded
|
|
38
|
+
'SHA1' => 28 + 1,
|
|
39
|
+
'SHA256' => 44 + 1
|
|
40
|
+
}.freeze
|
|
41
|
+
|
|
42
|
+
option(:request_checksum_calculation,
|
|
43
|
+
doc_default: 'when_supported',
|
|
44
|
+
doc_type: 'String',
|
|
45
|
+
docstring: <<~DOCS) do |cfg|
|
|
46
|
+
Determines when a checksum will be calculated for request payloads. Values are:
|
|
47
|
+
|
|
48
|
+
* `when_supported` - (default) When set, a checksum will be
|
|
49
|
+
calculated for all request payloads of operations modeled with the
|
|
50
|
+
`httpChecksum` trait where `requestChecksumRequired` is `true` and/or a
|
|
51
|
+
`requestAlgorithmMember` is modeled.
|
|
52
|
+
* `when_required` - When set, a checksum will only be calculated for
|
|
53
|
+
request payloads of operations modeled with the `httpChecksum` trait where
|
|
54
|
+
`requestChecksumRequired` is `true` or where a `requestAlgorithmMember`
|
|
55
|
+
is modeled and supplied.
|
|
56
|
+
DOCS
|
|
57
|
+
resolve_request_checksum_calculation(cfg)
|
|
58
|
+
end
|
|
32
59
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
60
|
+
option(:response_checksum_validation,
|
|
61
|
+
doc_default: 'when_supported',
|
|
62
|
+
doc_type: 'String',
|
|
63
|
+
docstring: <<~DOCS) do |cfg|
|
|
64
|
+
Determines when checksum validation will be performed on response payloads. Values are:
|
|
65
|
+
|
|
66
|
+
* `when_supported` - (default) When set, checksum validation is performed on all
|
|
67
|
+
response payloads of operations modeled with the `httpChecksum` trait where
|
|
68
|
+
`responseAlgorithms` is modeled, except when no modeled checksum algorithms
|
|
69
|
+
are supported.
|
|
70
|
+
* `when_required` - When set, checksum validation is not performed on
|
|
71
|
+
response payloads of operations unless the checksum algorithm is supported and
|
|
72
|
+
the `requestValidationModeMember` member is set to `ENABLED`.
|
|
73
|
+
DOCS
|
|
74
|
+
resolve_response_checksum_validation(cfg)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class << self
|
|
78
|
+
def digest_for_algorithm(algorithm)
|
|
79
|
+
case algorithm
|
|
80
|
+
when 'CRC32'
|
|
81
|
+
Digest.new(Zlib.method(:crc32), 'N')
|
|
82
|
+
when 'CRC32C'
|
|
83
|
+
Digest.new(Aws::Crt::Checksums.method(:crc32c), 'N')
|
|
84
|
+
when 'CRC64NVME'
|
|
85
|
+
Digest.new(Aws::Crt::Checksums.method(:crc64nvme), 'Q>')
|
|
86
|
+
when 'SHA1'
|
|
87
|
+
::Digest::SHA1.new
|
|
88
|
+
when 'SHA256'
|
|
89
|
+
::Digest::SHA256.new
|
|
90
|
+
else
|
|
91
|
+
raise ArgumentError,
|
|
92
|
+
"#{algorithm} is not a supported checksum algorithm."
|
|
93
|
+
end
|
|
94
|
+
end
|
|
38
95
|
|
|
39
|
-
|
|
96
|
+
# The trailer size (in bytes) is the overhead (0, \r, \n) + the trailer
|
|
97
|
+
# name + the bytesize of the base64 encoded checksum.
|
|
98
|
+
def trailer_length(algorithm, location_name)
|
|
99
|
+
7 + location_name.size + CHECKSUM_SIZE[algorithm]
|
|
100
|
+
end
|
|
40
101
|
|
|
41
|
-
|
|
42
|
-
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def resolve_request_checksum_calculation(cfg)
|
|
105
|
+
mode = ENV['AWS_REQUEST_CHECKSUM_CALCULATION'] ||
|
|
106
|
+
Aws.shared_config.request_checksum_calculation(profile: cfg.profile) ||
|
|
107
|
+
'when_supported'
|
|
108
|
+
mode = mode.downcase
|
|
109
|
+
unless %w[when_supported when_required].include?(mode)
|
|
110
|
+
raise ArgumentError,
|
|
111
|
+
'expected :request_checksum_calculation or' \
|
|
112
|
+
" ENV['AWS_REQUEST_CHECKSUM_CALCULATION'] to be " \
|
|
113
|
+
'`when_supported` or `when_required`.'
|
|
114
|
+
end
|
|
115
|
+
mode
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def resolve_response_checksum_validation(cfg)
|
|
119
|
+
mode = ENV['AWS_RESPONSE_CHECKSUM_VALIDATION'] ||
|
|
120
|
+
Aws.shared_config.response_checksum_validation(profile: cfg.profile) ||
|
|
121
|
+
'when_supported'
|
|
122
|
+
mode = mode.downcase
|
|
123
|
+
unless %w[when_supported when_required].include?(mode)
|
|
124
|
+
raise ArgumentError,
|
|
125
|
+
'expected :response_checksum_validation or' \
|
|
126
|
+
" ENV['AWS_RESPONSE_CHECKSUM_VALIDATION'] to be " \
|
|
127
|
+
'`when_supported` or `when_required`.'
|
|
128
|
+
end
|
|
129
|
+
mode
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Interface for computing digests on request/response bodies
|
|
134
|
+
# which may be files, strings or IO like objects.
|
|
135
|
+
# Applies only to digest functions that produce 32 or 64 bit
|
|
136
|
+
# integer checksums (eg CRC32 or CRC64).
|
|
137
|
+
class Digest
|
|
138
|
+
def initialize(digest_fn, directive)
|
|
43
139
|
@digest_fn = digest_fn
|
|
140
|
+
@directive = directive
|
|
44
141
|
@value = 0
|
|
45
142
|
end
|
|
46
143
|
|
|
@@ -49,124 +146,255 @@ module Aws
|
|
|
49
146
|
end
|
|
50
147
|
|
|
51
148
|
def base64digest
|
|
52
|
-
Base64.encode64([@value].pack(
|
|
149
|
+
Base64.encode64([@value].pack(@directive)).chomp
|
|
53
150
|
end
|
|
54
151
|
end
|
|
55
152
|
|
|
56
153
|
def add_handlers(handlers, _config)
|
|
57
154
|
handlers.add(OptionHandler, step: :initialize)
|
|
58
|
-
#
|
|
59
|
-
# built but before it is signed
|
|
155
|
+
# Priority is set low to ensure the checksum is computed AFTER the
|
|
156
|
+
# request is built but before it is signed.
|
|
60
157
|
handlers.add(ChecksumHandler, priority: 15, step: :build)
|
|
61
158
|
end
|
|
62
159
|
|
|
63
|
-
|
|
160
|
+
class OptionHandler < Seahorse::Client::Handler
|
|
161
|
+
def call(context)
|
|
162
|
+
context[:http_checksum] ||= {}
|
|
64
163
|
|
|
65
|
-
|
|
66
|
-
|
|
164
|
+
# Set validation mode to enabled when supported.
|
|
165
|
+
enable_request_validation_mode(context) if context.config.response_checksum_validation == 'when_supported'
|
|
67
166
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
167
|
+
@handler.call(context)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
71
171
|
|
|
72
|
-
|
|
73
|
-
|
|
172
|
+
def enable_request_validation_mode(context)
|
|
173
|
+
return unless context.operation.http_checksum
|
|
74
174
|
|
|
75
|
-
|
|
76
|
-
|
|
175
|
+
input_member = context.operation.http_checksum['requestValidationModeMember']
|
|
176
|
+
context.params[input_member.to_sym] ||= 'ENABLED' if input_member
|
|
177
|
+
end
|
|
77
178
|
end
|
|
78
179
|
|
|
79
|
-
|
|
80
|
-
|
|
180
|
+
class ChecksumHandler < Seahorse::Client::Handler
|
|
181
|
+
def call(context)
|
|
182
|
+
algorithm = nil
|
|
183
|
+
if should_calculate_request_checksum?(context)
|
|
184
|
+
algorithm = choose_request_algorithm!(context)
|
|
185
|
+
request_algorithm = {
|
|
186
|
+
algorithm: algorithm,
|
|
187
|
+
in: checksum_request_in(context),
|
|
188
|
+
name: "x-amz-checksum-#{algorithm.downcase}",
|
|
189
|
+
request_algorithm_header: request_algorithm_header(context)
|
|
190
|
+
}
|
|
191
|
+
context[:http_checksum][:request_algorithm] = request_algorithm
|
|
192
|
+
calculate_request_checksum(context, request_algorithm)
|
|
193
|
+
end
|
|
81
194
|
|
|
82
|
-
|
|
83
|
-
end
|
|
195
|
+
add_verify_response_checksum_handlers(context) if should_verify_response_checksum?(context)
|
|
84
196
|
|
|
197
|
+
with_metrics(context.config, algorithm) { @handler.call(context) }
|
|
198
|
+
end
|
|
85
199
|
|
|
86
|
-
|
|
87
|
-
class OptionHandler < Seahorse::Client::Handler
|
|
88
|
-
def call(context)
|
|
89
|
-
context[:http_checksum] ||= {}
|
|
200
|
+
private
|
|
90
201
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
202
|
+
def with_metrics(config, algorithm, &block)
|
|
203
|
+
metrics = []
|
|
204
|
+
add_request_config_metric(config, metrics)
|
|
205
|
+
add_response_config_metric(config, metrics)
|
|
206
|
+
add_request_checksum_metrics(algorithm, metrics)
|
|
207
|
+
Aws::Plugins::UserAgent.metric(*metrics, &block)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def add_request_config_metric(config, metrics)
|
|
211
|
+
case config.request_checksum_calculation
|
|
212
|
+
when 'when_supported'
|
|
213
|
+
metrics << 'FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED'
|
|
214
|
+
when 'when_required'
|
|
215
|
+
metrics << 'FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED'
|
|
100
216
|
end
|
|
217
|
+
end
|
|
101
218
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
context[:http_checksum][:validation_list] = validation_list
|
|
219
|
+
def add_response_config_metric(config, metrics)
|
|
220
|
+
case config.response_checksum_validation
|
|
221
|
+
when 'when_supported'
|
|
222
|
+
metrics << 'FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED'
|
|
223
|
+
when 'when_required'
|
|
224
|
+
metrics << 'FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED'
|
|
109
225
|
end
|
|
226
|
+
end
|
|
110
227
|
|
|
111
|
-
|
|
228
|
+
def add_request_checksum_metrics(algorithm, metrics)
|
|
229
|
+
case algorithm
|
|
230
|
+
when 'CRC32'
|
|
231
|
+
metrics << 'FLEXIBLE_CHECKSUMS_REQ_CRC32'
|
|
232
|
+
when 'CRC32C'
|
|
233
|
+
metrics << 'FLEXIBLE_CHECKSUMS_REQ_CRC32C'
|
|
234
|
+
when 'CRC64NVME'
|
|
235
|
+
metrics << 'FLEXIBLE_CHECKSUMS_REQ_CRC64'
|
|
236
|
+
when 'SHA1'
|
|
237
|
+
metrics << 'FLEXIBLE_CHECKSUMS_REQ_SHA1'
|
|
238
|
+
when 'SHA256'
|
|
239
|
+
metrics << 'FLEXIBLE_CHECKSUMS_REQ_SHA256'
|
|
240
|
+
end
|
|
112
241
|
end
|
|
113
|
-
end
|
|
114
242
|
|
|
115
|
-
|
|
116
|
-
|
|
243
|
+
def request_algorithm_selection(context)
|
|
244
|
+
return unless context.operation.http_checksum
|
|
117
245
|
|
|
118
|
-
|
|
119
|
-
if should_calculate_request_checksum?(context)
|
|
120
|
-
request_algorithm_input = ChecksumAlgorithm.request_algorithm_selection(context)
|
|
121
|
-
context[:checksum_algorithms] = request_algorithm_input
|
|
246
|
+
input_member = context.operation.http_checksum['requestAlgorithmMember']
|
|
122
247
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
248
|
+
context.params[input_member.to_sym] ||= DEFAULT_CHECKSUM if input_member
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def request_algorithm_header(context)
|
|
252
|
+
input_member = context.operation.http_checksum['requestAlgorithmMember']
|
|
253
|
+
shape = context.operation.input.shape.member(input_member)
|
|
254
|
+
shape.location_name if shape && shape.location == 'header'
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def request_validation_mode(context)
|
|
258
|
+
return unless context.operation.http_checksum
|
|
259
|
+
|
|
260
|
+
input_member = context.operation.http_checksum['requestValidationModeMember']
|
|
261
|
+
context.params[input_member.to_sym] if input_member
|
|
262
|
+
end
|
|
128
263
|
|
|
129
|
-
|
|
264
|
+
def operation_response_algorithms(context)
|
|
265
|
+
return unless context.operation.http_checksum
|
|
266
|
+
|
|
267
|
+
context.operation.http_checksum['responseAlgorithms']
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def checksum_provided_as_header?(headers)
|
|
271
|
+
headers.any? { |k, _| k.start_with?('x-amz-checksum-') }
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Determines whether a request checksum should be calculated.
|
|
275
|
+
# 1. **No existing checksum in header**: Skips if checksum header already present
|
|
276
|
+
# 2. **Operation support**: Considers model, client configuration and user input.
|
|
277
|
+
def should_calculate_request_checksum?(context)
|
|
278
|
+
!checksum_provided_as_header?(context.http_request.headers) && checksum_applicable?(context)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Checks if checksum calculation should proceed based on operation requirements and client settings.
|
|
282
|
+
# Returns true when any of these conditions are met:
|
|
283
|
+
# 1. http checksum's requestChecksumRequired is true
|
|
284
|
+
# 2. Config for request_checksum_calculation is "when_supported"
|
|
285
|
+
# 3. Config for request_checksum_calculation is "when_required" AND user provided checksum algorithm
|
|
286
|
+
def checksum_applicable?(context)
|
|
287
|
+
http_checksum = context.operation.http_checksum
|
|
288
|
+
return false unless http_checksum
|
|
289
|
+
|
|
290
|
+
return true if http_checksum['requestChecksumRequired']
|
|
291
|
+
|
|
292
|
+
return false unless (algorithm_member = http_checksum['requestAlgorithmMember'])
|
|
293
|
+
|
|
294
|
+
case context.config.request_checksum_calculation
|
|
295
|
+
when 'when_supported'
|
|
296
|
+
true
|
|
297
|
+
when 'when_required'
|
|
298
|
+
!context.params[algorithm_member.to_sym].nil?
|
|
299
|
+
else
|
|
300
|
+
false
|
|
130
301
|
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def choose_request_algorithm!(context)
|
|
305
|
+
algorithm = request_algorithm_selection(context).upcase
|
|
306
|
+
return algorithm if CLIENT_ALGORITHMS.include?(algorithm)
|
|
131
307
|
|
|
132
|
-
if
|
|
133
|
-
|
|
308
|
+
if CRT_ALGORITHMS.include?(algorithm)
|
|
309
|
+
raise ArgumentError,
|
|
310
|
+
'CRC32C and CRC64NVME requires CRT support ' \
|
|
311
|
+
'- install the aws-crt gem'
|
|
134
312
|
end
|
|
135
313
|
|
|
136
|
-
|
|
314
|
+
raise ArgumentError,
|
|
315
|
+
"#{algorithm} is not a supported checksum algorithm."
|
|
137
316
|
end
|
|
138
317
|
|
|
139
|
-
|
|
318
|
+
def checksum_request_in(context)
|
|
319
|
+
return 'header' unless supports_trailer_checksums?(context.operation)
|
|
140
320
|
|
|
141
|
-
|
|
142
|
-
context.operation.http_checksum &&
|
|
143
|
-
ChecksumAlgorithm.request_algorithm_selection(context)
|
|
321
|
+
should_fallback_to_header?(context) ? 'header' : 'trailer'
|
|
144
322
|
end
|
|
145
323
|
|
|
146
|
-
def
|
|
147
|
-
|
|
324
|
+
def supports_trailer_checksums?(operation)
|
|
325
|
+
operation['unsignedPayload'] || operation['authtype'] == 'v4-unsigned-body'
|
|
148
326
|
end
|
|
149
327
|
|
|
150
328
|
def calculate_request_checksum(context, checksum_properties)
|
|
151
|
-
|
|
329
|
+
headers = context.http_request.headers
|
|
330
|
+
if (algorithm_header = checksum_properties[:request_algorithm_header])
|
|
331
|
+
headers[algorithm_header] = checksum_properties[:algorithm]
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
case checksum_properties[:in]
|
|
152
335
|
when 'header'
|
|
153
|
-
|
|
154
|
-
body = context.http_request.body_contents
|
|
155
|
-
if body
|
|
156
|
-
context.http_request.headers[header_name] ||=
|
|
157
|
-
ChecksumAlgorithm.calculate_checksum(checksum_properties['algorithm'], body)
|
|
158
|
-
end
|
|
336
|
+
apply_request_checksum(context, headers, checksum_properties)
|
|
159
337
|
when 'trailer'
|
|
160
|
-
apply_request_trailer_checksum(context, checksum_properties)
|
|
338
|
+
apply_request_trailer_checksum(context, headers, checksum_properties)
|
|
339
|
+
else
|
|
340
|
+
# nothing
|
|
161
341
|
end
|
|
162
342
|
end
|
|
163
343
|
|
|
164
|
-
def
|
|
165
|
-
|
|
344
|
+
def should_fallback_to_header?(context)
|
|
345
|
+
# Trailer implementation within Mac/JRUBY environment is facing some
|
|
346
|
+
# network issues that will need further investigation:
|
|
347
|
+
# * https://github.com/jruby/jruby-openssl/issues/271
|
|
348
|
+
# * https://github.com/jruby/jruby-openssl/issues/317
|
|
349
|
+
return true if defined?(JRUBY_VERSION)
|
|
350
|
+
|
|
351
|
+
# Chunked signing is currently not supported
|
|
352
|
+
# Https is required for unsigned payload for security
|
|
353
|
+
return true if context.http_request.endpoint.scheme == 'http'
|
|
354
|
+
|
|
355
|
+
context[:skip_trailer_checksums]
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def apply_request_checksum(context, headers, checksum_properties)
|
|
359
|
+
header_name = checksum_properties[:name]
|
|
360
|
+
headers[header_name] = calculate_checksum(
|
|
361
|
+
checksum_properties[:algorithm],
|
|
362
|
+
context.http_request.body
|
|
363
|
+
)
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def calculate_checksum(algorithm, body)
|
|
367
|
+
digest = ChecksumAlgorithm.digest_for_algorithm(algorithm)
|
|
368
|
+
if body.respond_to?(:read)
|
|
369
|
+
body.rewind
|
|
370
|
+
update_in_chunks(digest, body)
|
|
371
|
+
body.rewind
|
|
372
|
+
else
|
|
373
|
+
digest.update(body)
|
|
374
|
+
end
|
|
375
|
+
digest.base64digest
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def update_in_chunks(digest, io)
|
|
379
|
+
loop do
|
|
380
|
+
chunk = io.read(CHECKSUM_CHUNK_SIZE)
|
|
381
|
+
break unless chunk
|
|
382
|
+
|
|
383
|
+
digest.update(chunk)
|
|
384
|
+
end
|
|
385
|
+
io.rewind
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def apply_request_trailer_checksum(context, headers, checksum_properties)
|
|
389
|
+
location_name = checksum_properties[:name]
|
|
166
390
|
|
|
167
391
|
# set required headers
|
|
168
|
-
headers =
|
|
169
|
-
|
|
392
|
+
headers['Content-Encoding'] =
|
|
393
|
+
if headers['Content-Encoding']
|
|
394
|
+
headers['Content-Encoding'] += ', aws-chunked'
|
|
395
|
+
else
|
|
396
|
+
'aws-chunked'
|
|
397
|
+
end
|
|
170
398
|
headers['X-Amz-Content-Sha256'] = 'STREAMING-UNSIGNED-PAYLOAD-TRAILER'
|
|
171
399
|
headers['X-Amz-Trailer'] = location_name
|
|
172
400
|
|
|
@@ -174,165 +402,172 @@ module Aws
|
|
|
174
402
|
# to set the Content-Length header (set by content_length plugin).
|
|
175
403
|
# This means we cannot use Transfer-Encoding=chunked
|
|
176
404
|
|
|
177
|
-
|
|
405
|
+
unless context.http_request.body.respond_to?(:size)
|
|
178
406
|
raise Aws::Errors::ChecksumError, 'Could not determine length of the body'
|
|
179
407
|
end
|
|
408
|
+
|
|
180
409
|
headers['X-Amz-Decoded-Content-Length'] = context.http_request.body.size
|
|
410
|
+
context.http_request.body =
|
|
411
|
+
AwsChunkedTrailerDigestIO.new(
|
|
412
|
+
io: context.http_request.body,
|
|
413
|
+
algorithm: checksum_properties[:algorithm],
|
|
414
|
+
location_name: location_name
|
|
415
|
+
)
|
|
416
|
+
end
|
|
181
417
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
checksum_properties['algorithm'],
|
|
185
|
-
location_name
|
|
186
|
-
)
|
|
418
|
+
def should_verify_response_checksum?(context)
|
|
419
|
+
request_validation_mode(context) == 'ENABLED'
|
|
187
420
|
end
|
|
188
421
|
|
|
189
422
|
# Add events to the http_response to verify the checksum as its read
|
|
190
423
|
# This prevents the body from being read multiple times
|
|
191
424
|
# verification is done only once a successful response has completed
|
|
192
425
|
def add_verify_response_checksum_handlers(context)
|
|
193
|
-
|
|
194
|
-
checksum_context
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
426
|
+
checksum_context = {}
|
|
427
|
+
add_verify_response_headers_handler(context, checksum_context)
|
|
428
|
+
add_verify_response_data_handler(context, checksum_context)
|
|
429
|
+
add_verify_response_success_handler(context, checksum_context)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def add_verify_response_headers_handler(context, checksum_context)
|
|
433
|
+
validation_list = CHECKSUM_ALGORITHM_PRIORITIES & operation_response_algorithms(context)
|
|
434
|
+
context[:http_checksum][:validation_list] = validation_list
|
|
435
|
+
|
|
436
|
+
context.http_response.on_headers do |_status, headers|
|
|
437
|
+
header_name, algorithm = response_header_to_verify(headers, validation_list)
|
|
438
|
+
next unless header_name
|
|
439
|
+
|
|
440
|
+
expected = headers[header_name]
|
|
441
|
+
next if context[:http_checksum][:skip_on_suffix] && /-\d+$/.match(expected)
|
|
208
442
|
|
|
209
|
-
|
|
210
|
-
checksum_context[:
|
|
443
|
+
checksum_context[:algorithm] = algorithm
|
|
444
|
+
checksum_context[:header_name] = header_name
|
|
445
|
+
checksum_context[:digest] = ChecksumAlgorithm.digest_for_algorithm(algorithm)
|
|
446
|
+
checksum_context[:expected] = expected
|
|
211
447
|
end
|
|
448
|
+
end
|
|
212
449
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
450
|
+
def add_verify_response_data_handler(context, checksum_context)
|
|
451
|
+
context.http_response.on_data do |chunk|
|
|
452
|
+
checksum_context[:digest]&.update(chunk)
|
|
453
|
+
end
|
|
454
|
+
end
|
|
216
455
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
"computed: #{computed}, expected: #{checksum_context[:expected]}"
|
|
221
|
-
end
|
|
456
|
+
def add_verify_response_success_handler(context, checksum_context)
|
|
457
|
+
context.http_response.on_success do
|
|
458
|
+
next unless checksum_context[:digest]
|
|
222
459
|
|
|
460
|
+
computed = checksum_context[:digest].base64digest
|
|
461
|
+
if computed == checksum_context[:expected]
|
|
223
462
|
context[:http_checksum][:validated] = checksum_context[:algorithm]
|
|
463
|
+
else
|
|
464
|
+
raise Aws::Errors::ChecksumError,
|
|
465
|
+
"Checksum validation failed on #{checksum_context[:header_name]} "\
|
|
466
|
+
"computed: #{computed}, expected: #{checksum_context[:expected]}"
|
|
224
467
|
end
|
|
225
468
|
end
|
|
226
469
|
end
|
|
227
470
|
|
|
228
|
-
# returns nil if no headers to verify
|
|
229
471
|
def response_header_to_verify(headers, validation_list)
|
|
230
472
|
validation_list.each do |algorithm|
|
|
231
|
-
header_name = "x-amz-checksum-#{algorithm}"
|
|
473
|
+
header_name = "x-amz-checksum-#{algorithm.downcase}"
|
|
232
474
|
return [header_name, algorithm] if headers[header_name]
|
|
233
475
|
end
|
|
234
476
|
nil
|
|
235
477
|
end
|
|
236
|
-
|
|
237
|
-
# determine where (header vs trailer) a request checksum should be added
|
|
238
|
-
def checksum_request_in(context)
|
|
239
|
-
if context.operation['authtype'].eql?('v4-unsigned-body')
|
|
240
|
-
'trailer'
|
|
241
|
-
else
|
|
242
|
-
'header'
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def self.calculate_checksum(algorithm, body)
|
|
249
|
-
digest = ChecksumAlgorithm.digest_for_algorithm(algorithm)
|
|
250
|
-
if body.respond_to?(:read)
|
|
251
|
-
ChecksumAlgorithm.update_in_chunks(digest, body)
|
|
252
|
-
else
|
|
253
|
-
digest.update(body)
|
|
254
|
-
end
|
|
255
|
-
digest.base64digest
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
def self.digest_for_algorithm(algorithm)
|
|
259
|
-
case algorithm
|
|
260
|
-
when 'CRC32'
|
|
261
|
-
Digest32.new(Zlib.method(:crc32))
|
|
262
|
-
when 'CRC32C'
|
|
263
|
-
# this will only be used if input algorithm is CRC32C AND client supports it (crt available)
|
|
264
|
-
Digest32.new(Aws::Crt::Checksums.method(:crc32c))
|
|
265
|
-
when 'SHA1'
|
|
266
|
-
Digest::SHA1.new
|
|
267
|
-
when 'SHA256'
|
|
268
|
-
Digest::SHA256.new
|
|
269
|
-
end
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
# The trailer size (in bytes) is the overhead + the trailer name +
|
|
273
|
-
# the length of the base64 encoded checksum
|
|
274
|
-
def self.trailer_length(algorithm, location_name)
|
|
275
|
-
CHECKSUM_SIZE[algorithm] + location_name.size
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
def self.update_in_chunks(digest, io)
|
|
279
|
-
loop do
|
|
280
|
-
chunk = io.read(CHUNK_SIZE)
|
|
281
|
-
break unless chunk
|
|
282
|
-
digest.update(chunk)
|
|
283
|
-
end
|
|
284
|
-
io.rewind
|
|
285
478
|
end
|
|
286
479
|
|
|
287
480
|
# Wrapper for request body that implements application-layer
|
|
288
481
|
# chunking with Digest computed on chunks + added as a trailer
|
|
289
482
|
class AwsChunkedTrailerDigestIO
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
@
|
|
295
|
-
@
|
|
296
|
-
@
|
|
297
|
-
@
|
|
483
|
+
CHUNK_OVERHEAD = 4 # "\r\n\r\n"
|
|
484
|
+
HEX_BASE = 16
|
|
485
|
+
|
|
486
|
+
def initialize(options = {})
|
|
487
|
+
@io = options.delete(:io)
|
|
488
|
+
@location_name = options.delete(:location_name)
|
|
489
|
+
@algorithm = options.delete(:algorithm)
|
|
490
|
+
@digest = ChecksumAlgorithm.digest_for_algorithm(@algorithm)
|
|
491
|
+
@chunk_size = Thread.current[:net_http_override_body_stream_chunk] || DEFAULT_TRAILER_CHUNK_SIZE
|
|
492
|
+
@overhead_bytes = calculate_overhead(@chunk_size)
|
|
493
|
+
@base_chunk_size = @chunk_size - @overhead_bytes
|
|
494
|
+
@encoded_buffer = +''
|
|
495
|
+
@eof = false
|
|
298
496
|
end
|
|
299
497
|
|
|
300
498
|
# the size of the application layer aws-chunked + trailer body
|
|
301
499
|
def size
|
|
302
|
-
# compute the number of chunks
|
|
303
|
-
# a full chunk has 4 + 4 bytes overhead, a partial chunk is len.to_s(16).size + 4
|
|
304
500
|
orig_body_size = @io.size
|
|
305
|
-
n_full_chunks = orig_body_size /
|
|
306
|
-
partial_bytes = orig_body_size %
|
|
307
|
-
|
|
308
|
-
|
|
501
|
+
n_full_chunks = orig_body_size / @base_chunk_size
|
|
502
|
+
partial_bytes = orig_body_size % @base_chunk_size
|
|
503
|
+
|
|
504
|
+
full_chunk_overhead = @base_chunk_size.to_s(HEX_BASE).size + CHUNK_OVERHEAD
|
|
505
|
+
chunked_body_size = n_full_chunks * (@base_chunk_size + full_chunk_overhead)
|
|
506
|
+
unless partial_bytes.zero?
|
|
507
|
+
chunked_body_size += partial_bytes.to_s(HEX_BASE).size + partial_bytes + CHUNK_OVERHEAD
|
|
508
|
+
end
|
|
309
509
|
trailer_size = ChecksumAlgorithm.trailer_length(@algorithm, @location_name)
|
|
310
510
|
chunked_body_size + trailer_size
|
|
311
511
|
end
|
|
312
512
|
|
|
313
513
|
def rewind
|
|
314
514
|
@io.rewind
|
|
515
|
+
@encoded_buffer = +''
|
|
516
|
+
@eof = false
|
|
517
|
+
@digest = ChecksumAlgorithm.digest_for_algorithm(@algorithm)
|
|
315
518
|
end
|
|
316
519
|
|
|
317
|
-
def read(length, buf = nil)
|
|
318
|
-
|
|
319
|
-
if
|
|
320
|
-
|
|
321
|
-
|
|
520
|
+
def read(length = nil, buf = nil)
|
|
521
|
+
return '' if length&.zero?
|
|
522
|
+
return if eof?
|
|
523
|
+
|
|
524
|
+
buf&.clear
|
|
525
|
+
output_buffer = buf || +''
|
|
526
|
+
|
|
527
|
+
fill_encoded_buffer(length)
|
|
322
528
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
@digest.update(chunk)
|
|
326
|
-
application_chunked = "#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n"
|
|
327
|
-
return StringIO.new(application_chunked).read(application_chunked.size, buf)
|
|
529
|
+
if length
|
|
530
|
+
output_buffer << @encoded_buffer.slice!(0, length)
|
|
328
531
|
else
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
532
|
+
output_buffer << @encoded_buffer
|
|
533
|
+
@encoded_buffer.clear
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
output_buffer.empty? && eof? ? nil : output_buffer
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
def eof?
|
|
540
|
+
@eof && @encoded_buffer.empty?
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
private
|
|
544
|
+
|
|
545
|
+
def calculate_overhead(chunk_size)
|
|
546
|
+
chunk_size.to_s(HEX_BASE).size + CHUNK_OVERHEAD
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
def fill_encoded_buffer(required_length)
|
|
550
|
+
return if required_length && @encoded_buffer.bytesize >= required_length
|
|
551
|
+
|
|
552
|
+
while !@eof && fill_data?(required_length)
|
|
553
|
+
chunk = @io.read(@base_chunk_size)
|
|
554
|
+
if chunk && !chunk.empty?
|
|
555
|
+
@digest.update(chunk)
|
|
556
|
+
@encoded_buffer << "#{chunk.bytesize.to_s(HEX_BASE)}\r\n#{chunk}\r\n"
|
|
557
|
+
else
|
|
558
|
+
@encoded_buffer << "0\r\n#{trailer_string}\r\n\r\n"
|
|
559
|
+
@eof = true
|
|
560
|
+
end
|
|
334
561
|
end
|
|
335
|
-
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
def trailer_string
|
|
565
|
+
{ @location_name => @digest.base64digest }.map { |k, v| "#{k}:#{v}" }.join("\r\n")
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# Returns true if more data needs to be read into the buffer
|
|
569
|
+
def fill_data?(length)
|
|
570
|
+
length.nil? || @encoded_buffer.bytesize < length
|
|
336
571
|
end
|
|
337
572
|
end
|
|
338
573
|
end
|