mirror of
				https://github.com/Instadapp/Swap-Aggregator-Subgraph.git
				synced 2024-07-29 21:57:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			443 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			443 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| const events = require('events');
 | |
| const isFunction = require('lodash/isFunction');
 | |
| const jayson = require('../');
 | |
| const utils = require('../utils');
 | |
| 
 | |
| /**
 | |
|  *  Constructor for a Jayson Server
 | |
|  *  @class Server
 | |
|  *  @extends require('events').EventEmitter
 | |
|  *  @param {Object<String,Function>} [methods] Methods to add
 | |
|  *  @param {Object} [options]
 | |
|  *  @param {Array|Object} [options.params] Passed to Jayson.Method as an option when created
 | |
|  *  @param {Boolean} [options.useContext=false] Passed to Jayson.Method as an option when created
 | |
|  *  @param {Function} [options.reviver] Reviver function for JSON
 | |
|  *  @param {Function} [options.replacer] Replacer function for JSON
 | |
|  *  @param {Function} [options.methodConstructor] Methods will be made instances of this class
 | |
|  *  @param {String} [options.encoding="utf8"] Encoding to use
 | |
|  *  @param {Number} [options.version=2] JSON-RPC version to use (1|2)
 | |
|  *  @param {Function} [options.router] Function to use for routing methods
 | |
|  *  @property {Object} options A reference to the internal options object that can be modified directly
 | |
|  *  @property {Object} errorMessages Map of error code to error message pairs that will be used in server responses
 | |
|  *  @property {ServerHttp} http HTTP interface constructor
 | |
|  *  @property {ServerHttps} https HTTPS interface constructor
 | |
|  *  @property {ServerTcp} tcp TCP interface constructor
 | |
|  *  @property {ServerTls} tls TLS interface constructor
 | |
|  *  @property {Middleware} middleware Middleware generator function
 | |
|  *  @return {Server}
 | |
|  */
 | |
| const Server = function(methods, options) {
 | |
|   if(!(this instanceof Server)) {
 | |
|     return new Server(methods, options);
 | |
|   }
 | |
| 
 | |
|   const defaults = {
 | |
|     reviver: null,
 | |
|     replacer: null,
 | |
|     encoding: 'utf8',
 | |
|     version: 2,
 | |
|     useContext: false,
 | |
|     methodConstructor: jayson.Method,
 | |
|     router: function(method) {
 | |
|       return this.getMethod(method);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   this.options = utils.merge(defaults, options || {});
 | |
| 
 | |
|   // bind router to the server
 | |
|   this.options.router = this.options.router.bind(this);
 | |
|   
 | |
|   this._methods = {};
 | |
| 
 | |
|   // adds methods passed to constructor
 | |
|   this.methods(methods || {});
 | |
| 
 | |
|   // assigns interfaces to this instance
 | |
|   const interfaces = Server.interfaces;
 | |
|   for(let name in interfaces) {
 | |
|     this[name] = interfaces[name].bind(interfaces[name], this);
 | |
|   }
 | |
| 
 | |
|   // copies error messages for defined codes into this instance
 | |
|   this.errorMessages = {};
 | |
|   for(let handle in Server.errors) {
 | |
|     const code = Server.errors[handle];
 | |
|     this.errorMessages[code] = Server.errorMessages[code];
 | |
|   }
 | |
| 
 | |
| };
 | |
| require('util').inherits(Server, events.EventEmitter);
 | |
| 
 | |
| module.exports = Server;
 | |
| 
 | |
| /**
 | |
|  * Interfaces that will be automatically bound as properties of a Server instance
 | |
|  * @enum {Function}
 | |
|  * @static
 | |
|  */
 | |
| Server.interfaces = {
 | |
|   http: require('./http'),
 | |
|   https: require('./https'),
 | |
|   tcp: require('./tcp'),
 | |
|   tls: require('./tls'),
 | |
|   middleware: require('./middleware')
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * JSON-RPC specification errors that map to an integer code
 | |
|  * @enum {Number}
 | |
|  * @static
 | |
|  */
 | |
| Server.errors = {
 | |
|   PARSE_ERROR: -32700,
 | |
|   INVALID_REQUEST: -32600,
 | |
|   METHOD_NOT_FOUND: -32601,
 | |
|   INVALID_PARAMS: -32602,
 | |
|   INTERNAL_ERROR: -32603
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Error codes that map to an error message
 | |
|  * @enum {String}
 | |
|  * @static
 | |
|  */
 | |
| Server.errorMessages = {};
 | |
| Server.errorMessages[Server.errors.PARSE_ERROR] = 'Parse Error';
 | |
| Server.errorMessages[Server.errors.INVALID_REQUEST] = 'Invalid request';
 | |
| Server.errorMessages[Server.errors.METHOD_NOT_FOUND] = 'Method not found';
 | |
| Server.errorMessages[Server.errors.INVALID_PARAMS] = 'Invalid method parameter(s)';
 | |
| Server.errorMessages[Server.errors.INTERNAL_ERROR] = 'Internal error';
 | |
| 
 | |
| /**
 | |
|  *  Adds a single method to the server
 | |
|  *  @param {String} name Name of method to add
 | |
|  *  @param {Function|Client} definition Function or Client for a relayed method
 | |
|  *  @throws {TypeError} Invalid parameters
 | |
|  */
 | |
| Server.prototype.method = function(name, definition) {
 | |
|   const Method = this.options.methodConstructor;
 | |
| 
 | |
|   const isRelay = definition instanceof jayson.Client;
 | |
|   const isMethod = definition instanceof Method;
 | |
|   const isDefinitionFunction = isFunction(definition);
 | |
| 
 | |
|   // a valid method is either a function or a client (relayed method)
 | |
|   if(!isRelay && !isMethod && !isDefinitionFunction) {
 | |
|     throw new TypeError('method definition must be either a function, an instance of jayson.Client or an instance of jayson.Method');
 | |
|   }
 | |
| 
 | |
|   if(!name || typeof(name) !== 'string') {
 | |
|     throw new TypeError('"' + name + '" must be a non-zero length string');
 | |
|   }
 | |
| 
 | |
|   if(/^rpc\./.test(name)) {
 | |
|     throw new TypeError('"' + name + '" is a reserved method name');
 | |
|   }
 | |
| 
 | |
|   // make instance of jayson.Method
 | |
|   if(!isRelay && !isMethod) {
 | |
|     definition = new Method(definition, {
 | |
|       params: this.options.params,
 | |
|       useContext: this.options.useContext
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   this._methods[name] = definition;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  *  Adds a batch of methods to the server
 | |
|  *  @param {Object} methods Methods to add
 | |
|  */
 | |
| Server.prototype.methods = function(methods) {
 | |
|   methods = methods || {};
 | |
| 
 | |
|   for(let name in methods) {
 | |
|     this.method(name, methods[name]);
 | |
|   }
 | |
| 
 | |
| };
 | |
| 
 | |
| /**
 | |
|  *  Checks if a method is registered with the server
 | |
|  *  @param {String} name Name of method
 | |
|  *  @return {Boolean}
 | |
|  */
 | |
| Server.prototype.hasMethod = function(name) {
 | |
|   return name in this._methods;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  *  Removes a method from the server
 | |
|  *  @param {String} name
 | |
|  */
 | |
| Server.prototype.removeMethod = function(name) {
 | |
|   if(this.hasMethod(name)) {
 | |
|     delete this._methods[name];
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Gets a method from the server
 | |
|  * @param {String} name
 | |
|  * @return {Method}
 | |
|  */
 | |
| Server.prototype.getMethod = function(name) {
 | |
|   return this._methods[name];
 | |
| };
 | |
| 
 | |
| /**
 | |
|  *  Returns a JSON-RPC compatible error property
 | |
|  *  @param {Number} [code=-32603] Error code
 | |
|  *  @param {String} [message="Internal error"] Error message
 | |
|  *  @param {Object} [data] Additional data that should be provided
 | |
|  *  @return {Object}
 | |
|  */
 | |
| Server.prototype.error = function(code, message, data) {
 | |
|   if(typeof(code) !== 'number') {
 | |
|     code = Server.errors.INTERNAL_ERROR;
 | |
|   }
 | |
| 
 | |
|   if(typeof(message) !== 'string') {
 | |
|     message = this.errorMessages[code] || '';
 | |
|   }
 | |
| 
 | |
|   const error = { code: code, message: message };
 | |
|   if(typeof(data) !== 'undefined') {
 | |
|     error.data = data;
 | |
|   }
 | |
|   return error;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  *  Calls a method on the server
 | |
|  *  @param {Object|Array|String} request A JSON-RPC request object. Object for single request, Array for batches and String for automatic parsing (using the reviver option)
 | |
|  *  @param {Object} [context] Optional context object passed to methods
 | |
|  *  @param {Function} [originalCallback] Callback that receives one of two arguments: first is an error and the second a response
 | |
|  */
 | |
| Server.prototype.call = function(request, context, originalCallback) {
 | |
|   const self = this;
 | |
| 
 | |
|   if(typeof(context) === 'function') {
 | |
|     originalCallback = context;
 | |
|     context = {};
 | |
|   }
 | |
| 
 | |
|   if(typeof(context) === 'undefined') {
 | |
|     context = {};
 | |
|   }
 | |
| 
 | |
|   if(typeof(originalCallback) !== 'function') {
 | |
|     originalCallback = function() {};
 | |
|   }
 | |
| 
 | |
|   // compose the callback so that we may emit an event on every response
 | |
|   const callback = function(error, response) {
 | |
|     self.emit('response', request, response || error);
 | |
|     originalCallback.apply(null, arguments);
 | |
|   };
 | |
| 
 | |
|   maybeParse(request, this.options, function(err, request) {
 | |
|     let error = null; // JSON-RPC error
 | |
| 
 | |
|     if(err) {
 | |
|       error = self.error(Server.errors.PARSE_ERROR, null, err);
 | |
|       callback(utils.response(error, undefined, undefined, self.options.version));
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // is this a batch request?
 | |
|     if(utils.Request.isBatch(request)) {
 | |
| 
 | |
|       // batch requests not allowed for version 1
 | |
|       if(self.options.version === 1) {
 | |
|         error = self.error(Server.errors.INVALID_REQUEST);
 | |
|         callback(utils.response(error, undefined, undefined, self.options.version));
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // special case if empty batch request
 | |
|       if(!request.length) {
 | |
|         error = self.error(Server.errors.INVALID_REQUEST);
 | |
|         callback(utils.response(error, undefined, undefined, self.options.version));
 | |
|         return;
 | |
|       }
 | |
|       self._batch(request, context, callback);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     self.emit('request', request);
 | |
| 
 | |
|     // is the request valid?
 | |
|     if(!utils.Request.isValidRequest(request, self.options.version)) {
 | |
|       error = self.error(Server.errors.INVALID_REQUEST);
 | |
|       callback(utils.response(error, undefined, undefined, self.options.version));
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // from now on we are "notification-aware" and can deliberately ignore errors for such requests
 | |
|     const respond = function(error, result) {
 | |
|       if(utils.Request.isNotification(request)) {
 | |
|         callback();
 | |
|         return;
 | |
|       }
 | |
|       const response = utils.response(error, result, request.id, self.options.version);
 | |
|       if(response.error) {
 | |
|         callback(response);
 | |
|       } else {
 | |
|         callback(null, response);
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     const method = self._resolveRouter(request.method, request.params);
 | |
| 
 | |
|     // are we attempting to invoke a relayed method?
 | |
|     if(method instanceof jayson.Client) {
 | |
|       return method.request(request.method, request.params, request.id, function(error, response) {
 | |
|         if(utils.Request.isNotification(request)) {
 | |
|           callback();
 | |
|           return;
 | |
|         }
 | |
|         callback(error, response);
 | |
|       });
 | |
|     }
 | |
|     
 | |
|     // does the method exist?
 | |
|     if(!(method instanceof jayson.Method)) {
 | |
|       respond(self.error(Server.errors.METHOD_NOT_FOUND));
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // execute jayson.Method instance
 | |
|     method.execute(self, request.params, context, function(error, result) {
 | |
|     
 | |
|       if(utils.Response.isValidError(error, self.options.version)) {
 | |
|         respond(error);
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // got an invalid error
 | |
|       if(error) {
 | |
|         respond(self.error(Server.errors.INTERNAL_ERROR));
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       respond(null, result);
 | |
|     
 | |
|     });
 | |
| 
 | |
|   });
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Invoke the router
 | |
|  * @param {String} method Method to resolve
 | |
|  * @param {Array|Object} params Request params
 | |
|  * @return {Method}
 | |
|  */
 | |
| Server.prototype._resolveRouter = function(method, params) {
 | |
| 
 | |
|   let router = this.options.router;
 | |
| 
 | |
|   if(!isFunction(router)) {
 | |
|     router = function(method) {
 | |
|       return this.getMethod(method);
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   const resolved = router.call(this, method, params);
 | |
| 
 | |
|   // got a jayson.Method or a jayson.Client, return it
 | |
|   if((resolved instanceof jayson.Method) || (resolved instanceof jayson.Client)) {
 | |
|     return resolved;
 | |
|   }
 | |
| 
 | |
|   // got a regular function, make it an instance of jayson.Method
 | |
|   if(isFunction(resolved)) {
 | |
|     return new jayson.Method(resolved);
 | |
|   }
 | |
| 
 | |
| };
 | |
| 
 | |
| /**
 | |
|  *  Evaluates a batch request
 | |
|  *  @private
 | |
|  */
 | |
| Server.prototype._batch = function(requests, context, callback) {
 | |
|   const self = this;
 | |
|   
 | |
|   const responses = [];
 | |
| 
 | |
|   this.emit('batch', requests);
 | |
| 
 | |
|   /**
 | |
|    * @ignore
 | |
|    */
 | |
|   const maybeRespond = function() {
 | |
| 
 | |
|     // done when we have filled up all the responses with a truthy value
 | |
|     const isDone = responses.every(function(response) { return response !== null; });
 | |
|     if(isDone) {
 | |
| 
 | |
|       // filters away notifications
 | |
|       const filtered = responses.filter(function(res) {
 | |
|         return res !== true;
 | |
|       });
 | |
| 
 | |
|       // only notifications in request means empty response
 | |
|       if(!filtered.length) {
 | |
|         return callback();
 | |
|       }
 | |
|       callback(null, filtered);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * @ignore
 | |
|    */
 | |
|   const wrapper = function(request, index) {
 | |
|     responses[index] = null;
 | |
|     return function() {
 | |
|       if(utils.Request.isValidRequest(request, self.options.version)) {
 | |
|         self.call(request, context, function(error, response) {
 | |
|           responses[index] = error || response || true;
 | |
|           maybeRespond();
 | |
|         });
 | |
|       } else {
 | |
|         const error = self.error(Server.errors.INVALID_REQUEST);
 | |
|         responses[index] = utils.response(error, undefined, undefined, self.options.version);
 | |
|         maybeRespond();
 | |
|       }
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   const stack = requests.map(function(request, index) {
 | |
|     // ignore possibly nested requests
 | |
|     if(utils.Request.isBatch(request)) {
 | |
|       return null;
 | |
|     }
 | |
|     return wrapper(request, index);
 | |
|   });
 | |
| 
 | |
|   stack.forEach(function(method) {
 | |
|     if(typeof(method) === 'function') {
 | |
|       method();
 | |
|     }
 | |
|   });
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Parse "request" if it is a string, else just invoke callback
 | |
|  * @ignore
 | |
|  */
 | |
| function maybeParse(request, options, callback) {
 | |
|   if(typeof(request) === 'string') {
 | |
|     utils.JSON.parse(request, options, callback);
 | |
|   } else {
 | |
|     callback(null, request);
 | |
|   }
 | |
| }
 | 
