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.
Files changed (205) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +715 -0
  3. data/VERSION +1 -1
  4. data/lib/aws-defaults.rb +4 -1
  5. data/lib/aws-sdk-core/arn.rb +1 -3
  6. data/lib/aws-sdk-core/assume_role_credentials.rb +21 -13
  7. data/lib/aws-sdk-core/assume_role_web_identity_credentials.rb +16 -9
  8. data/lib/aws-sdk-core/binary/decode_handler.rb +3 -9
  9. data/lib/aws-sdk-core/binary/encode_handler.rb +1 -1
  10. data/lib/aws-sdk-core/binary/event_builder.rb +34 -37
  11. data/lib/aws-sdk-core/binary/event_stream_decoder.rb +1 -0
  12. data/lib/aws-sdk-core/binary/event_stream_encoder.rb +4 -3
  13. data/lib/aws-sdk-core/cbor/decoder.rb +308 -0
  14. data/lib/aws-sdk-core/cbor/encoder.rb +243 -0
  15. data/lib/aws-sdk-core/cbor.rb +53 -0
  16. data/lib/aws-sdk-core/client_side_monitoring.rb +9 -0
  17. data/lib/aws-sdk-core/client_stubs.rb +39 -55
  18. data/lib/aws-sdk-core/credential_provider.rb +5 -1
  19. data/lib/aws-sdk-core/credential_provider_chain.rb +101 -25
  20. data/lib/aws-sdk-core/credentials.rb +19 -6
  21. data/lib/aws-sdk-core/ec2_metadata.rb +1 -1
  22. data/lib/aws-sdk-core/ecs_credentials.rb +92 -24
  23. data/lib/aws-sdk-core/endpoints/endpoint.rb +3 -1
  24. data/lib/aws-sdk-core/endpoints/matchers.rb +8 -10
  25. data/lib/aws-sdk-core/endpoints.rb +101 -21
  26. data/lib/aws-sdk-core/error_handler.rb +46 -0
  27. data/lib/aws-sdk-core/errors.rb +16 -4
  28. data/lib/aws-sdk-core/event_emitter.rb +1 -17
  29. data/lib/aws-sdk-core/instance_profile_credentials.rb +148 -157
  30. data/lib/aws-sdk-core/json/builder.rb +8 -1
  31. data/lib/aws-sdk-core/json/error_handler.rb +29 -13
  32. data/lib/aws-sdk-core/json/handler.rb +13 -6
  33. data/lib/aws-sdk-core/json/json_engine.rb +3 -1
  34. data/lib/aws-sdk-core/json/oj_engine.rb +7 -1
  35. data/lib/aws-sdk-core/json/parser.rb +32 -2
  36. data/lib/aws-sdk-core/json.rb +43 -14
  37. data/lib/aws-sdk-core/log/param_filter.rb +2 -2
  38. data/lib/aws-sdk-core/log/param_formatter.rb +7 -3
  39. data/lib/aws-sdk-core/log.rb +10 -0
  40. data/lib/aws-sdk-core/login_credentials.rb +229 -0
  41. data/lib/aws-sdk-core/lru_cache.rb +75 -0
  42. data/lib/aws-sdk-core/pageable_response.rb +1 -1
  43. data/lib/aws-sdk-core/param_validator.rb +7 -2
  44. data/lib/aws-sdk-core/plugins/bearer_authorization.rb +2 -0
  45. data/lib/aws-sdk-core/plugins/checksum_algorithm.rb +436 -201
  46. data/lib/aws-sdk-core/plugins/client_metrics_plugin.rb +1 -1
  47. data/lib/aws-sdk-core/plugins/client_metrics_send_plugin.rb +14 -2
  48. data/lib/aws-sdk-core/plugins/credentials_configuration.rb +78 -56
  49. data/lib/aws-sdk-core/plugins/endpoint_pattern.rb +40 -32
  50. data/lib/aws-sdk-core/plugins/global_configuration.rb +8 -9
  51. data/lib/aws-sdk-core/plugins/http_checksum.rb +3 -8
  52. data/lib/aws-sdk-core/plugins/invocation_id.rb +1 -11
  53. data/lib/aws-sdk-core/plugins/logging.rb +2 -0
  54. data/lib/aws-sdk-core/plugins/protocols/api_gateway.rb +3 -1
  55. data/lib/aws-sdk-core/plugins/protocols/ec2.rb +2 -24
  56. data/lib/aws-sdk-core/plugins/protocols/json_rpc.rb +6 -8
  57. data/lib/aws-sdk-core/plugins/protocols/query.rb +4 -2
  58. data/lib/aws-sdk-core/plugins/protocols/rest_json.rb +3 -15
  59. data/lib/aws-sdk-core/plugins/protocols/rest_xml.rb +3 -0
  60. data/lib/aws-sdk-core/plugins/protocols/rpc_v2.rb +17 -0
  61. data/lib/aws-sdk-core/plugins/regional_endpoint.rb +74 -25
  62. data/lib/aws-sdk-core/plugins/request_compression.rb +11 -2
  63. data/lib/aws-sdk-core/plugins/retries/clock_skew.rb +28 -16
  64. data/lib/aws-sdk-core/plugins/retry_errors.rb +12 -3
  65. data/lib/aws-sdk-core/plugins/sign.rb +55 -34
  66. data/lib/aws-sdk-core/plugins/signature_v2.rb +2 -1
  67. data/lib/aws-sdk-core/plugins/signature_v4.rb +2 -1
  68. data/lib/aws-sdk-core/plugins/stub_responses.rb +59 -9
  69. data/lib/aws-sdk-core/plugins/telemetry.rb +75 -0
  70. data/lib/aws-sdk-core/plugins/transfer_encoding.rb +16 -9
  71. data/lib/aws-sdk-core/plugins/user_agent.rb +103 -26
  72. data/lib/aws-sdk-core/plugins.rb +39 -0
  73. data/lib/aws-sdk-core/process_credentials.rb +48 -29
  74. data/lib/aws-sdk-core/query/ec2_handler.rb +27 -0
  75. data/lib/aws-sdk-core/query/ec2_param_builder.rb +5 -7
  76. data/lib/aws-sdk-core/query/handler.rb +4 -4
  77. data/lib/aws-sdk-core/query/param_builder.rb +2 -2
  78. data/lib/aws-sdk-core/query.rb +2 -1
  79. data/lib/aws-sdk-core/refreshing_credentials.rb +20 -17
  80. data/lib/aws-sdk-core/resources.rb +8 -0
  81. data/lib/aws-sdk-core/rest/content_type_handler.rb +60 -0
  82. data/lib/aws-sdk-core/rest/handler.rb +3 -4
  83. data/lib/aws-sdk-core/rest/request/body.rb +32 -5
  84. data/lib/aws-sdk-core/rest/request/endpoint.rb +24 -4
  85. data/lib/aws-sdk-core/rest/request/headers.rb +15 -7
  86. data/lib/aws-sdk-core/rest/request/querystring_builder.rb +23 -11
  87. data/lib/aws-sdk-core/rest/response/body.rb +15 -1
  88. data/lib/aws-sdk-core/rest/response/header_list_parser.rb +79 -0
  89. data/lib/aws-sdk-core/rest/response/headers.rb +8 -3
  90. data/lib/aws-sdk-core/rest.rb +1 -0
  91. data/lib/aws-sdk-core/rpc_v2/builder.rb +62 -0
  92. data/lib/aws-sdk-core/rpc_v2/cbor_engine.rb +18 -0
  93. data/lib/aws-sdk-core/rpc_v2/content_type_handler.rb +47 -0
  94. data/lib/aws-sdk-core/rpc_v2/error_handler.rb +95 -0
  95. data/lib/aws-sdk-core/rpc_v2/handler.rb +79 -0
  96. data/lib/aws-sdk-core/rpc_v2/parser.rb +98 -0
  97. data/lib/aws-sdk-core/rpc_v2.rb +69 -0
  98. data/lib/aws-sdk-core/shared_config.rb +109 -22
  99. data/lib/aws-sdk-core/shared_credentials.rb +1 -7
  100. data/lib/aws-sdk-core/sso_credentials.rb +5 -2
  101. data/lib/aws-sdk-core/static_token_provider.rb +1 -2
  102. data/lib/aws-sdk-core/stubbing/protocols/ec2.rb +12 -11
  103. data/lib/aws-sdk-core/stubbing/protocols/json.rb +11 -10
  104. data/lib/aws-sdk-core/stubbing/protocols/query.rb +7 -6
  105. data/lib/aws-sdk-core/stubbing/protocols/rest.rb +2 -1
  106. data/lib/aws-sdk-core/stubbing/protocols/rest_json.rb +9 -8
  107. data/lib/aws-sdk-core/stubbing/protocols/rest_xml.rb +6 -5
  108. data/lib/aws-sdk-core/stubbing/protocols/rpc_v2.rb +39 -0
  109. data/lib/aws-sdk-core/stubbing.rb +22 -0
  110. data/lib/aws-sdk-core/telemetry/base.rb +177 -0
  111. data/lib/aws-sdk-core/telemetry/no_op.rb +70 -0
  112. data/lib/aws-sdk-core/telemetry/otel.rb +235 -0
  113. data/lib/aws-sdk-core/telemetry/span_kind.rb +22 -0
  114. data/lib/aws-sdk-core/telemetry/span_status.rb +59 -0
  115. data/lib/aws-sdk-core/telemetry.rb +78 -0
  116. data/lib/aws-sdk-core/token.rb +3 -3
  117. data/lib/aws-sdk-core/token_provider.rb +4 -0
  118. data/lib/aws-sdk-core/token_provider_chain.rb +2 -6
  119. data/lib/aws-sdk-core/util.rb +41 -1
  120. data/lib/aws-sdk-core/waiters/poller.rb +10 -5
  121. data/lib/aws-sdk-core/xml/builder.rb +17 -9
  122. data/lib/aws-sdk-core/xml/error_handler.rb +35 -43
  123. data/lib/aws-sdk-core/xml/parser/frame.rb +4 -20
  124. data/lib/aws-sdk-core/xml/parser/stack.rb +2 -0
  125. data/lib/aws-sdk-core/xml/parser.rb +2 -6
  126. data/lib/aws-sdk-core.rb +86 -107
  127. data/lib/aws-sdk-signin/client.rb +604 -0
  128. data/lib/aws-sdk-signin/client_api.rb +119 -0
  129. data/lib/aws-sdk-signin/customizations.rb +1 -0
  130. data/lib/aws-sdk-signin/endpoint_parameters.rb +69 -0
  131. data/lib/aws-sdk-signin/endpoint_provider.rb +59 -0
  132. data/lib/aws-sdk-signin/endpoints.rb +20 -0
  133. data/lib/aws-sdk-signin/errors.rb +122 -0
  134. data/lib/aws-sdk-signin/plugins/endpoints.rb +77 -0
  135. data/lib/aws-sdk-signin/resource.rb +26 -0
  136. data/lib/aws-sdk-signin/types.rb +299 -0
  137. data/lib/aws-sdk-signin.rb +63 -0
  138. data/lib/aws-sdk-sso/client.rb +189 -96
  139. data/lib/aws-sdk-sso/client_api.rb +7 -0
  140. data/lib/aws-sdk-sso/endpoint_parameters.rb +13 -10
  141. data/lib/aws-sdk-sso/endpoint_provider.rb +16 -20
  142. data/lib/aws-sdk-sso/endpoints.rb +2 -54
  143. data/lib/aws-sdk-sso/plugins/endpoints.rb +23 -22
  144. data/lib/aws-sdk-sso/types.rb +1 -0
  145. data/lib/aws-sdk-sso.rb +15 -11
  146. data/lib/aws-sdk-ssooidc/client.rb +609 -129
  147. data/lib/aws-sdk-ssooidc/client_api.rb +94 -1
  148. data/lib/aws-sdk-ssooidc/endpoint_parameters.rb +13 -10
  149. data/lib/aws-sdk-ssooidc/endpoint_provider.rb +16 -20
  150. data/lib/aws-sdk-ssooidc/endpoints.rb +2 -40
  151. data/lib/aws-sdk-ssooidc/errors.rb +62 -0
  152. data/lib/aws-sdk-ssooidc/plugins/endpoints.rb +23 -20
  153. data/lib/aws-sdk-ssooidc/types.rb +419 -53
  154. data/lib/aws-sdk-ssooidc.rb +15 -11
  155. data/lib/aws-sdk-sts/client.rb +537 -156
  156. data/lib/aws-sdk-sts/client_api.rb +108 -8
  157. data/lib/aws-sdk-sts/customizations.rb +5 -2
  158. data/lib/aws-sdk-sts/endpoint_parameters.rb +15 -14
  159. data/lib/aws-sdk-sts/endpoint_provider.rb +52 -57
  160. data/lib/aws-sdk-sts/endpoints.rb +2 -118
  161. data/lib/aws-sdk-sts/errors.rb +79 -0
  162. data/lib/aws-sdk-sts/plugins/endpoints.rb +23 -30
  163. data/lib/aws-sdk-sts/presigner.rb +3 -7
  164. data/lib/aws-sdk-sts/types.rb +361 -35
  165. data/lib/aws-sdk-sts.rb +15 -11
  166. data/lib/seahorse/client/async_base.rb +4 -5
  167. data/lib/seahorse/client/async_response.rb +19 -0
  168. data/lib/seahorse/client/base.rb +18 -21
  169. data/lib/seahorse/client/h2/connection.rb +18 -28
  170. data/lib/seahorse/client/h2/handler.rb +19 -3
  171. data/lib/seahorse/client/handler.rb +1 -1
  172. data/lib/seahorse/client/http/response.rb +1 -1
  173. data/lib/seahorse/client/net_http/connection_pool.rb +15 -12
  174. data/lib/seahorse/client/net_http/handler.rb +21 -9
  175. data/lib/seahorse/client/net_http/patches.rb +44 -14
  176. data/lib/seahorse/client/networking_error.rb +1 -1
  177. data/lib/seahorse/client/plugin.rb +9 -0
  178. data/lib/seahorse/client/plugins/endpoint.rb +0 -1
  179. data/lib/seahorse/client/plugins/h2.rb +4 -4
  180. data/lib/seahorse/client/plugins/net_http.rb +57 -16
  181. data/lib/seahorse/client/request_context.rb +9 -2
  182. data/lib/seahorse/client/response.rb +2 -0
  183. data/lib/seahorse/model/shapes.rb +2 -2
  184. data/lib/seahorse/util.rb +2 -1
  185. data/sig/aws-sdk-core/async_client_stubs.rbs +21 -0
  186. data/sig/aws-sdk-core/client_stubs.rbs +10 -0
  187. data/sig/aws-sdk-core/errors.rbs +22 -0
  188. data/sig/aws-sdk-core/resources/collection.rbs +21 -0
  189. data/sig/aws-sdk-core/structure.rbs +4 -0
  190. data/sig/aws-sdk-core/telemetry/base.rbs +46 -0
  191. data/sig/aws-sdk-core/telemetry/otel.rbs +22 -0
  192. data/sig/aws-sdk-core/telemetry/span_kind.rbs +15 -0
  193. data/sig/aws-sdk-core/telemetry/span_status.rbs +24 -0
  194. data/sig/aws-sdk-core/waiters/errors.rbs +20 -0
  195. data/sig/aws-sdk-core.rbs +7 -0
  196. data/sig/seahorse/client/async_base.rbs +18 -0
  197. data/sig/seahorse/client/base.rbs +25 -0
  198. data/sig/seahorse/client/handler_builder.rbs +16 -0
  199. data/sig/seahorse/client/response.rbs +61 -0
  200. metadata +117 -23
  201. /data/lib/aws-sdk-core/xml/parser/{engines/libxml.rb → libxml_engine.rb} +0 -0
  202. /data/lib/aws-sdk-core/xml/parser/{engines/nokogiri.rb → nokogiri_engine.rb} +0 -0
  203. /data/lib/aws-sdk-core/xml/parser/{engines/oga.rb → oga_engine.rb} +0 -0
  204. /data/lib/aws-sdk-core/xml/parser/{engines/ox.rb → ox_engine.rb} +0 -0
  205. /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
- CHUNK_SIZE = 1 * 1024 * 1024 # one MB
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
- # priority order of checksum algorithms to validate responses against
22
- # Remove any algorithms not supported by client (ie, depending on CRT availability)
23
- CHECKSUM_ALGORITHM_PRIORITIES = %w[CRC32C SHA1 CRC32 SHA256] & CLIENT_ALGORITHMS
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' => 16,
28
- 'CRC32C' => 16,
29
- 'SHA1' => 36,
30
- 'SHA256' => 52
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
- # Interface for computing digests on request/response bodies
34
- # which may be files, strings or IO like objects
35
- # Applies only to digest functions that produce 32 bit integer checksums
36
- # (eg CRC32)
37
- class Digest32
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
- attr_reader :value
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
- # @param [Object] digest_fn
42
- def initialize(digest_fn)
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('N')).chomp
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
- # priority set low to ensure checksum is computed AFTER the request is
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
- private
160
+ class OptionHandler < Seahorse::Client::Handler
161
+ def call(context)
162
+ context[:http_checksum] ||= {}
64
163
 
65
- def self.request_algorithm_selection(context)
66
- return unless context.operation.http_checksum
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
- input_member = context.operation.http_checksum['requestAlgorithmMember']
69
- context.params[input_member.to_sym]&.upcase if input_member
70
- end
167
+ @handler.call(context)
168
+ end
169
+
170
+ private
71
171
 
72
- def self.request_validation_mode(context)
73
- return unless context.operation.http_checksum
172
+ def enable_request_validation_mode(context)
173
+ return unless context.operation.http_checksum
74
174
 
75
- input_member = context.operation.http_checksum['requestValidationModeMember']
76
- context.params[input_member.to_sym] if input_member
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
- def self.operation_response_algorithms(context)
80
- return unless context.operation.http_checksum
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
- context.operation.http_checksum['responseAlgorithms']
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
- # @api private
87
- class OptionHandler < Seahorse::Client::Handler
88
- def call(context)
89
- context[:http_checksum] ||= {}
200
+ private
90
201
 
91
- # validate request configuration
92
- if (request_input = ChecksumAlgorithm.request_algorithm_selection(context))
93
- unless CLIENT_ALGORITHMS.include? request_input
94
- if (request_input == 'CRC32C')
95
- raise ArgumentError, "CRC32C requires crt support - install the aws-crt gem for support."
96
- else
97
- raise ArgumentError, "#{request_input} is not a supported checksum algorithm."
98
- end
99
- end
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
- # validate response configuration
103
- if (ChecksumAlgorithm.request_validation_mode(context))
104
- # Compute an ordered list as the union between priority supported and the
105
- # operation's modeled response algorithms.
106
- validation_list = CHECKSUM_ALGORITHM_PRIORITIES &
107
- ChecksumAlgorithm.operation_response_algorithms(context)
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
- @handler.call(context)
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
- # @api private
116
- class ChecksumHandler < Seahorse::Client::Handler
243
+ def request_algorithm_selection(context)
244
+ return unless context.operation.http_checksum
117
245
 
118
- def call(context)
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
- request_checksum_property = {
124
- 'algorithm' => request_algorithm_input,
125
- 'in' => checksum_request_in(context),
126
- 'name' => "x-amz-checksum-#{request_algorithm_input.downcase}"
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
- calculate_request_checksum(context, request_checksum_property)
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 should_verify_response_checksum?(context)
133
- add_verify_response_checksum_handlers(context)
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
- @handler.call(context)
314
+ raise ArgumentError,
315
+ "#{algorithm} is not a supported checksum algorithm."
137
316
  end
138
317
 
139
- private
318
+ def checksum_request_in(context)
319
+ return 'header' unless supports_trailer_checksums?(context.operation)
140
320
 
141
- def should_calculate_request_checksum?(context)
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 should_verify_response_checksum?(context)
147
- context[:http_checksum][:validation_list] && !context[:http_checksum][:validation_list].empty?
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
- case checksum_properties['in']
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
- header_name = checksum_properties['name']
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 apply_request_trailer_checksum(context, checksum_properties)
165
- location_name = checksum_properties['name']
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 = context.http_request.headers
169
- headers['Content-Encoding'] = 'aws-chunked'
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
- if !context.http_request.body.respond_to?(:size)
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
- context.http_request.body = AwsChunkedTrailerDigestIO.new(
183
- context.http_request.body,
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
- http_response = context.http_response
194
- checksum_context = { }
195
- http_response.on_headers do |_status, headers|
196
- header_name, algorithm = response_header_to_verify(headers, context[:http_checksum][:validation_list])
197
- if header_name
198
- expected = headers[header_name]
199
-
200
- unless context[:http_checksum][:skip_on_suffix] && /-[\d]+$/.match(expected)
201
- checksum_context[:algorithm] = algorithm
202
- checksum_context[:header_name] = header_name
203
- checksum_context[:digest] = ChecksumAlgorithm.digest_for_algorithm(algorithm)
204
- checksum_context[:expected] = expected
205
- end
206
- end
207
- end
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
- http_response.on_data do |chunk|
210
- checksum_context[:digest].update(chunk) if checksum_context[:digest]
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
- http_response.on_success do
214
- if checksum_context[:digest] &&
215
- (computed = checksum_context[:digest].base64digest)
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
- if computed != checksum_context[:expected]
218
- raise Aws::Errors::ChecksumError,
219
- "Checksum validation failed on #{checksum_context[:header_name]} "\
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
- CHUNK_SIZE = 16384
291
-
292
- def initialize(io, algorithm, location_name)
293
- @io = io
294
- @location_name = location_name
295
- @algorithm = algorithm
296
- @digest = ChecksumAlgorithm.digest_for_algorithm(algorithm)
297
- @trailer_io = nil
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 / CHUNK_SIZE
306
- partial_bytes = orig_body_size % CHUNK_SIZE
307
- chunked_body_size = n_full_chunks * (CHUNK_SIZE + 8)
308
- chunked_body_size += partial_bytes.to_s(16).size + partial_bytes + 4 unless partial_bytes.zero?
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
- # account for possible leftover bytes at the end, if we have trailer bytes, send them
319
- if @trailer_io
320
- return @trailer_io.read(length, buf)
321
- end
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
- chunk = @io.read(length)
324
- if chunk
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
- trailers = {}
330
- trailers[@location_name] = @digest.base64digest
331
- trailers = trailers.map { |k,v| "#{k}:#{v}"}.join("\r\n")
332
- @trailer_io = StringIO.new("0\r\n#{trailers}\r\n\r\n")
333
- chunk = @trailer_io.read(length, buf)
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
- chunk
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