diff --git a/src/s2_sdk/_client.py b/src/s2_sdk/_client.py index 7150bbc..1baf9cf 100644 --- a/src/s2_sdk/_client.py +++ b/src/s2_sdk/_client.py @@ -376,15 +376,26 @@ async def checkout(self, base_url: str) -> _Checkout: except asyncio.TimeoutError: pass # Proceed with h2 defaults + if conn._goaway_received: + await conn.close() + raise ConnectError( + f"Server sent GOAWAY on connection to {host}:{port} before " + "an HTTP/2 request stream could be reserved" + ) + if conn._recv_dead: await conn.close() raise ConnectError( - f"Connection to {host}:{port} closed before HTTP/2 SETTINGS" + f"HTTP/2 receive loop for connection to {host}:{port} " + "terminated before a request stream could be reserved" ) if conn._settings_received.is_set() and conn.max_concurrent_streams <= 0: await conn.close() - raise ProtocolError("Connection has no available stream capacity") + raise ConnectError( + f"Server's initial HTTP/2 SETTINGS for {host}:{port} " + "advertised zero concurrent streams" + ) pc = await self._add_connection(base_url, conn) state = pc._conn.reserve_stream() diff --git a/tests/test_client.py b/tests/test_client.py index 7286f21..5641b1c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -13,8 +13,8 @@ _StreamState, ) from s2_sdk._exceptions import ( + ConnectError, ConnectionClosedError, - ProtocolError, ReadTimeoutError, S2ClientError, S2ServerError, @@ -37,6 +37,7 @@ def _mock_connection( conn.close = AsyncMock() conn._streams = {} conn._pending_streams = {} + conn._goaway_received = False conn._recv_dead = False conn._settings_received = asyncio.Event() conn._settings_received.set() @@ -176,7 +177,7 @@ async def test_new_connection_with_zero_stream_capacity_raises(pool: ConnectionP conn = _mock_connection(max_concurrent_streams=0) MockConn.return_value = conn - with pytest.raises(ProtocolError, match="no available stream capacity"): + with pytest.raises(ConnectError, match="zero concurrent streams"): await pool.checkout("https://example.com") conn.close.assert_awaited_once()