diff --git a/CHANGES/8878.bugfix.rst b/CHANGES/8878.bugfix.rst new file mode 100644 index 00000000000..df53dea3c35 --- /dev/null +++ b/CHANGES/8878.bugfix.rst @@ -0,0 +1 @@ +Fixed response reading from closed session to throw an error immediately instead of timing out -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/streams.py b/aiohttp/streams.py index b9b9c3fd96f..c927cfbb1b3 100644 --- a/aiohttp/streams.py +++ b/aiohttp/streams.py @@ -296,6 +296,9 @@ def end_http_chunk_receiving(self) -> None: set_result(waiter, None) async def _wait(self, func_name: str) -> None: + if not self._protocol.connected: + raise RuntimeError("Connection closed.") + # StreamReader uses a future to link the protocol feed_data() method # to a read coroutine. Running two read coroutines at the same time # would have an unexpected behaviour. It would not possible to know diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 566c47522ce..18fb5fe9f86 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -29,7 +29,7 @@ SocketTimeoutError, TooManyRedirects, ) -from aiohttp.pytest_plugin import AiohttpClient, TestClient +from aiohttp.pytest_plugin import AiohttpClient, AiohttpServer, TestClient from aiohttp.test_utils import unused_port @@ -3645,3 +3645,20 @@ async def handler(_: web.Request) -> web.Response: session = await aiohttp_client(app, raise_for_status=None) # type: ignore[arg-type] await session.get("/") + + +async def test_exception_when_read_outside_of_session( + aiohttp_server: AiohttpServer, +) -> None: + async def handler(request: web.Request) -> web.Response: + return web.Response(body=b"1" * 1000000) + + app = web.Application() + app.router.add_get("/", handler) + + server = await aiohttp_server(app) + async with aiohttp.ClientSession() as sess: + resp = await sess.get(server.make_url("/")) + + with pytest.raises(RuntimeError, match="Connection closed"): + await resp.read()