UNPKG

lisk-framework

Version:

Lisk blockchain application platform

155 lines 5.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WSServer = void 0; const lisk_cryptography_1 = require("@liskhq/lisk-cryptography"); const WebSocket = require("ws"); class WSServer { constructor(options) { var _a; this._subscriptions = {}; this._port = options.port; this._host = options.host; this._path = options.path; this._accessControlAllowOrigin = (_a = options.accessControlAllowOrigin) !== null && _a !== void 0 ? _a : '*'; } start(logger, messageHandler, httpServer) { var _a; this._logger = logger; if (httpServer) { this.server = new WebSocket.Server({ noServer: true, }); httpServer.on('upgrade', (request, socket, head) => { if (request.url === this._path) { this.server.handleUpgrade(request, socket, head, ws => { this.server.emit('connection', ws, request); }); } else { socket.destroy(); } }); } else { this.server = new WebSocket.Server({ path: this._path, port: this._port, host: this._host, clientTracking: true, }); } this.server.on('connection', (socket, req) => { const { origin } = req.headers; if (origin && this._accessControlAllowOrigin !== '*' && !this._accessControlAllowOrigin.includes(origin)) { socket.close(); return; } this._handleConnection(socket, messageHandler); }); this.server.on('error', error => { this._logger.error(error); }); this.server.on('listening', () => { this._logger.info({ host: this._host, port: this._port, path: this._path }, 'Websocket Server Ready'); }); this.server.on('close', () => { clearInterval(this._pingTimer); }); this._pingTimer = this._setUpPing(); this._logger.info(`RPC WS Server starting at ${(_a = this._host) !== null && _a !== void 0 ? _a : ''}:${this._port}${this._path}`); return this.server; } stop() { if (this.server) { for (const cli of this.server.clients) { cli.terminate(); } this.server.close(); } } broadcast(message) { for (const client of this.server.clients) { const wsClient = client; if (wsClient.readyState === WebSocket.OPEN && wsClient.id) { const subscription = this._subscriptions[wsClient.id]; if (!subscription) { continue; } if (Array.from(subscription).some(key => message.method === key || message.method.includes(key))) { client.send(JSON.stringify(message)); } } } } _handleConnection(socket, messageHandler) { socket.isAlive = true; socket.id = lisk_cryptography_1.utils.getRandomBytes(20).toString('hex'); socket.on('message', (message, isBytes) => { try { const parsedMessage = JSON.parse(isBytes ? message.toString() : message); if (parsedMessage.method === 'subscribe') { this._handleSubscription(socket, parsedMessage); return; } if (parsedMessage.method === 'unsubscribe') { this._handleUnsubscription(socket, parsedMessage); return; } } catch (error) { this._logger.error({ err: error }, 'Received invalid websocket message'); return; } messageHandler(socket, message); }); socket.on('pong', () => this._handleHeartbeat(socket)); socket.on('close', () => { delete this._subscriptions[socket.id]; }); this._logger.info('New web socket client connected'); } _handleHeartbeat(socket) { socket.isAlive = true; } _setUpPing() { return setInterval(() => { for (const socket of this.server.clients) { const aClient = socket; if (aClient.isAlive === false) { return socket.terminate(); } aClient.isAlive = false; aClient.ping(() => { }); } return null; }, 3000); } _handleSubscription(socket, message) { const { params } = message; if (!params || !Array.isArray(params.topics) || !params.topics.length) { throw new Error('Invalid subscription message.'); } if (!this._subscriptions[socket.id]) { this._subscriptions[socket.id] = new Set(); } for (const eventName of params.topics) { this._subscriptions[socket.id].add(eventName); } } _handleUnsubscription(socket, message) { const { params } = message; if (!params || !Array.isArray(params.topics) || !params.topics.length) { throw new Error('Invalid unsubscription message.'); } if (!this._subscriptions[socket.id]) { return; } for (const eventName of params.topics) { this._subscriptions[socket.id].delete(eventName); } } } exports.WSServer = WSServer; //# sourceMappingURL=ws_server.js.map