Make `code` and maybe `reason` of received websocket disconnect event accessible to application
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
Research direction
Read src/quart/asgi.py at ASGIWebsocketConnection around the websocket.disconnect handling near line 183, then trace how disconnects and cancellation reach application code. Define an application-facing behavior that distinguishes client or server disconnects from cancellation and exposes the disconnect code, with reason included only if supported; done means the selected behavior is specified and covered by tests.
Written by the indexing model from the issue text.
Description
Currently the code number of the websocket.disconnect event is discarded in ASGIWebsocketConnection making it inaccessible to the application.
It is currently not distinguishable whether
- the client closed the connection and for what reason,
- the server closed the connection, for example for shutdown, or
- the task was cancelled, i.e.
CancelledErroris risen, for some other reason (distinction is probably not relevant for most applications).
It would be helpful to distinguish these cases in application code and also read the code value that was given (which would handle case 1 and case 2).
I saw that ASGI currently does not yet specify the reason value for the received disconnect (see related https://github.com/django/asgiref/issues/234). I personally do not need the reason it but I noticed it to be missing in my tests.
Hacky workaround and first thoughts
Currently I hack this feature into my application by using a custom asgi_websocket_class value for my app:
class ASGIWebsocketConnectionWithDisconnectEvent(ASGIWebsocketConnection):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.scope["disconnect_event"] = None
async def handle_messages(self, receive) -> None:
while True:
event = await receive()
if event["type"] == "websocket.receive":
message = event.get("bytes") or event["text"]
await websocket_received.send_async(message)
await self.queue.put(message)
elif event["type"] == "websocket.disconnect":
self.scope["disconnect_event"] = event
return
app = Quart(__name__)
app.asgi_websocket_class = ASGIWebsocketConnectionWithDisconnectEvent
Something like that, i.e. in general checking for the websocket to be closed on a CancelledError, would work for me. Modifying the scope dict is only a workaround of course. My implementation does not cover the third case above. To avoid the except+if pattern
try:
...
except asyncio.CancelledError:
if websocket_closed:
...
a dedicated exception type risen on send and receive seems nice on first sight. On the other hand this will then only be risen on awaits on the websocket's send or receive whereas the cancellation strategy will stop more awaits. What one prefers is probably application dependent.
- Dominant language
- Python
- Stars
- 3.7k
- Forks
- 206
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from pallets/quart
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
Difficulty 1/5 Under an hour Newbie friendliness 68/100
-
Difficulty 3/5 1-2 days Newbie friendliness 42/100
-
Difficulty 3/5 1-2 days Newbie friendliness 45/100
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Similar issues
-
triage/confirmed
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
agentscope-ai/agentscope#2775 ·
-
comp/desktop P3 type/bug
Difficulty 1/5 Under an hour Newbie friendliness 92/100
NousResearch/hermes-agent#118866 ·
-
bug
Difficulty 1/5 Under an hour Newbie friendliness 90/100
apache/cloudstack#14222 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100