corrected equality comparison of response bodies

This commit is contained in:
HoneyryderChuck 2025-04-09 10:36:27 +01:00
parent 339af65cc1
commit 69f9557780
4 changed files with 50 additions and 39 deletions

View File

@ -11,6 +11,9 @@ module HTTPX
# Array of encodings contained in the response "content-encoding" header. # Array of encodings contained in the response "content-encoding" header.
attr_reader :encodings attr_reader :encodings
attr_reader :buffer
protected :buffer
# initialized with the corresponding HTTPX::Response +response+ and HTTPX::Options +options+. # initialized with the corresponding HTTPX::Response +response+ and HTTPX::Options +options+.
def initialize(response, options) def initialize(response, options)
@response = response @response = response
@ -148,12 +151,11 @@ module HTTPX
end end
def ==(other) def ==(other)
object_id == other.object_id || begin super || case other
if other.respond_to?(:read) when Response::Body
_with_same_buffer_pos { FileUtils.compare_stream(@buffer, other) } @buffer == other.buffer
else else
to_s == other.to_s @buffer = other
end
end end
end end
@ -226,19 +228,6 @@ module HTTPX
@state = nextstate @state = nextstate
end end
def _with_same_buffer_pos # :nodoc:
return yield unless @buffer && @buffer.respond_to?(:pos)
# @type ivar @buffer: StringIO | Tempfile
current_pos = @buffer.pos
@buffer.rewind
begin
yield
ensure
@buffer.pos = current_pos
end
end
class << self class << self
def initialize_inflater_by_encoding(encoding, response, **kwargs) # :nodoc: def initialize_inflater_by_encoding(encoding, response, **kwargs) # :nodoc:
case encoding case encoding

View File

@ -7,6 +7,9 @@ require "tempfile"
module HTTPX module HTTPX
# wraps and delegates to an internal buffer, which can be a StringIO or a Tempfile. # wraps and delegates to an internal buffer, which can be a StringIO or a Tempfile.
class Response::Buffer < SimpleDelegator class Response::Buffer < SimpleDelegator
attr_reader :buffer
protected :buffer
# initializes buffer with the +threshold_size+ over which the payload gets buffer to a tempfile, # initializes buffer with the +threshold_size+ over which the payload gets buffer to a tempfile,
# the initial +bytesize+, and the +encoding+. # the initial +bytesize+, and the +encoding+.
def initialize(threshold_size:, bytesize: 0, encoding: Encoding::BINARY) def initialize(threshold_size:, bytesize: 0, encoding: Encoding::BINARY)
@ -20,7 +23,14 @@ module HTTPX
def initialize_dup(other) def initialize_dup(other)
super super
@buffer = other.instance_variable_get(:@buffer).dup # create new descriptor in READ-ONLY mode
@buffer =
case other.buffer
when StringIO
StringIO.new(other.buffer.string, mode: File::RDONLY)
else
other.buffer.class.new(other.buffer.path, encoding: Encoding::BINARY, mode: File::RDONLY)
end
end end
# size in bytes of the buffered content. # size in bytes of the buffered content.
@ -46,7 +56,7 @@ module HTTPX
end end
when Tempfile when Tempfile
rewind rewind
content = _with_same_buffer_pos { @buffer.read } content = @buffer.read
begin begin
content.force_encoding(@encoding) content.force_encoding(@encoding)
rescue ArgumentError # ex: unknown encoding name - utf rescue ArgumentError # ex: unknown encoding name - utf
@ -61,6 +71,30 @@ module HTTPX
@buffer.unlink if @buffer.respond_to?(:unlink) @buffer.unlink if @buffer.respond_to?(:unlink)
end end
def ==(other)
super || begin
return false unless other.is_a?(Response::Buffer)
if @buffer.nil?
other.buffer.nil?
elsif @buffer.respond_to?(:read) &&
other.respond_to?(:read)
buffer_pos = @buffer.pos
other_pos = other.buffer.pos
@buffer.rewind
other.buffer.rewind
begin
FileUtils.compare_stream(@buffer, other.buffer)
ensure
@buffer.pos = buffer_pos
other.buffer.pos = other_pos
end
else
to_s == other.to_s
end
end
end
private private
# initializes the buffer into a StringIO, or turns it into a Tempfile when the threshold # initializes the buffer into a StringIO, or turns it into a Tempfile when the threshold
@ -82,15 +116,5 @@ module HTTPX
__setobj__(@buffer) __setobj__(@buffer)
end end
def _with_same_buffer_pos # :nodoc:
current_pos = @buffer.pos
@buffer.rewind
begin
yield
ensure
@buffer.pos = current_pos
end
end
end end
end end

View File

@ -7,14 +7,15 @@ module HTTPX
attr_reader encoding: Encoding attr_reader encoding: Encoding
attr_reader encodings: Array[String] attr_reader encodings: Array[String]
attr_reader buffer: Response::Buffer?
@response: Response @response: Response
@headers: Headers @headers: Headers
@options: Options @options: Options
@state: :idle | :memory | :buffer | :closed @state: :idle | :memory | :buffer | :closed
@window_size: Integer @window_size: Integer
@length: Integer @length: Integer
@buffer: StringIO | Tempfile | nil @reader: Response::Buffer?
@reader: StringIO | Tempfile | nil
@inflaters: Array[Transcoder::_Inflater] @inflaters: Array[Transcoder::_Inflater]
def initialize: (Response, Options) -> void def initialize: (Response, Options) -> void
@ -47,7 +48,5 @@ module HTTPX
def decode_chunk: (String chunk) -> String def decode_chunk: (String chunk) -> String
def transition: (Symbol nextstate) -> void def transition: (Symbol nextstate) -> void
def _with_same_buffer_pos: [A] () { () -> A } -> A
end end
end end

View File

@ -1,9 +1,10 @@
module HTTPX module HTTPX
class Response::Buffer class Response::Buffer
attr_reader buffer: StringIO | Tempfile
@threshold_size: Integer @threshold_size: Integer
@bytesize: Integer @bytesize: Integer
@encoding: Encoding @encoding: Encoding
@buffer: StringIO | Tempfile
def initialize: (threshold_size: Integer, ?bytesize: Integer, ?encoding: Encoding) -> void def initialize: (threshold_size: Integer, ?bytesize: Integer, ?encoding: Encoding) -> void
@ -18,7 +19,5 @@ module HTTPX
private private
def try_upgrade_buffer: () -> void def try_upgrade_buffer: () -> void
def _with_same_buffer_pos: () { () -> void } -> void
end end
end end