diff --git a/lib/tunnelbroker/secondary-tunnelbroker-connection.js b/lib/tunnelbroker/secondary-tunnelbroker-connection.js --- a/lib/tunnelbroker/secondary-tunnelbroker-connection.js +++ b/lib/tunnelbroker/secondary-tunnelbroker-connection.js @@ -6,16 +6,19 @@ export type SecondaryTunnelbrokerConnection = { // Used by an inactive tab to send messages - +sendMessage: (message: DeviceToTunnelbrokerRequest) => mixed, + +sendMessage: ( + identifier: string, + message: DeviceToTunnelbrokerRequest, + ) => mixed, // Active tab receives messages from inactive tabs +onSendMessage: ( - (message: DeviceToTunnelbrokerRequest) => mixed, + (identifier: string, message: DeviceToTunnelbrokerRequest) => mixed, ) => RemoveCallback, // Active tab sets the message status of messages from inactive tabs - +setMessageStatus: (messageID: string, error: ?string) => mixed, + +setMessageStatus: (identifier: string, error: ?string) => mixed, // Inactive tabs receive message status and resolve or reject promises +onMessageStatus: ( - ((messageID: string, error: ?string) => mixed), + ((identifier: string, error: ?string) => mixed), ) => RemoveCallback, }; 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 @@ -385,35 +385,36 @@ addLog, ]); - const sendMessage: (request: DeviceToTunnelbrokerRequest) => Promise = - React.useCallback( - request => { - const resultPromise: Promise = new Promise((resolve, reject) => { - const socketActive = - socketStateRef.current.connected && socket.current; - if (!shouldBeClosed && !socketActive) { - throw new Error('Tunnelbroker not connected'); - } - promises.current[request.clientMessageID] = { - resolve, - reject, - }; - if (socketActive) { - socket.current?.send(JSON.stringify(request)); - } else { - secondaryTunnelbrokerConnection?.sendMessage(request); - } - }); + const sendMessage: ( + identifier: string, + request: DeviceToTunnelbrokerRequest, + ) => Promise = React.useCallback( + (identifier: string, request: DeviceToTunnelbrokerRequest) => { + const resultPromise: Promise = new Promise((resolve, reject) => { + const socketActive = socketStateRef.current.connected && socket.current; + if (!shouldBeClosed && !socketActive) { + throw new Error('Tunnelbroker not connected'); + } + promises.current[identifier] = { + resolve, + reject, + }; + if (socketActive) { + socket.current?.send(JSON.stringify(request)); + } else { + secondaryTunnelbrokerConnection?.sendMessage(identifier, request); + } + }); - const timeoutPromise: Promise = (async () => { - await sleep(tunnelbrokerRequestTimeout); - throw new Error('Tunnelbroker request timed out'); - })(); + const timeoutPromise: Promise = (async () => { + await sleep(tunnelbrokerRequestTimeout); + throw new Error('Tunnelbroker request timed out'); + })(); - return Promise.race([resultPromise, timeoutPromise]); - }, - [secondaryTunnelbrokerConnection, shouldBeClosed], - ); + return Promise.race([resultPromise, timeoutPromise]); + }, + [secondaryTunnelbrokerConnection, shouldBeClosed], + ); const sendMessageToDevice: ( message: TunnelbrokerClientMessageToDevice, @@ -428,7 +429,7 @@ payload: message.payload, }; - return sendMessage(messageToDevice); + return sendMessage(clientMessageID, messageToDevice); }, [sendMessage], ); @@ -442,33 +443,41 @@ clientMessageID, payload, }; - return sendMessage(messageToTunnelbroker); + return sendMessage(clientMessageID, messageToTunnelbroker); + }, + [sendMessage], + ); + + const sendNotif: (notif: TunnelbrokerNotif) => Promise = + React.useCallback( + (notif: TunnelbrokerNotif) => { + return sendMessage(notif.clientMessageID, notif); }, [sendMessage], ); React.useEffect( () => - secondaryTunnelbrokerConnection?.onSendMessage(message => { - if (shouldBeClosed) { - // We aren't supposed to be handling it - return; - } - - void (async () => { - try { - await sendMessage(message); - secondaryTunnelbrokerConnection.setMessageStatus( - message.clientMessageID, - ); - } catch (error) { - secondaryTunnelbrokerConnection.setMessageStatus( - message.clientMessageID, - error, - ); + secondaryTunnelbrokerConnection?.onSendMessage( + (identifier: string, message: DeviceToTunnelbrokerRequest) => { + if (shouldBeClosed) { + // We aren't supposed to be handling it + return; } - })(); - }), + + void (async () => { + try { + await sendMessage(identifier, message); + secondaryTunnelbrokerConnection.setMessageStatus(identifier); + } catch (error) { + secondaryTunnelbrokerConnection.setMessageStatus( + identifier, + error, + ); + } + })(); + }, + ), [secondaryTunnelbrokerConnection, sendMessage, shouldBeClosed], ); @@ -536,7 +545,7 @@ () => ({ sendMessageToDevice, sendMessageToTunnelbroker, - sendNotif: sendMessage, + sendNotif, socketState, addListener, removeListener, @@ -545,7 +554,7 @@ }), [ sendMessageToDevice, - sendMessage, + sendNotif, sendMessageToTunnelbroker, socketState, addListener, diff --git a/web/app.react.js b/web/app.react.js --- a/web/app.react.js +++ b/web/app.react.js @@ -49,7 +49,7 @@ import type { LoadingStatus } from 'lib/types/loading-types.js'; import type { WebNavInfo } from 'lib/types/nav-types.js'; import type { Dispatch } from 'lib/types/redux-types.js'; -import type { MessageToDeviceRequest } from 'lib/types/tunnelbroker/message-to-device-request-types.js'; +import type { DeviceToTunnelbrokerRequest } from 'lib/types/tunnelbroker/messages.js'; import { getConfig, registerConfig } from 'lib/utils/config.js'; import { isDev } from 'lib/utils/dev-utils.js'; import { useDispatch } from 'lib/utils/redux-utils.js'; @@ -413,11 +413,11 @@ function useOtherTabsTunnelbrokerConnection(): SecondaryTunnelbrokerConnection { const onSendMessageCallbacks = React.useRef< - Set<(MessageToDeviceRequest) => mixed>, + Set<(identifier: string, message: DeviceToTunnelbrokerRequest) => mixed>, >(new Set()); const onMessageStatusCallbacks = React.useRef< - Set<(messageID: string, error: ?string) => mixed>, + Set<(identifier: string, error: ?string) => mixed>, >(new Set()); React.useEffect(() => { @@ -432,7 +432,12 @@ } const data = event.data; if (data.type === WEB_TUNNELBROKER_MESSAGE_TYPES.SEND_MESSAGE) { - if (typeof data.message !== 'object' || !data.message) { + if ( + typeof data.message !== 'object' || + !data.message || + typeof data.identifier !== 'string' || + !data.identifier + ) { console.log( 'Invalid tunnelbroker message request received ' + 'from shared tunnelbroker broadcast channel', @@ -441,20 +446,21 @@ return; } // We know that the input was already validated - const message: MessageToDeviceRequest = (data.message: any); + const message: DeviceToTunnelbrokerRequest = (data.message: any); + const identifier: string = data.identifier; for (const callback of onSendMessageCallbacks.current) { - callback(message); + callback(identifier, message); } } else if (data.type === WEB_TUNNELBROKER_MESSAGE_TYPES.MESSAGE_STATUS) { - if (typeof data.messageID !== 'string') { + if (typeof data.identifier !== 'string') { console.log( - 'Missing message id in message status message ' + + 'Missing identifier in message status message ' + 'from shared tunnelbroker broadcast channel', ); return; } - const messageID = data.messageID; + const identifier = data.identifier; if ( typeof data.error !== 'string' && @@ -471,7 +477,7 @@ const error = data.error; for (const callback of onMessageStatusCallbacks.current) { - callback(messageID, error); + callback(identifier, error); } } else { console.log( @@ -489,9 +495,10 @@ return React.useMemo( () => ({ - sendMessage: message => + sendMessage: (identifier: string, message: DeviceToTunnelbrokerRequest) => WEB_TUNNELBROKER_CHANNEL.postMessage({ type: WEB_TUNNELBROKER_MESSAGE_TYPES.SEND_MESSAGE, + identifier, message, }), onSendMessage: callback => { @@ -500,10 +507,10 @@ onSendMessageCallbacks.current.delete(callback); }; }, - setMessageStatus: (messageID, error) => { + setMessageStatus: (identifier, error) => { WEB_TUNNELBROKER_CHANNEL.postMessage({ type: WEB_TUNNELBROKER_MESSAGE_TYPES.MESSAGE_STATUS, - messageID, + identifier, error, }); },