delphirtl
Version:
RTL functions from Delphi
451 lines • 26.7 kB
JavaScript
"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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