aws-sdk-core 3.240.0 → 3.241.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfea66796a6586854e469946701deeb09f2b344e82967f505262052efea4c972
4
- data.tar.gz: 98d0d7a3da929b82b7b14098d0041532d94ec0f88bfa04e74c5bcf8286e090b3
3
+ metadata.gz: 2bb055425607f2a8b6ec90788d038eb14f05f611f705797d31acd04abaf429d0
4
+ data.tar.gz: defdafdbb1fad35d409e1562e0e2f8cad977901919655ba7fc31fa6f01a0178f
5
5
  SHA512:
6
- metadata.gz: 65092f9f2795f01ee929393ccdbb06ca55d2e0ea420ac7004c0d24fe6bfd9e3b155cd2d4869b96a6deb64e65c8d4fd0e917e8834ea0cac3602a9289df6de4c4f
7
- data.tar.gz: 806b5e412f3a25b956d607f948e957e71241b25fdea11306d43a9752570d5daa80e33740c543bc499d74412d63db8325c9c9bf39ae4738c971e318ac33503b47
6
+ metadata.gz: 1ef77628a938900170ccc335d06d6236ef0b01f438b1253e44c10002064f5bbff6aef8c223a828aec55cd2e85abcd4afcd5d99c54de1929e0d560ee7e2c0a4c7
7
+ data.tar.gz: 2dd72d4d3542b6798202763f461c1e4d0a39ce285ce03ce409dcbb5bd4168c1c5dddf56fac29f60f9416a3989dabc8595eab4889c11a1b60d377c0d3f7fa2684
data/CHANGELOG.md CHANGED
@@ -1,6 +1,21 @@
1
1
  Unreleased Changes
2
2
  ------------------
3
3
 
4
+ 3.241.2 (2026-01-07)
5
+ ------------------
6
+
7
+ * Issue - Preserve existing Content-Encoding when applying request trailer checksum.
8
+
9
+ 3.241.1 (2026-01-06)
10
+ ------------------
11
+
12
+ * Issue - Fix memory leak in ClockSkew retry plugin by normalizing endpoints to prevent unlimited hash growth.
13
+
14
+ 3.241.0 (2026-01-05)
15
+ ------------------
16
+
17
+ * Feature - Improved memory efficiency when calculating request checksums.
18
+
4
19
  3.240.0 (2025-12-16)
5
20
  ------------------
6
21
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.240.0
1
+ 3.241.2
@@ -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
@@ -21,6 +22,7 @@ module Aws
21
22
  end.freeze
22
23
 
23
24
  CRT_ALGORITHMS = %w[CRC32C CRC64NVME].freeze
25
+ DEFAULT_CHECKSUM = 'CRC32'
24
26
 
25
27
  # Priority order of checksum algorithms to validate responses against.
26
28
  # Remove any algorithms not supported by client (ie, depending on CRT availability).
@@ -37,8 +39,6 @@ module Aws
37
39
  'SHA256' => 44 + 1
38
40
  }.freeze
39
41
 
40
- DEFAULT_CHECKSUM = 'CRC32'
41
-
42
42
  option(:request_checksum_calculation,
43
43
  doc_default: 'when_supported',
44
44
  doc_type: 'String',
@@ -162,9 +162,7 @@ module Aws
162
162
  context[:http_checksum] ||= {}
163
163
 
164
164
  # Set validation mode to enabled when supported.
165
- if context.config.response_checksum_validation == 'when_supported'
166
- enable_request_validation_mode(context)
167
- end
165
+ enable_request_validation_mode(context) if context.config.response_checksum_validation == 'when_supported'
168
166
 
169
167
  @handler.call(context)
170
168
  end
@@ -194,9 +192,7 @@ module Aws
194
192
  calculate_request_checksum(context, request_algorithm)
195
193
  end
196
194
 
197
- if should_verify_response_checksum?(context)
198
- add_verify_response_checksum_handlers(context)
199
- end
195
+ add_verify_response_checksum_handlers(context) if should_verify_response_checksum?(context)
200
196
 
201
197
  with_metrics(context.config, algorithm) { @handler.call(context) }
202
198
  end
@@ -334,6 +330,13 @@ module Aws
334
330
  if (algorithm_header = checksum_properties[:request_algorithm_header])
335
331
  headers[algorithm_header] = checksum_properties[:algorithm]
336
332
  end
333
+
334
+ # Trailer implementation within Mac/JRUBY environment is facing some
335
+ # network issues that will need further investigation:
336
+ # * https://github.com/jruby/jruby-openssl/issues/271
337
+ # * https://github.com/jruby/jruby-openssl/issues/317
338
+ return apply_request_checksum(context, headers, checksum_properties) if defined?(JRUBY_VERSION)
339
+
337
340
  case checksum_properties[:in]
338
341
  when 'header'
339
342
  apply_request_checksum(context, headers, checksum_properties)
@@ -346,17 +349,18 @@ module Aws
346
349
 
347
350
  def apply_request_checksum(context, headers, checksum_properties)
348
351
  header_name = checksum_properties[:name]
349
- body = context.http_request.body_contents
350
352
  headers[header_name] = calculate_checksum(
351
353
  checksum_properties[:algorithm],
352
- body
354
+ context.http_request.body
353
355
  )
354
356
  end
355
357
 
356
358
  def calculate_checksum(algorithm, body)
357
359
  digest = ChecksumAlgorithm.digest_for_algorithm(algorithm)
358
360
  if body.respond_to?(:read)
361
+ body.rewind
359
362
  update_in_chunks(digest, body)
363
+ body.rewind
360
364
  else
361
365
  digest.update(body)
362
366
  end
@@ -365,7 +369,7 @@ module Aws
365
369
 
366
370
  def update_in_chunks(digest, io)
367
371
  loop do
368
- chunk = io.read(CHUNK_SIZE)
372
+ chunk = io.read(CHECKSUM_CHUNK_SIZE)
369
373
  break unless chunk
370
374
 
371
375
  digest.update(chunk)
@@ -377,7 +381,12 @@ module Aws
377
381
  location_name = checksum_properties[:name]
378
382
 
379
383
  # set required headers
380
- headers['Content-Encoding'] = 'aws-chunked'
384
+ headers['Content-Encoding'] =
385
+ if headers['Content-Encoding']
386
+ headers['Content-Encoding'] += ', aws-chunked'
387
+ else
388
+ 'aws-chunked'
389
+ end
381
390
  headers['X-Amz-Content-Sha256'] = 'STREAMING-UNSIGNED-PAYLOAD-TRAILER'
382
391
  headers['X-Amz-Trailer'] = location_name
383
392
 
@@ -388,13 +397,14 @@ module Aws
388
397
  unless context.http_request.body.respond_to?(:size)
389
398
  raise Aws::Errors::ChecksumError, 'Could not determine length of the body'
390
399
  end
391
- headers['X-Amz-Decoded-Content-Length'] = context.http_request.body.size
392
400
 
393
- context.http_request.body = AwsChunkedTrailerDigestIO.new(
394
- context.http_request.body,
395
- checksum_properties[:algorithm],
396
- location_name
397
- )
401
+ headers['X-Amz-Decoded-Content-Length'] = context.http_request.body.size
402
+ context.http_request.body =
403
+ AwsChunkedTrailerDigestIO.new(
404
+ io: context.http_request.body,
405
+ algorithm: checksum_properties[:algorithm],
406
+ location_name: location_name
407
+ )
398
408
  end
399
409
 
400
410
  def should_verify_response_checksum?(context)
@@ -417,10 +427,7 @@ module Aws
417
427
  context[:http_checksum][:validation_list] = validation_list
418
428
 
419
429
  context.http_response.on_headers do |_status, headers|
420
- header_name, algorithm = response_header_to_verify(
421
- headers,
422
- validation_list
423
- )
430
+ header_name, algorithm = response_header_to_verify(headers, validation_list)
424
431
  next unless header_name
425
432
 
426
433
  expected = headers[header_name]
@@ -466,52 +473,94 @@ module Aws
466
473
  # Wrapper for request body that implements application-layer
467
474
  # chunking with Digest computed on chunks + added as a trailer
468
475
  class AwsChunkedTrailerDigestIO
469
- CHUNK_SIZE = 16_384
476
+ CHUNK_OVERHEAD = 4 # "\r\n\r\n"
477
+ HEX_BASE = 16
470
478
 
471
- def initialize(io, algorithm, location_name)
472
- @io = io
473
- @location_name = location_name
474
- @algorithm = algorithm
475
- @digest = ChecksumAlgorithm.digest_for_algorithm(algorithm)
476
- @trailer_io = nil
479
+ def initialize(options = {})
480
+ @io = options.delete(:io)
481
+ @location_name = options.delete(:location_name)
482
+ @algorithm = options.delete(:algorithm)
483
+ @digest = ChecksumAlgorithm.digest_for_algorithm(@algorithm)
484
+ @chunk_size = Thread.current[:net_http_override_body_stream_chunk] || DEFAULT_TRAILER_CHUNK_SIZE
485
+ @overhead_bytes = calculate_overhead(@chunk_size)
486
+ @base_chunk_size = @chunk_size - @overhead_bytes
487
+ @encoded_buffer = +''
488
+ @eof = false
477
489
  end
478
490
 
479
491
  # the size of the application layer aws-chunked + trailer body
480
492
  def size
481
- # compute the number of chunks
482
- # a full chunk has 4 + 4 bytes overhead, a partial chunk is len.to_s(16).size + 4
483
493
  orig_body_size = @io.size
484
- n_full_chunks = orig_body_size / CHUNK_SIZE
485
- partial_bytes = orig_body_size % CHUNK_SIZE
486
- chunked_body_size = n_full_chunks * (CHUNK_SIZE + 8)
487
- chunked_body_size += partial_bytes.to_s(16).size + partial_bytes + 4 unless partial_bytes.zero?
494
+ n_full_chunks = orig_body_size / @base_chunk_size
495
+ partial_bytes = orig_body_size % @base_chunk_size
496
+
497
+ full_chunk_overhead = @base_chunk_size.to_s(HEX_BASE).size + CHUNK_OVERHEAD
498
+ chunked_body_size = n_full_chunks * (@base_chunk_size + full_chunk_overhead)
499
+ unless partial_bytes.zero?
500
+ chunked_body_size += partial_bytes.to_s(HEX_BASE).size + partial_bytes + CHUNK_OVERHEAD
501
+ end
488
502
  trailer_size = ChecksumAlgorithm.trailer_length(@algorithm, @location_name)
489
503
  chunked_body_size + trailer_size
490
504
  end
491
505
 
492
506
  def rewind
493
507
  @io.rewind
508
+ @encoded_buffer = +''
509
+ @eof = false
510
+ @digest = ChecksumAlgorithm.digest_for_algorithm(@algorithm)
494
511
  end
495
512
 
496
- def read(length, buf = nil)
497
- # account for possible leftover bytes at the end, if we have trailer bytes, send them
498
- if @trailer_io
499
- return @trailer_io.read(length, buf)
500
- end
513
+ def read(length = nil, buf = nil)
514
+ return '' if length&.zero?
515
+ return if eof?
516
+
517
+ buf&.clear
518
+ output_buffer = buf || +''
501
519
 
502
- chunk = @io.read(length)
503
- if chunk
504
- @digest.update(chunk)
505
- application_chunked = "#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n"
506
- return StringIO.new(application_chunked).read(application_chunked.size, buf)
520
+ fill_encoded_buffer(length)
521
+
522
+ if length
523
+ output_buffer << @encoded_buffer.slice!(0, length)
507
524
  else
508
- trailers = {}
509
- trailers[@location_name] = @digest.base64digest
510
- trailers = trailers.map { |k,v| "#{k}:#{v}" }.join("\r\n")
511
- @trailer_io = StringIO.new("0\r\n#{trailers}\r\n\r\n")
512
- chunk = @trailer_io.read(length, buf)
525
+ output_buffer << @encoded_buffer
526
+ @encoded_buffer.clear
527
+ end
528
+
529
+ output_buffer.empty? && eof? ? nil : output_buffer
530
+ end
531
+
532
+ def eof?
533
+ @eof && @encoded_buffer.empty?
534
+ end
535
+
536
+ private
537
+
538
+ def calculate_overhead(chunk_size)
539
+ chunk_size.to_s(HEX_BASE).size + CHUNK_OVERHEAD
540
+ end
541
+
542
+ def fill_encoded_buffer(required_length)
543
+ return if required_length && @encoded_buffer.bytesize >= required_length
544
+
545
+ while !@eof && fill_data?(required_length)
546
+ chunk = @io.read(@base_chunk_size)
547
+ if chunk && !chunk.empty?
548
+ @digest.update(chunk)
549
+ @encoded_buffer << "#{chunk.bytesize.to_s(HEX_BASE)}\r\n#{chunk}\r\n"
550
+ else
551
+ @encoded_buffer << "0\r\n#{trailer_string}\r\n\r\n"
552
+ @eof = true
553
+ end
513
554
  end
514
- chunk
555
+ end
556
+
557
+ def trailer_string
558
+ { @location_name => @digest.base64digest }.map { |k, v| "#{k}:#{v}" }.join("\r\n")
559
+ end
560
+
561
+ # Returns true if more data needs to be read into the buffer
562
+ def fill_data?(length)
563
+ length.nil? || @encoded_buffer.bytesize < length
515
564
  end
516
565
  end
517
566
  end
@@ -3,10 +3,8 @@
3
3
  module Aws
4
4
  module Plugins
5
5
  module Retries
6
-
7
6
  # @api private
8
7
  class ClockSkew
9
-
10
8
  CLOCK_SKEW_THRESHOLD = 5 * 60 # five minutes
11
9
 
12
10
  def initialize
@@ -22,9 +20,9 @@ module Aws
22
20
  end
23
21
 
24
22
  # Gets the clock_correction in seconds to apply to a given endpoint
25
- # @param endpoint [URI / String]
23
+ # @param endpoint [URI, String]
26
24
  def clock_correction(endpoint)
27
- @mutex.synchronize { @endpoint_clock_corrections[endpoint.to_s] }
25
+ @mutex.synchronize { @endpoint_clock_corrections[normalized_endpoint(endpoint)] }
28
26
  end
29
27
 
30
28
  # The estimated skew factors in any clock skew from
@@ -35,7 +33,7 @@ module Aws
35
33
  # Estimated Skew should not be used to correct clock skew errors
36
34
  # it should only be used to estimate TTL for a request
37
35
  def estimated_skew(endpoint)
38
- @mutex.synchronize { @endpoint_estimated_skews[endpoint.to_s] }
36
+ @mutex.synchronize { @endpoint_estimated_skews[normalized_endpoint(endpoint)] }
39
37
  end
40
38
 
41
39
  # Determines whether a request has clock skew by comparing
@@ -55,9 +53,9 @@ module Aws
55
53
  endpoint = context.http_request.endpoint
56
54
  now_utc = Time.now.utc
57
55
  server_time = server_time(context.http_response)
58
- if server_time && (now_utc - server_time).abs > CLOCK_SKEW_THRESHOLD
59
- set_clock_correction(endpoint, server_time - now_utc)
60
- end
56
+ return unless server_time && (now_utc - server_time).abs > CLOCK_SKEW_THRESHOLD
57
+
58
+ set_clock_correction(normalized_endpoint(endpoint), server_time - now_utc)
61
59
  end
62
60
 
63
61
  # Called for every request
@@ -69,20 +67,35 @@ module Aws
69
67
  now_utc = Time.now.utc
70
68
  server_time = server_time(context.http_response)
71
69
  return unless server_time
70
+
72
71
  @mutex.synchronize do
73
- @endpoint_estimated_skews[endpoint.to_s] = server_time - now_utc
72
+ @endpoint_estimated_skews[normalized_endpoint(endpoint)] = server_time - now_utc
74
73
  end
75
74
  end
76
75
 
77
76
  private
78
77
 
78
+ ##
79
+ # @param endpoint [URI, String]
80
+ # the endpoint to normalize
81
+ #
82
+ # @return [String]
83
+ # the endpoint's schema, host, and port - without any path or query arguments
84
+ def normalized_endpoint(endpoint)
85
+ uri = endpoint.is_a?(URI::Generic) ? endpoint : URI(endpoint.to_s)
86
+
87
+ return endpoint.to_s unless uri.scheme && uri.host
88
+
89
+ "#{uri.scheme}://#{uri.host}:#{uri.port}"
90
+ rescue URI::InvalidURIError
91
+ endpoint.to_s
92
+ end
93
+
79
94
  # @param response [Seahorse::Client::Http::Response:]
80
95
  def server_time(response)
81
- begin
82
- Time.parse(response.headers['date']).utc
83
- rescue
84
- nil
85
- end
96
+ Time.parse(response.headers['date']).utc
97
+ rescue StandardError
98
+ nil
86
99
  end
87
100
 
88
101
  # Sets the clock correction for an endpoint
@@ -90,11 +103,10 @@ module Aws
90
103
  # @param correction [Number]
91
104
  def set_clock_correction(endpoint, correction)
92
105
  @mutex.synchronize do
93
- @endpoint_clock_corrections[endpoint.to_s] = correction
106
+ @endpoint_clock_corrections[normalized_endpoint(endpoint)] = correction
94
107
  end
95
108
  end
96
109
  end
97
110
  end
98
111
  end
99
112
  end
100
-
@@ -579,7 +579,7 @@ module Aws::Signin
579
579
  tracer: tracer
580
580
  )
581
581
  context[:gem_name] = 'aws-sdk-core'
582
- context[:gem_version] = '3.240.0'
582
+ context[:gem_version] = '3.241.2'
583
583
  Seahorse::Client::Request.new(handlers, context)
584
584
  end
585
585
 
@@ -56,7 +56,7 @@ module Aws::Signin
56
56
  autoload :EndpointProvider, 'aws-sdk-signin/endpoint_provider'
57
57
  autoload :Endpoints, 'aws-sdk-signin/endpoints'
58
58
 
59
- GEM_VERSION = '3.240.0'
59
+ GEM_VERSION = '3.241.2'
60
60
 
61
61
  end
62
62
 
@@ -698,7 +698,7 @@ module Aws::SSO
698
698
  tracer: tracer
699
699
  )
700
700
  context[:gem_name] = 'aws-sdk-core'
701
- context[:gem_version] = '3.240.0'
701
+ context[:gem_version] = '3.241.2'
702
702
  Seahorse::Client::Request.new(handlers, context)
703
703
  end
704
704
 
data/lib/aws-sdk-sso.rb CHANGED
@@ -56,7 +56,7 @@ module Aws::SSO
56
56
  autoload :EndpointProvider, 'aws-sdk-sso/endpoint_provider'
57
57
  autoload :Endpoints, 'aws-sdk-sso/endpoints'
58
58
 
59
- GEM_VERSION = '3.240.0'
59
+ GEM_VERSION = '3.241.2'
60
60
 
61
61
  end
62
62
 
@@ -1081,7 +1081,7 @@ module Aws::SSOOIDC
1081
1081
  tracer: tracer
1082
1082
  )
1083
1083
  context[:gem_name] = 'aws-sdk-core'
1084
- context[:gem_version] = '3.240.0'
1084
+ context[:gem_version] = '3.241.2'
1085
1085
  Seahorse::Client::Request.new(handlers, context)
1086
1086
  end
1087
1087
 
@@ -56,7 +56,7 @@ module Aws::SSOOIDC
56
56
  autoload :EndpointProvider, 'aws-sdk-ssooidc/endpoint_provider'
57
57
  autoload :Endpoints, 'aws-sdk-ssooidc/endpoints'
58
58
 
59
- GEM_VERSION = '3.240.0'
59
+ GEM_VERSION = '3.241.2'
60
60
 
61
61
  end
62
62
 
@@ -2725,7 +2725,7 @@ module Aws::STS
2725
2725
  tracer: tracer
2726
2726
  )
2727
2727
  context[:gem_name] = 'aws-sdk-core'
2728
- context[:gem_version] = '3.240.0'
2728
+ context[:gem_version] = '3.241.2'
2729
2729
  Seahorse::Client::Request.new(handlers, context)
2730
2730
  end
2731
2731
 
data/lib/aws-sdk-sts.rb CHANGED
@@ -56,7 +56,7 @@ module Aws::STS
56
56
  autoload :EndpointProvider, 'aws-sdk-sts/endpoint_provider'
57
57
  autoload :Endpoints, 'aws-sdk-sts/endpoints'
58
58
 
59
- GEM_VERSION = '3.240.0'
59
+ GEM_VERSION = '3.241.2'
60
60
 
61
61
  end
62
62
 
@@ -6,28 +6,61 @@ module Seahorse
6
6
  module Client
7
7
  # @api private
8
8
  module NetHttp
9
-
10
9
  # @api private
11
10
  module Patches
12
-
13
11
  def self.apply!
14
- Net::HTTPGenericRequest.prepend(PatchDefaultContentType)
12
+ Net::HTTPGenericRequest.prepend(RequestPatches)
15
13
  end
16
14
 
17
- # For requests with bodies, Net::HTTP sets a default content type of:
18
- # 'application/x-www-form-urlencoded'
19
- # There are cases where we should not send content type at all.
20
- # Even when no body is supplied, Net::HTTP uses a default empty body
21
- # and sets it anyway. This patch disables the behavior when a Thread
22
- # local variable is set.
23
- module PatchDefaultContentType
15
+ # Patches intended to override Net::HTTP functionality
16
+ module RequestPatches
17
+ # For requests with bodies, Net::HTTP sets a default content type of:
18
+ # 'application/x-www-form-urlencoded'
19
+ # There are cases where we should not send content type at all.
20
+ # Even when no body is supplied, Net::HTTP uses a default empty body
21
+ # and sets it anyway. This patch disables the behavior when a Thread
22
+ # local variable is set.
23
+ # See: https://github.com/ruby/net-http/issues/205
24
24
  def supply_default_content_type
25
25
  return if Thread.current[:net_http_skip_default_content_type]
26
26
 
27
27
  super
28
28
  end
29
- end
30
29
 
30
+ # IO.copy_stream is capped at 16KB buffer so this patch intends to
31
+ # increase its chunk size for better performance.
32
+ # Only intended to use for S3 TM implementation.
33
+ # See: https://github.com/ruby/net-http/blob/master/lib/net/http/generic_request.rb#L292
34
+ def send_request_with_body_stream(sock, ver, path, f)
35
+ return super unless (chunk_size = Thread.current[:net_http_override_body_stream_chunk])
36
+
37
+ unless content_length || chunked?
38
+ raise ArgumentError, 'Content-Length not given and Transfer-Encoding is not `chunked`'
39
+ end
40
+
41
+ supply_default_content_type
42
+ write_header(sock, ver, path)
43
+ wait_for_continue sock, ver if sock.continue_timeout
44
+ if chunked?
45
+ chunker = Chunker.new(sock)
46
+ RequestIO.custom_stream(f, chunker, chunk_size)
47
+ chunker.finish
48
+ else
49
+ RequestIO.custom_stream(f, sock, chunk_size)
50
+ end
51
+ end
52
+
53
+ class RequestIO
54
+ def self.custom_stream(src, dst, chunk_size)
55
+ copied = 0
56
+ while (chunk = src.read(chunk_size))
57
+ dst.write(chunk)
58
+ copied += chunk.bytesize
59
+ end
60
+ copied
61
+ end
62
+ end
63
+ end
31
64
  end
32
65
  end
33
66
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-sdk-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.240.0
4
+ version: 3.241.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amazon Web Services