import { MutableRefObject } from 'react' ; import { setUpSocketIOPing } from './socket-io' ; import { DEFAULT_RECONNECT_LIMIT , DEFAULT_RECONNECT_INTERVAL_MS , ReadyState , isEventSourceSupported , } from './constants' ; import { Options , SendMessage , WebSocketLike } from './types' ; import { assertIsWebSocket } from './util' ; export interface Setters { setLastMessage : ( message : WebSocketEventMap [ 'message' ] ) => void ; setReadyState : ( readyState : ReadyState ) => void ; } const bindMessageHandler = ( webSocketInstance , optionsRef , setLastMessage ) => { webSocketInstance . onmessage = ( message ) => { optionsRef . current . onMessage && optionsRef . current . onMessage ( message ) ; if ( typeof optionsRef . current . filter === 'function' && optionsRef . current . filter ( message ) !== true ) { return ; } setLastMessage ( message ) ; } ; } ; const bindOpenHandler = ( webSocketInstance , optionsRef , setReadyState , reconnectCount ) => { webSocketInstance . onopen = ( event ) => { optionsRef . current . onOpen && optionsRef . current . onOpen ( event ) ; reconnectCount . current = 0 ; setReadyState ( ReadyState . OPEN ) ; } ; } ; const bindCloseHandler = ( webSocketInstance , optionsRef , setReadyState , reconnect , reconnectCount ) => { if ( isEventSourceSupported && webSocketInstance instanceof EventSource ) { return ( ) => { } ; } assertIsWebSocket ( webSocketInstance ) ; let reconnectTimeout ; webSocketInstance . onclose = ( event ) => { optionsRef . current . onClose && optionsRef . current . onClose ( event ) ; setReadyState ( ReadyState . CLOSED ) ; if ( optionsRef . current . shouldReconnect && optionsRef . current . shouldReconnect ( event ) ) { const reconnectAttempts = optionsRef . current . reconnectAttempts ?? DEFAULT_RECONNECT_LIMIT ; if ( reconnectCount . current < reconnectAttempts ) { reconnectTimeout = window . setTimeout ( ( ) => { reconnectCount . current ++ ; reconnect ( ) ; } , optionsRef . current . reconnectInterval ?? DEFAULT_RECONNECT_INTERVAL_MS ) ; } else { optionsRef . current . onReconnectStop && optionsRef . current . onReconnectStop ( reconnectAttempts ) ; console . warn ( `Max reconnect attempts of ${ reconnectAttempts } exceeded` ) ; } } } ; return ( ) => reconnectTimeout && window . clearTimeout ( reconnectTimeout ) ; } ; const bindErrorHandler = ( webSocketInstance , optionsRef , setReadyState , reconnect , reconnectCount ) => { let reconnectTimeout ; webSocketInstance . onerror = ( error ) => { optionsRef . current . onError && optionsRef . current . onError ( error ) ; if ( isEventSourceSupported && webSocketInstance instanceof EventSource ) { optionsRef . current . onClose && optionsRef . current . onClose ( { ... error , code : 1006 , reason : `An error occurred with the EventSource: ${ error }` , wasClean : false , } ) ; setReadyState ( ReadyState . CLOSED ) ; webSocketInstance . close ( ) ; } if ( optionsRef . current . retryOnError ) { if ( reconnectCount . current < ( optionsRef . current . reconnectAttempts ?? DEFAULT_RECONNECT_LIMIT ) ) { reconnectTimeout = window . setTimeout ( ( ) => { reconnectCount . current ++ ; reconnect ( ) ; } , optionsRef . current . reconnectInterval ?? DEFAULT_RECONNECT_INTERVAL_MS ) ; } else { optionsRef . current . onReconnectStop && optionsRef . current . onReconnectStop ( optionsRef . current . reconnectAttempts as number ) ; console . warn ( `Max reconnect attempts of ${ optionsRef . current . reconnectAttempts } exceeded` ) ; } } } ; return ( ) => reconnectTimeout && window . clearTimeout ( reconnectTimeout ) ; } ; export const attachListeners = ( webSocketInstance , setters , optionsRef , reconnect , reconnectCount , sendMessage ) : ( ( ) => void ) => { const { setLastMessage , setReadyState } = setters ; let interval ; let cancelReconnectOnClose ; let cancelReconnectOnError ; if ( optionsRef . current . fromSocketIO ) { interval = setUpSocketIOPing ( sendMessage ) ; } bindMessageHandler ( webSocketInstance , optionsRef , setLastMessage ) ; bindOpenHandler ( webSocketInstance , optionsRef , setReadyState , reconnectCount ) ; cancelReconnectOnClose = bindCloseHandler ( webSocketInstance , optionsRef , setReadyState , reconnect , reconnectCount ) ; cancelReconnectOnError = bindErrorHandler ( webSocketInstance , optionsRef , setReadyState , reconnect , reconnectCount ) ; return ( ) => { setReadyState ( ReadyState . CLOSING ) ; cancelReconnectOnClose ( ) ; cancelReconnectOnError ( ) ; webSocketInstance . close ( ) ; if ( interval ) clearInterval ( interval ) ; } ; } ;