Skip to content

Commit

Permalink
Return response body chunks in specified encoding
Browse files Browse the repository at this point in the history
MRI will return content read from the socket in the ASCII-8BIT (binary)
encoding, whereas JRuby will return it in the UTF-8 encoding. In
whichever encoding the body is retrieved, we want to force its encoding
to the one specified (charset response header if present, otherwise
binary). This is already the behaviour in Response#to_s, we just extend
it to Response#readpartial as well.

Fixes httprb#413
  • Loading branch information
janko committed May 24, 2017
1 parent 4445835 commit e3efe86
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 12 deletions.
25 changes: 14 additions & 11 deletions lib/http/response/body.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ def initialize(stream, length: nil, encoding: Encoding::BINARY)
@connection = stream.is_a?(Inflater) ? stream.connection : stream
@streaming = nil
@contents = nil
@encoding = encoding
@encoding = find_encoding(encoding)
@length = length || Float::INFINITY
end

# (see HTTP::Client#readpartial)
def readpartial(*args)
stream!
@stream.readpartial(*args)
chunk = @stream.readpartial(*args)
chunk.force_encoding(@encoding) if chunk
end

# Iterate over the body, allowing it to be enumerable
Expand All @@ -43,22 +44,15 @@ def to_s

raise StateError, "body is being streamed" unless @streaming.nil?

# see issue 312
begin
encoding = Encoding.find @encoding
rescue ArgumentError
encoding = Encoding::BINARY
end

begin
@streaming = false
@contents = String.new("").force_encoding(encoding)
@contents = String.new("").force_encoding(@encoding)

length = @length

while length > 0 && (chunk = @stream.readpartial)
length -= chunk.bytesize
@contents << chunk.force_encoding(encoding)
@contents << chunk.force_encoding(@encoding)
end
rescue
@contents = nil
Expand All @@ -79,6 +73,15 @@ def stream!
def inspect
"#<#{self.class}:#{object_id.to_s(16)} @streaming=#{!!@streaming}>"
end

private

# Retrieve encoding by name. If encoding cannot be found, default to binary.
def find_encoding(encoding)
Encoding.find encoding
rescue ArgumentError
Encoding::BINARY
end
end
end
end
14 changes: 13 additions & 1 deletion spec/lib/http/response/body_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@
body.readpartial
end
end

it "returns content in specified encoding" do
body = described_class.new(connection)
expect(connection).to receive(:readpartial).
and_return(String.new("content").force_encoding(Encoding::UTF_8))
expect(body.readpartial.encoding).to eq Encoding::BINARY

body = described_class.new(connection, :encoding => Encoding::UTF_8)
expect(connection).to receive(:readpartial).
and_return(String.new("content").force_encoding(Encoding::BINARY))
expect(body.readpartial.encoding).to eq Encoding::UTF_8
end
end

context "when body is gzipped" do
Expand All @@ -58,7 +70,7 @@
it "streams decoded body" do
[
"Hi, HTTP ",
String.new("here ☺").force_encoding("ASCII-8BIT"),
"here ☺",
nil
].each do |part|
expect(subject.readpartial).to eq(part)
Expand Down

0 comments on commit e3efe86

Please sign in to comment.