src/services/checkBrokenConnections/index.ts
import type { IConfig } from "../../config/index.ts";
import type { IClient } from "../../models/client.ts";
import type { IRealm } from "../../models/realm.ts";
const DEFAULT_CHECK_INTERVAL = 300;
type CustomConfig = Pick<IConfig, "alive_timeout">;
export class CheckBrokenConnections {
public readonly checkInterval: number;
private timeoutId: NodeJS.Timeout | null = null;
private readonly realm: IRealm;
private readonly config: CustomConfig;
private readonly onClose?: (client: IClient) => void;
constructor({
realm,
config,
checkInterval = DEFAULT_CHECK_INTERVAL,
onClose,
}: {
realm: IRealm;
config: CustomConfig;
checkInterval?: number;
onClose?: (client: IClient) => void;
}) {
this.realm = realm;
this.config = config;
this.onClose = onClose;
this.checkInterval = checkInterval;
}
public start(): void {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
}
this.timeoutId = setTimeout(() => {
this.checkConnections();
this.timeoutId = null;
this.start();
}, this.checkInterval);
}
public stop(): void {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
}
private checkConnections(): void {
const clientsIds = this.realm.getClientsIds();
const now = new Date().getTime();
const { alive_timeout: aliveTimeout } = this.config;
for (const clientId of clientsIds) {
const client = this.realm.getClientById(clientId);
if (!client) continue;
const timeSinceLastPing = now - client.getLastPing();
if (timeSinceLastPing < aliveTimeout) continue;
try {
client.getSocket()?.close();
} finally {
this.realm.clearMessageQueue(clientId);
this.realm.removeClientById(clientId);
client.setSocket(null);
this.onClose?.(client);
}
}
}
}