diff --git a/lib/tunnelbroker/tunnelbroker-context.js b/lib/tunnelbroker/tunnelbroker-context.js --- a/lib/tunnelbroker/tunnelbroker-context.js +++ b/lib/tunnelbroker/tunnelbroker-context.js @@ -5,6 +5,8 @@ import uuid from 'uuid'; import { tunnnelbrokerURL } from '../facts/tunnelbroker.js'; +import { tunnelbrokerHeartbeatTimeout } from '../shared/timeouts.js'; +import type { Heartbeat } from '../types/tunnelbroker/heartbeat-types.js'; import type { MessageReceiveConfirmation } from '../types/tunnelbroker/message-receive-confirmation-types.js'; import type { MessageSentStatus } from '../types/tunnelbroker/message-to-device-request-status-types.js'; import type { MessageToDeviceRequest } from '../types/tunnelbroker/message-to-device-request-types.js'; @@ -51,6 +53,22 @@ const listeners = React.useRef>(new Set()); const socket = React.useRef(null); const promises = React.useRef({}); + const heartbeatTimeoutID = React.useRef(); + + const stopHeartbeatTimeout = React.useCallback(() => { + if (heartbeatTimeoutID.current) { + clearTimeout(heartbeatTimeoutID.current); + heartbeatTimeoutID.current = null; + } + }, []); + + const resetHeartbeatTimeout = React.useCallback(() => { + stopHeartbeatTimeout(); + heartbeatTimeoutID.current = setTimeout(() => { + socket.current?.close(); + setConnected(false); + }, tunnelbrokerHeartbeatTimeout); + }, [stopHeartbeatTimeout]); React.useEffect(() => { if (connected || !initMessage) { @@ -89,6 +107,8 @@ } const message: TunnelbrokerMessage = rawMessage; + resetHeartbeatTimeout(); + for (const listener of listeners.current) { listener(message); } @@ -134,11 +154,16 @@ console.log('Tunnelbroker recorded InvalidRequest'); } } + } else if (message.type === tunnelbrokerMessageTypes.HEARTBEAT) { + const heartbeat: Heartbeat = { + type: tunnelbrokerMessageTypes.HEARTBEAT, + }; + socket.current?.send(JSON.stringify(heartbeat)); } }; socket.current = tunnelbrokerSocket; - }, [connected, initMessage]); + }, [connected, initMessage, resetHeartbeatTimeout, stopHeartbeatTimeout]); const sendMessage: (message: ClientMessageToDevice) => Promise = React.useCallback(