mirror of
https://github.com/nbd-wtf/nostr-tools.git
synced 2026-07-22 07:48:27 +00:00
automatically closed unused relay connections after a while.
This commit is contained in:
@@ -49,6 +49,7 @@ export class AbstractSimplePool {
|
||||
public verifyEvent: Nostr['verifyEvent']
|
||||
public enablePing: boolean | undefined
|
||||
public enableReconnect: boolean
|
||||
public idleTimeout: number = 20000
|
||||
public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)
|
||||
public trustedRelayURLs: Set<string> = new Set()
|
||||
public onRelayConnectionFailure?: (url: string) => void
|
||||
@@ -63,6 +64,7 @@ export class AbstractSimplePool {
|
||||
this._WebSocket = opts.websocketImplementation
|
||||
this.enablePing = opts.enablePing
|
||||
this.enableReconnect = opts.enableReconnect || false
|
||||
if (opts.idleTimeout) this.idleTimeout = opts.idleTimeout
|
||||
this.automaticallyAuth = opts.automaticallyAuth
|
||||
this.onRelayConnectionFailure = opts.onRelayConnectionFailure
|
||||
this.onRelayConnectionSuccess = opts.onRelayConnectionSuccess
|
||||
@@ -86,6 +88,7 @@ export class AbstractSimplePool {
|
||||
websocketImplementation: this._WebSocket,
|
||||
enablePing: this.enablePing,
|
||||
enableReconnect: this.enableReconnect,
|
||||
idleTimeout: this.idleTimeout,
|
||||
})
|
||||
relay.onclose = () => {
|
||||
this.relays.delete(url)
|
||||
|
||||
@@ -16,6 +16,7 @@ export type AbstractRelayConstructorOptions = {
|
||||
websocketImplementation?: typeof WebSocket
|
||||
enablePing?: boolean
|
||||
enableReconnect?: boolean
|
||||
idleTimeout?: number
|
||||
}
|
||||
|
||||
export class SendingOnClosedConnection extends Error {
|
||||
@@ -41,12 +42,14 @@ export class AbstractRelay {
|
||||
public openSubs: Map<string, Subscription> = new Map()
|
||||
public enablePing: boolean | undefined
|
||||
public enableReconnect: boolean
|
||||
public idleTimeout: number = 0 // auto-close after ms of inactivity, 0 = disabled
|
||||
public idleSince: number | undefined = Date.now() // when undefined that means it isn't idle
|
||||
public ongoingOperations: number = 0 // used to compute idleness
|
||||
private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined
|
||||
private pingIntervalHandle: ReturnType<typeof setInterval> | undefined
|
||||
private reconnectAttempts: number = 0
|
||||
private skipReconnection: boolean = false
|
||||
private idleTimeoutHandle: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
private connectionPromise: Promise<void> | undefined
|
||||
private openCountRequests = new Map<string, CountResolver>()
|
||||
@@ -65,6 +68,7 @@ export class AbstractRelay {
|
||||
this._WebSocket = opts.websocketImplementation || WebSocket
|
||||
this.enablePing = opts.enablePing
|
||||
this.enableReconnect = opts.enableReconnect || false
|
||||
if (opts.idleTimeout) this.idleTimeout = opts.idleTimeout
|
||||
}
|
||||
|
||||
static async connect(
|
||||
@@ -97,6 +101,24 @@ export class AbstractRelay {
|
||||
return this._connected
|
||||
}
|
||||
|
||||
private clearIdleTimeout() {
|
||||
if (this.idleTimeoutHandle) {
|
||||
clearTimeout(this.idleTimeoutHandle)
|
||||
this.idleTimeoutHandle = undefined
|
||||
}
|
||||
}
|
||||
|
||||
public scheduleIdleClose() {
|
||||
this.clearIdleTimeout()
|
||||
if (this.idleTimeout > 0) {
|
||||
this.idleTimeoutHandle = setTimeout(() => {
|
||||
if (this.ongoingOperations === 0 && this.idleSince) {
|
||||
this.close()
|
||||
}
|
||||
}, this.idleTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
private async reconnect(): Promise<void> {
|
||||
const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]
|
||||
this.reconnectAttempts++
|
||||
@@ -119,6 +141,7 @@ export class AbstractRelay {
|
||||
this._connected = false
|
||||
this.connectionPromise = undefined
|
||||
this.idleSince = undefined
|
||||
this.clearIdleTimeout()
|
||||
|
||||
if (this.enableReconnect && !this.skipReconnection) {
|
||||
this.reconnect()
|
||||
@@ -311,6 +334,7 @@ export class AbstractRelay {
|
||||
|
||||
public async publish(event: Event): Promise<string> {
|
||||
this.idleSince = undefined
|
||||
this.clearIdleTimeout()
|
||||
this.ongoingOperations++
|
||||
|
||||
const ret = new Promise<string>((resolve, reject) => {
|
||||
@@ -335,7 +359,10 @@ export class AbstractRelay {
|
||||
|
||||
// compute idleness state
|
||||
this.ongoingOperations--
|
||||
if (this.ongoingOperations === 0) this.idleSince = Date.now()
|
||||
if (this.ongoingOperations === 0) {
|
||||
this.idleSince = Date.now()
|
||||
this.scheduleIdleClose()
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
@@ -371,6 +398,7 @@ export class AbstractRelay {
|
||||
): Subscription {
|
||||
if (params.label !== '<forced-ping>') {
|
||||
this.idleSince = undefined
|
||||
this.clearIdleTimeout()
|
||||
this.ongoingOperations++
|
||||
}
|
||||
|
||||
@@ -408,6 +436,7 @@ export class AbstractRelay {
|
||||
this.closeAllSubscriptions('relay connection closed by us')
|
||||
this._connected = false
|
||||
this.idleSince = undefined
|
||||
this.clearIdleTimeout()
|
||||
this.onclose?.()
|
||||
if (this.ws?.readyState === this._WebSocket.OPEN) {
|
||||
this.ws?.close()
|
||||
@@ -622,7 +651,10 @@ export class Subscription {
|
||||
|
||||
// compute idleness state
|
||||
this.relay.ongoingOperations--
|
||||
if (this.relay.ongoingOperations === 0) this.relay.idleSince = Date.now()
|
||||
if (this.relay.ongoingOperations === 0) {
|
||||
this.relay.idleSince = Date.now()
|
||||
this.relay.scheduleIdleClose()
|
||||
}
|
||||
|
||||
this.onclose?.(reason)
|
||||
}
|
||||
|
||||
2
jsr.json
2
jsr.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nostr/tools",
|
||||
"version": "2.23.10",
|
||||
"version": "2.23.11",
|
||||
"exports": {
|
||||
".": "./index.ts",
|
||||
"./core": "./core.ts",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "nostr-tools",
|
||||
"version": "2.23.9",
|
||||
"version": "2.23.11",
|
||||
"description": "Tools for making a Nostr client.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
7
relay.ts
7
relay.ts
@@ -14,13 +14,16 @@ export function useWebSocketImplementation(websocketImplementation: any) {
|
||||
}
|
||||
|
||||
export class Relay extends AbstractRelay {
|
||||
constructor(url: string, options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>) {
|
||||
constructor(
|
||||
url: string,
|
||||
options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect' | 'idleTimeout'>,
|
||||
) {
|
||||
super(url, { verifyEvent, websocketImplementation: _WebSocket, ...options })
|
||||
}
|
||||
|
||||
static async connect(
|
||||
url: string,
|
||||
options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>,
|
||||
options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect' | 'idleTimeout'>,
|
||||
): Promise<Relay> {
|
||||
const relay = new Relay(url, options)
|
||||
await relay.connect()
|
||||
|
||||
Reference in New Issue
Block a user