Swap-Aggregator-Subgraph/node_modules/jayson/lib/utils.js
Richa-iitr d211083153 Revert "Revert "added handler""
This reverts commit c36ee8c5ca.
2022-07-03 07:30:05 +05:30

449 lines
12 KiB
JavaScript

'use strict';
const compact = require('lodash/compact');
const extend = require('lodash/extend');
const isFunction = require('lodash/isFunction');
const once = require('lodash/once');
const partial = require('lodash/partial');
const JSONStream = require('JSONStream');
const JSONstringify = require('json-stringify-safe');
const uuid = require('uuid/v4');
const generateRequest = require('./generateRequest');
/** * @namespace */
const Utils = module.exports;
// same reference as other files use, for tidyness
const utils = Utils;
Utils.request = generateRequest;
/**
* Generates a JSON-RPC 1.0 or 2.0 response
* @param {Object} error Error member
* @param {Object} result Result member
* @param {String|Number|null} id Id of request
* @param {Number} version JSON-RPC version to use
* @return {Object} A JSON-RPC 1.0 or 2.0 response
*/
Utils.response = function(error, result, id, version) {
id = typeof(id) === 'undefined' || id === null ? null : id;
error = typeof(error) === 'undefined' || error === null ? null : error;
version = typeof(version) === 'undefined' || version === null ? 2 : version;
const response = (version === 2) ? { jsonrpc: "2.0", id: id } : { id: id };
// errors are always included in version 1
if(version === 1) {
response.error = error;
}
// one or the other with precedence for errors
if(error) {
response.error = error;
} else {
response.result = result;
}
return response;
};
/**
* Generates a random UUID
* @return {String}
*/
Utils.generateId = function() {
return uuid();
};
/**
* Merges properties of object b into object a
* @param {...Object} Objects to be merged
* @return {Object}
* @private
*/
Utils.merge = function() {
return extend.apply(null, arguments);
};
/**
* Parses an incoming stream for requests using JSONStream
* @param {Stream} stream
* @param {Object} options
* @param {Function} onRequest - Called once for stream errors and an unlimited amount of times for valid requests
*/
Utils.parseStream = function(stream, options, onRequest) {
const onError = once(onRequest);
const onSuccess = partial(onRequest, null);
const result = JSONStream.parse();
result.on('data', function(data) {
// apply reviver walk function to prevent stringify/parse again
if(isFunction(options.reviver)) {
data = Utils.walk({'': data}, '', options.reviver);
}
onSuccess(data);
});
result.on('error', onError);
stream.on('error', onError);
stream.pipe(result);
};
/**
* Helper to parse a stream and interpret it as JSON
* @param {Stream} stream Stream instance
* @param {Function} [options] Optional options for JSON.parse
* @param {Function} callback
*/
Utils.parseBody = function(stream, options, callback) {
callback = once(callback);
let data = '';
stream.setEncoding('utf8');
stream.on('data', function(str) {
data += str;
});
stream.on('error', function(err) {
callback(err);
});
stream.on('end', function() {
utils.JSON.parse(data, options, function(err, request) {
if(err) {
return callback(err);
}
callback(null, request);
});
});
};
/**
* Returns a HTTP request listener bound to the server in the argument.
* @param {http.Server} self Instance of a HTTP server
* @param {JaysonServer} server Instance of JaysonServer (typically jayson.Server)
* @return {Function}
* @private
*/
Utils.getHttpListener = function(self, server) {
return function(req, res) {
const options = self.options || {};
server.emit('http request', req);
// 405 method not allowed if not POST
if(!Utils.isMethod(req, 'POST')) {
return respond('Method Not Allowed', 405, {'allow': 'POST'});
}
// 415 unsupported media type if Content-Type is not correct
if(!Utils.isContentType(req, 'application/json')) {
return respond('Unsupported Media Type', 415);
}
Utils.parseBody(req, options, function(err, request) {
if(err) {
return respond(err, 400);
}
server.call(request, function(error, success) {
const response = error || success;
if(!response) {
// no response received at all, must be a notification
return respond('', 204);
}
utils.JSON.stringify(response, options, function(err, body) {
if(err) {
return respond(err, 500);
}
const headers = {
'content-length': Buffer.byteLength(body, options.encoding),
'content-type': 'application/json; charset=utf-8'
};
respond(body, 200, headers);
});
});
});
function respond(response, code, headers) {
const body = response instanceof Error ? response.toString() : response;
server.emit('http response', res, req);
res.writeHead(code, headers || {});
res.end(body);
}
};
};
/**
* Determines if a HTTP Request comes with a specific Content-Type
* @param {ServerRequest} request
* @param {String} type
* @return {Boolean}
* @private
*/
Utils.isContentType = function(request, type) {
request = request || {headers: {}};
const contentType = request.headers['content-type'] || '';
return RegExp(type, 'i').test(contentType);
};
/**
* Determines if a HTTP Request is of a specific method
* @param {ServerRequest} request
* @param {String} method
* @return {Boolean}
* @private
*/
Utils.isMethod = function(request, method) {
method = (method || '').toUpperCase();
return (request.method || '') === method;
};
/**
* Recursively walk an object and apply a function on its members
* @param {Object} holder The object to walk
* @param {String} key The key to look at
* @param {Function} fn The function to apply to members
* @return {Object}
*/
Utils.walk = function(holder, key, fn) {
let k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = Utils.walk(value, k, fn);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return fn.call(holder, key, value);
};
/** * @namespace */
Utils.JSON = {};
/**
* Parses a JSON string and then invokes the given callback
* @param {String} str The string to parse
* @param {Object} options Object with options, possibly holding a "reviver" function
* @param {Function} callback
*/
Utils.JSON.parse = function(str, options, callback) {
let reviver = null;
let obj = null;
options = options || {};
if(isFunction(options.reviver)) {
reviver = options.reviver;
}
try {
obj = JSON.parse.apply(JSON, compact([str, reviver]));
} catch(err) {
return callback(err);
}
callback(null, obj);
};
/**
* Stringifies JSON and then invokes the given callback
* @param {Object} obj The object to stringify
* @param {Object} options Object with options, possibly holding a "replacer" function
* @param {Function} callback
*/
Utils.JSON.stringify = function(obj, options, callback) {
let replacer = null;
let str = null;
options = options || {};
if(isFunction(options.replacer)) {
replacer = options.replacer;
}
try {
str = JSONstringify.apply(JSON, compact([obj, replacer]));
} catch(err) {
return callback(err);
}
callback(null, str);
};
/** * @namespace */
Utils.Request = {};
/**
* Determines if the passed request is a batch request
* @param {Object} request The request
* @return {Boolean}
*/
Utils.Request.isBatch = function(request) {
return Array.isArray(request);
};
/**
* Determines if the passed request is a notification request
* @param {Object} request The request
* @return {Boolean}
*/
Utils.Request.isNotification = function(request) {
return Boolean(
request
&& !Utils.Request.isBatch(request)
&& (typeof(request.id) === 'undefined'
|| request.id === null)
);
};
/**
* Determines if the passed request is a valid JSON-RPC 2.0 Request
* @param {Object} request The request
* @return {Boolean}
*/
Utils.Request.isValidVersionTwoRequest = function(request) {
return Boolean(
request
&& typeof(request) === 'object'
&& request.jsonrpc === '2.0'
&& typeof(request.method) === 'string'
&& (
typeof(request.params) === 'undefined'
|| Array.isArray(request.params)
|| (request.params && typeof(request.params) === 'object')
)
&& (
typeof(request.id) === 'undefined'
|| typeof(request.id) === 'string'
|| typeof(request.id) === 'number'
|| request.id === null
)
);
};
/**
* Determines if the passed request is a valid JSON-RPC 1.0 Request
* @param {Object} request The request
* @return {Boolean}
*/
Utils.Request.isValidVersionOneRequest = function(request) {
return Boolean(
request
&& typeof(request) === 'object'
&& typeof(request.method) === 'string'
&& Array.isArray(request.params)
&& typeof(request.id) !== 'undefined'
);
};
/**
* Determines if the passed request is a valid JSON-RPC Request
* @param {Object} request The request
* @param {Number} [version=2] JSON-RPC version 1 or 2
* @return {Boolean}
*/
Utils.Request.isValidRequest = function(request, version) {
version = version === 1 ? 1 : 2;
return Boolean(
request
&& (
(version === 1 && Utils.Request.isValidVersionOneRequest(request)) ||
(version === 2 && Utils.Request.isValidVersionTwoRequest(request))
)
);
};
/** * @namespace */
Utils.Response = {};
/**
* Determines if the passed error is a valid JSON-RPC error response
* @param {Object} error The error
* @param {Number} [version=2] JSON-RPC version 1 or 2
* @return {Boolean}
*/
Utils.Response.isValidError = function(error, version) {
version = version === 1 ? 1 : 2;
return Boolean(
version === 1 && (
typeof(error) !== 'undefined'
&& error !== null
)
|| version === 2 && (
error
&& typeof(error.code) === 'number'
&& parseInt(error.code, 10) === error.code
&& typeof(error.message) === 'string'
)
);
};
/**
* Determines if the passed object is a valid JSON-RPC response
* @param {Object} response The response
* @param {Number} [version=2] JSON-RPC version 1 or 2
* @return {Boolean}
*/
Utils.Response.isValidResponse = function(response, version) {
version = version === 1 ? 1 : 2;
return Boolean(
response !== null
&& typeof response === 'object'
&& (version === 2 && (
// check version
response.jsonrpc === '2.0'
&& (
// check id
response.id === null
|| typeof response.id === 'string'
|| typeof response.id === 'number'
)
&& (
// result and error do not exist at the same time
(typeof response.result === 'undefined' && typeof response.error !== 'undefined')
|| (typeof response.result !== 'undefined' && typeof response.error === 'undefined')
)
&& (
// check result
(typeof response.result !== 'undefined')
// check error object
|| (
response.error !== null
&& typeof response.error === 'object'
&& typeof response.error.code === 'number'
// check error.code is integer
&& ((response.error.code | 0) === response.error.code)
&& typeof response.error.message === 'string'
)
)
)
|| version === 1 && (
typeof response.id !== 'undefined'
&& (
// result and error relation (the other null if one is not)
(typeof response.result !== 'undefined' && response.error === null)
|| (typeof response.error !== 'undefined' && response.result === null)
)
))
);
};