class Aws::Plugins::ChecksumAlgorithm::ChecksumHandler

def add_request_checksum_metrics(algorithm, metrics)

def add_request_checksum_metrics(algorithm, metrics)
  case algorithm
  when 'CRC32'
    metrics << 'FLEXIBLE_CHECKSUMS_REQ_CRC32'
  when 'CRC32C'
    metrics << 'FLEXIBLE_CHECKSUMS_REQ_CRC32C'
  when 'CRC64NVME'
    metrics << 'FLEXIBLE_CHECKSUMS_REQ_CRC64'
  when 'SHA1'
    metrics << 'FLEXIBLE_CHECKSUMS_REQ_SHA1'
  when 'SHA256'
    metrics << 'FLEXIBLE_CHECKSUMS_REQ_SHA256'
  end
end

def add_request_config_metric(config, metrics)

def add_request_config_metric(config, metrics)
  case config.request_checksum_calculation
  when 'when_supported'
    metrics << 'FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED'
  when 'when_required'
    metrics << 'FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED'
  end
end

def add_response_config_metric(config, metrics)

def add_response_config_metric(config, metrics)
  case config.response_checksum_validation
  when 'when_supported'
    metrics << 'FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED'
  when 'when_required'
    metrics << 'FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED'
  end
end

def add_verify_response_checksum_handlers(context)

verification is done only once a successful response has completed
This prevents the body from being read multiple times
Add events to the http_response to verify the checksum as its read
def add_verify_response_checksum_handlers(context)
  checksum_context = {}
  add_verify_response_headers_handler(context, checksum_context)
  add_verify_response_data_handler(context, checksum_context)
  add_verify_response_success_handler(context, checksum_context)
end

def add_verify_response_data_handler(context, checksum_context)

def add_verify_response_data_handler(context, checksum_context)
  context.http_response.on_data do |chunk|
    checksum_context[:digest]&.update(chunk)
  end
end

def add_verify_response_headers_handler(context, checksum_context)

def add_verify_response_headers_handler(context, checksum_context)
  validation_list = CHECKSUM_ALGORITHM_PRIORITIES &
                    operation_response_algorithms(context)
  context[:http_checksum][:validation_list] = validation_list
  context.http_response.on_headers do |_status, headers|
    header_name, algorithm = response_header_to_verify(
      headers,
      validation_list
    )
    next unless header_name
    expected = headers[header_name]
    next if context[:http_checksum][:skip_on_suffix] && /-\d+$/.match(expected)
    checksum_context[:algorithm] = algorithm
    checksum_context[:header_name] = header_name
    checksum_context[:digest] = ChecksumAlgorithm.digest_for_algorithm(algorithm)
    checksum_context[:expected] = expected
  end
end

def add_verify_response_success_handler(context, checksum_context)

def add_verify_response_success_handler(context, checksum_context)
  context.http_response.on_success do
    next unless checksum_context[:digest]
    computed = checksum_context[:digest].base64digest
    if computed == checksum_context[:expected]
      context[:http_checksum][:validated] = checksum_context[:algorithm]
    else
      raise Aws::Errors::ChecksumError,
            "Checksum validation failed on #{checksum_context[:header_name]} "\
            "computed: #{computed}, expected: #{checksum_context[:expected]}"
    end
  end
end

def apply_request_checksum(context, headers, checksum_properties)

def apply_request_checksum(context, headers, checksum_properties)
  header_name = checksum_properties[:name]
  body = context.http_request.body_contents
  headers[header_name] = calculate_checksum(
    checksum_properties[:algorithm],
    body
  )
end

def apply_request_trailer_checksum(context, headers, checksum_properties)

def apply_request_trailer_checksum(context, headers, checksum_properties)
  location_name = checksum_properties[:name]
  # set required headers
  headers['Content-Encoding'] = 'aws-chunked'
  headers['X-Amz-Content-Sha256'] = 'STREAMING-UNSIGNED-PAYLOAD-TRAILER'
  headers['X-Amz-Trailer'] = location_name
  # We currently always compute the size in the modified body wrapper - allowing us
  # to set the Content-Length header (set by content_length plugin).
  # This means we cannot use Transfer-Encoding=chunked
  unless context.http_request.body.respond_to?(:size)
    raise Aws::Errors::ChecksumError, 'Could not determine length of the body'
  end
  headers['X-Amz-Decoded-Content-Length'] = context.http_request.body.size
  context.http_request.body = AwsChunkedTrailerDigestIO.new(
    context.http_request.body,
    checksum_properties[:algorithm],
    location_name
  )
end

def calculate_checksum(algorithm, body)

def calculate_checksum(algorithm, body)
  digest = ChecksumAlgorithm.digest_for_algorithm(algorithm)
  if body.respond_to?(:read)
    update_in_chunks(digest, body)
  else
    digest.update(body)
  end
  digest.base64digest
end

def calculate_request_checksum(context, checksum_properties)

def calculate_request_checksum(context, checksum_properties)
  headers = context.http_request.headers
  if (algorithm_header = checksum_properties[:request_algorithm_header])
    headers[algorithm_header] = checksum_properties[:algorithm]
  end
  case checksum_properties[:in]
  when 'header'
    apply_request_checksum(context, headers, checksum_properties)
  when 'trailer'
    apply_request_trailer_checksum(context, headers, checksum_properties)
  else
    # nothing
  end
end

def call(context)

def call(context)
  algorithm = nil
  if should_calculate_request_checksum?(context)
    algorithm = choose_request_algorithm!(context)
    request_algorithm = {
      algorithm: algorithm,
      in: checksum_request_in(context),
      name: "x-amz-checksum-#{algorithm.downcase}",
      request_algorithm_header: request_algorithm_header(context)
    }
    context[:http_checksum][:request_algorithm] = request_algorithm
    calculate_request_checksum(context, request_algorithm)
  end
  if should_verify_response_checksum?(context)
    add_verify_response_checksum_handlers(context)
  end
  with_metrics(context.config, algorithm) { @handler.call(context) }
end

def checksum_optional?(context)

def checksum_optional?(context)
  context.operation.http_checksum &&
    context.config.request_checksum_calculation != 'when_required'
end

def checksum_provided_as_header?(headers)

def checksum_provided_as_header?(headers)
  headers.any? { |k, _| k.start_with?('x-amz-checksum-') }
end

def checksum_request_in(context)

def checksum_request_in(context)
  if context.operation['unsignedPayload'] ||
     context.operation['authtype'] == 'v4-unsigned-body'
    'trailer'
  else
    'header'
  end
end

def checksum_required?(context)

def checksum_required?(context)
  (http_checksum = context.operation.http_checksum) &&
    (checksum_required = http_checksum['requestChecksumRequired']) &&
    (checksum_required && context.config.request_checksum_calculation == 'when_required')
end

def choose_request_algorithm!(context)

def choose_request_algorithm!(context)
  algorithm = request_algorithm_selection(context).upcase
  return algorithm if CLIENT_ALGORITHMS.include?(algorithm)
  if CRT_ALGORITHMS.include?(algorithm)
    raise ArgumentError,
          'CRC32C and CRC64NVME requires CRT support ' \
          '- install the aws-crt gem'
  end
  raise ArgumentError,
        "#{algorithm} is not a supported checksum algorithm."
end

def operation_response_algorithms(context)

def operation_response_algorithms(context)
  return unless context.operation.http_checksum
  context.operation.http_checksum['responseAlgorithms']
end

def request_algorithm_header(context)

def request_algorithm_header(context)
  input_member = context.operation.http_checksum['requestAlgorithmMember']
  shape = context.operation.input.shape.member(input_member)
  shape.location_name if shape && shape.location == 'header'
end

def request_algorithm_selection(context)

def request_algorithm_selection(context)
  return unless context.operation.http_checksum
  input_member = context.operation.http_checksum['requestAlgorithmMember']
  context.params[input_member.to_sym] ||= DEFAULT_CHECKSUM if input_member
end

def request_validation_mode(context)

def request_validation_mode(context)
  return unless context.operation.http_checksum
  input_member = context.operation.http_checksum['requestValidationModeMember']
  context.params[input_member.to_sym] if input_member
end

def response_header_to_verify(headers, validation_list)

def response_header_to_verify(headers, validation_list)
  validation_list.each do |algorithm|
    header_name = "x-amz-checksum-#{algorithm.downcase}"
    return [header_name, algorithm] if headers[header_name]
  end
  nil
end

def should_calculate_request_checksum?(context)

def should_calculate_request_checksum?(context)
  !checksum_provided_as_header?(context.http_request.headers) &&
    request_algorithm_selection(context) &&
    (checksum_required?(context) || checksum_optional?(context))
end

def should_verify_response_checksum?(context)

def should_verify_response_checksum?(context)
  request_validation_mode(context) == 'ENABLED'
end

def update_in_chunks(digest, io)

def update_in_chunks(digest, io)
  loop do
    chunk = io.read(CHUNK_SIZE)
    break unless chunk
    digest.update(chunk)
  end
  io.rewind
end

def with_metrics(config, algorithm, &block)

def with_metrics(config, algorithm, &block)
  metrics = []
  add_request_config_metric(config, metrics)
  add_response_config_metric(config, metrics)
  add_request_checksum_metrics(algorithm, metrics)
  Aws::Plugins::UserAgent.metric(*metrics, &block)
end