Detect stale tcp connections to relay
Enable nostr-tools' internal stale tcp connection detection
This commit is contained in:
parent
4bcbef2aae
commit
22601be42d
2 changed files with 33 additions and 3 deletions
|
|
@ -85,6 +85,23 @@ to `now` in `disconnect()`. Any incoming message with `time < lastProcessedTimes
|
|||
dropped. This prevents the relay from re-delivering messages that were already handled before a
|
||||
disconnect.
|
||||
|
||||
### Keepalive
|
||||
|
||||
`RelayClient` creates its `SimplePool` with `enablePing: true`. This enables nostr-tools'
|
||||
built-in heartbeat: every 29 seconds the pool pings each connected relay and expects a
|
||||
response within 20 seconds. In Node.js this uses native WebSocket ping/pong frames; in
|
||||
browsers (where the WebSocket API doesn't expose ping) it falls back to sending a dummy
|
||||
subscription request and waiting for EOSE.
|
||||
|
||||
If a relay fails to respond, nostr-tools closes the WebSocket, which fires the subscription
|
||||
`onclose` callback, which emits `"disconnect"` on `RelayClient`, which triggers the
|
||||
connection manager's existing reconnect loop. This detects "zombie" TCP connections where the
|
||||
socket appears open but the relay is unreachable.
|
||||
|
||||
Additionally, if a `publishMessage()` call fails (all relays reject the event),
|
||||
`RelayClient` emits `"disconnect"` alongside the thrown error. This ensures the reconnect
|
||||
loop starts immediately rather than waiting for the next ping cycle.
|
||||
|
||||
### Sequence numbers
|
||||
|
||||
`nextSequence()` starts at a random offset in the safe integer range and increments by 2. This
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ export class RelayClient extends EventEmitter {
|
|||
private messageQueue: MessageQueue;
|
||||
private readyTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
private disconnecting: boolean = false;
|
||||
|
||||
private sequence: number = Math.floor(
|
||||
Math.random() * (Number.MAX_SAFE_INTEGER - 500_000),
|
||||
);
|
||||
|
|
@ -72,7 +74,7 @@ export class RelayClient extends EventEmitter {
|
|||
logNetworkActivity: true,
|
||||
...config,
|
||||
};
|
||||
this.pool = pool ?? new SimplePool();
|
||||
this.pool = pool ?? new SimplePool({ enablePing: true });
|
||||
this.sharedPool = pool !== undefined;
|
||||
|
||||
this.messageQueue = new MessageQueue({
|
||||
|
|
@ -120,11 +122,19 @@ export class RelayClient extends EventEmitter {
|
|||
return !this.config.pairedPublicKey.every((byte) => byte === 0);
|
||||
}
|
||||
|
||||
private emitDisconnect(error: Error): void {
|
||||
if (this.disconnecting) return;
|
||||
this.disconnecting = true;
|
||||
this.emit("disconnect", error);
|
||||
}
|
||||
|
||||
async connect(): Promise<void> {
|
||||
if (this.config.logNetworkActivity) {
|
||||
debug(Scope.Relay, `Connecting to relay...`);
|
||||
}
|
||||
|
||||
this.disconnecting = false;
|
||||
|
||||
if (this.lastProcessedTimestamp === 0) {
|
||||
this.lastProcessedTimestamp = Math.floor(Date.now() / 1000) - 2;
|
||||
}
|
||||
|
|
@ -150,7 +160,7 @@ export class RelayClient extends EventEmitter {
|
|||
if (this.config.logNetworkActivity) {
|
||||
debug(Scope.Relay, `Subscription closed: ${reasons.join(", ")}`);
|
||||
}
|
||||
this.emit("disconnect", new Error("Subscription closed"));
|
||||
this.emitDisconnect(new Error("Subscription closed"));
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
@ -251,6 +261,9 @@ export class RelayClient extends EventEmitter {
|
|||
error,
|
||||
);
|
||||
}
|
||||
this.emitDisconnect(
|
||||
error instanceof Error ? error : new Error(`Publish failed: ${error}`),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
@ -330,7 +343,7 @@ export class RelayClient extends EventEmitter {
|
|||
}
|
||||
|
||||
isConnected(): boolean {
|
||||
return true;
|
||||
return this.subscription !== null;
|
||||
}
|
||||
|
||||
private netlog(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue