camstreamerlib
Version:
Helper library for CamStreamer ACAP applications.
160 lines (159 loc) • 5.45 kB
JavaScript
"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;