aws-sdk-core 3.214.1 → 3.218.1
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 +60 -0
- data/VERSION +1 -1
- data/lib/aws-sdk-core/cbor/decoder.rb +0 -2
- data/lib/aws-sdk-core/cbor/encoder.rb +2 -2
- data/lib/aws-sdk-core/log/param_formatter.rb +7 -3
- data/lib/aws-sdk-core/plugins/checksum_algorithm.rb +332 -170
- data/lib/aws-sdk-core/plugins/http_checksum.rb +2 -8
- data/lib/aws-sdk-core/plugins/sign.rb +1 -1
- data/lib/aws-sdk-core/plugins/user_agent.rb +10 -1
- data/lib/aws-sdk-core/shared_config.rb +2 -0
- data/lib/aws-sdk-sso/client.rb +24 -1
- data/lib/aws-sdk-sso/endpoint_provider.rb +14 -18
- data/lib/aws-sdk-sso.rb +1 -1
- data/lib/aws-sdk-ssooidc/client.rb +48 -19
- data/lib/aws-sdk-ssooidc/endpoint_provider.rb +14 -18
- data/lib/aws-sdk-ssooidc/types.rb +20 -15
- data/lib/aws-sdk-ssooidc.rb +1 -1
- data/lib/aws-sdk-sts/client.rb +35 -13
- data/lib/aws-sdk-sts/endpoint_provider.rb +33 -38
- data/lib/aws-sdk-sts/types.rb +5 -6
- data/lib/aws-sdk-sts.rb +1 -1
- data/lib/seahorse/client/net_http/connection_pool.rb +2 -0
- data/lib/seahorse/client/response.rb +2 -0
- metadata +22 -8
| @@ -13,34 +13,131 @@ module Aws | |
| 13 13 | 
             
                    begin
         | 
| 14 14 | 
             
                      require 'aws-crt'
         | 
| 15 15 | 
             
                      supported << 'CRC32C'
         | 
| 16 | 
            +
                      supported << 'CRC64NVME' if Aws::Crt::GEM_VERSION >= '0.3.0'
         | 
| 16 17 | 
             
                    rescue LoadError
         | 
| 18 | 
            +
                      # Ignored
         | 
| 17 19 | 
             
                    end
         | 
| 18 20 | 
             
                    supported
         | 
| 19 21 | 
             
                  end.freeze
         | 
| 20 22 |  | 
| 21 | 
            -
                   | 
| 22 | 
            -
             | 
| 23 | 
            -
                   | 
| 23 | 
            +
                  CRT_ALGORITHMS = %w[CRC32C CRC64NVME].freeze
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  # Priority order of checksum algorithms to validate responses against.
         | 
| 26 | 
            +
                  # Remove any algorithms not supported by client (ie, depending on CRT availability).
         | 
| 27 | 
            +
                  # This list was chosen based on average performance.
         | 
| 28 | 
            +
                  CHECKSUM_ALGORITHM_PRIORITIES = %w[CRC32 CRC32C CRC64NVME SHA1 SHA256] & CLIENT_ALGORITHMS
         | 
| 24 29 |  | 
| 25 30 | 
             
                  # byte size of checksums, used in computing the trailer length
         | 
| 26 31 | 
             
                  CHECKSUM_SIZE = {
         | 
| 27 | 
            -
                    'CRC32' =>  | 
| 28 | 
            -
                    'CRC32C' =>  | 
| 29 | 
            -
                    ' | 
| 30 | 
            -
                     | 
| 31 | 
            -
             | 
| 32 | 
            +
                    'CRC32' => 9,
         | 
| 33 | 
            +
                    'CRC32C' => 9,
         | 
| 34 | 
            +
                    'CRC64NVME' => 13,
         | 
| 35 | 
            +
                    # SHA functions need 1 byte padding because of how they are encoded
         | 
| 36 | 
            +
                    'SHA1' => 28 + 1,
         | 
| 37 | 
            +
                    'SHA256' => 44 + 1
         | 
| 38 | 
            +
                  }.freeze
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  DEFAULT_CHECKSUM = 'CRC32'
         | 
| 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
         | 
| 38 76 |  | 
| 39 | 
            -
             | 
| 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
         | 
| 95 | 
            +
             | 
| 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,125 +146,223 @@ 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 | 
            -
                  private
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                  def self.request_algorithm_selection(context)
         | 
| 66 | 
            -
                    return unless context.operation.http_checksum
         | 
| 67 | 
            -
             | 
| 68 | 
            -
                    input_member = context.operation.http_checksum['requestAlgorithmMember']
         | 
| 69 | 
            -
                    context.params[input_member.to_sym]&.upcase if input_member
         | 
| 70 | 
            -
                  end
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                  def self.request_validation_mode(context)
         | 
| 73 | 
            -
                    return unless context.operation.http_checksum
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                    input_member = context.operation.http_checksum['requestValidationModeMember']
         | 
| 76 | 
            -
                    context.params[input_member.to_sym] if input_member
         | 
| 77 | 
            -
                  end
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                  def self.operation_response_algorithms(context)
         | 
| 80 | 
            -
                    return unless context.operation.http_checksum
         | 
| 81 | 
            -
             | 
| 82 | 
            -
                    context.operation.http_checksum['responseAlgorithms']
         | 
| 83 | 
            -
                  end
         | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
                  # @api private
         | 
| 87 160 | 
             
                  class OptionHandler < Seahorse::Client::Handler
         | 
| 88 161 | 
             
                    def call(context)
         | 
| 89 162 | 
             
                      context[:http_checksum] ||= {}
         | 
| 90 163 |  | 
| 91 | 
            -
                      #  | 
| 92 | 
            -
                      if  | 
| 93 | 
            -
                         | 
| 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
         | 
| 100 | 
            -
                      end
         | 
| 101 | 
            -
             | 
| 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
         | 
| 164 | 
            +
                      # Set validation mode to enabled when supported.
         | 
| 165 | 
            +
                      if context.config.response_checksum_validation == 'when_supported'
         | 
| 166 | 
            +
                        enable_request_validation_mode(context)
         | 
| 109 167 | 
             
                      end
         | 
| 110 168 |  | 
| 111 169 | 
             
                      @handler.call(context)
         | 
| 112 170 | 
             
                    end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                    private
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                    def enable_request_validation_mode(context)
         | 
| 175 | 
            +
                      return unless context.operation.http_checksum
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                      input_member = context.operation.http_checksum['requestValidationModeMember']
         | 
| 178 | 
            +
                      context.params[input_member.to_sym] ||= 'ENABLED' if input_member
         | 
| 179 | 
            +
                    end
         | 
| 113 180 | 
             
                  end
         | 
| 114 181 |  | 
| 115 | 
            -
                  # @api private
         | 
| 116 182 | 
             
                  class ChecksumHandler < Seahorse::Client::Handler
         | 
| 117 | 
            -
             | 
| 118 183 | 
             
                    def call(context)
         | 
| 184 | 
            +
                      algorithm = nil
         | 
| 119 185 | 
             
                      if should_calculate_request_checksum?(context)
         | 
| 120 | 
            -
                         | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
                           | 
| 126 | 
            -
                          'in' => checksum_request_in(context),
         | 
| 127 | 
            -
                          'name' => "x-amz-checksum-#{request_algorithm_input.downcase}"
         | 
| 186 | 
            +
                        algorithm = choose_request_algorithm!(context)
         | 
| 187 | 
            +
                        request_algorithm = {
         | 
| 188 | 
            +
                          algorithm: algorithm,
         | 
| 189 | 
            +
                          in: checksum_request_in(context),
         | 
| 190 | 
            +
                          name: "x-amz-checksum-#{algorithm.downcase}",
         | 
| 191 | 
            +
                          request_algorithm_header: request_algorithm_header(context)
         | 
| 128 192 | 
             
                        }
         | 
| 129 193 |  | 
| 130 | 
            -
                         | 
| 194 | 
            +
                        context[:http_checksum][:request_algorithm] = request_algorithm
         | 
| 195 | 
            +
                        calculate_request_checksum(context, request_algorithm)
         | 
| 131 196 | 
             
                      end
         | 
| 132 197 |  | 
| 133 198 | 
             
                      if should_verify_response_checksum?(context)
         | 
| 134 199 | 
             
                        add_verify_response_checksum_handlers(context)
         | 
| 135 200 | 
             
                      end
         | 
| 136 201 |  | 
| 137 | 
            -
                      @handler.call(context)
         | 
| 202 | 
            +
                      with_metrics(context.config, algorithm) { @handler.call(context) }
         | 
| 138 203 | 
             
                    end
         | 
| 139 204 |  | 
| 140 205 | 
             
                    private
         | 
| 141 206 |  | 
| 142 | 
            -
                    def  | 
| 207 | 
            +
                    def with_metrics(config, algorithm, &block)
         | 
| 208 | 
            +
                      metrics = []
         | 
| 209 | 
            +
                      add_request_config_metric(config, metrics)
         | 
| 210 | 
            +
                      add_response_config_metric(config, metrics)
         | 
| 211 | 
            +
                      add_request_checksum_metrics(algorithm, metrics)
         | 
| 212 | 
            +
                      Aws::Plugins::UserAgent.metric(*metrics, &block)
         | 
| 213 | 
            +
                    end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                    def add_request_config_metric(config, metrics)
         | 
| 216 | 
            +
                      case config.request_checksum_calculation
         | 
| 217 | 
            +
                      when 'when_supported'
         | 
| 218 | 
            +
                        metrics << 'FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED'
         | 
| 219 | 
            +
                      when 'when_required'
         | 
| 220 | 
            +
                        metrics << 'FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED'
         | 
| 221 | 
            +
                      end
         | 
| 222 | 
            +
                    end
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                    def add_response_config_metric(config, metrics)
         | 
| 225 | 
            +
                      case config.response_checksum_validation
         | 
| 226 | 
            +
                      when 'when_supported'
         | 
| 227 | 
            +
                        metrics << 'FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED'
         | 
| 228 | 
            +
                      when 'when_required'
         | 
| 229 | 
            +
                        metrics << 'FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED'
         | 
| 230 | 
            +
                      end
         | 
| 231 | 
            +
                    end
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                    def add_request_checksum_metrics(algorithm, metrics)
         | 
| 234 | 
            +
                      case algorithm
         | 
| 235 | 
            +
                      when 'CRC32'
         | 
| 236 | 
            +
                        metrics << 'FLEXIBLE_CHECKSUMS_REQ_CRC32'
         | 
| 237 | 
            +
                      when 'CRC32C'
         | 
| 238 | 
            +
                        metrics << 'FLEXIBLE_CHECKSUMS_REQ_CRC32C'
         | 
| 239 | 
            +
                      when 'CRC64NVME'
         | 
| 240 | 
            +
                        metrics << 'FLEXIBLE_CHECKSUMS_REQ_CRC64'
         | 
| 241 | 
            +
                      when 'SHA1'
         | 
| 242 | 
            +
                        metrics << 'FLEXIBLE_CHECKSUMS_REQ_SHA1'
         | 
| 243 | 
            +
                      when 'SHA256'
         | 
| 244 | 
            +
                        metrics << 'FLEXIBLE_CHECKSUMS_REQ_SHA256'
         | 
| 245 | 
            +
                      end
         | 
| 246 | 
            +
                    end
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                    def request_algorithm_selection(context)
         | 
| 249 | 
            +
                      return unless context.operation.http_checksum
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                      input_member = context.operation.http_checksum['requestAlgorithmMember']
         | 
| 252 | 
            +
                      context.params[input_member.to_sym] ||= DEFAULT_CHECKSUM if input_member
         | 
| 253 | 
            +
                    end
         | 
| 254 | 
            +
             | 
| 255 | 
            +
                    def request_algorithm_header(context)
         | 
| 256 | 
            +
                      input_member = context.operation.http_checksum['requestAlgorithmMember']
         | 
| 257 | 
            +
                      shape = context.operation.input.shape.member(input_member)
         | 
| 258 | 
            +
                      shape.location_name if shape && shape.location == 'header'
         | 
| 259 | 
            +
                    end
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                    def request_validation_mode(context)
         | 
| 262 | 
            +
                      return unless context.operation.http_checksum
         | 
| 263 | 
            +
             | 
| 264 | 
            +
                      input_member = context.operation.http_checksum['requestValidationModeMember']
         | 
| 265 | 
            +
                      context.params[input_member.to_sym] if input_member
         | 
| 266 | 
            +
                    end
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                    def operation_response_algorithms(context)
         | 
| 269 | 
            +
                      return unless context.operation.http_checksum
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                      context.operation.http_checksum['responseAlgorithms']
         | 
| 272 | 
            +
                    end
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                    def checksum_required?(context)
         | 
| 275 | 
            +
                      (http_checksum = context.operation.http_checksum) &&
         | 
| 276 | 
            +
                        (checksum_required = http_checksum['requestChecksumRequired']) &&
         | 
| 277 | 
            +
                        (checksum_required && context.config.request_checksum_calculation == 'when_required')
         | 
| 278 | 
            +
                    end
         | 
| 279 | 
            +
             | 
| 280 | 
            +
                    def checksum_optional?(context)
         | 
| 143 281 | 
             
                      context.operation.http_checksum &&
         | 
| 144 | 
            -
                         | 
| 145 | 
            -
                          context[:default_request_checksum_algorithm])
         | 
| 282 | 
            +
                        context.config.request_checksum_calculation != 'when_required'
         | 
| 146 283 | 
             
                    end
         | 
| 147 284 |  | 
| 148 | 
            -
                    def  | 
| 149 | 
            -
                       | 
| 285 | 
            +
                    def checksum_provided_as_header?(headers)
         | 
| 286 | 
            +
                      headers.any? { |k, _| k.start_with?('x-amz-checksum-') }
         | 
| 287 | 
            +
                    end
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                    def should_calculate_request_checksum?(context)
         | 
| 290 | 
            +
                      !checksum_provided_as_header?(context.http_request.headers) &&
         | 
| 291 | 
            +
                        request_algorithm_selection(context) &&
         | 
| 292 | 
            +
                        (checksum_required?(context) || checksum_optional?(context))
         | 
| 293 | 
            +
                    end
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                    def choose_request_algorithm!(context)
         | 
| 296 | 
            +
                      algorithm = request_algorithm_selection(context).upcase
         | 
| 297 | 
            +
                      return algorithm if CLIENT_ALGORITHMS.include?(algorithm)
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                      if CRT_ALGORITHMS.include?(algorithm)
         | 
| 300 | 
            +
                        raise ArgumentError,
         | 
| 301 | 
            +
                              'CRC32C and CRC64NVME requires CRT support ' \
         | 
| 302 | 
            +
                              '- install the aws-crt gem'
         | 
| 303 | 
            +
                      end
         | 
| 304 | 
            +
             | 
| 305 | 
            +
                      raise ArgumentError,
         | 
| 306 | 
            +
                            "#{algorithm} is not a supported checksum algorithm."
         | 
| 307 | 
            +
                    end
         | 
| 308 | 
            +
             | 
| 309 | 
            +
                    def checksum_request_in(context)
         | 
| 310 | 
            +
                      if context.operation['unsignedPayload'] ||
         | 
| 311 | 
            +
                         context.operation['authtype'] == 'v4-unsigned-body'
         | 
| 312 | 
            +
                        'trailer'
         | 
| 313 | 
            +
                      else
         | 
| 314 | 
            +
                        'header'
         | 
| 315 | 
            +
                      end
         | 
| 150 316 | 
             
                    end
         | 
| 151 317 |  | 
| 152 318 | 
             
                    def calculate_request_checksum(context, checksum_properties)
         | 
| 153 | 
            -
                       | 
| 319 | 
            +
                      headers = context.http_request.headers
         | 
| 320 | 
            +
                      if (algorithm_header = checksum_properties[:request_algorithm_header])
         | 
| 321 | 
            +
                        headers[algorithm_header] = checksum_properties[:algorithm]
         | 
| 322 | 
            +
                      end
         | 
| 323 | 
            +
                      case checksum_properties[:in]
         | 
| 154 324 | 
             
                      when 'header'
         | 
| 155 | 
            -
                         | 
| 156 | 
            -
                        body = context.http_request.body_contents
         | 
| 157 | 
            -
                        if body
         | 
| 158 | 
            -
                          context.http_request.headers[header_name] ||=
         | 
| 159 | 
            -
                            ChecksumAlgorithm.calculate_checksum(checksum_properties['algorithm'], body)
         | 
| 160 | 
            -
                        end
         | 
| 325 | 
            +
                        apply_request_checksum(context, headers, checksum_properties)
         | 
| 161 326 | 
             
                      when 'trailer'
         | 
| 162 | 
            -
                        apply_request_trailer_checksum(context, checksum_properties)
         | 
| 327 | 
            +
                        apply_request_trailer_checksum(context, headers, checksum_properties)
         | 
| 328 | 
            +
                      else
         | 
| 329 | 
            +
                        # nothing
         | 
| 330 | 
            +
                      end
         | 
| 331 | 
            +
                    end
         | 
| 332 | 
            +
             | 
| 333 | 
            +
                    def apply_request_checksum(context, headers, checksum_properties)
         | 
| 334 | 
            +
                      header_name = checksum_properties[:name]
         | 
| 335 | 
            +
                      body = context.http_request.body_contents
         | 
| 336 | 
            +
                      headers[header_name] = calculate_checksum(
         | 
| 337 | 
            +
                        checksum_properties[:algorithm],
         | 
| 338 | 
            +
                        body
         | 
| 339 | 
            +
                      )
         | 
| 340 | 
            +
                    end
         | 
| 341 | 
            +
             | 
| 342 | 
            +
                    def calculate_checksum(algorithm, body)
         | 
| 343 | 
            +
                      digest = ChecksumAlgorithm.digest_for_algorithm(algorithm)
         | 
| 344 | 
            +
                      if body.respond_to?(:read)
         | 
| 345 | 
            +
                        update_in_chunks(digest, body)
         | 
| 346 | 
            +
                      else
         | 
| 347 | 
            +
                        digest.update(body)
         | 
| 348 | 
            +
                      end
         | 
| 349 | 
            +
                      digest.base64digest
         | 
| 350 | 
            +
                    end
         | 
| 351 | 
            +
             | 
| 352 | 
            +
                    def update_in_chunks(digest, io)
         | 
| 353 | 
            +
                      loop do
         | 
| 354 | 
            +
                        chunk = io.read(CHUNK_SIZE)
         | 
| 355 | 
            +
                        break unless chunk
         | 
| 356 | 
            +
             | 
| 357 | 
            +
                        digest.update(chunk)
         | 
| 163 358 | 
             
                      end
         | 
| 359 | 
            +
                      io.rewind
         | 
| 164 360 | 
             
                    end
         | 
| 165 361 |  | 
| 166 | 
            -
                    def apply_request_trailer_checksum(context, checksum_properties)
         | 
| 167 | 
            -
                      location_name = checksum_properties[ | 
| 362 | 
            +
                    def apply_request_trailer_checksum(context, headers, checksum_properties)
         | 
| 363 | 
            +
                      location_name = checksum_properties[:name]
         | 
| 168 364 |  | 
| 169 365 | 
             
                      # set required headers
         | 
| 170 | 
            -
                      headers = context.http_request.headers
         | 
| 171 366 | 
             
                      headers['Content-Encoding'] = 'aws-chunked'
         | 
| 172 367 | 
             
                      headers['X-Amz-Content-Sha256'] = 'STREAMING-UNSIGNED-PAYLOAD-TRAILER'
         | 
| 173 368 | 
             
                      headers['X-Amz-Trailer'] = location_name
         | 
| @@ -176,121 +371,88 @@ module Aws | |
| 176 371 | 
             
                      # to set the Content-Length header (set by content_length plugin).
         | 
| 177 372 | 
             
                      # This means we cannot use Transfer-Encoding=chunked
         | 
| 178 373 |  | 
| 179 | 
            -
                       | 
| 374 | 
            +
                      unless context.http_request.body.respond_to?(:size)
         | 
| 180 375 | 
             
                        raise Aws::Errors::ChecksumError, 'Could not determine length of the body'
         | 
| 181 376 | 
             
                      end
         | 
| 182 377 | 
             
                      headers['X-Amz-Decoded-Content-Length'] = context.http_request.body.size
         | 
| 183 378 |  | 
| 184 379 | 
             
                      context.http_request.body = AwsChunkedTrailerDigestIO.new(
         | 
| 185 380 | 
             
                        context.http_request.body,
         | 
| 186 | 
            -
                        checksum_properties[ | 
| 381 | 
            +
                        checksum_properties[:algorithm],
         | 
| 187 382 | 
             
                        location_name
         | 
| 188 383 | 
             
                      )
         | 
| 189 384 | 
             
                    end
         | 
| 190 385 |  | 
| 386 | 
            +
                    def should_verify_response_checksum?(context)
         | 
| 387 | 
            +
                      request_validation_mode(context) == 'ENABLED'
         | 
| 388 | 
            +
                    end
         | 
| 389 | 
            +
             | 
| 191 390 | 
             
                    # Add events to the http_response to verify the checksum as its read
         | 
| 192 391 | 
             
                    # This prevents the body from being read multiple times
         | 
| 193 392 | 
             
                    # verification is done only once a successful response has completed
         | 
| 194 393 | 
             
                    def add_verify_response_checksum_handlers(context)
         | 
| 195 | 
            -
                       | 
| 196 | 
            -
                      checksum_context | 
| 197 | 
            -
                       | 
| 198 | 
            -
             | 
| 199 | 
            -
             | 
| 200 | 
            -
                          expected = headers[header_name]
         | 
| 201 | 
            -
             | 
| 202 | 
            -
                          unless context[:http_checksum][:skip_on_suffix] && /-[\d]+$/.match(expected)
         | 
| 203 | 
            -
                            checksum_context[:algorithm] = algorithm
         | 
| 204 | 
            -
                            checksum_context[:header_name] = header_name
         | 
| 205 | 
            -
                            checksum_context[:digest] = ChecksumAlgorithm.digest_for_algorithm(algorithm)
         | 
| 206 | 
            -
                            checksum_context[:expected] = expected
         | 
| 207 | 
            -
                          end
         | 
| 208 | 
            -
                        end
         | 
| 209 | 
            -
                      end
         | 
| 394 | 
            +
                      checksum_context = {}
         | 
| 395 | 
            +
                      add_verify_response_headers_handler(context, checksum_context)
         | 
| 396 | 
            +
                      add_verify_response_data_handler(context, checksum_context)
         | 
| 397 | 
            +
                      add_verify_response_success_handler(context, checksum_context)
         | 
| 398 | 
            +
                    end
         | 
| 210 399 |  | 
| 211 | 
            -
             | 
| 212 | 
            -
             | 
| 400 | 
            +
                    def add_verify_response_headers_handler(context, checksum_context)
         | 
| 401 | 
            +
                      validation_list = CHECKSUM_ALGORITHM_PRIORITIES &
         | 
| 402 | 
            +
                                        operation_response_algorithms(context)
         | 
| 403 | 
            +
                      context[:http_checksum][:validation_list] = validation_list
         | 
| 404 | 
            +
             | 
| 405 | 
            +
                      context.http_response.on_headers do |_status, headers|
         | 
| 406 | 
            +
                        header_name, algorithm = response_header_to_verify(
         | 
| 407 | 
            +
                          headers,
         | 
| 408 | 
            +
                          validation_list
         | 
| 409 | 
            +
                        )
         | 
| 410 | 
            +
                        next unless header_name
         | 
| 411 | 
            +
             | 
| 412 | 
            +
                        expected = headers[header_name]
         | 
| 413 | 
            +
                        next if context[:http_checksum][:skip_on_suffix] && /-\d+$/.match(expected)
         | 
| 414 | 
            +
             | 
| 415 | 
            +
                        checksum_context[:algorithm] = algorithm
         | 
| 416 | 
            +
                        checksum_context[:header_name] = header_name
         | 
| 417 | 
            +
                        checksum_context[:digest] = ChecksumAlgorithm.digest_for_algorithm(algorithm)
         | 
| 418 | 
            +
                        checksum_context[:expected] = expected
         | 
| 213 419 | 
             
                      end
         | 
| 420 | 
            +
                    end
         | 
| 214 421 |  | 
| 215 | 
            -
             | 
| 216 | 
            -
             | 
| 217 | 
            -
             | 
| 422 | 
            +
                    def add_verify_response_data_handler(context, checksum_context)
         | 
| 423 | 
            +
                      context.http_response.on_data do |chunk|
         | 
| 424 | 
            +
                        checksum_context[:digest]&.update(chunk)
         | 
| 425 | 
            +
                      end
         | 
| 426 | 
            +
                    end
         | 
| 218 427 |  | 
| 219 | 
            -
             | 
| 220 | 
            -
             | 
| 221 | 
            -
             | 
| 222 | 
            -
                                  "computed: #{computed}, expected: #{checksum_context[:expected]}"
         | 
| 223 | 
            -
                          end
         | 
| 428 | 
            +
                    def add_verify_response_success_handler(context, checksum_context)
         | 
| 429 | 
            +
                      context.http_response.on_success do
         | 
| 430 | 
            +
                        next unless checksum_context[:digest]
         | 
| 224 431 |  | 
| 432 | 
            +
                        computed = checksum_context[:digest].base64digest
         | 
| 433 | 
            +
                        if computed == checksum_context[:expected]
         | 
| 225 434 | 
             
                          context[:http_checksum][:validated] = checksum_context[:algorithm]
         | 
| 435 | 
            +
                        else
         | 
| 436 | 
            +
                          raise Aws::Errors::ChecksumError,
         | 
| 437 | 
            +
                                "Checksum validation failed on #{checksum_context[:header_name]} "\
         | 
| 438 | 
            +
                                "computed: #{computed}, expected: #{checksum_context[:expected]}"
         | 
| 226 439 | 
             
                        end
         | 
| 227 440 | 
             
                      end
         | 
| 228 441 | 
             
                    end
         | 
| 229 442 |  | 
| 230 | 
            -
                    # returns nil if no headers to verify
         | 
| 231 443 | 
             
                    def response_header_to_verify(headers, validation_list)
         | 
| 232 444 | 
             
                      validation_list.each do |algorithm|
         | 
| 233 | 
            -
                        header_name = "x-amz-checksum-#{algorithm}"
         | 
| 445 | 
            +
                        header_name = "x-amz-checksum-#{algorithm.downcase}"
         | 
| 234 446 | 
             
                        return [header_name, algorithm] if headers[header_name]
         | 
| 235 447 | 
             
                      end
         | 
| 236 448 | 
             
                      nil
         | 
| 237 449 | 
             
                    end
         | 
| 238 | 
            -
             | 
| 239 | 
            -
                    # determine where (header vs trailer) a request checksum should be added
         | 
| 240 | 
            -
                    def checksum_request_in(context)
         | 
| 241 | 
            -
                      if context.operation['unsignedPayload'] ||
         | 
| 242 | 
            -
                         context.operation['authtype'] == 'v4-unsigned-body'
         | 
| 243 | 
            -
                        'trailer'
         | 
| 244 | 
            -
                      else
         | 
| 245 | 
            -
                        'header'
         | 
| 246 | 
            -
                      end
         | 
| 247 | 
            -
                    end
         | 
| 248 | 
            -
             | 
| 249 | 
            -
                  end
         | 
| 250 | 
            -
             | 
| 251 | 
            -
                  def self.calculate_checksum(algorithm, body)
         | 
| 252 | 
            -
                    digest = ChecksumAlgorithm.digest_for_algorithm(algorithm)
         | 
| 253 | 
            -
                    if body.respond_to?(:read)
         | 
| 254 | 
            -
                      ChecksumAlgorithm.update_in_chunks(digest, body)
         | 
| 255 | 
            -
                    else
         | 
| 256 | 
            -
                      digest.update(body)
         | 
| 257 | 
            -
                    end
         | 
| 258 | 
            -
                    digest.base64digest
         | 
| 259 | 
            -
                  end
         | 
| 260 | 
            -
             | 
| 261 | 
            -
                  def self.digest_for_algorithm(algorithm)
         | 
| 262 | 
            -
                    case algorithm
         | 
| 263 | 
            -
                    when 'CRC32'
         | 
| 264 | 
            -
                      Digest32.new(Zlib.method(:crc32))
         | 
| 265 | 
            -
                    when 'CRC32C'
         | 
| 266 | 
            -
                      # this will only be used if input algorithm is CRC32C AND client supports it (crt available)
         | 
| 267 | 
            -
                      Digest32.new(Aws::Crt::Checksums.method(:crc32c))
         | 
| 268 | 
            -
                    when 'SHA1'
         | 
| 269 | 
            -
                      Digest::SHA1.new
         | 
| 270 | 
            -
                    when 'SHA256'
         | 
| 271 | 
            -
                      Digest::SHA256.new
         | 
| 272 | 
            -
                    end
         | 
| 273 | 
            -
                  end
         | 
| 274 | 
            -
             | 
| 275 | 
            -
                  # The trailer size (in bytes) is the overhead + the trailer name +
         | 
| 276 | 
            -
                  # the length of the base64 encoded checksum
         | 
| 277 | 
            -
                  def self.trailer_length(algorithm, location_name)
         | 
| 278 | 
            -
                    CHECKSUM_SIZE[algorithm] + location_name.size
         | 
| 279 | 
            -
                  end
         | 
| 280 | 
            -
             | 
| 281 | 
            -
                  def self.update_in_chunks(digest, io)
         | 
| 282 | 
            -
                    loop do
         | 
| 283 | 
            -
                      chunk = io.read(CHUNK_SIZE)
         | 
| 284 | 
            -
                      break unless chunk
         | 
| 285 | 
            -
                      digest.update(chunk)
         | 
| 286 | 
            -
                    end
         | 
| 287 | 
            -
                    io.rewind
         | 
| 288 450 | 
             
                  end
         | 
| 289 451 |  | 
| 290 452 | 
             
                  # Wrapper for request body that implements application-layer
         | 
| 291 453 | 
             
                  # chunking with Digest computed on chunks + added as a trailer
         | 
| 292 454 | 
             
                  class AwsChunkedTrailerDigestIO
         | 
| 293 | 
            -
                    CHUNK_SIZE =  | 
| 455 | 
            +
                    CHUNK_SIZE = 16_384
         | 
| 294 456 |  | 
| 295 457 | 
             
                    def initialize(io, algorithm, location_name)
         | 
| 296 458 | 
             
                      @io = io
         | 
| @@ -331,7 +493,7 @@ module Aws | |
| 331 493 | 
             
                      else
         | 
| 332 494 | 
             
                        trailers = {}
         | 
| 333 495 | 
             
                        trailers[@location_name] = @digest.base64digest
         | 
| 334 | 
            -
                        trailers = trailers.map { |k,v| "#{k}:#{v}"}.join("\r\n")
         | 
| 496 | 
            +
                        trailers = trailers.map { |k,v| "#{k}:#{v}" }.join("\r\n")
         | 
| 335 497 | 
             
                        @trailer_io = StringIO.new("0\r\n#{trailers}\r\n\r\n")
         | 
| 336 498 | 
             
                        chunk = @trailer_io.read(length, buf)
         | 
| 337 499 | 
             
                      end
         | 
| @@ -11,8 +11,8 @@ module Aws | |
| 11 11 | 
             
                    CHUNK_SIZE = 1 * 1024 * 1024 # one MB
         | 
| 12 12 |  | 
| 13 13 | 
             
                    def call(context)
         | 
| 14 | 
            -
                      if  | 
| 15 | 
            -
                         !context[: | 
| 14 | 
            +
                      if context.operation.http_checksum_required &&
         | 
| 15 | 
            +
                         !context[:http_checksum][:request_algorithm] && # skip in favor of flexible checksum
         | 
| 16 16 | 
             
                         !context[:s3_express_endpoint] # s3 express endpoints do not support md5
         | 
| 17 17 | 
             
                        body = context.http_request.body
         | 
| 18 18 | 
             
                        context.http_request.headers['Content-Md5'] ||= md5(body)
         | 
| @@ -22,12 +22,6 @@ module Aws | |
| 22 22 |  | 
| 23 23 | 
             
                    private
         | 
| 24 24 |  | 
| 25 | 
            -
                    def checksum_required?(context)
         | 
| 26 | 
            -
                      context.operation.http_checksum_required ||
         | 
| 27 | 
            -
                        (context.operation.http_checksum &&
         | 
| 28 | 
            -
                          context.operation.http_checksum['requestChecksumRequired'])
         | 
| 29 | 
            -
                    end
         | 
| 30 | 
            -
             | 
| 31 25 | 
             
                    # @param [File, Tempfile, IO#read, String] value
         | 
| 32 26 | 
             
                    # @return [String<MD5>]
         | 
| 33 27 | 
             
                    def md5(value)
         | 
| @@ -113,7 +113,7 @@ module Aws | |
| 113 113 | 
             
                          signing_algorithm: scheme_name.to_sym,
         | 
| 114 114 | 
             
                          uri_escape_path: !!!auth_scheme['disableDoubleEncoding'],
         | 
| 115 115 | 
             
                          normalize_path: !!!auth_scheme['disableNormalizePath'],
         | 
| 116 | 
            -
                          unsigned_headers: %w[content-length user-agent x-amzn-trace-id]
         | 
| 116 | 
            +
                          unsigned_headers: %w[content-length user-agent x-amzn-trace-id expect transfer-encoding connection]
         | 
| 117 117 | 
             
                        )
         | 
| 118 118 | 
             
                      rescue Aws::Sigv4::Errors::MissingCredentialsError
         | 
| 119 119 | 
             
                        raise Aws::Errors::MissingCredentialsError
         | 
| @@ -25,7 +25,16 @@ module Aws | |
| 25 25 | 
             
                      "ACCOUNT_ID_MODE_DISABLED": "Q",
         | 
| 26 26 | 
             
                      "ACCOUNT_ID_MODE_REQUIRED": "R",
         | 
| 27 27 | 
             
                      "SIGV4A_SIGNING": "S",
         | 
| 28 | 
            -
                      "RESOLVED_ACCOUNT_ID": "T"
         | 
| 28 | 
            +
                      "RESOLVED_ACCOUNT_ID": "T",
         | 
| 29 | 
            +
                      "FLEXIBLE_CHECKSUMS_REQ_CRC32" : "U",
         | 
| 30 | 
            +
                      "FLEXIBLE_CHECKSUMS_REQ_CRC32C" : "V",
         | 
| 31 | 
            +
                      "FLEXIBLE_CHECKSUMS_REQ_CRC64" : "W",
         | 
| 32 | 
            +
                      "FLEXIBLE_CHECKSUMS_REQ_SHA1" : "X",
         | 
| 33 | 
            +
                      "FLEXIBLE_CHECKSUMS_REQ_SHA256" : "Y",
         | 
| 34 | 
            +
                      "FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED" : "Z",
         | 
| 35 | 
            +
                      "FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED" : "a",
         | 
| 36 | 
            +
                      "FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED" : "b",
         | 
| 37 | 
            +
                      "FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED" : "c"
         | 
| 29 38 | 
             
                    }
         | 
| 30 39 | 
             
                  METRICS
         | 
| 31 40 |  |