diff --git a/lib/async/http/protocol/http2/client.rb b/lib/async/http/protocol/http2/client.rb index b1c1dd4..f52abab 100644 --- a/lib/async/http/protocol/http2/client.rb +++ b/lib/async/http/protocol/http2/client.rb @@ -37,7 +37,8 @@ def call(request) # The remote peer has sent a GOAWAY frame, so it will not process any new streams on this connection. The request has not been sent yet, so it is safe to retry it on a new connection, even if it is not idempotent. This is checked first, because a connection with no streams left to drain is closed by the GOAWAY itself. raise ::Protocol::HTTP::RefusedError, "Connection is going away!" if self.goaway_received? - raise ::Protocol::HTTP2::Error, "Connection closed!" if self.closed? + # The connection can close after being acquired from the pool. No stream has been created yet, so the request has not been written and is safe to retry. + raise ::Protocol::HTTP::RefusedError, "Connection closed!" if self.closed? response = create_response write_request(response, request) diff --git a/releases.md b/releases.md index 6573786..509a268 100644 --- a/releases.md +++ b/releases.md @@ -2,6 +2,7 @@ ## Unreleased + - Requests assigned to an HTTP/2 connection which has already closed are refused before being written, allowing them to be retried safely. - HTTP/2 connections which received a graceful `GOAWAY` are removed from availability immediately, but remain in the pool until the server has finished answering the streams it accepted. The connection is closed after its final user releases it, so those requests no longer fail with `EOFError: Connection closed with N active stream(s)!`. ## v0.101.0 diff --git a/test/async/http/protocol/http2/connection_close_with_active_streams.rb b/test/async/http/protocol/http2/connection_close_with_active_streams.rb index dcc24b9..3370138 100644 --- a/test/async/http/protocol/http2/connection_close_with_active_streams.rb +++ b/test/async/http/protocol/http2/connection_close_with_active_streams.rb @@ -85,5 +85,17 @@ expect(client_connection).to be(:closed?) end.wait end + + it "refuses a request when the connection is already closed" do + Async do + client_connection = Async::HTTP::Protocol::HTTP2::Client.new(client_stream) + client_connection.open! + client_connection.close + + expect do + client_connection.call(Protocol::HTTP::Request["POST", "/", {}, ["Hello"]]) + end.to raise_exception(Protocol::HTTP::RefusedError) + end.wait + end end end