diff --git a/ui/src/routes/devices.$id.tsx b/ui/src/routes/devices.$id.tsx index a1ace077..1fcd46ab 100644 --- a/ui/src/routes/devices.$id.tsx +++ b/ui/src/routes/devices.$id.tsx @@ -146,6 +146,7 @@ export default function KvmIdRoute() { const { otaState, setOtaState, setModalView } = useUpdateStore(); const [loadingMessage, setLoadingMessage] = useState("Connecting to device..."); + const cleanupAndStopReconnecting = useCallback( function cleanupAndStopReconnecting() { console.log("Closing peer connection"); @@ -182,11 +183,11 @@ export default function KvmIdRoute() { pc: RTCPeerConnection, remoteDescription: RTCSessionDescriptionInit, ) { - setLoadingMessage("Setting remote description"); + setLoadingMessage("Setting remote description type:" + remoteDescription.type); try { await pc.setRemoteDescription(new RTCSessionDescription(remoteDescription)); - console.log("[setRemoteSessionDescription] Remote description set successfully"); + console.log("[setRemoteSessionDescription] Remote description set successfully to: " + remoteDescription.sdp); setLoadingMessage("Establishing secure connection..."); } catch (error) { console.error( @@ -231,9 +232,15 @@ export default function KvmIdRoute() { const ignoreOffer = useRef(false); const isSettingRemoteAnswerPending = useRef(false); const makingOffer = useRef(false); + const reconnectAttemptsRef = useRef(20); const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:"; + const reconnectInterval = (attempt: number) => { + // Exponential backoff with a max of 10 seconds between attempts + return Math.min(500 * 2 ** attempt, 10000); + } + const { sendMessage, getWebSocket } = useWebSocket( isOnDevice ? `${wsProtocol}//${window.location.host}/webrtc/signaling/client` @@ -241,17 +248,16 @@ export default function KvmIdRoute() { { heartbeat: true, retryOnError: true, - reconnectAttempts: 15, - reconnectInterval: 1000, - onReconnectStop: () => { - console.debug("Reconnect stopped"); + reconnectAttempts: reconnectAttemptsRef.current, + reconnectInterval: reconnectInterval, + onReconnectStop: (attempt: number) => { + console.debug("Reconnect stopped after ", attempt, "attempts"); cleanupAndStopReconnecting(); }, shouldReconnect(event) { console.debug("[Websocket] shouldReconnect", event); - // TODO: Why true? - return true; + return !connectionFailed; // we always want to try to reconnect unless we're explicitly stopped }, onClose(event) { @@ -284,6 +290,7 @@ export default function KvmIdRoute() { */ const parsedMessage = JSON.parse(message.data); + if (parsedMessage.type === "device-metadata") { const { deviceVersion } = parsedMessage.data; console.debug("[Websocket] Received device-metadata message"); @@ -300,10 +307,12 @@ export default function KvmIdRoute() { console.log("[Websocket] Device is using new signaling"); isLegacySignalingEnabled.current = false; } + setupPeerConnection(); } if (!peerConnection) return; + if (parsedMessage.type === "answer") { console.debug("[Websocket] Received answer"); const readyForOffer = @@ -594,7 +603,9 @@ export default function KvmIdRoute() { api.POST(`${CLOUD_API}/webrtc/turn_activity`, { bytesReceived: bytesReceivedDelta, bytesSent: bytesSentDelta, - }); + }).catch(()=>{ + // we don't care about errors here, but we don't want unhandled promise rejections + }); }, 10000); const { setNetworkState} = useNetworkStateStore();