From a869de3c954d779d4756905d3fe0d45ecc8437b1 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:58:07 -0500 Subject: [PATCH] mseplayer fixes - close all sockets, even those in CONNECTING state - store attached listener reference so removeEventListener removes the correct function after callback recreation --- web/src/components/player/MsePlayer.tsx | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/web/src/components/player/MsePlayer.tsx b/web/src/components/player/MsePlayer.tsx index 1a2b1b6cbf..e8daed4437 100644 --- a/web/src/components/player/MsePlayer.tsx +++ b/web/src/components/player/MsePlayer.tsx @@ -81,6 +81,7 @@ function MSEPlayer({ const wsRef = useRef(null); const reconnectTIDRef = useRef(null); const intentionalDisconnectRef = useRef(false); + const onCloseRef = useRef<(() => void) | null>(null); const ondataRef = useRef<((data: ArrayBufferLike) => void) | null>(null); const onmessageRef = useRef<{ [key: string]: (msg: { value: string; type: string }) => void; @@ -167,6 +168,8 @@ function MSEPlayer({ wsRef.current = new WebSocket(wsURL); wsRef.current.binaryType = "arraybuffer"; wsRef.current.addEventListener("open", onOpen); + // Capture current onClose identity so removeEventListener can find it later + onCloseRef.current = onClose; wsRef.current.addEventListener("close", onClose); // we know that these deps are correct // eslint-disable-next-line react-hooks/exhaustive-deps @@ -200,20 +203,23 @@ function MSEPlayer({ intentionalDisconnectRef.current = true; setWsState(WebSocket.CLOSED); - // Remove event listeners to prevent them firing during close + // Remove event listeners to prevent them firing during close. + // Use onCloseRef to remove the exact function that was attached in onConnect, + // since onClose may have been recreated by React since then. try { ws.removeEventListener("open", onOpen); - ws.removeEventListener("close", onClose); + if (onCloseRef.current) { + ws.removeEventListener("close", onCloseRef.current); + onCloseRef.current = null; + } } catch { // Ignore errors removing listeners } - // Only call close() if the socket is OPEN or CLOSING - // For CONNECTING or CLOSED sockets, just let it die - if ( - currentReadyState === WebSocket.OPEN || - currentReadyState === WebSocket.CLOSING - ) { + // Close the socket in any non-CLOSED state, including CONNECTING. + // A CONNECTING socket that is not closed will complete its handshake + // and remain open, leaking a browser connection. + if (currentReadyState !== WebSocket.CLOSED) { try { ws.close(); } catch {