Skip to content

Commit 2c8fead

Browse files
RX14Martin Verzilli
authored and
Martin Verzilli
committedJun 2, 2017
Add limits to HTTP request parsing (#4428)
1 parent 66ac243 commit 2c8fead

File tree

5 files changed

+27
-3
lines changed

5 files changed

+27
-3
lines changed
 

Diff for: ‎spec/std/http/client/response_spec.cr

+10
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,16 @@ class HTTP::Client
123123
response.body.should eq("")
124124
end
125125

126+
it "parses long request lines" do
127+
request = Response.from_io?(IO::Memory.new("HTTP/1.1 200 #{"OK" * 16000}\r\n\r\n"))
128+
request.should eq(nil)
129+
end
130+
131+
it "parses long headers" do
132+
request = Response.from_io?(IO::Memory.new("HTTP/1.1 200 OK\r\n#{"X-Test-Header: A pretty log header value\r\n" * 1000}\r\n"))
133+
request.should eq(nil)
134+
end
135+
126136
it "doesn't sets content length for 1xx, 204 or 304" do
127137
[100, 101, 204, 304].each do |status|
128138
response = Response.new(status)

Diff for: ‎spec/std/http/request_spec.cr

+10
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,16 @@ module HTTP
176176
request.should be_a(Request::BadRequest)
177177
end
178178

179+
it "handles long request lines" do
180+
request = Request.from_io(IO::Memory.new("GET /#{"a" * 4096} HTTP/1.1\r\n\r\n"))
181+
request.should be_a(Request::BadRequest)
182+
end
183+
184+
it "handles long headers" do
185+
request = Request.from_io(IO::Memory.new("GET / HTTP/1.1\r\n#{"X-Test-Header: A pretty log header value\r\n" * 1000}\r\n"))
186+
request.should be_a(Request::BadRequest)
187+
end
188+
179189
describe "keep-alive" do
180190
it "is false by default in HTTP/1.0" do
181191
request = Request.new "GET", "/", version: "HTTP/1.0"

Diff for: ‎src/http/client/response.cr

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ class HTTP::Client::Response
116116
# it to the block. Might yield `nil` if there's no data in the `IO`,
117117
# which probably means that the connection was closed.
118118
def self.from_io?(io, ignore_body = false, decompress = true, &block)
119-
line = io.gets
119+
line = io.gets(4096, chomp: true)
120120
return yield nil unless line
121121

122122
pieces = line.split(3)

Diff for: ‎src/http/common.cr

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ module HTTP
1717
def self.parse_headers_and_body(io, body_type : BodyType = BodyType::OnDemand, decompress = true)
1818
headers = Headers.new
1919

20-
while line = io.gets
20+
headers_size = 0
21+
while line = io.gets(16_384, chomp: true)
22+
headers_size += line.bytesize
23+
break if headers_size > 16_384
24+
2125
if line.empty?
2226
body = nil
2327
if body_type.prohibited?

Diff for: ‎src/http/request.cr

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class HTTP::Request
8787

8888
# Returns a `HTTP::Request` instance if successfully parsed, returns `nil` on EOF, or returns `BadRequest`.
8989
def self.from_io(io)
90-
request_line = io.gets
90+
request_line = io.gets(4096, chomp: true)
9191
return unless request_line
9292

9393
parts = request_line.split

0 commit comments

Comments
 (0)
Please sign in to comment.