fix(room): widen WS handler exception scope to JSONDecodeError + RuntimeError
A single malformed JSON message (or a "WebSocket is not connected" race
on disconnect) was killing the per-client handler with an uncaught
exception in the ASGI app. The surrounding try/except only caught
WebSocketDisconnect, so the server would log a stack trace and the
client would silently drop.
Wrap receive_json() to catch JSONDecodeError, send a structured
{"type":"error","code":"bad_message"} ack, and continue. Widen the
outer except to (WebSocketDisconnect, RuntimeError) so disconnect
races on send/receive after close exit the handler cleanly instead
of bubbling up the ASGI stack.
Both student_ws and instructor_ws hardened in parallel. 31/31 pytest
suite still passes; this fixes the recurring fuzz-scenario warn and
the cycle-187-style cascade observed in the stress loop.
This commit is contained in:
22
app/room.py
22
app/room.py
@@ -84,7 +84,14 @@ class RoomManager:
|
||||
try:
|
||||
await self.send_student_snapshot(websocket, sid, identity)
|
||||
while True:
|
||||
data = await websocket.receive_json()
|
||||
try:
|
||||
data = await websocket.receive_json()
|
||||
except json.JSONDecodeError:
|
||||
try:
|
||||
await websocket.send_json({"type": "error", "code": "bad_message", "message": "Invalid JSON"})
|
||||
except (WebSocketDisconnect, RuntimeError):
|
||||
break
|
||||
continue
|
||||
msg_type = data.get("type")
|
||||
if msg_type == "ping":
|
||||
await websocket.send_json({"type": "pong"})
|
||||
@@ -93,7 +100,7 @@ class RoomManager:
|
||||
await websocket.send_json(ack)
|
||||
else:
|
||||
await websocket.send_json({"type": "error", "code": "bad_message", "message": "Unknown message type"})
|
||||
except WebSocketDisconnect:
|
||||
except (WebSocketDisconnect, RuntimeError):
|
||||
pass
|
||||
finally:
|
||||
self.student_clients[sid].pop(websocket, None)
|
||||
@@ -104,7 +111,14 @@ class RoomManager:
|
||||
try:
|
||||
await self.send_instructor_snapshot(websocket, sid)
|
||||
while True:
|
||||
data = await websocket.receive_json()
|
||||
try:
|
||||
data = await websocket.receive_json()
|
||||
except json.JSONDecodeError:
|
||||
try:
|
||||
await websocket.send_json({"type": "error", "code": "bad_message", "message": "Invalid JSON"})
|
||||
except (WebSocketDisconnect, RuntimeError):
|
||||
break
|
||||
continue
|
||||
msg_type = data.get("type")
|
||||
if msg_type == "ping":
|
||||
await websocket.send_json({"type": "pong"})
|
||||
@@ -118,7 +132,7 @@ class RoomManager:
|
||||
await self.end_session(sid)
|
||||
else:
|
||||
await websocket.send_json({"type": "error", "code": "bad_message", "message": "Unknown message type"})
|
||||
except WebSocketDisconnect:
|
||||
except (WebSocketDisconnect, RuntimeError):
|
||||
pass
|
||||
finally:
|
||||
self.instructor_clients[sid].discard(websocket)
|
||||
|
||||
Reference in New Issue
Block a user