Fix WebSocket reconnect: refused upgrades are 1006 not 1008, back off, prompt once

This commit is contained in:
Chris Amow 2026-08-10 04:53:41 +00:00
parent 1f1544bab1
commit 821f0a0a8f

View file

@ -5,9 +5,17 @@ const { createApp, ref, computed, watch, onMounted, onUnmounted } = Vue;
const TOKEN_KEY = 'chart-token';
let authToken = localStorage.getItem(TOKEN_KEY) || '';
// Reconnects and the status poll both hit 401s, so without this a visitor
// who cancels gets asked again every couple of seconds.
let promptDeclined = false;
function promptForToken() {
if (promptDeclined) return false;
const entered = window.prompt('Access token for this chart', '');
if (entered === null) return false;
if (entered === null) {
promptDeclined = true;
return false;
}
authToken = entered.trim();
localStorage.setItem(TOKEN_KEY, authToken);
return true;
@ -55,6 +63,7 @@ createApp({
const now = ref(Date.now());
let chartApi = null;
let socket = null;
let retryDelay = 2000;
let timer = null;
const barAge = computed(() => {
@ -76,6 +85,7 @@ createApp({
const query = authToken ? `?token=${encodeURIComponent(authToken)}` : '';
socket = new WebSocket(`${protocol}://${location.host}/ws${query}`);
socket.onopen = () => {
retryDelay = 2000;
socket.send(JSON.stringify({ type: 'subscribe', tf: timeframe.value }));
sendPrefs();
};
@ -102,11 +112,17 @@ createApp({
playAlert();
}
};
socket.onclose = event => {
socket.onclose = async () => {
status.value.stream = 'disconnected';
// 1008 is the server rejecting our token; ask once, then reconnect.
if (event.code === 1008 && !promptForToken()) return;
setTimeout(connect, 2000);
// A refused upgrade reaches the browser as 1006, not 1008: the server
// rejects the handshake with HTTP 403 before any close frame exists.
// So the close code cannot distinguish "bad token" from "server
// restarting". Re-check over HTTP instead — apiFetch prompts for a
// token when that is what is actually wrong — and back off so a
// visitor without one is not reconnecting twice a second forever.
await refreshStatus();
retryDelay = Math.min(retryDelay * 2, 30000);
setTimeout(connect, retryDelay);
};
}