UNPKG

lisk-framework

Version:

Lisk blockchain application platform

273 lines 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Bus = void 0; const eventemitter2_1 = require("eventemitter2"); const zeromq_1 = require("zeromq"); const types_1 = require("../types"); const request_1 = require("./request"); const constants_1 = require("./constants"); const event_1 = require("./event"); const JSONRPC = require("./jsonrpc"); const endpoint_1 = require("../endpoint"); const parseError = (id, err) => { if (err instanceof JSONRPC.JSONRPCError) { return err; } return new JSONRPC.JSONRPCError(err.message, JSONRPC.errorResponse(id, JSONRPC.internalError(err.message))); }; class Bus { constructor(config) { this._emitter = new eventemitter2_1.EventEmitter2({ wildcard: true, delimiter: ':', maxListeners: 1000, }); this._endpointInfos = {}; this._events = {}; this._channels = {}; this._rpcClients = {}; this._rpcRequestIds = new Set(); this._internalIPCServer = config.internalIPCServer; this._handleRPCResponse = async (rpcClient) => { for await (const [requestId, result] of rpcClient) { if (this._rpcRequestIds.has(requestId.toString())) { this._emitter.emit(requestId.toString(), JSON.parse(result.toString())); continue; } } }; this._handleRPC = async (rpcServer) => { for await (const [sender, request, params] of rpcServer) { switch (request.toString()) { case constants_1.IPC_EVENTS.REGISTER_CHANNEL: { const { moduleName, eventsList, endpointInfo, options } = JSON.parse(params.toString()); this.registerChannel(moduleName, eventsList, endpointInfo, options).catch(err => { this._logger.debug(err, `Error occurred while Registering channel for module ${moduleName}.`); }); break; } case constants_1.IPC_EVENTS.RPC_EVENT: { const requestData = JSON.parse(params.toString()); this.invoke(requestData) .then(result => { rpcServer .send([sender, requestData.id, JSON.stringify(result)]) .catch(error => { this._logger.debug({ err: error }, `Failed to send request response: ${requestData.id} to ipc client.`); }); }) .catch((err) => { rpcServer .send([sender, requestData.id, JSON.stringify(err.response)]) .catch(error => { this._logger.debug({ err: error }, `Failed to send error response: ${requestData.id} to ipc client.`); }); }); break; } default: break; } } }; this._handleEvents = async (subSocket) => { for await (const [_event, eventData] of subSocket) { this.publish(eventData.toString()); } }; } async start(logger) { this._logger = logger; await this._setupIPCInternalServer(); } async registerChannel(namespace, events, endpointInfo, options) { if (Object.keys(this._channels).includes(namespace)) { throw new Error(`Channel for module ${namespace} is already registered.`); } events.forEach(eventName => { if (this._events[(0, endpoint_1.getEndpointPath)(namespace, eventName)] !== undefined) { throw new Error(`Event "${eventName}" already registered with bus.`); } this._events[(0, endpoint_1.getEndpointPath)(namespace, eventName)] = true; }); for (const methodName of Object.keys(endpointInfo)) { if (this._endpointInfos[(0, endpoint_1.getEndpointPath)(namespace, methodName)] !== undefined) { throw new Error(`Endpoint "${methodName}" already registered with bus.`); } this._endpointInfos[(0, endpoint_1.getEndpointPath)(namespace, methodName)] = endpointInfo[methodName]; } if (options.type === types_1.ChannelType.ChildProcess && options.socketPath) { const rpcClient = new zeromq_1.Dealer(); rpcClient.connect(options.socketPath); this._rpcClients[namespace] = rpcClient; this._handleRPCResponse(rpcClient).catch(err => { this._logger.debug(err, 'Error occured while listening to RPC results on RPC Dealer.'); }); this._channels[namespace] = { rpcClient, events, endpointInfo, type: types_1.ChannelType.ChildProcess, }; } else { this._channels[namespace] = { channel: options.channel, events, endpointInfo, type: types_1.ChannelType.InMemory, }; } } async invoke(rawRequest, context) { let requestObj; if (!rawRequest) { this._logger.error('Empty invoke request.'); throw new JSONRPC.JSONRPCError('Invalid invoke request.', JSONRPC.errorResponse(null, JSONRPC.invalidRequest())); } try { requestObj = typeof rawRequest === 'string' ? JSON.parse(rawRequest) : rawRequest; } catch (error) { throw new JSONRPC.JSONRPCError('Invalid invoke request.', JSONRPC.errorResponse(null, JSONRPC.invalidRequest())); } try { JSONRPC.validateJSONRPCRequest(requestObj); } catch (error) { this._logger.error({ err: error }, 'Invalid invoke request.'); throw new JSONRPC.JSONRPCError('Invalid invoke request.', JSONRPC.errorResponse(requestObj.id, JSONRPC.invalidRequest())); } const request = request_1.Request.fromJSONRPCRequest(requestObj); const actionFullName = request.key(); if (this._endpointInfos[actionFullName] === undefined) { throw new JSONRPC.JSONRPCError(`Request '${actionFullName}' is not registered to bus.`, JSONRPC.errorResponse(request.id, JSONRPC.internalError(`Request '${actionFullName}' is not registered to bus.`))); } const actionParams = request.params; const channelInfo = this._channels[request.namespace]; if (channelInfo.type === types_1.ChannelType.InMemory) { try { const result = await channelInfo.channel.invoke({ context: context !== null && context !== void 0 ? context : {}, methodName: actionFullName, params: actionParams, }); return request.buildJSONRPCResponse({ result, }); } catch (error) { throw parseError(request.id, error); } } return new Promise((resolve, reject) => { this._rpcRequestIds.add(request.id); channelInfo.rpcClient .send([constants_1.IPC_EVENTS.RPC_EVENT, JSON.stringify(request.toJSONRPCRequest())]) .then(_ => { const requestTimeout = setTimeout(() => { reject(new Error('Request timed out on invoke.')); }, constants_1.IPC_EVENTS.RPC_REQUEST_TIMEOUT); this._emitter.once(request.id, (response) => { clearTimeout(requestTimeout); this._rpcRequestIds.delete(request.id); if (response.error) { reject(new Error(response.error.message)); return; } resolve(response); }); }) .catch(err => { this._logger.debug(err, 'Error occurred while sending RPC request.'); }); }); } publish(rawRequest) { let request; if (!rawRequest) { this._logger.error('Empty publish request.'); throw new JSONRPC.JSONRPCError('Invalid publish request.', JSONRPC.errorResponse(null, JSONRPC.invalidRequest())); } try { request = typeof rawRequest === 'string' ? JSON.parse(rawRequest) : rawRequest; } catch (error) { throw new JSONRPC.JSONRPCError('Invalid publish request.', JSONRPC.errorResponse(null, JSONRPC.invalidRequest())); } try { JSONRPC.validateJSONRPCNotification(request); } catch (error) { this._logger.error({ err: error }, 'Invalid publish request.'); throw new JSONRPC.JSONRPCError('Invalid publish request.', JSONRPC.errorResponse(null, JSONRPC.invalidRequest())); } const event = event_1.Event.fromJSONRPCNotification(rawRequest); const eventName = event.key(); const notification = event.toJSONRPCNotification(); if (!this.getEvents().includes(eventName)) { throw new JSONRPC.JSONRPCError(`Event ${eventName} is not registered to bus.`, JSONRPC.errorResponse(null, JSONRPC.internalError(`Event ${eventName} is not registered to bus.`))); } this._emitter.emit(eventName, notification); if (this._internalIPCServer) { this._internalIPCServer.pubSocket .send([eventName, JSON.stringify(notification)]) .catch(error => { this._logger.debug({ err: error }, `Failed to publish event: ${eventName} to ipc server.`); }); } } subscribe(eventName, cb) { if (!this.getEvents().includes(eventName)) { this._logger.info(`Event ${eventName} was subscribed but not registered to the bus yet.`); } this._emitter.on(eventName, cb); } unsubscribe(eventName, cb) { if (!this.getEvents().includes(eventName)) { this._logger.info(`Can't unsubscribe to event ${eventName} that was not registered to the bus yet.`); } this._emitter.off(eventName, cb); } once(eventName, cb) { if (!this.getEvents().includes(eventName)) { this._logger.info(`Event ${eventName} was subscribed but not registered to the bus yet.`); } this._emitter.once(eventName, cb); return this; } getEndpoints() { return Object.keys(this._endpointInfos); } getEvents() { return Object.keys(this._events); } async cleanup() { var _a; this._emitter.removeAllListeners(); (_a = this._internalIPCServer) === null || _a === void 0 ? void 0 : _a.stop(); for (const key of Object.keys(this._rpcClients)) { this._rpcClients[key].close(); } } async _setupIPCInternalServer() { if (!this._internalIPCServer) { return; } await this._internalIPCServer.start(); this._handleEvents(this._internalIPCServer.subSocket).catch(err => { this._logger.debug(err, 'Error occured while listening to events on subscriber.'); }); this._handleRPC(this._internalIPCServer.rpcServer).catch(err => { this._logger.debug(err, 'Error occured while listening to RPCs on RPC router.'); }); } } exports.Bus = Bus; //# sourceMappingURL=bus.js.map