Skip to content

Commit b86d8c0

Browse files
committed
don't read unused request bodies
1 parent b49f02a commit b86d8c0

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

lib/async/http/protocol/http1/server.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def each(task: Task.current)
8383
version = request.version
8484

8585
# Same as above:
86-
request = nil unless body
86+
request = nil unless request.body
8787
response = nil
8888

8989
write_body(version, body, head, trailer)
@@ -97,8 +97,13 @@ def each(task: Task.current)
9797
write_body(request.version, nil)
9898
end
9999

100-
# Gracefully finish reading the request body if it was not already done so.
101-
request&.finish
100+
if request&.body
101+
# Read one chunk to allow small or 0-length bodies to identify the body as complete.
102+
request.body.read unless request.body.empty?
103+
# If request body has outstanding data, will close the stream. This avoids wasting cycles if remaining
104+
# body size is large or unknown. If body was already read, leaves stream open for potential keep-alive.
105+
request.close
106+
end
102107

103108
# This ensures we yield at least once every iteration of the loop and allow other fibers to execute.
104109
task.yield
@@ -109,6 +114,7 @@ def each(task: Task.current)
109114
end
110115
end
111116
end
117+
112118
end
113119
end
114120
end

test/async/http/protocol/http11.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
require 'async/http/protocol/http11'
99
require 'async/http/a_protocol'
10+
require 'protocol/http/body/readable'
1011

1112
describe Async::HTTP::Protocol::HTTP11 do
1213
it_behaves_like Async::HTTP::AProtocol
@@ -32,6 +33,37 @@ def around
3233
end
3334
end
3435

36+
with 'bad request body' do
37+
let(:app) do
38+
Protocol::HTTP::Middleware.for do |request|
39+
Protocol::HTTP::Response[400, {}, ["Bad request"]]
40+
end
41+
end
42+
43+
class EndlessBody < Protocol::HTTP::Body::Readable
44+
attr :chunks_read
45+
def initialize
46+
@chunks_read = 0
47+
end
48+
def read
49+
@chunks_read += 1
50+
Async::Task.current.yield
51+
'endless'
52+
end
53+
end
54+
55+
it "should close the connection when request unread" do
56+
body = EndlessBody.new
57+
response = client.post('/', {}, body)
58+
59+
expect(response).to be(:bad_request?)
60+
61+
response.read
62+
sleep 0.01 # brief window for multiple chunks to be read and avoid Async::Stop
63+
expect(body.chunks_read).to be :<, 5
64+
end
65+
end
66+
3567
with 'head request' do
3668
let(:app) do
3769
Protocol::HTTP::Middleware.for do |request|

0 commit comments

Comments
 (0)