Skip to content

Commit c5f93e0

Browse files
committed
fix: add timeouts to rpcs
1 parent ff870f7 commit c5f93e0

8 files changed

Lines changed: 51 additions & 23 deletions

File tree

proto/api.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ enum ErrorCode {
5050
ERROR_BGP_ANNOUNCEMENT_OVERLAP = 8;
5151
// authentication failed
5252
ERROR_AUTHENTICATION_FAILED = 9;
53+
// timed out waiting for the server to respond
54+
ERROR_TIMEOUT = 10;
5355
}
5456

5557
enum ZKProofEngine {

src/client/utils/client-socket.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { base64 } from 'ethers/lib/utils'
2-
import { DEFAULT_METADATA } from 'src/config'
2+
import { DEFAULT_METADATA, DEFAULT_RPC_TIMEOUT_MS } from 'src/config'
33
import { InitResponse, RPCMessages } from 'src/proto/api'
44
import { IAttestorClient, IAttestorClientCreateOpts, RPCEvent, RPCRequestData, RPCResponseData, RPCType } from 'src/types'
5-
import { AttestorError, getRpcRequestType, logger as LOGGER, packRpcMessages } from 'src/utils'
5+
import { AttestorError, generateRpcMessageId, getRpcRequestType, logger as LOGGER, packRpcMessages } from 'src/utils'
66
import { AttestorSocket } from 'src/utils/socket-base'
77
import { makeWebSocket as defaultMakeWebSocket } from 'src/utils/ws'
88

@@ -40,7 +40,7 @@ export class AttestorClient extends AttestorSocket implements IAttestorClient {
4040

4141
const initReqId = msg.messages[0].id
4242
this.waitForInitPromise = this
43-
.waitForResponse<'init'>(initReqId)
43+
.waitForResponse<'init'>(initReqId, DEFAULT_RPC_TIMEOUT_MS)
4444
.then(res => {
4545
logger.info('client initialised')
4646
this.isInitialised = true
@@ -58,15 +58,15 @@ export class AttestorClient extends AttestorSocket implements IAttestorClient {
5858

5959
async rpc<T extends RPCType>(
6060
type: T,
61-
request: Partial<RPCRequestData<T>>
61+
request: Partial<RPCRequestData<T>>,
62+
timeoutMs: number = DEFAULT_RPC_TIMEOUT_MS
6263
) {
6364
this.logger.debug({ type }, 'sending rpc request')
6465
const now = Date.now()
6566
try {
66-
const {
67-
messages: [{ id }]
68-
} = await this.sendMessage({ [getRpcRequestType(type)]: request })
69-
const rslt = await this.waitForResponse<T>(id)
67+
const msgId = generateRpcMessageId()
68+
const rslt = await this.waitForResponse<T>(msgId, timeoutMs)
69+
await this.sendMessage({ id: msgId, [getRpcRequestType(type)]: request })
7070

7171
return rslt
7272
} finally {
@@ -75,7 +75,10 @@ export class AttestorClient extends AttestorSocket implements IAttestorClient {
7575
}
7676
}
7777

78-
waitForResponse<T extends RPCType>(id: number) {
78+
waitForResponse<T extends RPCType>(
79+
id: number,
80+
timeoutMs: number = DEFAULT_RPC_TIMEOUT_MS
81+
) {
7982
if(this.isClosed) {
8083
throw new AttestorError(
8184
'ERROR_NETWORK_ERROR',
@@ -118,7 +121,19 @@ export class AttestorClient extends AttestorSocket implements IAttestorClient {
118121
reject(event.data)
119122
}
120123

124+
const timeout = setTimeout(() => {
125+
removeHandlers()
126+
reject(
127+
new AttestorError(
128+
'ERROR_TIMEOUT',
129+
`RPC request timed out after ${timeoutMs}ms`,
130+
{ id }
131+
)
132+
)
133+
}, timeoutMs)
134+
121135
const removeHandlers = () => {
136+
clearTimeout(timeout)
122137
this.removeEventListener('rpc-response', handler)
123138
this.removeEventListener('connection-terminated', terminateHandler)
124139
}

src/client/utils/message-handler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export async function wsMessageHandler(this: IAttestorSocket, data: unknown) {
1212
}
1313

1414
export function handleMessage(this: IAttestorSocket, msg: RPCMessage) {
15+
this.logger?.trace({ msg }, 'received message')
1516
// handle connection termination alert
1617
if(msg.connectionTerminationAlert) {
1718
const err = AttestorError.fromProto(

src/config/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export const MAX_PAYLOAD_SIZE = 512 * 1024 * 1024 // 512MB
4747

4848
export const DEFAULT_AUTH_EXPIRY_S = 15 * 60 // 15m
4949

50+
export const DEFAULT_RPC_TIMEOUT_MS = 90_000
51+
5052
export const TOPRF_DOMAIN_SEPARATOR = 'reclaim-toprf'
5153

5254
export const BGP_WS_URL = 'wss://ris-live.ripe.net/v1/ws/?client=reclaim-hijack-detector'

src/proto/api.ts

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/server/socket.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { handleMessage } from 'src/client/utils/message-handler'
2+
import { DEFAULT_RPC_TIMEOUT_MS } from 'src/config'
23
import { TunnelMessage } from 'src/proto/api'
34
import { HANDLERS } from 'src/server/handlers'
45
import { getApm } from 'src/server/utils/apm'
@@ -102,24 +103,15 @@ async function handleTunnelMessage(
102103
const tunnel = this.getTunnel(tunnelId)
103104
await tunnel.write(message)
104105
} catch(err) {
105-
this.logger?.error(
106-
{
107-
err,
108-
tunnelId,
109-
},
110-
'error writing to tunnel'
111-
)
106+
this.logger?.error({ err, tunnelId }, 'error writing to tunnel')
112107
}
113108
}
114109

115110
async function handleRpcRequest(
116111
this: IAttestorServerSocket,
117112
{ data: { data, requestId, respond, type } }: RPCEvent<'rpc-request'>
118113
) {
119-
const logger = this.logger.child({
120-
rpc: type,
121-
requestId
122-
})
114+
const logger = this.logger.child({ rpc: type, requestId })
123115

124116
const apm = getApm()
125117
const tx = apm?.startTransaction(type)
@@ -131,6 +123,10 @@ async function handleRpcRequest(
131123
tx?.setLabel('authUserId', userId)
132124
}
133125

126+
const timeout = setTimeout(() => {
127+
logger.warn({ type, requestId }, 'RPC took too long to respond')
128+
}, DEFAULT_RPC_TIMEOUT_MS)
129+
134130
try {
135131
logger.debug({ data }, 'handling RPC request')
136132

@@ -147,6 +143,7 @@ async function handleRpcRequest(
147143

148144
apm?.captureError(err, { parent: tx })
149145
} finally {
146+
clearTimeout(timeout)
150147
tx?.end()
151148
}
152149
}

src/types/client.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,16 @@ export declare class IAttestorClient extends IAttestorSocket {
167167
* the promise will reject.
168168
*/
169169
waitForResponse<T extends RPCType>(
170-
id: number
170+
id: number,
171+
timeoutMs?: number
171172
): Promise<RPCResponseData<T>>
172173
/**
173174
* Make an RPC request to the other end of the WebSocket.
174175
*/
175176
rpc<T extends RPCType>(
176177
type: T,
177-
request: Partial<RPCRequestData<T>>
178+
request: Partial<RPCRequestData<T>>,
179+
timeoutMs?: number
178180
): Promise<RPCResponseData<T>>
179181
/**
180182
* Waits for the "init" request to be responded to

src/utils/socket-base.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ export class AttestorSocket implements IAttestorSocket {
7575
const msg = packRpcMessages(...msgs)
7676
const bytes = RPCMessages.encode(msg).finish()
7777

78+
this.logger.trace({ msg }, 'sending messages')
79+
7880
if('sendPromise' in this.socket && this.socket.sendPromise) {
7981
await this.socket.sendPromise(bytes)
8082
} else {

0 commit comments

Comments
 (0)