UNPKG

lisk-framework

Version:

Lisk blockchain application platform

231 lines 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RPCServer = void 0; const constants_1 = require("../../constants"); const http_server_1 = require("../../controller/http/http_server"); const ipc_server_1 = require("../../controller/ipc/ipc_server"); const ws_server_1 = require("../../controller/ws/ws_server"); const request_1 = require("../../controller/request"); const JSONRPC = require("../../controller/jsonrpc"); const jsonrpc_1 = require("../../controller/jsonrpc"); const system_dirs_1 = require("../../system_dirs"); class RPCServer { constructor(dataPath, config) { this._rpcHandler = new Map(); this._config = config; if (this._config.modes.includes(constants_1.RPC_MODES.IPC)) { const dirs = (0, system_dirs_1.systemDirs)(dataPath); this._ipcServer = new ipc_server_1.IPCServer({ socketsDir: dirs.sockets, name: 'engine', externalSocket: true, }); } if (this._config.modes.includes(constants_1.RPC_MODES.WS)) { this._wsServer = new ws_server_1.WSServer({ path: '/rpc-ws', port: this._config.port, host: this._config.host, accessControlAllowOrigin: this._config.accessControlAllowOrigin, }); } if (this._config.modes.includes(constants_1.RPC_MODES.HTTP)) { this._httpServer = new http_server_1.HTTPServer({ host: this._config.host, port: this._config.port, path: '/rpc', ignorePaths: ['/rpc-ws'], accessControlAllowOrigin: this._config.accessControlAllowOrigin, }); } } init(options) { this._logger = options.logger; this._chainID = options.chainID; } async start() { var _a, _b; if (this._ipcServer) { await this._ipcServer.start(); this._handleIPCRequest(this._ipcServer.rpcServer).catch(err => { this._logger.info({ status: 'error', err: err }, 'Failed to handle IPC request'); }); this._logger.info(`RPC IPC Server starting at ${this._ipcServer.socketPaths.rpcServer}`); } if (this._httpServer) { this._httpServer.start(this._logger, (req, res, message) => { this._logger.debug({ remoteAddress: req.socket.remoteAddress }, 'Received HTTP request'); this._handleRequest(message) .then(data => { res.end(JSON.stringify(data)); }) .catch((error) => { this._logger.info({ status: 'error', err: error }, 'Failed to handle HTTP request'); res.end(JSON.stringify(error.response)); }); }); } if (this._wsServer) { this._wsServer.start(this._logger, (socket, message) => { this._logger.debug({ id: socket.id, url: socket.url }, 'Received WS request'); this._handleRequest(message) .then(data => { socket.send(JSON.stringify(data)); }) .catch((error) => { this._logger.info({ status: 'error', err: error }, 'Failed to handle WS request'); socket.send(JSON.stringify(error.response)); }); }, (_a = this._httpServer) === null || _a === void 0 ? void 0 : _a.server); } (_b = this._httpServer) === null || _b === void 0 ? void 0 : _b.listen(); } stop() { if (this._ipcServer) { this._ipcServer.stop(); } if (this._wsServer) { this._wsServer.stop(); } if (this._httpServer) { this._httpServer.stop(); } } async publish(eventName, data) { await this._publishIPC(eventName, data); this._publishWS(eventName, data); } registerEndpoint(namespace, method, handler) { var _a; if (!this._rpcHandler.has(namespace)) { this._rpcHandler.set(namespace, new Map()); } const existingNamespace = this._rpcHandler.get(namespace); const existingMethod = (_a = this._rpcHandler.get(namespace)) === null || _a === void 0 ? void 0 : _a.get(method); if (existingMethod) { throw new Error(`Method ${method} in ${namespace} is already registered.`); } existingNamespace.set(method, handler); this._logger.info({ method: `${namespace}_${method}` }, `Registered endpoint`); } registerNotFoundEndpoint(handler) { if (this._notFoundHandler) { throw new Error('NotFoundHandler is already registered.'); } this._notFoundHandler = handler; } async _handleIPCRequest(rpcServer) { for await (const [sender, request] of rpcServer) { this._logger.debug({ sender: sender.toString('hex') }, 'Received IPC request'); this._handleRequest(request.toString()) .then(result => { rpcServer.send([sender, JSON.stringify(result)]).catch(error => { this._logger.debug({ err: error }, `Failed to send request response: ${result.id} to ipc client.`); }); }) .catch((err) => { this._logger.info({ status: 'error', err }, 'Failed to handle IPC request'); rpcServer.send([sender, JSON.stringify(err.response)]).catch(error => { this._logger.debug({ err: error }, `Failed to send IPC error response.`); }); }); } } async _handleRequest(rawRequest) { var _a; if (!rawRequest) { this._logger.error('Empty invoke request.'); throw new JSONRPC.JSONRPCError('Invalid invoke request.', JSONRPC.errorResponse(null, JSONRPC.invalidRequest('Invalid invoke request.'))); } let requestObj; try { requestObj = JSON.parse(rawRequest); } catch (error) { throw new JSONRPC.JSONRPCError('Invalid RPC request. Failed to parse request params.', JSONRPC.errorResponse(null, JSONRPC.invalidRequest('Invalid RPC request. Failed to parse request params.'))); } try { JSONRPC.validateJSONRPCRequest(requestObj); } catch (error) { this._logger.error({ err: error }, 'Invalid RPC request.'); throw new JSONRPC.JSONRPCError('Invalid RPC request. Invalid request format.', JSONRPC.errorResponse(null, JSONRPC.invalidRequest('Invalid RPC request. Invalid request format.'))); } try { const request = request_1.Request.fromJSONRPCRequest(requestObj); if (!this._isAllowedMethod(request.namespace, request.name)) { const disabledMethodErrorMessage = 'Requested method or namespace is disabled in node config.'; throw new JSONRPC.JSONRPCError(disabledMethodErrorMessage, JSONRPC.errorResponse(request.id, JSONRPC.invalidRequest(disabledMethodErrorMessage))); } const handler = this._getHandler(request.namespace, request.name); const context = { logger: this._logger, chainID: this._chainID, params: (_a = requestObj.params) !== null && _a !== void 0 ? _a : {}, }; if (!handler) { if (this._notFoundHandler) { const result = await this._notFoundHandler(request.namespace, request.name, context); return request.buildJSONRPCResponse({ result }); } throw new JSONRPC.JSONRPCError(`Method ${request.name} with in namespace ${request.namespace} does not exist`, JSONRPC.errorResponse(requestObj.id, JSONRPC.methodNotFound())); } const result = await handler(context); this._logger.info({ status: 'success', method: `${request.namespace}_${request.name}`, id: request.id }, 'Handled RPC request'); return request.buildJSONRPCResponse({ result: result !== null && result !== void 0 ? result : {}, }); } catch (error) { if (error instanceof JSONRPC.JSONRPCError) { throw error; } throw new JSONRPC.JSONRPCError(error.message, JSONRPC.errorResponse(requestObj.id, JSONRPC.invalidRequest(error.message))); } } async _publishIPC(eventName, data) { if (!this._ipcServer) { return; } const notification = (0, jsonrpc_1.notificationRequest)(eventName, data); try { await this._ipcServer.pubSocket.send([eventName, JSON.stringify(notification)]); this._logger.info({ status: 'success', event: eventName }, 'IPCServer published event'); } catch (error) { this._logger.info({ status: 'error', event: eventName }, 'IPCServer published event'); this._logger.debug({ err: error, event: eventName }, `Failed to publish event from ipc server.`); } } _publishWS(eventName, data) { if (!this._wsServer) { return; } const notification = (0, jsonrpc_1.notificationRequest)(eventName, data); this._wsServer.broadcast(notification); this._logger.info({ status: 'success', event: eventName }, 'WSServer published event'); } _getHandler(namespace, method) { const existingNamespace = this._rpcHandler.get(namespace); if (!existingNamespace) { return undefined; } const existingMethod = existingNamespace.get(method); if (!existingMethod) { return undefined; } return existingMethod; } _isAllowedMethod(namespace, method) { const { allowedMethods } = this._config; if (!allowedMethods || allowedMethods.length === 0) { return false; } if (allowedMethods.includes('*')) { return true; } return allowedMethods.includes(namespace) || allowedMethods.includes(`${namespace}_${method}`); } } exports.RPCServer = RPCServer; //# sourceMappingURL=rpc_server.js.map