Merge branch 'fix-hexdigest-on-compressed-bodies' into 'master'

aws sigv4support calculation of hexdigest on top of compressed bodies in correct way

See merge request os85/httpx!355
This commit is contained in:
HoneyryderChuck 2024-11-27 18:06:39 +00:00
commit 8797434ae7
5 changed files with 69 additions and 21 deletions

View File

@ -89,7 +89,7 @@ module HTTPX
sts = "#{algo_line}" \
"\n#{datetime}" \
"\n#{credential_scope}" \
"\n#{hexdigest(creq)}"
"\n#{OpenSSL::Digest.new(@algorithm).hexdigest(creq)}"
# signature
k_date = hmac("#{upper_provider_prefix}#{@credentials.password}", date)
@ -110,22 +110,38 @@ module HTTPX
private
def hexdigest(value)
if value.respond_to?(:to_path)
# files, pathnames
OpenSSL::Digest.new(@algorithm).file(value.to_path).hexdigest
elsif value.respond_to?(:each)
digest = OpenSSL::Digest.new(@algorithm)
digest = OpenSSL::Digest.new(@algorithm)
mb_buffer = value.each.with_object("".b) do |chunk, buffer|
buffer << chunk
break if buffer.bytesize >= 1024 * 1024
if value.respond_to?(:read)
if value.respond_to?(:to_path)
# files, pathnames
digest.file(value.to_path).hexdigest
else
# gzipped request bodies
raise Error, "request body must be rewindable" unless value.respond_to?(:rewind)
buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
begin
IO.copy_stream(value, buffer)
buffer.flush
digest.file(buffer.to_path).hexdigest
ensure
value.rewind
buffer.close
buffer.unlink
end
end
else
# error on endless generators
raise Error, "hexdigest for endless enumerators is not supported" if value.unbounded_body?
mb_buffer = value.each.with_object("".b) do |chunk, b|
b << chunk
break if b.bytesize >= 1024 * 1024
end
digest.update(mb_buffer)
value.rewind
digest.hexdigest
else
OpenSSL::Digest.new(@algorithm).hexdigest(value)
digest.hexdigest(mb_buffer)
end
end

View File

@ -1,8 +1,5 @@
# frozen_string_literal: true
require "forwardable"
require "uri"
require "stringio"
require "zlib"
module HTTPX

View File

@ -1,13 +1,10 @@
# frozen_string_literal: true
require "forwardable"
require_relative "body_reader"
module HTTPX
module Transcoder
class Deflater
extend Forwardable
attr_reader :content_type
def initialize(body)
@ -51,6 +48,12 @@ module HTTPX
@closed = true
end
def rewind
return unless @buffer
@buffer.rewind
end
private
# rubocop:disable Naming/MemoizedInstanceVariableName

View File

@ -41,7 +41,7 @@ module HTTPX
) -> untyped
def hexdigest: (bodyIO value) -> String
def hexdigest: (Request::Body value) -> String
def hmac: (String key, String value) -> String

View File

@ -58,6 +58,23 @@ class HTTPXAwsSigv4Test < Minitest::Test
assert request.headers["x-amz-content-sha256"] == Digest::SHA256.hexdigest("abcd")
end
def test_plugin_aws_sigv4_x_amz_content_sha256_array
request = sigv4_session.build_request("GET", "http://domain.com", body: %w[a b c d])
assert request.headers["x-amz-content-sha256"] == Digest::SHA256.hexdigest("abcd")
end
def test_plugin_aws_sigv4_x_amz_content_sha256_endless
body = Enumerator.new do |y|
loop do
y << "a"
end
end
ex = assert_raises(HTTPX::Error) do
sigv4_session.build_request("GET", "http://domain.com", body: body)
end
assert ex.message.include?("hexdigest for endless enumerators is not supported")
end
def test_plugin_aws_sigv4_x_amz_content_sha256_file
body = Tempfile.new("httpx")
body.write("abcd")
@ -72,6 +89,21 @@ class HTTPXAwsSigv4Test < Minitest::Test
end
end
def test_plugin_aws_sigv4_x_amz_content_sha256_compressed_file
body = Tempfile.new("httpx")
body.write("abcd")
body.flush
request = sigv4_session.build_request("GET", "http://domain.com", body: body, headers: { "content-encoding" => "gzip" })
assert request.headers["x-amz-content-sha256"] == Digest::SHA256.hexdigest(request.body.read)
ensure
if defined?(body)
body.close
body.unlink
end
end
def test_plugin_aws_sigv4_authorization_unsigned_headers
request = sigv4_session(
service: "SERVICE",