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
|
// Define types for the SSE data structure
|
||||||
export interface SSEMessageBase {
|
export interface SSEMessageBase {
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -35,8 +37,10 @@ declare global {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const HEARTBEAT_TIMEOUT_MILLISECONDS = 30000;
|
||||||
export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
||||||
let connectionPromise: Promise<EventSource> | null = null;
|
let connectionPromise: Promise<EventSource> | null = null;
|
||||||
|
let heartbeatTimeout: number | null = null;
|
||||||
let isConnected: boolean = false;
|
let isConnected: boolean = false;
|
||||||
let eventSource: EventSource | null = null;
|
let eventSource: EventSource | null = null;
|
||||||
let serverUrl: string = "";
|
let serverUrl: string = "";
|
||||||
|
|
@ -55,17 +59,18 @@ export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
||||||
eventSource.onopen = function (): void {
|
eventSource.onopen = function (): void {
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
|
|
||||||
|
heartbeatTimeoutSet();
|
||||||
eventSource!.addEventListener("message", (message: MessageEvent) => {
|
eventSource!.addEventListener("message", (message: MessageEvent) => {
|
||||||
const data: SSEMessageBase = JSON.parse(message.data);
|
const data: SSEMessageBase = JSON.parse(message.data);
|
||||||
handleMessage(data);
|
handleMessage(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("SSE connected");
|
log.info("SSE connected");
|
||||||
resolve(eventSource!);
|
resolve(eventSource!);
|
||||||
};
|
};
|
||||||
|
|
||||||
eventSource.onerror = function (err: Event): void {
|
eventSource.onerror = function (err: Event): void {
|
||||||
console.error("SSE error:", err);
|
log.error("SSE error:", err);
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
|
|
||||||
// Close old connection
|
// Close old connection
|
||||||
|
|
@ -75,7 +80,7 @@ export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
||||||
|
|
||||||
// Reconnect after delay
|
// Reconnect after delay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log("SSE reconnecting");
|
log.info("SSE reconnecting");
|
||||||
connectionPromise = null;
|
connectionPromise = null;
|
||||||
connect(url);
|
connect(url);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
@ -93,14 +98,16 @@ export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
||||||
if (eventSource) {
|
if (eventSource) {
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
eventSource = null;
|
eventSource = null;
|
||||||
|
heartbeatTimeoutClear();
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
connectionPromise = null;
|
connectionPromise = null;
|
||||||
console.log("SSE disconnected");
|
log.info("SSE disconnected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMessage(msg: SSEMessageBase) {
|
function handleMessage(msg: SSEMessageBase) {
|
||||||
if (msg.type == "heartbeat") {
|
if (msg.type == "heartbeat") {
|
||||||
|
heartbeatTimeoutReset();
|
||||||
return;
|
return;
|
||||||
} else if (msg.type == "status") {
|
} else if (msg.type == "status") {
|
||||||
subscribersStatus.forEach((handler: SSEHandlerStatus, _: string) => {
|
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 {
|
function ready(callback: (eventSource: EventSource) => void): void {
|
||||||
if (connectionPromise) {
|
if (connectionPromise) {
|
||||||
|
|
@ -128,6 +153,7 @@ export const SSEManager: SSEManagerType = (function (): SSEManagerType {
|
||||||
}
|
}
|
||||||
|
|
||||||
function reconnect(delay: number) {
|
function reconnect(delay: number) {
|
||||||
|
log.info("Reconnecting SSEManager to", serverUrl);
|
||||||
disconnect();
|
disconnect();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
connect(serverUrl);
|
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