Skip to content

Commit 91178a9

Browse files
committed
Better error handling.
1 parent 25b9e48 commit 91178a9

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

lib/protocol/http1/connection.rb

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,18 +349,25 @@ def read(length)
349349

350350
# Read a line from the connection.
351351
#
352-
# @returns [String | Nil] the line read, or nil if the connection is closed.
353-
# @raises [EOFError] if the connection is closed.
352+
# @returns [String | Nil] the line read, or nil if the connection is gracefully closed.
354353
# @raises [LineLengthError] if the line is too long.
354+
# @raises [ProtocolError] if the line is not terminated properly.
355355
def read_line?
356356
if line = @stream.gets(CRLF, @maximum_line_length)
357357
unless line.chomp!(CRLF)
358-
# This basically means that the request line, response line, header, or chunked length line is too long.
359-
raise LineLengthError, "Line too long!"
358+
if line.bytesize == @maximum_line_length
359+
# This basically means that the request line, response line, header, or chunked length line is too long:
360+
raise LineLengthError, "Line too long!"
361+
else
362+
# This means the line was not terminated properly, which is a protocol violation:
363+
raise ProtocolError, "Line not terminated properly!"
364+
end
360365
end
361366
end
362367

363368
return line
369+
370+
# I considered rescuing Errno::ECONNRESET here, but it seems like that would be ignoring a potentially serious error.
364371
end
365372

366373
# Read a line from the connection.

releases.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Releases
22

3+
## Unreleased
4+
5+
- Tidy up implementation of `read_line?` to handle line length errors and protocol violations more clearly.
6+
37
## v0.35.0
48

59
- Add traces provider for `Protocol::HTTP1::Connection`.

test/protocol/http1/connection.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,41 @@
1515
describe Protocol::HTTP1::Connection do
1616
include_context ConnectionContext
1717

18+
with "#read_line?" do
19+
it "reads a line from the stream" do
20+
client.stream.write "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"
21+
client.stream.close
22+
23+
expect(server.read_line?).to be == "GET / HTTP/1.1"
24+
expect(server.read_line?).to be == "Host: localhost"
25+
expect(server.read_line?).to be == ""
26+
expect(server.read_line?).to be_nil
27+
end
28+
29+
it "raises LineLengthError if line is too long" do
30+
# We create a thread since the write is liable to block until we try to read:
31+
thread = Thread.new do
32+
client.stream.write "GET / HTTP/1.#{"1" * 10000}"
33+
client.stream.close
34+
end
35+
36+
expect do
37+
server.read_line?
38+
end.to raise_exception(Protocol::HTTP1::LineLengthError)
39+
ensure
40+
thread.join
41+
end
42+
43+
it "raises ProtocolError if line is not terminated properly" do
44+
client.stream.write "GET / HTTP/1.1\r"
45+
client.stream.close
46+
47+
expect do
48+
server.read_line?
49+
end.to raise_exception(Protocol::HTTP1::ProtocolError)
50+
end
51+
end
52+
1853
with "#read_request" do
1954
it "reads request without body" do
2055
client.stream.write "GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n"

0 commit comments

Comments
 (0)