Skip to content

Commit

Permalink
don't read unused request bodies
Browse files Browse the repository at this point in the history
  • Loading branch information
zarqman committed Sep 18, 2023
1 parent b49f02a commit b86d8c0
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 3 deletions.
12 changes: 9 additions & 3 deletions lib/async/http/protocol/http1/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def each(task: Task.current)
version = request.version

# Same as above:
request = nil unless body
request = nil unless request.body
response = nil

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

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

# This ensures we yield at least once every iteration of the loop and allow other fibers to execute.
task.yield
Expand All @@ -109,6 +114,7 @@ def each(task: Task.current)
end
end
end

end
end
end
Expand Down
32 changes: 32 additions & 0 deletions test/async/http/protocol/http11.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

require 'async/http/protocol/http11'
require 'async/http/a_protocol'
require 'protocol/http/body/readable'

describe Async::HTTP::Protocol::HTTP11 do
it_behaves_like Async::HTTP::AProtocol
Expand All @@ -32,6 +33,37 @@ def around
end
end

with 'bad request body' do
let(:app) do
Protocol::HTTP::Middleware.for do |request|
Protocol::HTTP::Response[400, {}, ["Bad request"]]
end
end

class EndlessBody < Protocol::HTTP::Body::Readable
attr :chunks_read
def initialize
@chunks_read = 0
end
def read
@chunks_read += 1
Async::Task.current.yield
'endless'
end
end

it "should close the connection when request unread" do
body = EndlessBody.new
response = client.post('/', {}, body)

expect(response).to be(:bad_request?)

response.read
sleep 0.01 # brief window for multiple chunks to be read and avoid Async::Stop
expect(body.chunks_read).to be :<, 5
end
end

with 'head request' do
let(:app) do
Protocol::HTTP::Middleware.for do |request|
Expand Down

0 comments on commit b86d8c0

Please sign in to comment.