diff --git a/CHANGES/10434.bugfix.rst b/CHANGES/10434.bugfix.rst new file mode 100644 index 00000000000..c4bc50dc6aa --- /dev/null +++ b/CHANGES/10434.bugfix.rst @@ -0,0 +1,2 @@ +Avoid break statement inside the finally block in :py:class:`~aiohttp.web.RequestHandler` +-- by :user:`Cycloctane`. diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index 4dbda424bd2..0b99caf114a 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -633,26 +633,28 @@ async def start(self) -> None: except asyncio.CancelledError: self.log_debug("Ignored premature client disconnection") + self.force_close() raise except Exception as exc: self.log_exception("Unhandled exception", exc_info=exc) self.force_close() + except BaseException: + self.force_close() + raise finally: if self.transport is None and resp is not None: self.log_debug("Ignored premature client disconnection.") - elif not self._force_close: - if self._keepalive and not self._close: - # start keep-alive timer - if keepalive_timeout is not None: - now = loop.time() - close_time = now + keepalive_timeout - self._next_keepalive_close_time = close_time - if self._keepalive_handle is None: - self._keepalive_handle = loop.call_at( - close_time, self._process_keepalive - ) - else: - break + + if self._keepalive and not self._close and not self._force_close: + # start keep-alive timer + close_time = loop.time() + keepalive_timeout + self._next_keepalive_close_time = close_time + if self._keepalive_handle is None: + self._keepalive_handle = loop.call_at( + close_time, self._process_keepalive + ) + else: + break # remove handler, close transport if no handlers left if not self._force_close: diff --git a/tests/test_web_server.py b/tests/test_web_server.py index c4a45f732be..cf287743ba6 100644 --- a/tests/test_web_server.py +++ b/tests/test_web_server.py @@ -248,6 +248,24 @@ async def handler(request: web.BaseRequest) -> NoReturn: logger.debug.assert_called_with("Ignored premature client disconnection") +async def test_raw_server_does_not_swallow_base_exceptions( + aiohttp_raw_server: AiohttpRawServer, aiohttp_client: AiohttpClient +) -> None: + class UnexpectedException(BaseException): + """Dummy base exception.""" + + async def handler(request: web.BaseRequest) -> NoReturn: + raise UnexpectedException() + + loop = asyncio.get_event_loop() + loop.set_debug(True) + server = await aiohttp_raw_server(handler) + cli = await aiohttp_client(server) + + with pytest.raises(client.ServerDisconnectedError): + await cli.get("/path/to", timeout=client.ClientTimeout(10)) + + async def test_raw_server_cancelled_in_write_eof( aiohttp_raw_server: AiohttpRawServer, aiohttp_client: AiohttpClient ) -> None: