Page MenuHomePhorge

D15188.1768452837.diff
No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None

D15188.1768452837.diff

diff --git a/lib/shared/timeouts.js b/lib/shared/timeouts.js
--- a/lib/shared/timeouts.js
+++ b/lib/shared/timeouts.js
@@ -44,6 +44,9 @@
// Client-side timeout duration for certain Tunnelbroker WebSocket requests.
export const tunnelbrokerRequestTimeout = 10000; // in milliseconds
+// Client-side timeout duration for certain Farcaster requests.
+export const farcasterRequestTimeout = 5000; // in milliseconds
+
// This controls how long the client waits before trying to reconnect a
// disconnected Tunnelbroker socket.
export const clientTunnelbrokerSocketReconnectDelay = 3000;
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
@@ -15,11 +15,13 @@
import { DMOpsQueueHandler } from '../shared/dm-ops/dm-ops-queue-handler.react.js';
import { IdentityClientContext } from '../shared/identity-client-context.js';
import {
+ farcasterRequestTimeout,
tunnelbrokerHeartbeatTimeout,
tunnelbrokerRequestTimeout,
} from '../shared/timeouts.js';
import { isWebPlatform } from '../types/device-types.js';
import type { MessageSentStatus } from '../types/tunnelbroker/device-to-tunnelbroker-request-status-types.js';
+import type { FarcasterAPIRequest } from '../types/tunnelbroker/farcaster-messages-types.js';
import type { MessageReceiveConfirmation } from '../types/tunnelbroker/message-receive-confirmation-types.js';
import type { MessageToDeviceRequest } from '../types/tunnelbroker/message-to-device-request-types.js';
import type { MessageToTunnelbrokerRequest } from '../types/tunnelbroker/message-to-tunnelbroker-request-types.js';
@@ -40,6 +42,11 @@
import type { Heartbeat } from '../types/websocket/heartbeat-types.js';
import { getConfig } from '../utils/config.js';
import { getContentSigningKey } from '../utils/crypto-utils.js';
+import {
+ FarcasterAPIError,
+ FarcasterMissingTokenError,
+} from '../utils/errors.js';
+import { promiseWithTimeout } from '../utils/promises.js';
import { useSelector } from '../utils/redux-utils.js';
import sleep from '../utils/sleep.js';
@@ -57,6 +64,13 @@
+reject: (error: string) => void,
};
type Promises = { [clientMessageID: string]: PromiseCallbacks };
+type PromiseRequestCallbacks = {
+ +resolve: (res: string) => void,
+ +reject: (
+ error: string | FarcasterAPIError | FarcasterMissingTokenError | Error,
+ ) => void,
+};
+type FarcasterRequests = { [requestID: string]: PromiseRequestCallbacks };
export type TunnelbrokerSocketState =
| {
@@ -73,6 +87,15 @@
retryCount: 0,
};
+type SendFarcasterRequestPayload = $Diff<
+ FarcasterAPIRequest,
+ {
+ +type: 'FarcasterAPIRequest',
+ +requestID: string,
+ +userID: string,
+ },
+>;
+
type TunnelbrokerContextType = {
+sendMessageToDevice: (
message: TunnelbrokerClientMessageToDevice,
@@ -85,6 +108,9 @@
+socketState: TunnelbrokerSocketState,
+setUnauthorizedDeviceID: (unauthorizedDeviceID: ?string) => void,
+confirmMessageToTunnelbroker: (messageID: string) => void,
+ +sendFarcasterRequest: (
+ request: SendFarcasterRequestPayload,
+ ) => Promise<string>,
};
const TunnelbrokerContext: React.Context<?TunnelbrokerContextType> =
@@ -172,6 +198,7 @@
const socket = React.useRef<?WebSocket>(null);
const socketSessionCounter = React.useRef(0);
const promises = React.useRef<Promises>({});
+ const requests = React.useRef<FarcasterRequests>({});
const heartbeatTimeoutID = React.useRef<?TimeoutID>();
const identityContext = React.useContext(IdentityClientContext);
@@ -356,6 +383,37 @@
type: deviceToTunnelbrokerMessageTypes.HEARTBEAT,
};
socket.current?.send(JSON.stringify(heartbeat));
+ } else if (
+ message.type ===
+ tunnelbrokerToDeviceMessageTypes.FARCASTER_API_RESPONSE
+ ) {
+ const { requestID, response } = message;
+ if (response.type === 'Success') {
+ requests.current[requestID]?.resolve(response.data);
+ } else if (response.type === 'ErrorResponse') {
+ requests.current[requestID].reject(
+ new FarcasterAPIError(
+ response.data.status,
+ response.data.message,
+ ),
+ );
+ } else if (response.type === 'Error') {
+ requests.current[requestID].reject(new Error(response.data));
+ } else if (response.type === 'SerializationError') {
+ console.log(
+ 'SerializationError for Farcaster request: ',
+ response.data,
+ );
+ requests.current[requestID].reject(new Error(response.type));
+ } else if (response.type === 'InvalidRequest') {
+ console.log('Tunnelbroker recorded InvalidRequest');
+ requests.current[requestID].reject(new Error(response.type));
+ } else if (response.type === 'MissingFarcasterDCsToken') {
+ requests.current[requestID].reject(
+ new FarcasterMissingTokenError(response.type),
+ );
+ }
+ delete requests.current[requestID];
}
};
@@ -456,6 +514,44 @@
[sendMessage],
);
+ const sendFarcasterRequest: (
+ request: SendFarcasterRequestPayload,
+ ) => Promise<string> = React.useCallback(
+ request => {
+ const resultPromise: Promise<string> = new Promise((resolve, reject) => {
+ const socketActive = socketStateRef.current.connected && socket.current;
+ if (!shouldBeClosed && !socketActive) {
+ throw new Error('Tunnelbroker not connected');
+ }
+ invariant(userID, 'userID should be defined');
+
+ const requestID = uuid.v4();
+ requests.current[requestID] = {
+ resolve,
+ reject,
+ };
+ const fullRequest: FarcasterAPIRequest = {
+ ...request,
+ type: deviceToTunnelbrokerMessageTypes.FARCASTER_API_REQUEST,
+ requestID,
+ userID,
+ };
+ if (socketActive) {
+ socket.current?.send(JSON.stringify(fullRequest));
+ } else {
+ secondaryTunnelbrokerConnection?.sendMessage(requestID, fullRequest);
+ }
+ });
+
+ return promiseWithTimeout(
+ resultPromise,
+ farcasterRequestTimeout,
+ 'Farcaster request',
+ );
+ },
+ [secondaryTunnelbrokerConnection, shouldBeClosed, userID],
+ );
+
React.useEffect(
() =>
secondaryTunnelbrokerConnection?.onSendMessage(
@@ -551,15 +647,17 @@
removeListener,
setUnauthorizedDeviceID,
confirmMessageToTunnelbroker,
+ sendFarcasterRequest,
}),
[
+ addListener,
+ confirmMessageToTunnelbroker,
+ removeListener,
+ sendFarcasterRequest,
sendMessageToDevice,
- sendNotif,
sendMessageToTunnelbroker,
+ sendNotif,
socketState,
- addListener,
- removeListener,
- confirmMessageToTunnelbroker,
],
);
diff --git a/lib/utils/errors.js b/lib/utils/errors.js
--- a/lib/utils/errors.js
+++ b/lib/utils/errors.js
@@ -77,6 +77,21 @@
}
}
+class FarcasterAPIError extends ExtendableError {
+ status: number;
+
+ constructor(status: number, message: string) {
+ super(message);
+ this.status = status;
+ }
+}
+
+class FarcasterMissingTokenError extends ExtendableError {
+ constructor(message: string) {
+ super(message);
+ }
+}
+
function getMessageForException(e: mixed): ?string {
if (typeof e === 'string') {
return e;
@@ -100,4 +115,6 @@
SocketTimeout,
SendMessageError,
BackupIsNewerError,
+ FarcasterAPIError,
+ FarcasterMissingTokenError,
};

File Metadata

Mime Type
text/plain
Expires
Thu, Jan 15, 4:53 AM (1 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5935990
Default Alt Text
D15188.1768452837.diff (7 KB)

Event Timeline