





























@@ -83,6 +83,12 @@ type FingerprintCheckingClientOptions = Omit<ClientOptions, "checkServerIdentity
8383checkServerIdentity?: (servername: string, cert: CertMeta) => Error | undefined;
8484};
858586+export type GatewayReconnectPausedInfo = {
87+code: number;
88+reason: string;
89+detailCode: string | null;
90+};
91+8692export class GatewayClientRequestError extends Error {
8793readonly gatewayCode: string;
8894readonly details?: unknown;
@@ -130,6 +136,7 @@ export type GatewayClientOptions = {
130136onEvent?: (evt: EventFrame) => void;
131137onHelloOk?: (hello: HelloOk) => void;
132138onConnectError?: (err: Error) => void;
139+onReconnectPaused?: (info: GatewayReconnectPausedInfo) => void;
133140onClose?: (code: number, reason: string) => void;
134141onGap?: (info: { expected: number; received: number }) => void;
135142};
@@ -190,6 +197,7 @@ export class GatewayClient {
190197private connectNonce: string | null = null;
191198private connectSent = false;
192199private connectTimer: NodeJS.Timeout | null = null;
200+private reconnectTimer: NodeJS.Timeout | null = null;
193201private pendingDeviceTokenRetry = false;
194202private deviceTokenRetryBudgetUsed = false;
195203private pendingConnectErrorDetailCode: string | null = null;
@@ -219,6 +227,7 @@ export class GatewayClient {
219227if (this.closed) {
220228return;
221229}
230+this.clearReconnectTimer();
222231this.clearConnectChallengeTimeout();
223232this.connectNonce = null;
224233this.connectSent = false;
@@ -332,6 +341,11 @@ export class GatewayClient {
332341}
333342this.flushPendingErrors(new Error(`gateway closed (${code}): ${reasonText}`));
334343if (this.shouldPauseReconnectAfterAuthFailure(connectErrorDetailCode)) {
344+this.opts.onReconnectPaused?.({
345+ code,
346+reason: reasonText,
347+detailCode: connectErrorDetailCode,
348+});
335349this.opts.onClose?.(code, reasonText);
336350return;
337351}
@@ -384,6 +398,7 @@ export class GatewayClient {
384398this.pendingDeviceTokenRetry = false;
385399this.deviceTokenRetryBudgetUsed = false;
386400this.pendingConnectErrorDetailCode = null;
401+this.clearReconnectTimer();
387402if (this.tickTimer) {
388403clearInterval(this.tickTimer);
389404this.tickTimer = null;
@@ -817,6 +832,13 @@ export class GatewayClient {
817832}
818833}
819834835+private clearReconnectTimer() {
836+if (this.reconnectTimer) {
837+clearTimeout(this.reconnectTimer);
838+this.reconnectTimer = null;
839+}
840+}
841+820842private armConnectChallengeTimeout() {
821843const connectChallengeTimeoutMs = resolveGatewayClientConnectChallengeTimeoutMs(this.opts);
822844const armedAt = Date.now();
@@ -843,9 +865,13 @@ export class GatewayClient {
843865clearInterval(this.tickTimer);
844866this.tickTimer = null;
845867}
868+this.clearReconnectTimer();
846869const delay = this.backoffMs;
847870this.backoffMs = Math.min(this.backoffMs * 2, 30_000);
848-setTimeout(() => this.start(), delay).unref();
871+this.reconnectTimer = setTimeout(() => {
872+this.reconnectTimer = null;
873+this.start();
874+}, delay);
849875}
850876851877private flushPendingErrors(err: Error) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。