mseplayer fixes

- close all sockets, even those in CONNECTING state
- store attached listener reference so removeEventListener removes the correct function after callback recreation
This commit is contained in:
Josh Hawkins 2026-04-04 21:58:07 -05:00
parent fa7858a1c9
commit a869de3c95

View File

@ -81,6 +81,7 @@ function MSEPlayer({
const wsRef = useRef<WebSocket | null>(null);
const reconnectTIDRef = useRef<number | null>(null);
const intentionalDisconnectRef = useRef<boolean>(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 {