POST /room/:code/leave for beacon teardown#12
Conversation
Lets clients deliver an "I'm leaving" signal via navigator.sendBeacon from a pagehide handler. Android Chrome routinely drops the WebSocket close frame on tab close (crbug 40378664), so the renderer-independent network-service delivery of sendBeacon is what gets the leave to the server reliably. Body is form-urlencoded with clientId (the slot's bearer secret). Idempotent: missing room or wrong clientId resolves to 204 to avoid leaking room/slot existence. Bumps to 2.1.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "room cleanup on solo leave" test slept 50ms before checking room deletion, suggesting cleanup was async — but it happens synchronously inside the HTTP handler before the response is returned, so by the time fetch() resolves the room is already gone. Replace with direct post-response assertion. Add a concurrent WS-close + HTTP-leave race test. The detach guards in both paths (removeFromRoom checks ws.data.index, leave checks member.ws) should make this idempotent regardless of arrival order — the test asserts host receives exactly one peer_left. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Closing without merge. This endpoint was designed as the receiving end for So the beacon never gets queued in the case we cared about — there's no caller for this endpoint that delivers a real improvement. Without that, adding the endpoint is just dead surface area. The right fix is on this server's side instead: tighten the WebSocket |
Summary
Add
POST /room/:code/leaveso clients can deliver an "I'm leaving" signal vianavigator.sendBeaconfrom apagehidehandler.Why: Android Chrome routinely drops the WebSocket close frame when a tab is closed (crbug 40378664, crbug 40839988, websockets/ws#1380) — the renderer is killed before the network service flushes the close handshake. The relay then only detects the dead WS via TCP-level teardown, and clients fall back to their own heartbeat (multi-second). Beacons are queued by the browser's network service and delivered independently of the renderer, so they survive the teardown that drops WS frames.
Endpoint
clientId=<the slot's bearer secret>clientId4001, reason"leave"The handler reuses existing region/instance routing (the path matches the same
:codeextraction asGET /room/:code), so beacons land on the right machine on Fly.Bumps to 2.1.0.
Test plan
bun test— 73 pass, including new endpoint coverage