Skip to content

Commit

Permalink
Add a Request.wait_for_disconnection() method (#4200) (#8504)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Svetlov <[email protected]>
Co-authored-by: Gustavo J. A. M. Carneiro <[email protected]>
Co-authored-by: J. Nick Koston <[email protected]>
  • Loading branch information
4 people authored Jul 17, 2024
1 parent 1edeb9d commit 1caebc9
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES/2492.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a Request.wait_for_disconnection() method, as means of allowing request handlers to be notified of premature client disconnections.
1 change: 1 addition & 0 deletions aiohttp/web_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ async def finish_response(
can get exception information. Returns True if the client disconnects
prematurely.
"""
request._finish()
if self._request_parser is not None:
self._request_parser.set_upgraded(False)
self._upgrade = False
Expand Down
19 changes: 19 additions & 0 deletions aiohttp/web_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
MutableMapping,
Optional,
Pattern,
Set,
Tuple,
Union,
cast,
Expand Down Expand Up @@ -49,6 +50,7 @@
reify,
sentinel,
set_exception,
set_result,
)
from .http_parser import RawRequestMessage
from .http_writer import HttpVersion
Expand Down Expand Up @@ -144,6 +146,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
"_loop",
"_transport_sslcontext",
"_transport_peername",
"_disconnection_waiters",
]
)

Expand Down Expand Up @@ -191,6 +194,7 @@ def __init__(
self._task = task
self._client_max_size = client_max_size
self._loop = loop
self._disconnection_waiters: Set[asyncio.Future[None]] = set()

transport = self._protocol.transport
assert transport is not None
Expand Down Expand Up @@ -818,6 +822,21 @@ async def _prepare_hook(self, response: StreamResponse) -> None:

def _cancel(self, exc: BaseException) -> None:
set_exception(self._payload, exc)
for fut in self._disconnection_waiters:
set_result(fut, None)

def _finish(self) -> None:
for fut in self._disconnection_waiters:
fut.cancel()

async def wait_for_disconnection(self) -> None:
loop = asyncio.get_event_loop()
fut = loop.create_future() # type: asyncio.Future[None]
self._disconnection_waiters.add(fut)
try:
await fut
finally:
self._disconnection_waiters.remove(fut)


class Request(BaseRequest):
Expand Down
12 changes: 12 additions & 0 deletions docs/web_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,18 @@ and :ref:`aiohttp-web-signals` handlers.
required work will be processed by :mod:`aiohttp.web`
internal machinery.

.. method:: wait_for_disconnection()

Returns when the connection that sent this request closes

If there is no client disconnection during request handling, this
coroutine gets cancelled automatically at the end of this request being
handled.

This can be used in handlers as a means of receiving a notification of
premature client disconnection.

.. versionadded:: 3.10

.. class:: Request

Expand Down

0 comments on commit 1caebc9

Please sign in to comment.