@egodigital/egoose
Version:
Helper classes and functions for Node.js 10 or later.
243 lines • 7.66 kB
JavaScript
;
/**
* This file is part of the @egodigital/egoose distribution.
* Copyright (c) e.GO Digital GmbH, Aachen, Germany (https://www.e-go-digital.com/)
*
* @egodigital/egoose is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, version 3.
*
* @egodigital/egoose is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebSocketClient = exports.WebSocketHost = void 0;
const index_1 = require("../index");
const _ = require("lodash");
const events = require("events");
const http = require("http");
const ws = require("ws");
/**
* A web socket host.
*/
class WebSocketHost extends events.EventEmitter {
/**
* Initializes a new instance of that class.
*
* @param {WebSocketHostOptions} [options] Custom options for the host.
*/
constructor(options) {
super();
this.options = options;
if (!this.options) {
this.options = {};
}
}
/**
* Gets if the server is currently running or not.
*/
get isRunning() {
return !!this._server;
}
/**
* Starts the host.
*
* @return {Promise<boolean>} The promise that indicates if operation was succesfull or not.
*/
start() {
return new Promise(async (resolve, reject) => {
try {
if (this.isRunning) {
resolve(false);
return;
}
let serverFactory = this.options.serverFactory;
if (!serverFactory) {
serverFactory = () => {
return http.createServer();
};
}
let port = parseInt(index_1.toStringSafe(this.options.port)
.trim());
if (isNaN(port)) {
port = 5979;
}
const NEW_SERVER = await Promise.resolve(serverFactory());
if (!NEW_SERVER) {
resolve(false);
return;
}
NEW_SERVER.once('error', (err) => {
reject(err);
});
const VERIFY_CLIENT = async (info, callback) => {
let isValid = true;
try {
if (this.options.verifyClient) {
isValid = index_1.toBooleanSafe(await Promise.resolve(this.options.verifyClient({
isSecure: info.secure,
request: info.req,
})));
}
}
catch {
isValid = false;
}
if (isValid) {
callback(true);
}
else {
callback(false, 401);
}
};
const WSS = new ws.Server({
server: NEW_SERVER,
verifyClient: VERIFY_CLIENT,
});
WSS.on('error', (err) => {
/* ignore errors */
});
WSS.on('connection', (ws) => {
try {
const CONN = new WebSocketClient(this, ws);
CONN.init();
this.emit('connection', CONN);
}
catch {
try {
ws.close();
}
catch { }
}
});
NEW_SERVER.listen(port, () => {
this._server = NEW_SERVER;
resolve(true);
});
}
catch (e) {
reject(e);
}
});
}
/**
* Stops the host.
*
* @return {Promise<boolean>} The promise that indicates if operation was succesfull or not.
*/
stop() {
return new Promise((resolve, reject) => {
try {
const SERVER = this._server;
if (!SERVER) {
resolve(false);
return;
}
SERVER.close((err) => {
if (err) {
reject(err);
}
else {
this._server = null;
resolve(true);
}
});
}
catch (e) {
reject(e);
}
});
}
}
exports.WebSocketHost = WebSocketHost;
/**
* A web socket client.
*/
class WebSocketClient extends events.EventEmitter {
/**
* Initializes a new instance of that class.
*
* @param {WebSocketHost} host The underlying host.
* @param {ws} socket The underlying socket.
*/
constructor(host, socket) {
super();
this.host = host;
this.socket = socket;
}
/**
* Initializes the instance.
*/
init() {
this.socket.on('error', (err) => {
/* ignore errors */
});
this.socket.on('message', (data) => {
try {
const MSG = JSON.parse(index_1.toStringSafe(data));
if (MSG) {
this.emit('message', MSG);
const TYPE = index_1.normalizeString(MSG.type);
this.emit('message.' + TYPE, MSG, TYPE);
}
}
catch { }
});
this.socket.once('close', () => {
this.emit('close');
});
}
onType(checker, listener) {
let predicate = checker;
if (!_.isFunction(predicate)) {
if (_.isRegExp(checker)) {
predicate = (t) => checker.test(t);
}
else {
predicate = (t) => t === index_1.normalizeString(checker);
}
}
const MSG_LISTENER = (msg) => {
const TYPE = index_1.normalizeString(msg.type);
if (index_1.toBooleanSafe(predicate(TYPE))) {
listener(msg.data, TYPE);
}
};
this.on('message', MSG_LISTENER);
return MSG_LISTENER;
}
/**
* Sends a message to the remote client.
*
* @param {string} type The type.
* @param {any} [data] The data to send.
*/
send(type, data) {
return new Promise((resolve, reject) => {
try {
const MSG = {
data: data,
type: index_1.normalizeString(type),
};
this.socket.send(JSON.stringify(MSG), (err) => {
if (err) {
reject(err);
}
else {
resolve();
}
});
}
catch (e) {
reject(e);
}
});
}
}
exports.WebSocketClient = WebSocketClient;
//# sourceMappingURL=websockets.js.map