Fix iOS WebSocket hang with self-signed TLS certificates
iOS Safari silently rejects wss:// connections to servers using self-signed certificates — the trust exception accepted for the page does not extend to WebSocket handshakes. This causes the UI to hang on "Establishing WebSocket connection..." indefinitely. - Make --certificate and --key optional; server runs plain HTTP when omitted - When TLS is enabled, also start a plain HTTP listener on --http-port (default 8081) so iOS clients can connect via ws:// instead of wss:// - Add a 5-second connection timeout on the frontend that detects the hang and shows a clickable link to the HTTP fallback URL https://claude.ai/code/session_01VJ4CsBALnYcVhFJpY5cD5k
This commit is contained in:
parent
bd3a5e2af0
commit
c617a371cf
2 changed files with 92 additions and 17 deletions
|
|
@ -500,25 +500,44 @@
|
|||
function connect() {
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsUrl = `${protocol}//${window.location.host}/ws`;
|
||||
|
||||
|
||||
connectionStatus.textContent = 'Establishing WebSocket connection...';
|
||||
state.ws = new WebSocket(wsUrl);
|
||||
|
||||
|
||||
// iOS Safari silently rejects wss:// connections to servers with
|
||||
// self-signed certificates — onopen never fires and the UI hangs.
|
||||
// Use a timeout to detect this and show a helpful message.
|
||||
const connectTimeout = setTimeout(() => {
|
||||
if (state.ws && state.ws.readyState !== WebSocket.OPEN) {
|
||||
console.warn('WebSocket connection timed out');
|
||||
state.ws.close();
|
||||
if (window.location.protocol === 'https:') {
|
||||
const httpPort = parseInt(window.location.port || '443', 10) + 1;
|
||||
const httpUrl = `http://${window.location.hostname}:${httpPort}`;
|
||||
connectionStatus.innerHTML =
|
||||
'Connection failed — iOS may not support self-signed WSS certificates.<br>' +
|
||||
'Try the plain HTTP URL instead: <a href="' + httpUrl + '" style="color:#60a5fa;text-decoration:underline">' + httpUrl + '</a>';
|
||||
}
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
state.ws.onopen = () => {
|
||||
clearTimeout(connectTimeout);
|
||||
console.log('WebSocket connected');
|
||||
state.reconnectAttempts = 0;
|
||||
connectionStatus.textContent = 'Connected, waiting for welcome...';
|
||||
};
|
||||
|
||||
|
||||
state.ws.onmessage = (event) => {
|
||||
const msg = JSON.parse(event.data);
|
||||
handleSignal(msg);
|
||||
};
|
||||
|
||||
|
||||
state.ws.onclose = () => {
|
||||
clearTimeout(connectTimeout);
|
||||
console.log('WebSocket disconnected');
|
||||
updateConnectionStatus(false);
|
||||
|
||||
|
||||
if (state.reconnectAttempts < state.maxReconnectAttempts) {
|
||||
state.reconnectAttempts++;
|
||||
const delay = Math.min(1000 * Math.pow(2, state.reconnectAttempts), 30000);
|
||||
|
|
@ -527,8 +546,9 @@
|
|||
setTimeout(connect, delay);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
state.ws.onerror = (err) => {
|
||||
clearTimeout(connectTimeout);
|
||||
console.error('WebSocket error:', err);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue