mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
move reconnector to own folder.
This commit is contained in:
@ -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
|
||||
|
||||
|
185
src/component/internal/reconnector/index.ts
Normal file
185
src/component/internal/reconnector/index.ts
Normal 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()
|
||||
}
|
||||
}
|
49
src/component/internal/reconnector/webrtc.ts
Normal file
49
src/component/internal/reconnector/webrtc.ts
Normal 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)
|
||||
}
|
||||
}
|
51
src/component/internal/reconnector/websocket.ts
Normal file
51
src/component/internal/reconnector/websocket.ts
Normal 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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user