move reconnector to own folder.

This commit is contained in:
Miroslav Šedivý
2021-07-26 23:29:41 +02:00
parent 5f1cca5ab2
commit 28f256b5b5
4 changed files with 106 additions and 90 deletions

View File

@ -5,7 +5,10 @@ import * as EVENT from '../types/events'
import { NekoWebSocket } from './websocket'
import { NekoWebRTC } from './webrtc'
import { Connection, WebRTCStats } from '../types/state'
import { Reconnector, ReconnectorAbstract } from '../utils/reconnector'
import { Reconnector } from './reconnector'
import { WebsocketReconnector } from './reconnector/websocket'
import { WebrtcReconnector } from './reconnector/webrtc'
const WEBRTC_RECONN_MAX_LOSS = 25
const WEBRTC_RECONN_FAILED_ATTEMPTS = 5
@ -14,94 +17,6 @@ export interface NekoConnectionEvents {
disconnect: (error?: Error) => void
}
class WebsocketReconnector extends ReconnectorAbstract {
private _state: Connection
private _websocket: NekoWebSocket
private _onConnectHandle: () => void
private _onDisconnectHandle: (error?: Error) => void
constructor(state: Connection, websocket: NekoWebSocket) {
super()
this._state = state
this._websocket = websocket
this._onConnectHandle = () => this.emit('connect')
this._websocket.on('connected', this._onConnectHandle)
this._onDisconnectHandle = (error?: Error) => this.emit('disconnect', error)
this._websocket.on('disconnected', this._onDisconnectHandle)
}
public get connected() {
return this._websocket.connected
}
public connect() {
let url = this._state.url
url = url.replace(/^http/, 'ws').replace(/\/+$/, '') + '/api/ws'
const token = this._state.token
if (token) {
url += '?token=' + encodeURIComponent(token)
}
this._websocket.connect(url)
}
public disconnect() {
this._websocket.disconnect()
}
public destroy() {
this._websocket.off('connected', this._onConnectHandle)
this._websocket.off('disconnected', this._onDisconnectHandle)
}
}
class WebrtcReconnector extends ReconnectorAbstract {
private _state: Connection
private _websocket: NekoWebSocket
private _webrtc: NekoWebRTC
private _onConnectHandle: () => void
private _onDisconnectHandle: (error?: Error) => void
constructor(state: Connection, websocket: NekoWebSocket, webrtc: NekoWebRTC) {
super()
this._state = state
this._websocket = websocket
this._webrtc = webrtc
this._onConnectHandle = () => this.emit('connect')
this._webrtc.on('connected', this._onConnectHandle)
this._onDisconnectHandle = (error?: Error) => this.emit('disconnect', error)
this._webrtc.on('disconnected', this._onDisconnectHandle)
}
public get connected() {
return this._webrtc.connected
}
public connect() {
if (this._websocket.connected) {
this._websocket.send(EVENT.SIGNAL_REQUEST, { video: this._state.webrtc.video })
}
}
public disconnect() {
this._webrtc.disconnect()
}
public destroy() {
this._webrtc.off('connected', this._onConnectHandle)
this._webrtc.off('disconnected', this._onDisconnectHandle)
}
}
export class NekoConnection extends EventEmitter<NekoConnectionEvents> {
private _state: Connection

View File

@ -0,0 +1,185 @@
import EventEmitter from 'eventemitter3'
import { ReconnectorConfig } from '../../types/reconnector'
export interface ReconnectorAbstractEvents {
connect: () => void
disconnect: (error?: Error) => void
}
export abstract class ReconnectorAbstract extends EventEmitter<ReconnectorAbstractEvents> {
constructor() {
super()
if (this.constructor == ReconnectorAbstract) {
throw new Error("Abstract classes can't be instantiated.")
}
}
public abstract get connected(): boolean
public abstract connect(): void
public abstract disconnect(): void
public abstract destroy(): void
}
export interface ReconnectorEvents {
open: () => void
connect: () => void
disconnect: () => void
close: (error?: Error) => void
}
export class Reconnector extends EventEmitter<ReconnectorEvents> {
private _conn: ReconnectorAbstract
private _config: ReconnectorConfig
private _timeout?: number
private _open = false
private _total_reconnects = 0
private _last_connected?: Date
private _onConnectHandle: () => void
private _onDisconnectHandle: (error?: Error) => void
constructor(conn: ReconnectorAbstract, config?: ReconnectorConfig) {
super()
this._conn = conn
this._config = {
maxReconnects: 10,
timeoutMs: 1500,
backoffMs: 750,
...config,
}
this._onConnectHandle = this.onConnect.bind(this)
this._conn.on('connect', this._onConnectHandle)
this._onDisconnectHandle = this.onDisconnect.bind(this)
this._conn.on('disconnect', this._onDisconnectHandle)
}
private onConnect() {
if (this._timeout) {
window.clearTimeout(this._timeout)
this._timeout = undefined
}
if (this._open) {
this._last_connected = new Date()
this.emit('connect')
} else {
this._conn.disconnect()
}
}
private onDisconnect() {
if (this._timeout) {
window.clearTimeout(this._timeout)
this._timeout = undefined
}
if (this._open) {
this.emit('disconnect')
this.reconnect()
}
}
public get isOpen(): boolean {
return this._open
}
public get isConnected(): boolean {
return this._conn.connected
}
public get totalReconnects(): number {
return this._total_reconnects
}
public get lastConnected(): Date | undefined {
return this._last_connected
}
public get config(): ReconnectorConfig {
return { ...this._config }
}
public set config(conf: ReconnectorConfig) {
this._config = { ...conf }
if (this._config.maxReconnects > this._total_reconnects) {
this.close(new Error('reconnection config changed'))
}
}
public open(deferredConnection = false): void {
if (this._timeout) {
window.clearTimeout(this._timeout)
this._timeout = undefined
}
if (!this._open) {
this._open = true
this.emit('open')
}
if (!deferredConnection && !this._conn.connected) {
this.connect()
}
}
public close(error?: Error): void {
if (this._timeout) {
window.clearTimeout(this._timeout)
this._timeout = undefined
}
if (this._open) {
this._open = false
this._last_connected = undefined
this.emit('close', error)
}
if (this._conn.connected) {
this._conn.disconnect()
}
}
public connect(): void {
if (this._timeout) {
window.clearTimeout(this._timeout)
this._timeout = undefined
}
this._conn.connect()
this._timeout = window.setTimeout(this.onDisconnect.bind(this), this._config.timeoutMs)
}
public reconnect(): void {
if (this._timeout) {
window.clearTimeout(this._timeout)
this._timeout = undefined
}
this._total_reconnects++
if (this._config.maxReconnects > this._total_reconnects || this._total_reconnects < 0) {
this._timeout = window.setTimeout(this.connect.bind(this), this._config.backoffMs)
} else {
this.close(new Error('reconnection failed'))
}
}
public destroy() {
if (this._timeout) {
window.clearTimeout(this._timeout)
this._timeout = undefined
}
this._conn.off('connect', this._onConnectHandle)
this._conn.off('disconnect', this._onDisconnectHandle)
this._conn.destroy()
}
}

View File

@ -0,0 +1,49 @@
import * as EVENT from '../../types/events'
import { Connection } from '../../types/state'
import { NekoWebSocket } from '../websocket'
import { NekoWebRTC } from '../webrtc'
import { ReconnectorAbstract } from '.'
export class WebrtcReconnector extends ReconnectorAbstract {
private _state: Connection
private _websocket: NekoWebSocket
private _webrtc: NekoWebRTC
private _onConnectHandle: () => void
private _onDisconnectHandle: (error?: Error) => void
constructor(state: Connection, websocket: NekoWebSocket, webrtc: NekoWebRTC) {
super()
this._state = state
this._websocket = websocket
this._webrtc = webrtc
this._onConnectHandle = () => this.emit('connect')
this._webrtc.on('connected', this._onConnectHandle)
this._onDisconnectHandle = (error?: Error) => this.emit('disconnect', error)
this._webrtc.on('disconnected', this._onDisconnectHandle)
}
public get connected() {
return this._webrtc.connected
}
public connect() {
if (this._websocket.connected) {
this._websocket.send(EVENT.SIGNAL_REQUEST, { video: this._state.webrtc.video })
}
}
public disconnect() {
this._webrtc.disconnect()
}
public destroy() {
this._webrtc.off('connected', this._onConnectHandle)
this._webrtc.off('disconnected', this._onDisconnectHandle)
}
}

View File

@ -0,0 +1,51 @@
import { Connection } from '../../types/state'
import { NekoWebSocket } from '../websocket'
import { ReconnectorAbstract } from '.'
export class WebsocketReconnector extends ReconnectorAbstract {
private _state: Connection
private _websocket: NekoWebSocket
private _onConnectHandle: () => void
private _onDisconnectHandle: (error?: Error) => void
constructor(state: Connection, websocket: NekoWebSocket) {
super()
this._state = state
this._websocket = websocket
this._onConnectHandle = () => this.emit('connect')
this._websocket.on('connected', this._onConnectHandle)
this._onDisconnectHandle = (error?: Error) => this.emit('disconnect', error)
this._websocket.on('disconnected', this._onDisconnectHandle)
}
public get connected() {
return this._websocket.connected
}
public connect() {
let url = this._state.url
url = url.replace(/^http/, 'ws').replace(/\/+$/, '') + '/api/ws'
const token = this._state.token
if (token) {
url += '?token=' + encodeURIComponent(token)
}
this._websocket.connect(url)
}
public disconnect() {
this._websocket.disconnect()
}
public destroy() {
this._websocket.off('connected', this._onConnectHandle)
this._websocket.off('disconnected', this._onDisconnectHandle)
}
}