Handle websocket disconnect in event loop
See original GitHub issueIt seems that the starlette.websockets.WebSocketDisconnect
exception is not raised when a client disconnects.
This can cause orphaned async tasks as multiple clients connect/disconnect. As someone first referenced on StackOverflow, it can also result in Uvicorn’s shutdown/reload to block.
Example
#!/usr/bin/env python
import asyncio
from starlette.applications import Starlette
from starlette.responses import HTMLResponse
app = Starlette()
@app.route('/')
async def index(req):
"""Return basic websocket connection template at base route"""
return HTMLResponse('''
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Example</title>
</head>
<body>
Intentionally empty. Messages echoed to js console.
<script>
ws = new WebSocket("ws://localhost:8000/ws");
ws.onmessage = e => {
console.log(e);
};
</script>
</body>
</html>
''')
@app.websocket_route('/ws')
async def ws(websocket):
await websocket.accept()
try:
while True:
# Expected behavior: print "Looping..." until client
# disconnects, then close websocket
print(f'Looping. Client state: {websocket.client_state}')
# await websocket.send_text('Hello world!')
await asyncio.sleep(1)
finally:
await websocket.close()
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, log_level='debug')
After accessing the base route to create a websocket connection, closing the window/connection does not raise starlette.websockets.WebSocketDisconnect
, leaves the loop running indefinitely, and does not modify websocket.client_state
. Additional connections start additional loops.
If you add the line to send the Hello world! message over the websocket after the client has disconnected, websockets.exceptions.ConnectionClosedOK
will be raised and can be handled. However, this likely won’t be a workaround for all use cases.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:6
- Comments:11 (6 by maintainers)
Top GitHub Comments
you can subclass
WebSocketEndpoint
@benfasoli , here’s an example of a dummy counterunderstood , the 40 to 50s you see is exactly what is described here: https://github.com/aaugustin/websockets/blob/3bab7fd155636c73b79b258de752b36687bba347/src/websockets/protocol.py#L803-L810
close_timeout default to 10s so maybe setting it to a lower value might help