Skip to content

Commit 4e9f48d

Browse files
frankie567Kludex
andauthored
Add reason support on WebSocketDisconnectEvent (#2324)
* Add `reason` support WebSocketDisconnectEvent * cutify test --------- Co-authored-by: Marcelo Trylesinski <[email protected]>
1 parent 44a3071 commit 4e9f48d

File tree

4 files changed

+12
-4
lines changed

4 files changed

+12
-4
lines changed

tests/protocols/test_websocket.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,20 +616,25 @@ async def websocket_session(url: str):
616616

617617

618618
async def test_client_close(ws_protocol_cls: WSProtocol, http_protocol_cls: HTTPProtocol, unused_tcp_port: int):
619+
disconnect_message: WebSocketDisconnectEvent | None = None
620+
619621
async def app(scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable):
622+
nonlocal disconnect_message
620623
while True:
621624
message = await receive()
622625
if message["type"] == "websocket.connect":
623626
await send({"type": "websocket.accept"})
624627
elif message["type"] == "websocket.receive":
625628
pass
626629
elif message["type"] == "websocket.disconnect":
630+
disconnect_message = message
627631
break
628632

629633
async def websocket_session(url: str):
630634
async with websockets.client.connect(url) as websocket:
631635
await websocket.ping()
632636
await websocket.send("abc")
637+
await websocket.close(code=1001, reason="custom reason")
633638

634639
config = Config(
635640
app=app,
@@ -641,6 +646,8 @@ async def websocket_session(url: str):
641646
async with run_server(config):
642647
await websocket_session(f"ws://127.0.0.1:{unused_tcp_port}")
643648

649+
assert disconnect_message == {"type": "websocket.disconnect", "code": 1001, "reason": "custom reason"}
650+
644651

645652
async def test_client_connection_lost(
646653
ws_protocol_cls: WSProtocol, http_protocol_cls: HTTPProtocol, unused_tcp_port: int
@@ -1262,7 +1269,7 @@ async def send_text(url: str):
12621269
await send_text(f"ws://127.0.0.1:{unused_tcp_port}")
12631270

12641271
assert frames == [b"abc", b"abc", b"abc"]
1265-
assert disconnect_message == {"type": "websocket.disconnect", "code": 1000}
1272+
assert disconnect_message == {"type": "websocket.disconnect", "code": 1000, "reason": ""}
12661273

12671274

12681275
async def test_default_server_headers(

uvicorn/_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ class WebSocketResponseBodyEvent(TypedDict):
204204
class WebSocketDisconnectEvent(TypedDict):
205205
type: Literal["websocket.disconnect"]
206206
code: int
207+
reason: NotRequired[str | None]
207208

208209

209210
class WebSocketCloseEvent(TypedDict):

uvicorn/protocols/websockets/websockets_impl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ async def asgi_receive(
382382
self.closed_event.set()
383383
if self.ws_server.closing:
384384
return {"type": "websocket.disconnect", "code": 1012}
385-
return {"type": "websocket.disconnect", "code": exc.code}
385+
return {"type": "websocket.disconnect", "code": exc.code, "reason": exc.reason}
386386

387387
if isinstance(data, str):
388388
return {"type": "websocket.receive", "text": data}

uvicorn/protocols/websockets/wsproto_impl.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ def handle_bytes(self, event: events.BytesMessage) -> None:
212212
def handle_close(self, event: events.CloseConnection) -> None:
213213
if self.conn.state == ConnectionState.REMOTE_CLOSING:
214214
self.transport.write(self.conn.send(event.response()))
215-
self.queue.put_nowait({"type": "websocket.disconnect", "code": event.code})
215+
self.queue.put_nowait({"type": "websocket.disconnect", "code": event.code, "reason": event.reason})
216216
self.transport.close()
217217

218218
def handle_ping(self, event: events.Ping) -> None:
@@ -336,7 +336,7 @@ async def send(self, message: ASGISendEvent) -> None:
336336
self.close_sent = True
337337
code = message.get("code", 1000)
338338
reason = message.get("reason", "") or ""
339-
self.queue.put_nowait({"type": "websocket.disconnect", "code": code})
339+
self.queue.put_nowait({"type": "websocket.disconnect", "code": code, "reason": reason})
340340
output = self.conn.send(wsproto.events.CloseConnection(code=code, reason=reason))
341341
if not self.transport.is_closing():
342342
self.transport.write(output)

0 commit comments

Comments
 (0)