forked from Kieran/snort
tweak ephemeral connection timeout logic
This commit is contained in:
parent
c02cd9c300
commit
119d1c526e
@ -1,6 +1,6 @@
|
|||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
import { unwrap, ExternalStore } from "@snort/shared";
|
import { unwrap, ExternalStore, unixNowMs } from "@snort/shared";
|
||||||
|
|
||||||
import { DefaultConnectTimeout } from "./Const";
|
import { DefaultConnectTimeout } from "./Const";
|
||||||
import { ConnectionStats } from "./ConnectionStats";
|
import { ConnectionStats } from "./ConnectionStats";
|
||||||
@ -39,6 +39,9 @@ export interface ConnectionStateSnapshot {
|
|||||||
|
|
||||||
export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
||||||
#log = debug("Connection");
|
#log = debug("Connection");
|
||||||
|
#ephemeralCheck?: ReturnType<typeof setInterval>;
|
||||||
|
#activity: number = unixNowMs();
|
||||||
|
|
||||||
Id: string;
|
Id: string;
|
||||||
Address: string;
|
Address: string;
|
||||||
Socket: WebSocket | null = null;
|
Socket: WebSocket | null = null;
|
||||||
@ -66,7 +69,6 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
AwaitingAuth: Map<string, boolean>;
|
AwaitingAuth: Map<string, boolean>;
|
||||||
Authed = false;
|
Authed = false;
|
||||||
Ephemeral: boolean;
|
Ephemeral: boolean;
|
||||||
EphemeralTimeout?: ReturnType<typeof setTimeout>;
|
|
||||||
Down = true;
|
Down = true;
|
||||||
|
|
||||||
constructor(addr: string, options: RelaySettings, auth?: AuthHandler, ephemeral: boolean = false) {
|
constructor(addr: string, options: RelaySettings, auth?: AuthHandler, ephemeral: boolean = false) {
|
||||||
@ -81,17 +83,6 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
this.Ephemeral = ephemeral;
|
this.Ephemeral = ephemeral;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetEphemeralTimeout() {
|
|
||||||
if (this.EphemeralTimeout) {
|
|
||||||
clearTimeout(this.EphemeralTimeout);
|
|
||||||
}
|
|
||||||
if (this.Ephemeral) {
|
|
||||||
this.EphemeralTimeout = setTimeout(() => {
|
|
||||||
this.Close();
|
|
||||||
}, 30_000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async Connect() {
|
async Connect() {
|
||||||
try {
|
try {
|
||||||
if (this.Info === undefined) {
|
if (this.Info === undefined) {
|
||||||
@ -111,8 +102,8 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
this.Info = data;
|
this.Info = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch {
|
||||||
this.#log("Could not load relay information %O", e);
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.Socket) {
|
if (this.Socket) {
|
||||||
@ -132,10 +123,6 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
|
|
||||||
Close() {
|
Close() {
|
||||||
this.IsClosed = true;
|
this.IsClosed = true;
|
||||||
if (this.ReconnectTimer !== null) {
|
|
||||||
clearTimeout(this.ReconnectTimer);
|
|
||||||
this.ReconnectTimer = undefined;
|
|
||||||
}
|
|
||||||
this.Socket?.close();
|
this.Socket?.close();
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
}
|
}
|
||||||
@ -144,18 +131,12 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
this.ConnectTimeout = DefaultConnectTimeout;
|
this.ConnectTimeout = DefaultConnectTimeout;
|
||||||
this.#log(`[${this.Address}] Open!`);
|
this.#log(`[${this.Address}] Open!`);
|
||||||
this.Down = false;
|
this.Down = false;
|
||||||
if (this.Ephemeral) {
|
this.#setupEphemeral();
|
||||||
this.ResetEphemeralTimeout();
|
|
||||||
}
|
|
||||||
this.OnConnected?.();
|
this.OnConnected?.();
|
||||||
this.#sendPendingRaw();
|
this.#sendPendingRaw();
|
||||||
}
|
}
|
||||||
|
|
||||||
OnClose(e: CloseEvent) {
|
OnClose(e: CloseEvent) {
|
||||||
if (this.EphemeralTimeout) {
|
|
||||||
clearTimeout(this.EphemeralTimeout);
|
|
||||||
this.EphemeralTimeout = undefined;
|
|
||||||
}
|
|
||||||
if (this.ReconnectTimer) {
|
if (this.ReconnectTimer) {
|
||||||
clearTimeout(this.ReconnectTimer);
|
clearTimeout(this.ReconnectTimer);
|
||||||
this.ReconnectTimer = undefined;
|
this.ReconnectTimer = undefined;
|
||||||
@ -168,7 +149,7 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
} else if (!this.IsClosed) {
|
} else if (!this.IsClosed) {
|
||||||
this.ConnectTimeout = this.ConnectTimeout * 2;
|
this.ConnectTimeout = this.ConnectTimeout * 2;
|
||||||
this.#log(
|
this.#log(
|
||||||
`[${this.Address}] Closed (${e.reason}), trying again in ${(this.ConnectTimeout / 1000)
|
`[${this.Address}] Closed (code=${e.code}), trying again in ${(this.ConnectTimeout / 1000)
|
||||||
.toFixed(0)
|
.toFixed(0)
|
||||||
.toLocaleString()} sec`
|
.toLocaleString()} sec`
|
||||||
);
|
);
|
||||||
@ -182,19 +163,20 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.OnDisconnect?.(this.Id);
|
this.OnDisconnect?.(this.Id);
|
||||||
this.#ResetQueues();
|
this.#resetQueues();
|
||||||
// reset connection Id on disconnect, for query-tracking
|
// reset connection Id on disconnect, for query-tracking
|
||||||
this.Id = uuid();
|
this.Id = uuid();
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
OnMessage(e: MessageEvent) {
|
OnMessage(e: MessageEvent) {
|
||||||
|
this.#activity = unixNowMs();
|
||||||
if (e.data.length > 0) {
|
if (e.data.length > 0) {
|
||||||
const msg = JSON.parse(e.data);
|
const msg = JSON.parse(e.data);
|
||||||
const tag = msg[0];
|
const tag = msg[0];
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case "AUTH": {
|
case "AUTH": {
|
||||||
this._OnAuthAsync(msg[1])
|
this.#onAuthAsync(msg[1])
|
||||||
.then(() => this.#sendPendingRaw())
|
.then(() => this.#sendPendingRaw())
|
||||||
.catch(this.#log);
|
.catch(this.#log);
|
||||||
this.Stats.EventsReceived++;
|
this.Stats.EventsReceived++;
|
||||||
@ -250,7 +232,7 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const req = ["EVENT", e];
|
const req = ["EVENT", e];
|
||||||
this.#SendJson(req);
|
this.#sendJson(req);
|
||||||
this.Stats.EventsSent++;
|
this.Stats.EventsSent++;
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
}
|
}
|
||||||
@ -273,7 +255,7 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const req = ["EVENT", e];
|
const req = ["EVENT", e];
|
||||||
this.#SendJson(req);
|
this.#sendJson(req);
|
||||||
this.Stats.EventsSent++;
|
this.Stats.EventsSent++;
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
});
|
});
|
||||||
@ -299,16 +281,15 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
this.#log("Queuing: %s %O", this.Address, cmd);
|
this.#log("Queuing: %s %O", this.Address, cmd);
|
||||||
} else {
|
} else {
|
||||||
this.ActiveRequests.add(cmd[1]);
|
this.ActiveRequests.add(cmd[1]);
|
||||||
this.#SendJson(cmd);
|
this.#sendJson(cmd);
|
||||||
cbSent();
|
cbSent();
|
||||||
}
|
}
|
||||||
this.ResetEphemeralTimeout();
|
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseReq(id: string) {
|
CloseReq(id: string) {
|
||||||
if (this.ActiveRequests.delete(id)) {
|
if (this.ActiveRequests.delete(id)) {
|
||||||
this.#SendJson(["CLOSE", id]);
|
this.#sendJson(["CLOSE", id]);
|
||||||
this.OnEose?.(id);
|
this.OnEose?.(id);
|
||||||
this.#SendQueuedRequests();
|
this.#SendQueuedRequests();
|
||||||
}
|
}
|
||||||
@ -343,7 +324,7 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
const p = this.PendingRequests.shift();
|
const p = this.PendingRequests.shift();
|
||||||
if (p) {
|
if (p) {
|
||||||
this.ActiveRequests.add(p.cmd[1]);
|
this.ActiveRequests.add(p.cmd[1]);
|
||||||
this.#SendJson(p.cmd);
|
this.#sendJson(p.cmd);
|
||||||
p.cb();
|
p.cb();
|
||||||
this.#log("Sent pending REQ %s %O", this.Address, p.cmd);
|
this.#log("Sent pending REQ %s %O", this.Address, p.cmd);
|
||||||
}
|
}
|
||||||
@ -351,14 +332,14 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ResetQueues() {
|
#resetQueues() {
|
||||||
this.ActiveRequests.clear();
|
this.ActiveRequests.clear();
|
||||||
this.PendingRequests = [];
|
this.PendingRequests = [];
|
||||||
this.PendingRaw = [];
|
this.PendingRaw = [];
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
#SendJson(obj: object) {
|
#sendJson(obj: object) {
|
||||||
const authPending = !this.Authed && (this.AwaitingAuth.size > 0 || this.Info?.limitation?.auth_required === true);
|
const authPending = !this.Authed && (this.AwaitingAuth.size > 0 || this.Info?.limitation?.auth_required === true);
|
||||||
if (this.Socket?.readyState !== WebSocket.OPEN || authPending) {
|
if (this.Socket?.readyState !== WebSocket.OPEN || authPending) {
|
||||||
this.PendingRaw.push(obj);
|
this.PendingRaw.push(obj);
|
||||||
@ -386,11 +367,12 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
throw new Error(`Socket is not open, state is ${this.Socket?.readyState}`);
|
throw new Error(`Socket is not open, state is ${this.Socket?.readyState}`);
|
||||||
}
|
}
|
||||||
const json = JSON.stringify(obj);
|
const json = JSON.stringify(obj);
|
||||||
|
this.#activity = unixNowMs();
|
||||||
this.Socket.send(json);
|
this.Socket.send(json);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _OnAuthAsync(challenge: string): Promise<void> {
|
async #onAuthAsync(challenge: string): Promise<void> {
|
||||||
const authCleanup = () => {
|
const authCleanup = () => {
|
||||||
this.AwaitingAuth.delete(challenge);
|
this.AwaitingAuth.delete(challenge);
|
||||||
};
|
};
|
||||||
@ -426,4 +408,23 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
get #maxSubscriptions() {
|
get #maxSubscriptions() {
|
||||||
return this.Info?.limitation?.max_subscriptions ?? 25;
|
return this.Info?.limitation?.max_subscriptions ?? 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#setupEphemeral() {
|
||||||
|
if (this.Ephemeral) {
|
||||||
|
if (this.#ephemeralCheck) {
|
||||||
|
clearInterval(this.#ephemeralCheck);
|
||||||
|
this.#ephemeralCheck = undefined;
|
||||||
|
}
|
||||||
|
this.#ephemeralCheck = setInterval(() => {
|
||||||
|
const lastActivity = unixNowMs() - this.#activity;
|
||||||
|
if (lastActivity > 30_000 && !this.IsClosed) {
|
||||||
|
if (this.ActiveRequests.size > 0) {
|
||||||
|
this.#log("%s Inactive connection has %d active requests! %O", this.Address, this.ActiveRequests.size, this.ActiveRequests);
|
||||||
|
} else {
|
||||||
|
this.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 5_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user