diff --git a/lib/http/request/writer.rb b/lib/http/request/writer.rb index b2f383fe..06823654 100644 --- a/lib/http/request/writer.rb +++ b/lib/http/request/writer.rb @@ -60,22 +60,32 @@ def join_headers @request_header.join(CRLF) + CRLF * 2 end + # Writes HTTP request data into the socket. def send_request - # It's important to send the request in a single write call when - # possible in order to play nicely with Nagle's algorithm. Making - # two writes in a row triggers a pathological case where Nagle is - # expecting a third write that never happens. + each_chunk { |chunk| write chunk } + rescue Errno::EPIPE + # server doesn't need any more data + nil + end + + # Yields chunks of request data that should be sent to the socket. + # + # It's important to send the request in a single write call when possible + # in order to play nicely with Nagle's algorithm. Making two writes in a + # row triggers a pathological case where Nagle is expecting a third write + # that never happens. + def each_chunk data = join_headers @body.each do |chunk| data << encode_chunk(chunk) - write(data) + yield data data.clear end - write(data) unless data.empty? + yield data unless data.empty? - write(CHUNKED_END) if chunked? + yield CHUNKED_END if chunked? end # Returns the chunk encoded for to the specified "Transfer-Encoding" header. @@ -100,6 +110,8 @@ def write(data) break unless data.bytesize > length data = data.byteslice(length..-1) end + rescue Errno::EPIPE + raise rescue IOError, SocketError, SystemCallError => ex raise ConnectionError, "error writing to socket: #{ex}", ex.backtrace end diff --git a/spec/lib/http/request/writer_spec.rb b/spec/lib/http/request/writer_spec.rb index 62780a6f..80a67809 100644 --- a/spec/lib/http/request/writer_spec.rb +++ b/spec/lib/http/request/writer_spec.rb @@ -75,11 +75,21 @@ end end - context "when writing to socket raises an exception" do + context "when server won't accept any more data" do before do expect(io).to receive(:write).and_raise(Errno::EPIPE) end + it "aborts silently" do + writer.stream + end + end + + context "when writing to socket raises an exception" do + before do + expect(io).to receive(:write).and_raise(Errno::ECONNRESET) + end + it "raises a ConnectionError" do expect { writer.stream }.to raise_error(HTTP::ConnectionError) end