mirror of
https://github.com/Instadapp/Swap-Aggregator-Subgraph.git
synced 2024-07-29 21:57:12 +00:00
449 lines
12 KiB
JavaScript
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)
|
|
)
|
|
))
|
|
);
|
|
};
|