feat: add WebSocket auto-reconnection with exponential backoff#133
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds client-side resiliency to the chat WebSocket by automatically reconnecting with exponential backoff, surfacing reconnect status in the UI, and validating behavior via Playwright E2E tests.
Changes:
- Refactors the WebSocket setup into a
connect()flow with reconnect scheduling (exponential backoff) and “reconnect now” triggers (visibilitychange,online). - Adds a “Reconnecting…” UI state and styling.
- Introduces E2E coverage for forced close → auto-reconnect and sending messages after reconnect.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
hono/static/ChatPage.ts |
Implements the reconnect/backoff logic and reconnect triggers; updates connection status UI states. |
hono/components/ChatPage.css |
Adds .reconnecting style for the new reconnecting status indicator. |
e2e/ws-reconnection.spec.ts |
Adds Playwright tests to validate automatic reconnection and post-reconnect messaging. |
Comments suppressed due to low confidence (2)
hono/static/ChatPage.ts:117
- After a 1008 (unauthorized) close, the
onclosehandler returns early, butvisibilitychange/onlinestill callreconnectNow(), which will reconnect becausews.readyStateis CLOSED. This defeats the intent of “skip reconnection on 1008” and can cause repeated reconnect attempts on focus/network changes. Consider tracking the last close code (or areconnectDisabledflag) and short-circuitingreconnectNow()(and/or removing those listeners) when the last close was 1008.
if (event.code === UNAUTHORIZED_CLOSE_CODE) {
statusDiv.textContent = "Disconnected (unauthorized)";
statusDiv.className = `${STATUS_CLASS} ${DISCONNECTED_CLASS}`;
return;
}
hono/static/ChatPage.ts:116
- The “don’t reconnect on 1008 (unauthorized)” behavior is new and security-sensitive, but the new E2E spec only covers generic reconnect-after-close. Please add an E2E case that forces a 1008 close and asserts no automatic reconnect occurs (including via
visibilitychange/online), and that the UI stays in an unauthorized/disconnected state.
if (event.code === UNAUTHORIZED_CLOSE_CODE) {
statusDiv.textContent = "Disconnected (unauthorized)";
statusDiv.className = `${STATUS_CLASS} ${DISCONNECTED_CLASS}`;
return;
| if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) { | ||
| return; | ||
| } | ||
| cancelScheduledReconnect(); | ||
| reconnectDelay = INITIAL_RECONNECT_DELAY_MS; |
There was a problem hiding this comment.
reconnectNow() only guards against OPEN/CONNECTING. If the current socket is in CLOSING, reconnectNow() will create a new WebSocket while the old one is still closing; when that old socket finally fires onclose, it will still run scheduleReconnect()/UI updates and can interfere with the newer connection. Consider treating CLOSING as “don’t reconnect yet”, and/or capturing the created socket in connect() and ignoring onclose/onerror events from stale socket instances (e.g., if (ws !== socket) return).
This issue also appears in the following locations of the same file:
- line 113
- line 113
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
Reconnect automatically when the WebSocket connection drops, using exponential backoff (1s to 30s). Also reconnect immediately when the browser tab becomes visible or the network comes back online. Skip reconnection attempts when the server rejects with 1008 (unauthorized). Add E2E tests that verify reconnection behavior after a forced close.
b3698b8 to
41421f1
Compare
…ation (#135) * Initial plan * fix: guard against CLOSING state and ignore stale WebSocket events Agent-Logs-Url: https://github.com/mahata/mlack/sessions/04bf024c-81f8-4edc-9d89-c064aaec3da4 Co-authored-by: mahata <23497+mahata@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mahata <23497+mahata@users.noreply.github.com>
Summary
visibilitychange) and network recovery (onlineevent)Changes
hono/static/ChatPage.tsconnect()function, addedscheduleReconnect()/reconnectNow()/cancelScheduledReconnect()helpers, addedvisibilitychangeandonlineevent listenershono/components/ChatPage.css.reconnectingclass (orange#ff9800)e2e/ws-reconnection.spec.tsTesting
pnpm lint— passedpnpm build:check— passedpnpm test:run— 44/44 tests passedpnpm test:e2e)