import type { RawData, WebSocket } from 'ws'; import { getMessageObjectInfo } from '../../messages/utils/getMessageObjectInfo'; import { set } from '../../messages/utils/set'; import { SendBinary } from '../utils/SendBinary'; import type { OutgoingMessage, IncomingMessage, MessageObjectInfo } from '../../messages'; import { ClientAdapter } from './ClientAdapter'; import { ClientAdapterConfig } from './ClientAdapterConfig'; export class WebSocketClientAdapter extends ClientAdapter { public reconnectInterval = 5000; private reconnectTryCounter = 0; private reconnectTimer?: NodeJS.Timeout; private isClosed = false; public constructor(public ws: WebSocket, cfg: Partial = {}) { super(cfg); this.init(); } public close() { this.isClosed = true; this.ws.close(); } public setReconnectInterval(interval: number) { this.reconnectInterval = interval; } public async send(message: IncomingMessage): Promise { const info = this.prepareBinary(message); await this.ws.send(JSON.stringify(message)); if (info.binarySize > 0) { const sb = new SendBinary(message, this); await sb.send(); } } public async sendBinary(message: BinaryData): Promise { await this.ws.send(message, { binary: true }); } public getUrl(): string { return this.ws.url; } protected init() { this.reconnectTryCounter = 0; this.prepareWs(); this.initOnClose(); this.initOnOpen(); } protected prepareWs() { // const { ws } = this; // if (!ws.on && ws.onopen) } public prepareBinary(message: IncomingMessage): MessageObjectInfo { const info = getMessageObjectInfo(message); if (info.binarySize > 0) { info.binaryData.forEach((item) => { set(message, item.path, null); }); // eslint-disable-next-line no-param-reassign message.meta = { messageObjectInfo: info }; } return info; } protected initOnOpen() { this.ws.addEventListener('open', () => { this.client?.connection.onConnect(); this.ws.addEventListener('message', (m) => { this.onMessage( this.createMessageObject(m.data as any), ); }); }); } protected initOnClose() { this.ws.addEventListener('close', (m) => { if (m.code === 3400) { this.isClosed = true; } if (!this.isClosed) { this.reconnect(); } else { this.client?.connection.onClosed(m.reason.toString()); } this.client?.connection.onDisconnect(); }); } protected reconnect() { if (!this.autoReconnect) { return; } if (!this.reconnectTimer && this.reconnectTryCounter <= this.maxAutoReconnectTry) { this.reconnectTimer = setTimeout(() => this.doReconnect(), this.reconnectInterval); } } protected doReconnect() { this.reconnectTryCounter += 1; this.clearReconnectTimer(); this.ws.removeAllListeners?.(); const WebSocketConstr = this.ws.constructor as typeof WebSocket; this.ws = new WebSocketConstr(this.ws.url); this.init(); } protected createMessageObject(msg: RawData): OutgoingMessage { const m = JSON.parse(msg.toString()); return m as any; } protected clearReconnectTimer() { if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); delete this.reconnectTimer; } } }