Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3384181
D11372.id38277.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
D11372.id38277.diff
View Options
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
@@ -1,6 +1,7 @@
// @flow
import invariant from 'invariant';
+import _isEqual from 'lodash/fp/isEqual.js';
import * as React from 'react';
import uuid from 'uuid';
@@ -9,6 +10,7 @@
import { peerToPeerMessageHandler } from '../handlers/peer-to-peer-message-handler.js';
import { IdentityClientContext } from '../shared/identity-client-context.js';
import { tunnelbrokerHeartbeatTimeout } from '../shared/timeouts.js';
+import { isWebPlatform } from '../types/device-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';
@@ -23,10 +25,14 @@
} from '../types/tunnelbroker/peer-to-peer-message-types.js';
import type {
AnonymousInitializationMessage,
- TunnelbrokerInitializationMessage,
ConnectionInitializationMessage,
+ TunnelbrokerInitializationMessage,
+ TunnelbrokerDeviceTypes,
} from '../types/tunnelbroker/session-types.js';
import type { Heartbeat } from '../types/websocket/heartbeat-types.js';
+import { getConfig } from '../utils/config.js';
+import { getContentSigningKey } from '../utils/crypto-utils.js';
+import { useSelector } from '../utils/redux-utils.js';
export type ClientMessageToDevice = {
+deviceID: string,
@@ -59,55 +65,68 @@
+children: React.Node,
+shouldBeClosed?: boolean,
+onClose?: () => mixed,
- +initMessage: ?ConnectionInitializationMessage,
+secondaryTunnelbrokerConnection?: SecondaryTunnelbrokerConnection,
};
+function getTunnelbrokerDeviceType(): TunnelbrokerDeviceTypes {
+ return isWebPlatform(getConfig().platformDetails.platform) ? 'web' : 'mobile';
+}
+
function createAnonymousInitMessage(
deviceID: string,
): AnonymousInitializationMessage {
return ({
type: 'AnonymousInitializationMessage',
deviceID,
- deviceType: 'mobile',
+ deviceType: getTunnelbrokerDeviceType(),
}: AnonymousInitializationMessage);
}
function TunnelbrokerProvider(props: Props): React.Node {
- const {
- children,
- shouldBeClosed,
- onClose,
- initMessage: initMessageProp,
- secondaryTunnelbrokerConnection,
- } = props;
- const [connected, setConnected] = React.useState(false);
- const listeners = React.useRef<Set<TunnelbrokerSocketListener>>(new Set());
- const socket = React.useRef<?WebSocket>(null);
- const promises = React.useRef<Promises>({});
- const heartbeatTimeoutID = React.useRef<?TimeoutID>();
+ const { children, shouldBeClosed, onClose, secondaryTunnelbrokerConnection } =
+ props;
+
+ const accessToken = useSelector(state => state.commServicesAccessToken);
+ const userID = useSelector(state => state.currentUserInfo?.id);
+
const [unauthorizedDeviceID, setUnauthorizedDeviceID] =
React.useState<?string>(null);
const isAuthorized = !unauthorizedDeviceID;
- const identityContext = React.useContext(IdentityClientContext);
- invariant(identityContext, 'Identity context should be set');
- const { identityClient } = identityContext;
-
- const initMessage = React.useMemo(() => {
+ const createInitMessage = React.useCallback(async () => {
if (shouldBeClosed) {
return null;
}
- if (!unauthorizedDeviceID) {
- return initMessageProp;
+
+ if (unauthorizedDeviceID) {
+ return createAnonymousInitMessage(unauthorizedDeviceID);
+ }
+
+ const deviceID = await getContentSigningKey();
+ if (!deviceID || !accessToken || !userID) {
+ return null;
}
- return createAnonymousInitMessage(unauthorizedDeviceID);
- }, [shouldBeClosed, unauthorizedDeviceID, initMessageProp]);
+ return ({
+ type: 'ConnectionInitializationMessage',
+ deviceID,
+ accessToken,
+ userID,
+ deviceType: getTunnelbrokerDeviceType(),
+ }: ConnectionInitializationMessage);
+ }, [accessToken, shouldBeClosed, unauthorizedDeviceID, userID]);
const previousInitMessage =
- React.useRef<?TunnelbrokerInitializationMessage>(initMessage);
- const initMessageChanged = initMessage !== previousInitMessage.current;
- previousInitMessage.current = initMessage;
+ React.useRef<?TunnelbrokerInitializationMessage>(null);
+
+ const [connected, setConnected] = React.useState(false);
+ const listeners = React.useRef<Set<TunnelbrokerSocketListener>>(new Set());
+ const socket = React.useRef<?WebSocket>(null);
+ const promises = React.useRef<Promises>({});
+ const heartbeatTimeoutID = React.useRef<?TimeoutID>();
+
+ const identityContext = React.useContext(IdentityClientContext);
+ invariant(identityContext, 'Identity context should be set');
+ const { identityClient } = identityContext;
const stopHeartbeatTimeout = React.useCallback(() => {
if (heartbeatTimeoutID.current) {
@@ -138,144 +157,155 @@
// - DISCONNECTING: isSocketActive = false, connected = true
// This lasts between socket.close() and socket.onclose()
React.useEffect(() => {
- // when initMessage changes, we need to close the socket and open a new one
- if (
- (!initMessage || initMessageChanged) &&
- isSocketActive &&
- socket.current
- ) {
- socket.current?.close();
- return;
- }
-
- // when we're already connected (or pending disconnection),
- // or there's no init message to start with, we don't need to do anything
- if (connected || !initMessage || socket.current) {
- return;
- }
-
- const tunnelbrokerSocket = new WebSocket(tunnnelbrokerURL);
-
- tunnelbrokerSocket.onopen = () => {
- tunnelbrokerSocket.send(JSON.stringify(initMessage));
- };
-
- tunnelbrokerSocket.onclose = () => {
- // this triggers the effect hook again and reconnect
- setConnected(false);
- onClose?.();
- socket.current = null;
- console.log('Connection to Tunnelbroker closed');
- };
- tunnelbrokerSocket.onerror = e => {
- console.log('Tunnelbroker socket error:', e.message);
- };
- tunnelbrokerSocket.onmessage = (event: MessageEvent) => {
- if (typeof event.data !== 'string') {
- console.log('socket received a non-string message');
- return;
- }
- let rawMessage;
- try {
- rawMessage = JSON.parse(event.data);
- } catch (e) {
- console.log('error while parsing Tunnelbroker message:', e.message);
+ void (async () => {
+ const initMessage = await createInitMessage();
+ const initMessageChanged = !_isEqual(
+ previousInitMessage.current,
+ initMessage,
+ );
+ previousInitMessage.current = initMessage;
+
+ // when initMessage changes, we need to close the socket
+ // and open a new one
+ if (
+ (!initMessage || initMessageChanged) &&
+ isSocketActive &&
+ socket.current
+ ) {
+ socket.current?.close();
return;
}
- if (!tunnelbrokerMessageValidator.is(rawMessage)) {
- console.log('invalid TunnelbrokerMessage');
+ // when we're already connected (or pending disconnection),
+ // or there's no init message to start with, we don't need to do anything
+ if (connected || !initMessage || socket.current) {
return;
}
- const message: TunnelbrokerMessage = rawMessage;
-
- resetHeartbeatTimeout();
- for (const listener of listeners.current) {
- listener(message);
- }
-
- if (
- message.type ===
- tunnelbrokerMessageTypes.CONNECTION_INITIALIZATION_RESPONSE
- ) {
- if (message.status.type === 'Success' && !connected) {
- setConnected(true);
- console.log(
- 'session with Tunnelbroker created. isAuthorized:',
- isAuthorized,
- );
- } else if (message.status.type === 'Success' && connected) {
- console.log(
- 'received ConnectionInitializationResponse with status: Success for already connected socket',
- );
- } else {
- setConnected(false);
- console.log(
- 'creating session with Tunnelbroker error:',
- message.status.data,
- );
+ const tunnelbrokerSocket = new WebSocket(tunnnelbrokerURL);
+
+ tunnelbrokerSocket.onopen = () => {
+ tunnelbrokerSocket.send(JSON.stringify(initMessage));
+ };
+
+ tunnelbrokerSocket.onclose = () => {
+ // this triggers the effect hook again and reconnect
+ setConnected(false);
+ onClose?.();
+ socket.current = null;
+ console.log('Connection to Tunnelbroker closed');
+ };
+ tunnelbrokerSocket.onerror = e => {
+ console.log('Tunnelbroker socket error:', e.message);
+ };
+ tunnelbrokerSocket.onmessage = (event: MessageEvent) => {
+ if (typeof event.data !== 'string') {
+ console.log('socket received a non-string message');
+ return;
}
- } else if (message.type === tunnelbrokerMessageTypes.MESSAGE_TO_DEVICE) {
- const confirmation: MessageReceiveConfirmation = {
- type: tunnelbrokerMessageTypes.MESSAGE_RECEIVE_CONFIRMATION,
- messageIDs: [message.messageID],
- };
- socket.current?.send(JSON.stringify(confirmation));
-
- let rawPeerToPeerMessage;
+ let rawMessage;
try {
- rawPeerToPeerMessage = JSON.parse(message.payload);
+ rawMessage = JSON.parse(event.data);
} catch (e) {
- console.log(
- 'error while parsing Tunnelbroker peer-to-peer message:',
- e.message,
- );
+ console.log('error while parsing Tunnelbroker message:', e.message);
return;
}
- if (!peerToPeerMessageValidator.is(rawPeerToPeerMessage)) {
- console.log('invalid Tunnelbroker PeerToPeerMessage');
+ if (!tunnelbrokerMessageValidator.is(rawMessage)) {
+ console.log('invalid TunnelbrokerMessage');
return;
}
- const peerToPeerMessage: PeerToPeerMessage = rawPeerToPeerMessage;
- void peerToPeerMessageHandler(peerToPeerMessage, identityClient);
- } else if (
- message.type ===
- tunnelbrokerMessageTypes.MESSAGE_TO_DEVICE_REQUEST_STATUS
- ) {
- for (const status: MessageSentStatus of message.clientMessageIDs) {
- if (status.type === 'Success') {
- promises.current[status.data]?.resolve();
- delete promises.current[status.data];
- } else if (status.type === 'Error') {
- promises.current[status.data.id]?.reject(status.data.error);
- delete promises.current[status.data.id];
- } else if (status.type === 'SerializationError') {
- console.log('SerializationError for message: ', status.data);
- } else if (status.type === 'InvalidRequest') {
- console.log('Tunnelbroker recorded InvalidRequest');
+ const message: TunnelbrokerMessage = rawMessage;
+
+ resetHeartbeatTimeout();
+
+ for (const listener of listeners.current) {
+ listener(message);
+ }
+
+ if (
+ message.type ===
+ tunnelbrokerMessageTypes.CONNECTION_INITIALIZATION_RESPONSE
+ ) {
+ if (message.status.type === 'Success' && !connected) {
+ setConnected(true);
+ console.log(
+ 'session with Tunnelbroker created. isAuthorized:',
+ isAuthorized,
+ );
+ } else if (message.status.type === 'Success' && connected) {
+ console.log(
+ 'received ConnectionInitializationResponse with status: Success for already connected socket',
+ );
+ } else {
+ setConnected(false);
+ console.log(
+ 'creating session with Tunnelbroker error:',
+ message.status.data,
+ );
}
+ } else if (
+ message.type === tunnelbrokerMessageTypes.MESSAGE_TO_DEVICE
+ ) {
+ const confirmation: MessageReceiveConfirmation = {
+ type: tunnelbrokerMessageTypes.MESSAGE_RECEIVE_CONFIRMATION,
+ messageIDs: [message.messageID],
+ };
+ socket.current?.send(JSON.stringify(confirmation));
+
+ let rawPeerToPeerMessage;
+ try {
+ rawPeerToPeerMessage = JSON.parse(message.payload);
+ } catch (e) {
+ console.log(
+ 'error while parsing Tunnelbroker peer-to-peer message:',
+ e.message,
+ );
+ return;
+ }
+
+ if (!peerToPeerMessageValidator.is(rawPeerToPeerMessage)) {
+ console.log('invalid Tunnelbroker PeerToPeerMessage');
+ return;
+ }
+ const peerToPeerMessage: PeerToPeerMessage = rawPeerToPeerMessage;
+ void peerToPeerMessageHandler(peerToPeerMessage, identityClient);
+ } else if (
+ message.type ===
+ tunnelbrokerMessageTypes.MESSAGE_TO_DEVICE_REQUEST_STATUS
+ ) {
+ for (const status: MessageSentStatus of message.clientMessageIDs) {
+ if (status.type === 'Success') {
+ promises.current[status.data]?.resolve();
+ delete promises.current[status.data];
+ } else if (status.type === 'Error') {
+ promises.current[status.data.id]?.reject(status.data.error);
+ delete promises.current[status.data.id];
+ } else if (status.type === 'SerializationError') {
+ console.log('SerializationError for message: ', status.data);
+ } else if (status.type === 'InvalidRequest') {
+ console.log('Tunnelbroker recorded InvalidRequest');
+ }
+ }
+ } else if (message.type === tunnelbrokerMessageTypes.HEARTBEAT) {
+ const heartbeat: Heartbeat = {
+ type: tunnelbrokerMessageTypes.HEARTBEAT,
+ };
+ socket.current?.send(JSON.stringify(heartbeat));
}
- } else if (message.type === tunnelbrokerMessageTypes.HEARTBEAT) {
- const heartbeat: Heartbeat = {
- type: tunnelbrokerMessageTypes.HEARTBEAT,
- };
- socket.current?.send(JSON.stringify(heartbeat));
- }
- };
+ };
- socket.current = tunnelbrokerSocket;
+ socket.current = tunnelbrokerSocket;
+ })();
}, [
connected,
- initMessage,
- initMessageChanged,
isSocketActive,
isAuthorized,
resetHeartbeatTimeout,
stopHeartbeatTimeout,
identityClient,
onClose,
+ createInitMessage,
]);
const sendMessageToDeviceRequest: (
diff --git a/native/root.react.js b/native/root.react.js
--- a/native/root.react.js
+++ b/native/root.react.js
@@ -80,7 +80,6 @@
import { DarkTheme, LightTheme } from './themes/navigation.js';
import ThemeHandler from './themes/theme-handler.react.js';
import { provider } from './utils/ethers-utils.js';
-import { useTunnelbrokerInitMessage } from './utils/tunnelbroker-utils.js';
// Add custom items to expo-dev-menu
import './dev-menu.js';
@@ -262,8 +261,6 @@
return undefined;
})();
- const tunnelbrokerInitMessage = useTunnelbrokerInitMessage();
-
const gated: React.Node = (
<>
<LifecycleHandler />
@@ -303,7 +300,7 @@
<GestureHandlerRootView style={styles.app}>
<StaffContextProvider>
<IdentityServiceContextProvider>
- <TunnelbrokerProvider initMessage={tunnelbrokerInitMessage}>
+ <TunnelbrokerProvider>
<IdentitySearchProvider>
<FeatureFlagsProvider>
<NavContext.Provider value={navContext}>
diff --git a/native/utils/tunnelbroker-utils.js b/native/utils/tunnelbroker-utils.js
deleted file mode 100644
--- a/native/utils/tunnelbroker-utils.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// @flow
-
-import * as React from 'react';
-
-import type { ConnectionInitializationMessage } from 'lib/types/tunnelbroker/session-types.js';
-
-import { commCoreModule } from '../native-modules.js';
-import { useSelector } from '../redux/redux-utils.js';
-
-function useTunnelbrokerInitMessage(): ?ConnectionInitializationMessage {
- const [deviceID, setDeviceID] = React.useState<?string>();
- const [userID, setUserID] = React.useState<?string>();
- const accessToken = useSelector(state => state.commServicesAccessToken);
-
- React.useEffect(() => {
- void (async () => {
- const { userID: identityUserID, deviceID: contentSigningKey } =
- await commCoreModule.getCommServicesAuthMetadata();
- setDeviceID(contentSigningKey);
- setUserID(identityUserID);
- })();
- }, [accessToken]);
-
- return React.useMemo(() => {
- if (!deviceID || !accessToken || !userID) {
- return null;
- }
- return ({
- type: 'ConnectionInitializationMessage',
- deviceID,
- accessToken,
- userID,
- deviceType: 'mobile',
- }: ConnectionInitializationMessage);
- }, [accessToken, deviceID, userID]);
-}
-
-export { useTunnelbrokerInitMessage };
diff --git a/web/app.react.js b/web/app.react.js
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -71,7 +71,6 @@
import VisibilityHandler from './redux/visibility-handler.react.js';
import history from './router-history.js';
import { MessageSearchStateProvider } from './search/message-search-state-provider.react.js';
-import { createTunnelbrokerInitMessage } from './selectors/tunnelbroker-selectors.js';
import AccountSettings from './settings/account-settings.react.js';
import DangerZone from './settings/danger-zone.react.js';
import KeyserverSelectionList from './settings/keyserver-selection-list.react.js';
@@ -520,8 +519,6 @@
[modalContext.modals],
);
- const tunnelbrokerInitMessage = useSelector(createTunnelbrokerInitMessage);
-
const { lockStatus, releaseLockOrAbortRequest } = useWebLock(
TUNNELBROKER_LOCK_NAME,
);
@@ -532,7 +529,6 @@
return (
<AppThemeWrapper>
<TunnelbrokerProvider
- initMessage={tunnelbrokerInitMessage}
shouldBeClosed={lockStatus !== 'acquired'}
onClose={releaseLockOrAbortRequest}
secondaryTunnelbrokerConnection={secondaryTunnelbrokerConnection}
diff --git a/web/selectors/tunnelbroker-selectors.js b/web/selectors/tunnelbroker-selectors.js
deleted file mode 100644
--- a/web/selectors/tunnelbroker-selectors.js
+++ /dev/null
@@ -1,30 +0,0 @@
-// @flow
-
-import { createSelector } from 'reselect';
-
-import type { ConnectionInitializationMessage } from 'lib/types/tunnelbroker/session-types.js';
-
-import type { AppState } from '../redux/redux-setup.js';
-
-export const createTunnelbrokerInitMessage: AppState => ?ConnectionInitializationMessage =
- createSelector(
- (state: AppState) => state.cryptoStore?.primaryIdentityKeys?.ed25519,
- (state: AppState) => state.commServicesAccessToken,
- (state: AppState) => state.currentUserInfo?.id,
- (
- deviceID: ?string,
- accessToken: ?string,
- userID: ?string,
- ): ?ConnectionInitializationMessage => {
- if (!deviceID || !accessToken || !userID) {
- return null;
- }
- return ({
- type: 'ConnectionInitializationMessage',
- deviceID,
- accessToken,
- userID,
- deviceType: 'web',
- }: ConnectionInitializationMessage);
- },
- );
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 29, 7:50 PM (19 h, 6 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2597958
Default Alt Text
D11372.id38277.diff (19 KB)
Attached To
Mode
D11372: [web/native] Create tunnelbroker init message on connection
Attached
Detach File
Event Timeline
Log In to Comment