Files
rplace/src
tiennm99 4f02d30393 feat(canvas): WS hardening, client race fix, and ping/pong heartbeat
Server:
- Origin allowlist on /api/ws (env.ALLOWED_ORIGINS, comma-separated; empty
  = allow all for dev/preview)
- per-identity WS connection cap (MAX_WS_PER_IDENTITY = 5) using
  acceptWebSocket(socket, [identity]) tagging; 6th upgrade returns 429
- ws.send 'ping' triggers a {type:'pong'} reply so dead connections fire
  onclose promptly instead of waiting on TCP keepalive

Client:
- buffer WS pixels arriving during the initial canvas fetch and replay
  them after committedColors is replaced; previously the post-fetch
  Uint8Array assignment silently overwrote any pixels broadcast in the
  fetch window (the documented C2 race)
- 30s ping / 60s pong watchdog closes the socket if pong stops arriving,
  routing through the existing exponential-backoff reconnect path

Tests:
- four /api/ws cases: missing upgrade, disallowed origin, allowed origin,
  empty allowlist (dev default). Sentinel uses status 200 because undici
  rejects 101 in Node-side Response constructors.

Plan: plans/260510-0232-fix-do-migration-followups/phase-03-ws-hardening-client-race.md
2026-05-10 03:05:56 +07:00
..