feat(push): smart push queueing with page-visibility fast path and app-ping/pong fallback
This commit is contained in:
@@ -12,6 +12,8 @@ export abstract class ClientConnection {
|
||||
readonly transportName: string;
|
||||
sessionId: string | null = null;
|
||||
onDisconnect: ((conn: ClientConnection) => void) | null = null;
|
||||
/** True while the client tab/app is in the foreground. Starts true (assume visible until told otherwise). */
|
||||
pageVisible: boolean = true;
|
||||
|
||||
constructor(transportName: string) {
|
||||
this.transportName = transportName;
|
||||
|
||||
@@ -18,6 +18,9 @@ import type { ClientMessage } from '../types/messages.js';
|
||||
export class WebSocketTransport extends EventEmitter {
|
||||
private wss: WebSocketServer | null = null;
|
||||
private pingInterval: ReturnType<typeof setInterval> | null = null;
|
||||
// Tracks whether each WS responded to the last ping. Initialized true so a
|
||||
// connection is never terminated before it has a chance to respond.
|
||||
private alive = new WeakMap<WebSocket, boolean>();
|
||||
|
||||
/** Create WebSocketServer on /ws path with JWT verification and ping/pong keepalive. */
|
||||
setup(server: HttpServer | HttpsServer): void {
|
||||
@@ -36,6 +39,9 @@ export class WebSocketTransport extends EventEmitter {
|
||||
});
|
||||
|
||||
this.wss.on('connection', (ws: WebSocket) => {
|
||||
this.alive.set(ws, true);
|
||||
ws.on('pong', () => this.alive.set(ws, true));
|
||||
|
||||
const conn = new WebSocketConnection(ws);
|
||||
|
||||
this.emit('connection', conn);
|
||||
@@ -52,13 +58,16 @@ export class WebSocketTransport extends EventEmitter {
|
||||
});
|
||||
});
|
||||
|
||||
// Ping/pong keepalive every 30s
|
||||
// Ping/pong keepalive every 30s — terminate connections that miss a pong.
|
||||
this.pingInterval = setInterval(() => {
|
||||
if (!this.wss) return;
|
||||
for (const ws of this.wss.clients) {
|
||||
if (ws.readyState === 1) {
|
||||
ws.ping();
|
||||
if (!this.alive.get(ws)) {
|
||||
ws.terminate(); // no pong since last ping — dead connection
|
||||
continue;
|
||||
}
|
||||
this.alive.set(ws, false);
|
||||
if (ws.readyState === 1) ws.ping();
|
||||
}
|
||||
}, 30_000);
|
||||
this.pingInterval.unref();
|
||||
|
||||
Reference in New Issue
Block a user