Reconnect SSE connection if we miss heartbeats for 30 seconds
Some checks failed
/ golint (push) Failing after 10s
Some checks failed
/ golint (push) Failing after 10s
Issue: #11
This commit is contained in:
parent
03d40774cb
commit
614f4d274e
2 changed files with 52 additions and 4 deletions
|
|
@ -1,3 +1,5 @@
|
|||
import { log } from "@/log";
|
||||
|
||||
// Define types for the SSE data structure
|
||||
export interface SSEMessageBase {
|
||||
type: string;
|
||||
|
|
@ -35,8 +37,10 @@ declare global {
|
|||
}
|
||||
*/
|
||||
|
||||
const HEARTBEAT_TIMEOUT_MILLISECONDS = 30000;
|
||||
export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
||||
let connectionPromise: Promise<EventSource> | null = null;
|
||||
let heartbeatTimeout: number | null = null;
|
||||
let isConnected: boolean = false;
|
||||
let eventSource: EventSource | null = null;
|
||||
let serverUrl: string = "";
|
||||
|
|
@ -55,17 +59,18 @@ export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
|||
eventSource.onopen = function (): void {
|
||||
isConnected = true;
|
||||
|
||||
heartbeatTimeoutSet();
|
||||
eventSource!.addEventListener("message", (message: MessageEvent) => {
|
||||
const data: SSEMessageBase = JSON.parse(message.data);
|
||||
handleMessage(data);
|
||||
});
|
||||
|
||||
console.log("SSE connected");
|
||||
log.info("SSE connected");
|
||||
resolve(eventSource!);
|
||||
};
|
||||
|
||||
eventSource.onerror = function (err: Event): void {
|
||||
console.error("SSE error:", err);
|
||||
log.error("SSE error:", err);
|
||||
isConnected = false;
|
||||
|
||||
// Close old connection
|
||||
|
|
@ -75,7 +80,7 @@ export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
|||
|
||||
// Reconnect after delay
|
||||
setTimeout(() => {
|
||||
console.log("SSE reconnecting");
|
||||
log.info("SSE reconnecting");
|
||||
connectionPromise = null;
|
||||
connect(url);
|
||||
}, 5000);
|
||||
|
|
@ -93,14 +98,16 @@ export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
|||
if (eventSource) {
|
||||
eventSource.close();
|
||||
eventSource = null;
|
||||
heartbeatTimeoutClear();
|
||||
isConnected = false;
|
||||
connectionPromise = null;
|
||||
console.log("SSE disconnected");
|
||||
log.info("SSE disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
function handleMessage(msg: SSEMessageBase) {
|
||||
if (msg.type == "heartbeat") {
|
||||
heartbeatTimeoutReset();
|
||||
return;
|
||||
} else if (msg.type == "status") {
|
||||
subscribersStatus.forEach((handler: SSEHandlerStatus, _: string) => {
|
||||
|
|
@ -112,6 +119,24 @@ export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
|||
});
|
||||
}
|
||||
}
|
||||
function heartbeatTimeoutClear() {
|
||||
if (heartbeatTimeout) {
|
||||
clearTimeout(heartbeatTimeout);
|
||||
}
|
||||
heartbeatTimeout = 0;
|
||||
}
|
||||
function heartbeatTimeoutReset() {
|
||||
heartbeatTimeoutClear();
|
||||
heartbeatTimeoutSet();
|
||||
}
|
||||
function heartbeatTimeoutSet() {
|
||||
if (heartbeatTimeout) {
|
||||
throw new Error("can't set heartbeat timeout - already set");
|
||||
}
|
||||
heartbeatTimeout = setTimeout(function () {
|
||||
reconnect(0);
|
||||
}, HEARTBEAT_TIMEOUT_MILLISECONDS);
|
||||
}
|
||||
|
||||
function ready(callback: (eventSource: EventSource) => void): void {
|
||||
if (connectionPromise) {
|
||||
|
|
@ -128,6 +153,7 @@ export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
|||
}
|
||||
|
||||
function reconnect(delay: number) {
|
||||
log.info("Reconnecting SSEManager to", serverUrl);
|
||||
disconnect();
|
||||
setTimeout(() => {
|
||||
connect(serverUrl);
|
||||
|
|
|
|||
22
ts/log.ts
Normal file
22
ts/log.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// log.ts
|
||||
const pageLoadTime = performance.now();
|
||||
|
||||
function getTimestamp(): string {
|
||||
const elapsed = performance.now() - pageLoadTime;
|
||||
|
||||
const hours = Math.floor(elapsed / 3600000);
|
||||
const minutes = Math.floor((elapsed % 3600000) / 60000);
|
||||
const seconds = Math.floor((elapsed % 60000) / 1000);
|
||||
|
||||
return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
export const log = {
|
||||
info(...args: any[]): void {
|
||||
console.log(`[${getTimestamp()}]`, ...args);
|
||||
},
|
||||
|
||||
error(...args: any[]): void {
|
||||
console.error(`[${getTimestamp()}]`, ...args);
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue