From e2db39fe6988d82131a02a763908eab0f69c0b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Wed, 9 Mar 2022 00:05:50 +0100 Subject: [PATCH] add comments to reconnector. --- src/component/internal/connection.ts | 12 +-- src/component/internal/reconnector/index.ts | 88 +++++++++++++++------ src/component/types/state.ts | 4 +- 3 files changed, 71 insertions(+), 33 deletions(-) diff --git a/src/component/internal/connection.ts b/src/component/internal/connection.ts index 97861b26..475221cc 100644 --- a/src/component/internal/connection.ts +++ b/src/component/internal/connection.ts @@ -74,7 +74,7 @@ export class NekoConnection extends EventEmitter { this._onCloseHandle = this.close.bind(this) - // bind events to all reconnecters + // bind events to all reconnectors Object.values(this._reconnector).forEach((r) => { r.on('connect', this._onConnectHandle) r.on('disconnect', this._onDisconnectHandle) @@ -179,7 +179,7 @@ export class NekoConnection extends EventEmitter { Vue.set(this._state, 'status', 'connecting') - // open all reconnecters + // open all reconnectors with deferred connection Object.values(this._reconnector).forEach((r) => r.open(true)) this._reconnector.websocket.connect() @@ -189,6 +189,7 @@ export class NekoConnection extends EventEmitter { if (this._open) { this._open = false + // set state to disconnected Vue.set(this._state.websocket, 'connected', false) Vue.set(this._state.webrtc, 'connected', false) Vue.set(this._state, 'status', 'disconnected') @@ -196,7 +197,7 @@ export class NekoConnection extends EventEmitter { this.emit('close', error) } - // close all reconnecters + // close all reconnectors Object.values(this._reconnector).forEach((r) => r.close()) } @@ -206,16 +207,17 @@ export class NekoConnection extends EventEmitter { // TODO: Use server side congestion control. this.webrtc.off('stats', this._webrtcCongestionControlHandle) - // unbind events from all reconnecters + // unbind events from all reconnectors Object.values(this._reconnector).forEach((r) => { r.off('connect', this._onConnectHandle) r.off('disconnect', this._onDisconnectHandle) r.off('close', this._onCloseHandle) }) - // destroy all reconnecters + // destroy all reconnectors Object.values(this._reconnector).forEach((r) => r.destroy()) + // set state to disconnected Vue.set(this._state.websocket, 'connected', false) Vue.set(this._state.webrtc, 'connected', false) Vue.set(this._state, 'status', 'disconnected') diff --git a/src/component/internal/reconnector/index.ts b/src/component/internal/reconnector/index.ts index 50310c56..c58f7e54 100644 --- a/src/component/internal/reconnector/index.ts +++ b/src/component/internal/reconnector/index.ts @@ -23,6 +23,25 @@ export abstract class ReconnectorAbstract extends EventEmitter void connect: () => void @@ -48,6 +67,7 @@ export class Reconnector extends EventEmitter { ) { super() + // setup default config values this._config = { max_reconnects: 10, timeout_ms: 1500, @@ -55,6 +75,10 @@ export class Reconnector extends EventEmitter { ...config, } + // register connect and disconnect handlers with current class + // as 'this' context, store them to a variable so that they + // can be later unregistered + this._onConnectHandle = this.onConnect.bind(this) this._conn.on('connect', this._onConnectHandle) @@ -62,27 +86,33 @@ export class Reconnector extends EventEmitter { this._conn.on('disconnect', this._onDisconnectHandle) } - private onConnect() { + private clearTimeout() { if (this._timeout) { window.clearTimeout(this._timeout) this._timeout = undefined } + } + private onConnect() { + this.clearTimeout() + + // only if connection is open, fire connect event if (this._open) { this._last_connected = new Date() this._total_reconnects = 0 this.emit('connect') } else { + // in this case we are connected but this connection + // has been closed, so we simply disconnect again this._conn.disconnect() } } private onDisconnect() { - if (this._timeout) { - window.clearTimeout(this._timeout) - this._timeout = undefined - } + this.clearTimeout() + // only if connection is open, fire disconnect event + // and start reconnecteing logic if (this._open) { this.emit('disconnect') this.reconnect() @@ -110,18 +140,21 @@ export class Reconnector extends EventEmitter { } public set config(conf: ReconnectorConfig) { - this._config = { ...conf } + this._config = { ...this._config, ...conf } if (this._config.max_reconnects <= this._total_reconnects) { this.close(new Error('reconnection config changed')) } } + // allows future reconnect attempts and connects if not set + // deferred connection to true public open(deferredConnection = false): void { - if (this._timeout) { - window.clearTimeout(this._timeout) - this._timeout = undefined - } + this.clearTimeout() + + // assuming open event can fire multiple times, we need to + // ensure, that open event get fired only once along with + // resetting total reconnects counter if (!this._open) { this._open = true @@ -134,50 +167,53 @@ export class Reconnector extends EventEmitter { } } + // disconnects and forbids future reconnect attempts public close(error?: Error): void { - if (this._timeout) { - window.clearTimeout(this._timeout) - this._timeout = undefined - } + this.clearTimeout() + // assuming close event can fire multiple times, the same + // precautions need to be taken as in open event, so that + // close event fires only once + if (this._open) { this._open = false this._last_connected = undefined this.emit('close', error) } + // if connected, tries to disconnect even if it has been + // called multiple times by user + if (this._conn.connected) { this._conn.disconnect() } } + // tries to connect and calls on disconnected if it could not + // connect within specified timeout according to config public connect(): void { - if (this._timeout) { - window.clearTimeout(this._timeout) - this._timeout = undefined - } + this.clearTimeout() this._conn.connect() this._timeout = window.setTimeout(this.onDisconnect.bind(this), this._config.timeout_ms) } + // tries to connect with specified backoff time if + // maximum reconnect theshold was not exceeded, otherwise + // closes the connection with an error message public reconnect(): void { - if (this._timeout) { - window.clearTimeout(this._timeout) - this._timeout = undefined - } + this.clearTimeout() - this._total_reconnects++ - - if (this._config.max_reconnects > this._total_reconnects || this._config.max_reconnects == -1) { + if (this._config.max_reconnects > ++this._total_reconnects || this._config.max_reconnects == -1) { this._timeout = window.setTimeout(this.connect.bind(this), this._config.backoff_ms) } else { this.close(new Error('reconnection failed')) } } + // closes connection and unregisters all events public destroy() { - this.close() + this.close(new Error('connection destroyed')) this._conn.off('connect', this._onConnectHandle) this._conn.off('disconnect', this._onDisconnectHandle) diff --git a/src/component/types/state.ts b/src/component/types/state.ts index c4b1f2f4..ee3d3e44 100644 --- a/src/component/types/state.ts +++ b/src/component/types/state.ts @@ -1,5 +1,5 @@ import * as webrtcTypes from './webrtc' -import * as reconnecterTypes from './reconnector' +import * as reconnectorTypes from './reconnector' export default interface State { authenticated: boolean @@ -39,7 +39,7 @@ export interface WebRTC { videos: string[] } -export interface ReconnectorConfig extends reconnecterTypes.ReconnectorConfig {} +export interface ReconnectorConfig extends reconnectorTypes.ReconnectorConfig {} export interface WebRTCStats extends webrtcTypes.WebRTCStats {}