fix(chat): prevent stale websocket close handler from triggering reconnect loop

This commit is contained in:
zwitschi committed 2026-08-15 16:28:32 +02:00
1 parent cef002862e
commit b6705e6dbd
2 files changed
+36 -5

No files matched your search

+19
View File
@@ -253,6 +253,25 @@ describe("WebSocket reconnect", () => {
expect(websocketInstances).toHaveLength(2);
expect(websocketInstances[1].url).toContain("room=lobby");
});
it("does not reconnect from a socket that was intentionally replaced", async () => {
vi.useFakeTimers();
window.switchRoom("lobby");
expect(websocketInstances).toHaveLength(1);
const replaced = websocketInstances[0];
// A room switch intentionally replaces the socket; the old socket must not
// schedule its own reconnect, otherwise history is re-pulled on a loop.
window.switchRoom("main");
expect(websocketInstances).toHaveLength(2);
expect(replaced.onclose).toBeNull();
replaced.onclose?.();
await vi.advanceTimersByTimeAsync(3000);
expect(websocketInstances).toHaveLength(2);
});
});
describe("URL resolution", () => {
+17 -5
View File
@@ -79,19 +79,28 @@ export function resolveManifestUrl() {
}
function connectWebSocket(room: string) {
if (ws) ws.close();
// Detach the old socket's close handler before closing it. Otherwise an
// intentional reconnect (login/logout/room switch/session refresh) would
// have the replaced socket schedule yet another reconnect, and each new
// connection re-pulls the room's history — filling the panel with old
// messages on a loop.
if (ws) {
ws.onclose = null;
ws.close();
}
const socketUrl = new URL(resolveWebSocketBaseUrl());
socketUrl.searchParams.set("username", username);
socketUrl.searchParams.set("room", room);
ws = new WebSocket(socketUrl);
const socket = new WebSocket(socketUrl);
ws = socket;
ws.onopen = () => {
socket.onopen = () => {
console.log(`Connected to chat room: ${room}`);
addMessage("system", "Connected to " + room);
};
ws.onmessage = (event) => {
socket.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === "typing") {
showTyping(msg.username);
@@ -119,7 +128,10 @@ function connectWebSocket(room: string) {
addMessage(msg.type || "public", msg.text, msg.username, msg.timestamp);
};
ws.onclose = () => {
socket.onclose = () => {
// Ignore close events from sockets that were intentionally replaced.
if (ws !== socket) return;
ws = null;
console.log("Chat disconnected. Reconnecting in 3s...");
setTimeout(() => connectWebSocket(room), 3000);
};