diff --git a/lib/http/connection.rb b/lib/http/connection.rb index adeac882..8d7c0b96 100644 --- a/lib/http/connection.rb +++ b/lib/http/connection.rb @@ -34,7 +34,7 @@ def initialize(req, options) @pending_request = false @pending_response = false @failed_proxy_connect = false - @buffer = "".b + @buffer = String.new(capacity: BUFFER_SIZE, encoding: Encoding::BINARY) @parser = Response::Parser.new @@ -89,18 +89,19 @@ def send_request(req) # Read a chunk of the body # # @return [String] data chunk - # @return [nil] when no more data left - def readpartial(size = BUFFER_SIZE) - return unless @pending_response + # @raise [EOFError] when there's no more data left + def readpartial(size = BUFFER_SIZE, outbuf = nil) + raise EOFError unless @pending_response chunk = @parser.read(size) - return chunk if chunk - finished = (read_more(size) == :eof) || @parser.finished? - chunk = @parser.read(size) - finish_response if finished + unless chunk + finished = (read_more(size) == :eof) || @parser.finished? + chunk = @parser.read(size) || String.new(encoding: Encoding::BINARY) + finish_response if finished + end - chunk || "".b + outbuf ? outbuf.replace(chunk) : chunk end # Reads data from socket up until headers are loaded diff --git a/lib/http/response/body.rb b/lib/http/response/body.rb index 8148fd08..5409afbb 100644 --- a/lib/http/response/body.rb +++ b/lib/http/response/body.rb @@ -45,14 +45,16 @@ def to_s raise StateError, "body is being streamed" unless @streaming.nil? - begin - @streaming = false - @contents = String.new("", encoding: @encoding) + @streaming = false + @contents = String.new("", encoding: @encoding) + begin while (chunk = @stream.readpartial) @contents << String.new(chunk, encoding: @encoding) chunk = nil # deallocate string end + rescue EOFError + # do nothing rescue @contents = nil raise diff --git a/spec/lib/http/connection_spec.rb b/spec/lib/http/connection_spec.rb index fbe5dd10..493baac4 100644 --- a/spec/lib/http/connection_spec.rb +++ b/spec/lib/http/connection_spec.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "stringio" + RSpec.describe HTTP::Connection do let(:req) do HTTP::Request.new( @@ -77,12 +79,38 @@ it "reads data in parts" do connection.read_headers! buffer = String.new - while (s = connection.readpartial(3)) - expect(connection.finished_request?).to be false if s != "" - buffer << s + + begin + while (s = connection.readpartial(3)) + expect(connection.finished_request?).to be false if s != "" + buffer << s + end + rescue EOFError end + expect(buffer).to eq "1234567890" expect(connection.finished_request?).to be true end + + it "fill outbuf when present" do + connection.read_headers! + outbuf = String.new + buffer = String.new + + begin + buffer << outbuf while connection.readpartial(2, outbuf) + rescue EOFError + end + + expect(buffer).to eq "1234567890" + end + + it "can be used with IO.copy_stream" do + output = StringIO.new + + IO.copy_stream(connection, output) + + expect(output.string).to eq "1234567890" + end end end