UNPKG

@cks-systems/manifest-sdk

Version:
96 lines (95 loc) 3.3 kB
import WebSocket from 'ws'; /** * Manages WebSocket server with client heartbeat functionality */ export class WebSocketManager { wss; clientHeartbeats = new Map(); heartbeatInterval; constructor(port, heartbeatInterval = 30000) { this.heartbeatInterval = heartbeatInterval; this.wss = new WebSocket.Server({ port }); this.wss.on('connection', (ws) => { console.log('New client connected'); // Start heartbeat for this client this.startClientHeartbeat(ws); ws.on('message', (message) => { console.log(`Received message: ${message}`); }); ws.on('pong', () => { // Client is still alive, reset the heartbeat timer this.resetClientHeartbeat(ws); }); ws.on('close', () => { console.log('Client disconnected'); this.stopClientHeartbeat(ws); }); ws.on('error', (error) => { console.error('WebSocket error:', error); this.stopClientHeartbeat(ws); }); }); } /** * Broadcast a message to all connected clients */ broadcast(message) { this.wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(message); } }); } /** * Close the WebSocket server and clean up */ close() { // Clean up all heartbeats before closing this.clientHeartbeats.forEach((timeout, ws) => { clearTimeout(timeout); ws.close(); }); this.clientHeartbeats.clear(); this.wss.close(); } startClientHeartbeat(ws) { const timeout = setTimeout(() => { console.log('Client heartbeat timeout, closing connection'); ws.terminate(); this.clientHeartbeats.delete(ws); }, this.heartbeatInterval * 2); // Wait for 2x heartbeat interval before considering dead this.clientHeartbeats.set(ws, timeout); // Send initial ping if (ws.readyState === WebSocket.OPEN) { ws.ping(); } } resetClientHeartbeat(ws) { // Clear existing timeout const existingTimeout = this.clientHeartbeats.get(ws); if (existingTimeout) { clearTimeout(existingTimeout); } // Set new timeout and send next ping const timeout = setTimeout(() => { if (ws.readyState === WebSocket.OPEN) { ws.ping(); // Set another timeout for the pong response const pongTimeout = setTimeout(() => { console.log('Client pong timeout, closing connection'); ws.terminate(); this.clientHeartbeats.delete(ws); }, this.heartbeatInterval); this.clientHeartbeats.set(ws, pongTimeout); } }, this.heartbeatInterval); this.clientHeartbeats.set(ws, timeout); } stopClientHeartbeat(ws) { const timeout = this.clientHeartbeats.get(ws); if (timeout) { clearTimeout(timeout); this.clientHeartbeats.delete(ws); } } }