Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/async/http/protocol/http2/stream.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ def update_local_window(frame)
# request_window_update
end

# A closed stream receives no more data, so there is no window to extend. This happens when the application reads a body which was fully buffered before the connection closed, e.g. after a graceful GOAWAY.
def request_window_update
super unless closed?
end

# Process an incoming DATA frame and write it to the input body.
# @parameter frame [Protocol::HTTP2::DataFrame] The data frame to process.
# @returns [String] The unpacked data.
Expand Down
41 changes: 41 additions & 0 deletions test/async/http/protocol/http2/graceful_goaway.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require "sus/fixtures/async/scheduler_context"

require "async/queue"
require "protocol/http/body/writable"
require "io/stream"
require "socket"

Expand Down Expand Up @@ -117,6 +118,46 @@ def handle_connection(socket, index, processed)
end
end

with "a response body which is still buffered when the GOAWAY closes the connection" do
let(:sockets) {::Socket.pair(::Socket::AF_UNIX, ::Socket::SOCK_STREAM)}

def after(error = nil)
sockets.each{|socket| socket.close unless socket.closed?}

super
end

it "can read the whole body" do
chunk = "x" * 100_000

server_task = Async do
server = subject.server(sockets.last)

server.each do
body = ::Protocol::HTTP::Body::Writable.new

Async do
60.times{body.write(chunk)}
server.write_frame(goaway_frame(1))
body.close_write
end

::Protocol::HTTP::Response[200, {}, body]
end
end

client = subject.client(sockets.first)
response = client.call(::Protocol::HTTP::Request["GET", "/", authority: "localhost", scheme: "http"])

# The stream completes, so the connection closes, before the body has been read:
Async::Task.current.sleep(0.01) until client.closed?

expect(response.read.bytesize).to be == 6_000_000
ensure
server_task&.stop
end
end

with "a connection which received a GOAWAY" do
let(:sockets) {::Socket.pair(::Socket::AF_UNIX, ::Socket::SOCK_STREAM)}
let(:connection) {Async::HTTP::Protocol::HTTP2::Client.new(IO::Stream(sockets.first))}
Expand Down