From f03f665e083250b9c1721d40556e488b4347293c Mon Sep 17 00:00:00 2001 From: ProjectMoon Date: Tue, 30 Jul 2024 16:03:38 +0200 Subject: [PATCH 1/3] Do not kill entire chat window when websocket closes. Reopens the websocket on close. Prevents badly configured reverse proxies from making the application stop working. --- ui/components/ChatWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/ChatWindow.tsx b/ui/components/ChatWindow.tsx index ea9a93d3..34e41148 100644 --- a/ui/components/ChatWindow.tsx +++ b/ui/components/ChatWindow.tsx @@ -194,7 +194,7 @@ const useSocket = ( ws.onclose = () => { clearTimeout(timeoutId); - setError(true); + setWs(null); console.log('[DEBUG] closed'); }; From 84821d289caf93bb2bbfc513f56be8e79dba1253 Mon Sep 17 00:00:00 2001 From: projectmoon Date: Tue, 30 Jul 2024 19:45:20 +0200 Subject: [PATCH 2/3] Do not set websocket to null if in error state. --- ui/components/ChatWindow.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/components/ChatWindow.tsx b/ui/components/ChatWindow.tsx index 34e41148..61dfb135 100644 --- a/ui/components/ChatWindow.tsx +++ b/ui/components/ChatWindow.tsx @@ -25,6 +25,7 @@ const useSocket = ( url: string, setIsWSReady: (ready: boolean) => void, setError: (error: boolean) => void, + hasError: boolean ) => { const [ws, setWs] = useState(null); @@ -194,7 +195,9 @@ const useSocket = ( ws.onclose = () => { clearTimeout(timeoutId); - setWs(null); + if (!hasError) { + setWs(null); // forces websocket to reopen when needed. + } console.log('[DEBUG] closed'); }; @@ -285,6 +288,7 @@ const ChatWindow = ({ id }: { id?: string }) => { process.env.NEXT_PUBLIC_WS_URL!, setIsWSReady, setHasError, + hasError ); const [loading, setLoading] = useState(false); From db7565b752cfc3403df7eb53d07962f9e64ebc5f Mon Sep 17 00:00:00 2001 From: projectmoon Date: Thu, 1 Aug 2024 17:12:21 +0200 Subject: [PATCH 3/3] Exponential backoff with useRef --- ui/components/ChatWindow.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ui/components/ChatWindow.tsx b/ui/components/ChatWindow.tsx index 61dfb135..ed41711d 100644 --- a/ui/components/ChatWindow.tsx +++ b/ui/components/ChatWindow.tsx @@ -28,6 +28,8 @@ const useSocket = ( hasError: boolean ) => { const [ws, setWs] = useState(null); + const reconnectTimeout = useRef(0); + const reconnectAttempts = useRef(0); useEffect(() => { if (!ws) { @@ -182,6 +184,8 @@ const useSocket = ( ws.onopen = () => { console.log('[DEBUG] open'); + reconnectTimeout.current = 0; + reconnectAttempts.current = 0; clearTimeout(timeoutId); setError(false); setIsWSReady(true); @@ -195,7 +199,7 @@ const useSocket = ( ws.onclose = () => { clearTimeout(timeoutId); - if (!hasError) { + if (!hasError && reconnectAttempts.current < 3) { setWs(null); // forces websocket to reopen when needed. } console.log('[DEBUG] closed'); @@ -211,7 +215,15 @@ const useSocket = ( setWs(ws); }; - connectWs(); + if (reconnectAttempts.current < 3) { + console.log(`[DEBUG] Attempting to reconnect (${reconnectAttempts.current + 1}/3)`); + setTimeout(connectWs, reconnectTimeout.current); + reconnectTimeout.current = reconnectTimeout.current > 0 ? reconnectTimeout.current * 2 : 1000; + reconnectAttempts.current += 1; + } else { + console.log('[DEBUG] WebSocket reconnect failure after 3 retries'); + setError(true); + } } return () => {