Skip to content

Commit

Permalink
Handle early responses
Browse files Browse the repository at this point in the history
HTTP servers typically wait until the whole request body has been
received before sending the response. However, some servers might send
out a response early, before they've read the whole request body. For
example, they might detect the request headers were invalid, and they
want to tell that to the client immediately.

For example, sending a large request to http://google.com will return an
early `400 Bad Request` response. In this case HTTP.rb will raise an
Errno::EPIPE exception, because it tried to write to the socket but it
couldn't because the server closed its end. However, the server still
returned a valid response, so it's not really erroneous behaviour (e.g.
curl doesn't print any errors in the same situations).

  # Before
  HTTP.get("http://google.com", body: "a" * 1*1024*1024)
  #~> Errno::EPIPE

  # After
  HTTP.get("http://google.com", body: "a" * 1*1024*1024)
  #=> #<HTTP::Response 400 Bad Request ...>

This commit ignores Errno::EPIPE exceptions when writing request data to
the socket.
  • Loading branch information
janko committed May 23, 2018
1 parent 09de754 commit 7aa3e43
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 1 deletion.
2 changes: 2 additions & 0 deletions lib/http/request/writer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ def write(data)
break unless data.bytesize > length
data = data.byteslice(length..-1)
end
rescue Errno::EPIPE
# server doesn't need any more data
rescue IOError, SocketError, SystemCallError => ex
raise ConnectionError, "error writing to socket: #{ex}", ex.backtrace
end
Expand Down
12 changes: 11 additions & 1 deletion spec/lib/http/request/writer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7aa3e43

Please sign in to comment.