fix(ext): v0.5.8 false positive zombie socket disconnect bug resolve (timestamp replace setTimeout)

This commit is contained in:
Variet Worker
2026-03-24 07:00:43 +09:00
parent ecebec3906
commit f13bcc871c
12 changed files with 141 additions and 73 deletions

View File

@@ -122,8 +122,8 @@ export class WSBridgeClient {
private reconnectDelay = INITIAL_RECONNECT_DELAY;
private reconnectTimer: NodeJS.Timeout | null = null;
private heartbeatTimer: NodeJS.Timeout | null = null;
private pongTimeoutTimer: NodeJS.Timeout | null = null;
private authTimer: NodeJS.Timeout | null = null;
private lastPongTime: number = 0;
// Message queue (survives reconnection)
private messageQueue: WSMessage[] = [];
@@ -240,16 +240,9 @@ export class WSBridgeClient {
this._onDisconnect();
});
ws.on('error', (err: Error) => {
this.logFn(`[WS] Error: ${err.message}`);
});
ws.on('pong', () => {
// Server responded to our ping — connection is alive
if (this.pongTimeoutTimer) {
clearTimeout(this.pongTimeoutTimer);
this.pongTimeoutTimer = null;
}
this.lastPongTime = Date.now();
});
} else {
// ─── Browser-style WebSocket API (.onopen / .onmessage) ───
@@ -348,6 +341,7 @@ export class WSBridgeClient {
this.instanceNumber = authOk.instance_number;
this.sessionToken = authOk.session_token;
this.reconnectDelay = INITIAL_RECONNECT_DELAY;
this.lastPongTime = Date.now(); // Reset pong timer on auth
if (this.authTimer) {
clearTimeout(this.authTimer);
@@ -470,20 +464,20 @@ export class WSBridgeClient {
this._stopHeartbeat();
this.heartbeatTimer = setInterval(() => {
if (this.ws && this.connected) {
// Check for zombie connection (no pong for 60s)
if (Date.now() - this.lastPongTime > 60000) {
this.logFn('[WS] Heartbeat timeout — no pong received for 60s (zombie connection), terminating');
if (this.ws) {
try { this.ws.terminate(); } catch { try { this.ws.close(); } catch { } }
}
this._onDisconnect();
return;
}
try {
// Node.js ws has .ping(), browser WebSocket doesn't
if (typeof this.ws.ping === 'function') {
this.ws.ping();
// Set timeout waiting for pong
if (this.pongTimeoutTimer) clearTimeout(this.pongTimeoutTimer);
this.pongTimeoutTimer = setTimeout(() => {
this.logFn('[WS] Heartbeat timeout — no pong received, terminating connection');
if (this.ws) {
try { this.ws.terminate(); } catch { try { this.ws.close(); } catch { } }
}
this._onDisconnect();
}, 10000); // 10s timeout
} else {
// Fallback: send heartbeat as JSON message
this.ws.send(JSON.stringify({ type: 'heartbeat' }));
@@ -500,10 +494,6 @@ export class WSBridgeClient {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
if (this.pongTimeoutTimer) {
clearTimeout(this.pongTimeoutTimer);
this.pongTimeoutTimer = null;
}
}
// ─── Reconnection ───
@@ -559,11 +549,6 @@ export class WSBridgeClient {
this.reconnectTimer = null;
}
if (this.pongTimeoutTimer) {
clearTimeout(this.pongTimeoutTimer);
this.pongTimeoutTimer = null;
}
if (this.ws) {
try {
this.ws.close();