RocketChat/Rocket.Chat

View on GitHub
packages/ddp-client/src/TimeoutControl.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { Emitter } from '@rocket.chat/emitter';

import type { Connection } from './Connection';
import type { DDPClient } from './types/DDPClient';

export interface TimeoutControlEvents
    extends Emitter<{
        timeout: void;
        heartbeat: void;
    }> {
    reset(): void;
    stop(): void;
    readonly timeout: number;
    readonly heartbeat: number;
}

export class TimeoutControl
    extends Emitter<{
        timeout: void;
        heartbeat: void;
    }>
    implements TimeoutControlEvents
{
    private timeoutId: ReturnType<typeof setTimeout> | undefined;

    private heartbeatId: ReturnType<typeof setTimeout> | undefined;

    constructor(readonly timeout: number = 60_000, readonly heartbeat: number = timeout / 2) {
        super();
        /* istanbul ignore next */
        if (this.heartbeat >= this.timeout) {
            throw new Error('Heartbeat must be less than timeout');
        }
    }

    reset() {
        if (this.timeoutId) {
            clearTimeout(this.timeoutId);
        }
        if (this.heartbeatId) {
            clearTimeout(this.heartbeatId);
        }
        this.timeoutId = setTimeout(() => this.emit('timeout'), this.timeout);
        this.heartbeatId = setTimeout(() => this.emit('heartbeat'), this.heartbeat);
    }

    stop() {
        if (this.timeoutId) {
            clearTimeout(this.timeoutId);
        }
        if (this.heartbeatId) {
            clearTimeout(this.heartbeatId);
        }
    }

    static create(ddp: DDPClient, connection: Connection, timeout?: number, heartbeat?: number): TimeoutControl {
        const timeoutControl = new TimeoutControl(timeout, heartbeat);

        timeoutControl.on('heartbeat', () => {
            ddp.ping();
        });

        timeoutControl.on('timeout', () => {
            connection.close();
        });

        ddp.onMessage(() => timeoutControl.reset());

        connection.on('close', () => {
            timeoutControl.stop();
        });

        connection.on('disconnected', () => {
            timeoutControl.stop();
        });

        connection.on('connected', () => {
            timeoutControl.reset();
        });

        return timeoutControl;
    }
}