Skip to content

Commit 1341ace

Browse files
damenchoclaude
andcommitted
fix: Add WebSocket heartbeat and auto-reconnect to WebhookProxy
Prevents idle connection drops by sending a ping every 30s, and automatically reconnects after 3s on unexpected disconnection. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f9ccfe2 commit 1341ace

1 file changed

Lines changed: 27 additions & 2 deletions

File tree

src/WebhookProxy.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ class WebhookProxy {
2424
private logs: WebhookLogEntry[] = [];
2525
private isConnecting: boolean = false;
2626
private isConnected: boolean = false;
27+
private heartbeatInterval: ReturnType<typeof setInterval> | undefined;
28+
private reconnectTimeout: ReturnType<typeof setTimeout> | undefined;
29+
private intentionalDisconnect: boolean = false;
30+
private static readonly HEARTBEAT_INTERVAL_MS = 30000;
31+
private static readonly RECONNECT_DELAY_MS = 3000;
2732

2833
constructor(url: string, secret: string, conferenceName: string, tenant: string) {
2934
this.url = url;
@@ -53,7 +58,8 @@ class WebhookProxy {
5358
console.log(`⚠️ WebhookProxy for ${this.conferenceName} already connecting/connected`);
5459
return;
5560
}
56-
61+
62+
this.intentionalDisconnect = false;
5763
this.isConnecting = true;
5864

5965
// Connect directly to remote proxy server with secret parameter
@@ -74,7 +80,14 @@ class WebhookProxy {
7480
this.logInfo('connected', { type: 'direct_proxy' });
7581
this.isConnecting = false;
7682
this.isConnected = true;
77-
83+
84+
// Start heartbeat to keep connection alive
85+
this.heartbeatInterval = setInterval(() => {
86+
if (this.ws?.readyState === WebSocket.OPEN) {
87+
this.ws.send(JSON.stringify({ type: 'ping' }));
88+
}
89+
}, WebhookProxy.HEARTBEAT_INTERVAL_MS);
90+
7891
// Subscribe to room events after connection is established
7992
this.subscribeToRoom();
8093
};
@@ -84,6 +97,13 @@ class WebhookProxy {
8497
this.logInfo('websocket_closed', { reason: 'connection_closed' });
8598
this.isConnecting = false;
8699
this.isConnected = false;
100+
clearInterval(this.heartbeatInterval);
101+
this.heartbeatInterval = undefined;
102+
103+
if (!this.intentionalDisconnect) {
104+
this.logInfo('reconnecting', { delay: WebhookProxy.RECONNECT_DELAY_MS });
105+
this.reconnectTimeout = setTimeout(() => this.connect(), WebhookProxy.RECONNECT_DELAY_MS);
106+
}
87107
};
88108

89109
this.ws.onmessage = async (event) => {
@@ -201,6 +221,11 @@ class WebhookProxy {
201221
}
202222

203223
disconnect() {
224+
this.intentionalDisconnect = true;
225+
clearInterval(this.heartbeatInterval);
226+
this.heartbeatInterval = undefined;
227+
clearTimeout(this.reconnectTimeout);
228+
this.reconnectTimeout = undefined;
204229
if (this.ws) {
205230
this.ws.close();
206231
console.log('WebhookProxy disconnected');

0 commit comments

Comments
 (0)