lisk-framework
Version:
Lisk blockchain application platform
273 lines • 11.9 kB
JavaScript
;
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