UNPKG

camstreamerlib

Version:

Helper library for CamStreamer ACAP applications.

160 lines (159 loc) 5.45 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WsClient = void 0; const ws_1 = __importDefault(require("ws")); const Digest_1 = require("./Digest"); class WsClient { user; pass; address; protocol; pingInterval; wsOptions; digestAddress; isAlive = true; pingTimer; ws; isClosed = false; constructor(options) { const tls = options.tls ?? false; const tlsInsecure = options.tlsInsecure ?? false; const ip = options.ip ?? '127.0.0.1'; const port = options.port ?? (tls ? 443 : 80); this.user = options.user ?? ''; this.pass = options.pass ?? ''; const protocol = tls ? 'wss' : 'ws'; this.address = `${protocol}://${ip}:${port}${options.address}`; this.digestAddress = options.address; this.pingInterval = options.pingInterval ?? 30000; this.protocol = options.protocol; this.wsOptions = { auth: `${this.user}:${this.pass}`, rejectUnauthorized: !tlsInsecure, headers: options.headers ?? {}, }; } open(wwwAuthenticateHeader) { try { if (this.ws !== undefined) { return; } this.isClosed = false; if (this.protocol === undefined) { this.ws = new ws_1.default(this.address, this.wsOptions); } else { this.ws = new ws_1.default(this.address, this.protocol, this.wsOptions); } this.ws.binaryType = 'arraybuffer'; this.isAlive = true; this.pingTimer = setInterval(() => { if ((this.ws && this.ws.readyState !== ws_1.default.OPEN) || this.isAlive === false) { this.onError(new Error('Connection timeout')); this.closeWsConnection(); } else { this.isAlive = false; this.ws?.ping(); } }, this.pingInterval); this.ws.on('pong', () => { this.isAlive = true; }); if (wwwAuthenticateHeader !== undefined) { this.wsOptions.headers['Authorization'] = new Digest_1.Digest().getAuthHeader(this.user, this.pass, 'GET', this.digestAddress, wwwAuthenticateHeader); } this.ws.on('unexpected-response', (req, res) => { if (res.statusCode === 401 && res.headers['www-authenticate'] !== undefined) { if (this.pingTimer) { clearInterval(this.pingTimer); } this.ws?.removeAllListeners(); this.ws = undefined; this.open(res.headers['www-authenticate']); } else { this.onError(new Error('Status code: ' + res.statusCode)); this.closeWsConnection(); } }); this.ws.on('open', () => this.onOpen()); this.ws.on('message', (data, isBinary) => { const message = isBinary ? data : data.toString(); this.onMessage(message); }); this.ws.on('error', (error) => { this.onError(error); this.closeWsConnection(); }); this.ws.on('close', () => this.closeWsConnection()); } catch (error) { this.onError(error instanceof Error ? error : new Error('Unknown error')); this.closeWsConnection(); } } onMessage = (_) => { }; onOpen = () => { }; onClose = () => { }; onError = (error) => { console.error(error); }; send(data) { if (this.ws === undefined) { throw new Error("This websocket hasn't been opened yet."); } if (this.ws.readyState === this.ws.OPEN) { this.ws.send(data); } } destroy() { if (this.isClosed) { return; } this.isClosed = true; this.closeWsConnection(); } reconnect() { this.closeWsConnection(); } closeWsConnection() { if (this.ws === undefined) { return; } const wsCopy = this.ws; this.ws = undefined; try { if (this.pingTimer) { clearInterval(this.pingTimer); } wsCopy.removeAllListeners(); wsCopy.on('error', () => { }); if (wsCopy.readyState !== ws_1.default.CLOSING && wsCopy.readyState !== ws_1.default.CLOSED) { wsCopy.close(); } setTimeout(() => { if (wsCopy.readyState !== ws_1.default.CLOSED) { wsCopy.terminate(); } }, 5000); this.onClose(); } catch (err) { console.error(err); } finally { const shouldRestart = !this.isClosed; setTimeout(() => { wsCopy.removeAllListeners(); if (shouldRestart && !this.isClosed) { this.open(); } }, 10000); } } } exports.WsClient = WsClient;