hedgedoc/commons/src/connection-keep-alive-handler.ts

78 lines
2.3 KiB
TypeScript
Raw Normal View History

/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { MessageType } from './messages/message-type.enum.js'
import type { YDocMessageTransporter } from './y-doc-message-transporter.js'
import { createEncoder, toUint8Array, writeVarUint } from 'lib0/encoding'
/**
* Provides a keep alive ping for a given {@link WebSocket websocket} connection by sending a periodic message.
*/
export class ConnectionKeepAliveHandler {
private pongReceived = false
private static readonly pingTimeout = 30 * 1000
private intervalId: NodeJS.Timer | undefined
/**
* Constructs the instance and starts the interval.
*
* @param messageTransporter The websocket to keep alive
*/
constructor(private messageTransporter: YDocMessageTransporter) {
this.messageTransporter.on('disconnected', () => this.stopTimer())
this.messageTransporter.on('ready', () => this.startTimer())
this.messageTransporter.on(String(MessageType.PING), () => {
this.sendPongMessage()
})
this.messageTransporter.on(
String(MessageType.PONG),
() => (this.pongReceived = true)
)
}
/**
* Starts the ping timer.
*/
public startTimer(): void {
this.pongReceived = false
this.intervalId = setInterval(
() => this.check(),
ConnectionKeepAliveHandler.pingTimeout
)
this.sendPingMessage()
}
public stopTimer(): void {
clearInterval(this.intervalId)
}
/**
* Checks if a pong has been received since the last run. If not, the connection is probably dead and will be terminated.
*/
private check(): void {
if (this.pongReceived) {
this.pongReceived = false
this.sendPingMessage()
} else {
this.messageTransporter.disconnect()
console.error(
`No pong received in the last ${ConnectionKeepAliveHandler.pingTimeout} seconds. Connection seems to be dead.`
)
}
}
private sendPingMessage(): void {
const encoder = createEncoder()
writeVarUint(encoder, MessageType.PING)
this.messageTransporter.send(toUint8Array(encoder))
}
private sendPongMessage(): void {
const encoder = createEncoder()
writeVarUint(encoder, MessageType.PONG)
this.messageTransporter.send(toUint8Array(encoder))
}
}