lisk-framework
Version: 
Lisk blockchain application platform
206 lines • 8.48 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.IPCChannel = void 0;
const eventemitter2_1 = require("eventemitter2");
const path_1 = require("path");
const request_1 = require("../request");
const event_1 = require("../event");
const base_channel_1 = require("./base_channel");
const ipc_client_1 = require("../ipc/ipc_client");
const types_1 = require("../../types");
const constants_1 = require("../constants");
class IPCChannel extends base_channel_1.BaseChannel {
    constructor(logger, namespace, events, endpoints, chainID, options) {
        super(logger, namespace, events, endpoints);
        this._chainID = chainID;
        this._ipcClient = new ipc_client_1.IPCClient({
            socketsDir: options.socketsPath,
            name: namespace,
            rpcServerSocketPath: `ipc://${(0, path_1.join)(options.socketsPath, 'bus.internal.rpc.ipc')}`,
        });
        this._rpcRequestIds = new Set();
        this._emitter = new eventemitter2_1.EventEmitter2({
            wildcard: true,
            delimiter: ':',
            maxListeners: 1000,
        });
    }
    async startAndListen() {
        await this._ipcClient.start();
        this._subSocket.subscribe(constants_1.IPC_EVENTS.RPC_EVENT);
        const listenToMessages = async () => {
            for await (const [_event, eventData] of this._subSocket) {
                const eventDataJSON = event_1.Event.fromJSONRPCNotification(JSON.parse(eventData.toString()));
                this._emitter.emit(eventDataJSON.key(), eventDataJSON.toJSONRPCNotification());
            }
        };
        listenToMessages().catch(error => {
            throw error;
        });
        const listenToRPC = async () => {
            for await (const [sender, event, eventData] of this._rpcServer) {
                if (event.toString() === constants_1.IPC_EVENTS.RPC_EVENT) {
                    const request = request_1.Request.fromJSONRPCRequest(JSON.parse(eventData.toString()));
                    if (request.namespace === this.namespace) {
                        this.invoke({
                            methodName: request.key(),
                            context: {},
                            params: request.params,
                        })
                            .then(result => {
                            this._rpcServer
                                .send([
                                sender,
                                String(request.id),
                                JSON.stringify(request.buildJSONRPCResponse({ result })),
                            ])
                                .catch(error => {
                                this._logger.error({ err: error }, 'Fail to send success response');
                            });
                        })
                            .catch(error => {
                            this._rpcServer
                                .send([
                                sender,
                                String(request.id),
                                JSON.stringify(request.buildJSONRPCResponse({
                                    error: { code: -32600, message: error.message },
                                })),
                            ])
                                .catch(err => {
                                this._logger.error({ err: err }, 'Fail to send error response');
                            });
                        });
                    }
                    continue;
                }
            }
        };
        listenToRPC().catch(error => {
            throw error;
        });
        const listenToRPCResponse = async () => {
            for await (const [requestId, result] of this._rpcClient) {
                if (this._rpcRequestIds.has(requestId.toString())) {
                    this._emitter.emit(requestId.toString(), JSON.parse(result.toString()));
                    continue;
                }
            }
        };
        listenToRPCResponse().catch(error => {
            throw error;
        });
    }
    async registerToBus() {
        await this.startAndListen();
        let endpointInfo = {};
        endpointInfo = Array.from(this.endpointHandlers.keys()).reduce((accumulator, value) => {
            accumulator[value] = {
                method: value,
                namespace: this.namespace,
            };
            return accumulator;
        }, endpointInfo);
        const registerObj = {
            moduleName: this.namespace,
            eventsList: this.eventsList.map((event) => event),
            endpointInfo,
            options: {
                type: types_1.ChannelType.ChildProcess,
                socketPath: this._ipcClient.socketPaths.rpcServer,
            },
        };
        this._rpcClient
            .send([constants_1.IPC_EVENTS.REGISTER_CHANNEL, JSON.stringify(registerObj)])
            .catch(error => {
            throw error;
        });
    }
    subscribe(eventName, cb) {
        const event = new event_1.Event(eventName);
        this._subSocket.subscribe(eventName);
        this._emitter.on(event.key(), (notification) => setImmediate(cb, event_1.Event.fromJSONRPCNotification(notification).data));
    }
    unsubscribe(eventName, cb) {
        this._subSocket.unsubscribe(eventName);
        this._emitter.off(eventName, cb);
    }
    once(eventName, cb) {
        const event = new event_1.Event(eventName);
        this._subSocket.subscribe(eventName);
        this._emitter.once(event.key(), (notification) => {
            setImmediate(cb, event_1.Event.fromJSONRPCNotification(notification).data);
        });
    }
    publish(eventName, data) {
        const event = new event_1.Event(eventName, data);
        if (event.module !== this.namespace || !this.eventsList.includes(event.name)) {
            throw new Error(`Event "${eventName}" not registered in "${this.namespace}" module.`);
        }
        this._pubSocket
            .send([event.key(), JSON.stringify(event.toJSONRPCNotification())])
            .catch(error => {
            throw error;
        });
    }
    async invoke(req) {
        var _a;
        const request = new request_1.Request(this._getNextRequestId(), req.methodName, req.params);
        if (request.namespace === this.namespace) {
            const handler = this.endpointHandlers.get(request.name);
            if (!handler) {
                throw new Error(`The action '${request.name}' on module '${this.namespace}' does not exist.`);
            }
            return handler({
                getStore: () => {
                    throw new Error('getStore cannot be called on IPC channel');
                },
                getImmutableMethodContext: () => {
                    throw new Error('getImmutableMethodContext cannot be called on IPC channel');
                },
                header: req.context.header,
                params: (_a = request.params) !== null && _a !== void 0 ? _a : {},
                getOffchainStore: () => {
                    throw new Error('getOffchainStore cannot be called on IPC channel');
                },
                chainID: this._chainID,
                logger: this._logger,
            });
        }
        return new Promise((resolve, reject) => {
            this._rpcRequestIds.add(request.id);
            this._rpcClient
                .send(['invoke', 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);
                    return resolve(response.result);
                });
            })
                .catch(error => {
                throw error;
            });
        });
    }
    cleanup(_status, _message) {
        this._ipcClient.stop();
    }
    get _pubSocket() {
        return this._ipcClient.pubSocket;
    }
    get _subSocket() {
        return this._ipcClient.subSocket;
    }
    get _rpcServer() {
        return this._ipcClient.rpcServer;
    }
    get _rpcClient() {
        return this._ipcClient.rpcClient;
    }
}
exports.IPCChannel = IPCChannel;
//# sourceMappingURL=ipc_channel.js.map