UNPKG

delphirtl

Version:
451 lines 26.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CNoContent = exports.SParseError = exports.SMethodNotFound = exports.SInvalidRequest = exports.SInvalidParams = exports.SEntityParseFailed = exports.JSONRPCServer = exports.JSONRPCClient = exports.JSONRPC = exports.JSONRPCErrorException = exports.JSONRPCErrorCode = exports.isJSONRPCID = exports.createJSONRPCErrorResponse = exports.BaseJsonRpcServer = void 0; const json_rpc_2_0_1 = require("json-rpc-2.0"); Object.defineProperty(exports, "createJSONRPCErrorResponse", { enumerable: true, get: function () { return json_rpc_2_0_1.createJSONRPCErrorResponse; } }); Object.defineProperty(exports, "isJSONRPCID", { enumerable: true, get: function () { return json_rpc_2_0_1.isJSONRPCID; } }); Object.defineProperty(exports, "JSONRPC", { enumerable: true, get: function () { return json_rpc_2_0_1.JSONRPC; } }); Object.defineProperty(exports, "JSONRPCClient", { enumerable: true, get: function () { return json_rpc_2_0_1.JSONRPCClient; } }); Object.defineProperty(exports, "JSONRPCErrorCode", { enumerable: true, get: function () { return json_rpc_2_0_1.JSONRPCErrorCode; } }); Object.defineProperty(exports, "JSONRPCErrorException", { enumerable: true, get: function () { return json_rpc_2_0_1.JSONRPCErrorException; } }); Object.defineProperty(exports, "JSONRPCServer", { enumerable: true, get: function () { return json_rpc_2_0_1.JSONRPCServer; } }); const express_1 = __importDefault(require("express")); const http_terminator_1 = require("http-terminator"); const rtl_1 = require("./rtl"); /** * Invalid params * * @type {"Invalid params"} * @category Constants */ const SInvalidParams = "Invalid params"; exports.SInvalidParams = SInvalidParams; /** * Invalid request * * @type {"Invalid Request"} * @category Constants */ const SInvalidRequest = "Invalid Request"; exports.SInvalidRequest = SInvalidRequest; /** * Method not found * * @type {"Method not found"} * @category Constants */ const SMethodNotFound = "Method not found"; exports.SMethodNotFound = SMethodNotFound; /** * Parse error * * @type {"Parse error"} * @category Constants */ const SParseError = "Parse error"; exports.SParseError = SParseError; /** * Entity parse failed * * @type {"entity.parse.failed"} * @category Constants */ const SEntityParseFailed = "entity.parse.failed"; exports.SEntityParseFailed = SEntityParseFailed; /** * No content * * @type {204} * @category Constants */ const CNoContent = 204; exports.CNoContent = CNoContent; /** * Default port * * @type {8080} * @category Constants */ const CDefaultPort = 8080; /** * String * * @type {"string"} * @category Constants */ const CString = "string"; /** * Powered by * * @type {"x-powered-by"} * @category Constants */ const CXPoweredBy = "x-powered-by"; /** * This is a JSON RPC server that conforms to the JSON RPC 2.0 spec as documented at https://www.jsonrpc.org/specification * * @class BaseJsonRpcServer * @typedef {BaseJsonRpcServer} * @category JSON RPC */ class BaseJsonRpcServer { /** * Constructs a BaseJsonRpcServer server and returns it. * Listens on localhost:8080 by default, on both IPv4 and IPv6 * To listen only on IPv4, override onBeforeListening and call dns.setDefaultResultOrder('ipv4first'); * To listen only on IPv6, if both IPv4 and IPv6 is enabled, just pass "localhost" to the host parameter * * @param {string} [host="::"] The IP address/hostname to listen on * @param {number} [port=CDefaultPort] The port number to listen on * @returns {BaseJsonRpcServer} BaseJsonRpcServer */ constructor(host = "::", port = CDefaultPort) { this.mListeningHost = host; this.mListeningPort = port; this.mJsonRpcServer = this.initJsonRpcServer(); this.mExpressServer = this.initExpress(); this.afterConstruction(); } /** * Adds all necessary JSON RPC methods to this.mJsonRpcServer */ addRPCMethods() { const methods = this.implementedRPCMethods(); for (const method of methods) { const name = method.name; if (this.mJsonRpcServer.hasMethod(name)) { continue; } const boundMethod = method.bind(this); this.mJsonRpcServer.addMethod(name, boundMethod); } ; this.logListeningMethods(methods); } /** * Allows descendants to do something after class construction * * @returns {void} void */ afterConstruction() { } /** * Creates an Invalid Params JSON RPC Error exception, with the given message. * The caller must throw the return of this call. * * @returns {string} Invalid Params JSON RPC Error exception */ createInvalidParamsMessage(message, data) { const error = new json_rpc_2_0_1.JSONRPCErrorException(message, json_rpc_2_0_1.JSONRPCErrorCode.InvalidParams, data); return error; } /** * Creates an Invalid Params JSON RPC Error exception, with the message as Invalid Params. * The caller must throw the return of this call. * * @returns {string} Invalid Params JSON RPC Error exception */ createInvalidParams(data) { const error = this.createInvalidParamsMessage(SInvalidParams, data); return error; } /** * Creates an Invalid Request JSON RPC Error response. * * @returns {string} Invalid Params JSON RPC response */ createInvalidRequestResponse(request) { const response = (0, json_rpc_2_0_1.createJSONRPCErrorResponse)((0, json_rpc_2_0_1.isJSONRPCID)(request.id) ? request.id : null, json_rpc_2_0_1.JSONRPCErrorCode.InvalidRequest, SInvalidRequest); return response; } /** * Creates a Method not found JSON RPC Error response, with the message as Invalid Params. * The caller must throw the return of this call. * * @returns {string} Invalid Request JSON RPC Error exception */ createMethodNotFoundResponse(request) { const error = new json_rpc_2_0_1.JSONRPCErrorException(SMethodNotFound, json_rpc_2_0_1.JSONRPCErrorCode.MethodNotFound); return error; } /** * Creates an Invalid Request JSON RPC Error exception, with the message as Invalid Params. * The caller must throw the return of this call. * * @returns {string} Invalid Request JSON RPC Error exception */ createInvalidRequest(data) { const error = new json_rpc_2_0_1.JSONRPCErrorException(SInvalidRequest, json_rpc_2_0_1.JSONRPCErrorCode.InvalidRequest, data); return error; } /** * Calls this.onParseError * If onParseError doesn't handle the error, doParseError will send a JSON RPC Error response * if the error is a parsing error, or pass the error up to the next middleware to handle * * @returns {string} Invalid Request JSON RPC Error exception */ doParseError(err, req, res, next) { let handled = false; // creates a custom response with a send function // if the send function is called, sets the handled flag const response = { send: (body) => { handled = true; res.send(body); } }; this.mResponse = undefined; // prevents mResponse from being used by overridden onParseError methods this.onParseError(err, req, response); // if this is not set, we'll know that onParseError didn't call it, so we should handle it if (!handled) { // this entity.parse.failed constant is used/sent by Express if (err.type === SEntityParseFailed) { const obj = (0, json_rpc_2_0_1.createJSONRPCErrorResponse)(null, json_rpc_2_0_1.JSONRPCErrorCode.ParseError, SParseError); res.send(obj); } else { next(err); } } } /** * Handles a single or a batch JSON RPC request. * * @returns {void} */ async handleRequest(request, response) { try { const temp = request.body; // checks that the request body fits a JSON RPC call const isBatch = Array.isArray(temp); if (isBatch && (temp.length === 0)) { response.send(this.createInvalidRequestResponse(request)); return; } const jsonRpcResponse = await this.mJsonRpcServer.receive(request.body); if (jsonRpcResponse) { response.send(jsonRpcResponse); } else { switch (isBatch) { case false: { if (typeof request.body.method != CString) { response.send(this.createInvalidRequestResponse(request)); break; } response.sendStatus(CNoContent); break; } case true: { response.send({}); // empty response for a notification } } } } catch (e) { const error = (0, json_rpc_2_0_1.createJSONRPCErrorResponse)((0, json_rpc_2_0_1.isJSONRPCID)(request.body.id) ? request.body.id : null, json_rpc_2_0_1.JSONRPCErrorCode.InvalidRequest, SInvalidRequest); response.send(error); } } /** * Returns RPC methods implemented by the server * Override to return an array of RPC methods implemented. * @returns {SimpleJSONRPCMethod<void>[]} array of RPC methods implemented */ implementedRPCMethods() { return [this.echo]; } /** * Initializes the Express server and returns it * * @returns {Express} */ initExpress() { const result = (0, express_1.default)(); result.disable(CXPoweredBy); // removes the X-Powered-By header result.use(express_1.default.json()); this.initParseErrorHandler(result); result.post(this.listeningPath, async (request, response) => { this.mResponse = response; this.mRequest = request; try { await this.handleRequest(request, response); } finally { // @ts-ignore this.mRequest = undefined; // @ts-ignore this.mResponse = undefined; } }); return result; } /** * Initializes the JSON RPC server and returns it * * @returns {JSONRPCServer} */ initJsonRpcServer() { const result = new json_rpc_2_0_1.JSONRPCServer({ errorListener: this.JsonRpcErrorListener.bind(this) }); const checkRequest = (next, request, serverParams) => { return this.onBeforeDispatchRequest(next, request, serverParams); }; result.applyMiddleware(checkRequest); return result; } /** * Returns a JSON parse error during express.json parsing * * @returns {void} */ initParseErrorHandler(aExpress) { const parseErrorHandler = (err, req, res, next) => { this.doParseError(err, req, res, next); }; aExpress.use(parseErrorHandler); } /** * Error listener for JSON RPC, override to handle. * Called when an error occurred during the JSON RPC method call. * Do not throw an exception within this method * * @returns {void} */ JsonRpcErrorListener(message, data) { } /** * Starts listening for RPC requests */ async listen(port) { this.addRPCMethods(); switch (typeof port) { case "undefined": { port = this.mListeningPort; break; } } this.onBeforeListening(); this.mServer = this.mExpressServer.listen(port, this.mListeningHost, () => { this.log(`Listening on port: ${port}`); }); this.mHttpTerminator = (0, http_terminator_1.createHttpTerminator)({ server: this.mServer }); await this.waitForServerListening(); } /** * Returns the path on which to listen to requests for. Override to listen on another path. * * @returns {string} path on which to listen to requests for */ get listeningPath() { return "/json-rpc/"; } log(message, ...optionalParams) { console.log(message, ...optionalParams); } /** * Displays the JSON RPC methods being listened to */ logListeningMethods(methods) { this.log("Listening for these JSON RPC methods:"); for (const method of methods) { this.log(method.name); } } /** * Called before every valid JSON body is dispatched, this doesn't mean that the request is a valid JSON RPC call though. * If the request is valid, the method should call next(request, serverParams) and return its result. * If the request is invalid, it should throw an appropriate error. * * @returns {JSONRPCResponsePromise} */ onBeforeDispatchRequest(next, request, serverParams) { switch (typeof request.method) { case CString: { return next(request, serverParams); } default: { throw this.createInvalidRequestResponse(request); } } } /** * This is called before the JSON RPC server starts listening on the port * Override this, for example, to listen just on IPv4 by calling dns.setDefaultResultOrder('ipv4first'); */ onBeforeListening() { } /** * Called when there's a parsing error. The overridden method should create a JSON RPC Error response and * send it using response.send(err_response) * If the error is not handled, this class will return an error * * @returns {void} */ onParseError(err, request, response) { } /** * Creates an Invalid Request JSON RPC Error exception and sends it. * * @returns {} */ sendInvalidRequest(data) { const error = this.createInvalidRequest(data); this.mResponse?.send(error); } /** * Creates an Invalid Params JSON RPC Error exception and sends it. * * @returns {} */ sendInvalidParams(data) { const error = this.createInvalidParams(data); this.mResponse?.send(error); } /** * Stops the RPC server. This can be called by a RPC call or by the app itself */ async stop() { await this.stopListening(); this.log("Stop listening..."); } async stopListening() { await this.mHttpTerminator?.terminate(); this.mServer.close(); await this.waitForServerStopListening(); } ; /** * Waits for the server to starts listening before returning. * Override to disable this */ async waitForServerListening() { let listenAddr = null; do { listenAddr = this.mServer.address(); await (0, rtl_1.sleep)(100); } while (listenAddr === null); } async waitForServerStopListening() { let listenAddr = null; do { listenAddr = this.mServer.address(); if (listenAddr !== null) { await (0, rtl_1.sleep)(100); } } while (listenAddr !== null); } echo(params) { return params; } } exports.BaseJsonRpcServer = BaseJsonRpcServer; /* Chee-Wee Chua, Nov 2022, Singapore */ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQmFzZUpzb25ScGNTZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvQmFzZUpzb25ScGNTZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsK0NBSXNCO0FBeWZsQiwyR0E1ZkEseUNBQTBCLE9BNGZBO0FBQUUsNEZBNWZBLDBCQUFXLE9BNGZBO0FBQ0Usd0ZBN2ZBLHNCQUFPLE9BNmZBO0FBQUUsOEZBN2ZBLDRCQUFhLE9BNmZBO0FBQS9ELGlHQTdmaUUsK0JBQWdCLE9BNmZqRTtBQUFFLHNHQTdmaUUsb0NBQXFCLE9BNmZqRTtBQUNDLDhGQTdmcUMsNEJBQWEsT0E2ZnJDO0FBMWZ6RCxzREFBbUU7QUFFbkUscURBQXVFO0FBQ3ZFLCtCQUE4QjtBQWE5Qjs7Ozs7R0FLRztBQUNILE1BQU0sY0FBYyxHQUFHLGdCQUFnQixDQUFDO0FBdWVwQyx3Q0FBYztBQXJlbEI7Ozs7O0dBS0c7QUFDSCxNQUFNLGVBQWUsR0FBRyxpQkFBaUIsQ0FBQztBQWdldEMsMENBQWU7QUE5ZG5COzs7OztHQUtHO0FBQ0gsTUFBTSxlQUFlLEdBQUcsa0JBQWtCLENBQUM7QUF5ZHZDLDBDQUFlO0FBdmRuQjs7Ozs7R0FLRztBQUNILE1BQU0sV0FBVyxHQUFHLGFBQWEsQ0FBQztBQWtkOUIsa0NBQVc7QUFoZGY7Ozs7O0dBS0c7QUFDSCxNQUFNLGtCQUFrQixHQUFHLHFCQUFxQixDQUFDO0FBc2M3QyxnREFBa0I7QUFwY3RCOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDO0FBbWNuQixnQ0FBVTtBQWpjZDs7Ozs7R0FLRztBQUNILE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQztBQUUxQjs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQztBQUV6Qjs7Ozs7R0FLRztBQUNILE1BQU0sV0FBVyxHQUFHLGNBQWMsQ0FBQztBQUVuQzs7Ozs7O0dBTUc7QUFDSCxNQUFNLGlCQUFpQjtJQWtCbkI7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