Back off from a link that dies as soon as it comes up
A bind that returns is not proof the link works: an unreadable stream is only found afterwards, so resetting the delay there handed a link that died on arrival a fresh minDelay every cycle — one connect and bind per second forever, which is how an account gets blocked for bind flooding. The loop resets only once a link has outlasted maxDelay. Also covers the unreadable stream that has no loop to retry it.
This commit is contained in:
+12
-1
@@ -7,19 +7,23 @@ export type ReconnectLoopOptions = {
|
||||
log: SmppLog;
|
||||
maxDelay: number;
|
||||
minDelay: number;
|
||||
now?: (() => number) | undefined;
|
||||
/** Brings the owner back up on a freshly opened socket. An err means try again. */
|
||||
onConnected: (sock: Socket) => Promise<VoidResult>;
|
||||
};
|
||||
|
||||
/** Reopens a dropped connection, backing off between attempts until it is told to stop. */
|
||||
export class ReconnectLoop {
|
||||
private readonly now: () => number;
|
||||
private readonly options: ReconnectLoopOptions;
|
||||
private attempting = false;
|
||||
private delay: number;
|
||||
private halted = false;
|
||||
private timer: NodeJS.Timeout | undefined;
|
||||
private upAt: number | undefined;
|
||||
|
||||
constructor(options: ReconnectLoopOptions) {
|
||||
this.now = options.now ?? Date.now;
|
||||
this.options = options;
|
||||
this.delay = options.minDelay;
|
||||
}
|
||||
@@ -32,6 +36,13 @@ export class ReconnectLoop {
|
||||
schedule(): void {
|
||||
if (this.timer || this.attempting || this.isStopped()) return;
|
||||
|
||||
// Coming up is not proof: a stream we cannot read is only found once the link is bound.
|
||||
if (this.upAt !== undefined && this.now() - this.upAt >= this.options.maxDelay) {
|
||||
this.delay = this.options.minDelay;
|
||||
}
|
||||
|
||||
this.upAt = undefined;
|
||||
|
||||
const delay = this.delay;
|
||||
|
||||
this.options.log.info('reconnect - retrying after a drop', { delay });
|
||||
@@ -98,7 +109,7 @@ export class ReconnectLoop {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.delay = this.options.minDelay;
|
||||
this.upAt = this.now();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user