Initial commit
This commit is contained in:
14
node_modules/jayson/lib/client/browser/index.d.ts
generated
vendored
Normal file
14
node_modules/jayson/lib/client/browser/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import * as jayson from '../../..';
|
||||
|
||||
type ClientBrowserCallServerFunctionCallback = (err?:Error | null, response?:string) => void;
|
||||
|
||||
type ClientBrowserCallServerFunction = (request:string, callback:ClientBrowserCallServerFunctionCallback) => void;
|
||||
|
||||
declare class ClientBrowser {
|
||||
constructor(callServer:ClientBrowserCallServerFunction, options:jayson.ClientOptions);
|
||||
request(method: string, params: jayson.RequestParamsLike, id?: string | null, callback?: jayson.JSONRPCCallbackType): jayson.JSONRPCRequest;
|
||||
request(method: string, params: jayson.RequestParamsLike, callback?: jayson.JSONRPCCallbackType): jayson.JSONRPCRequest;
|
||||
request(method: Array<jayson.JSONRPCRequestLike>, callback: jayson.JSONRPCCallbackTypeBatch): Array<jayson.JSONRPCRequest>;
|
||||
}
|
||||
|
||||
export = ClientBrowser;
|
||||
163
node_modules/jayson/lib/client/browser/index.js
generated
vendored
Normal file
163
node_modules/jayson/lib/client/browser/index.js
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
'use strict';
|
||||
|
||||
const uuid = require('uuid').v4;
|
||||
const generateRequest = require('../../generateRequest');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson Browser Client that does not depend any node.js core libraries
|
||||
* @class ClientBrowser
|
||||
* @param {Function} callServer Method that calls the server, receives the stringified request and a regular node-style callback
|
||||
* @param {Object} [options]
|
||||
* @param {Function} [options.reviver] Reviver function for JSON
|
||||
* @param {Function} [options.replacer] Replacer function for JSON
|
||||
* @param {Number} [options.version=2] JSON-RPC version to use (1|2)
|
||||
* @param {Function} [options.generator] Function to use for generating request IDs
|
||||
* @param {Boolean} [options.notificationIdNull=false] When true, version 2 requests will set id to null instead of omitting it
|
||||
* @return {ClientBrowser}
|
||||
*/
|
||||
const ClientBrowser = function(callServer, options) {
|
||||
if(!(this instanceof ClientBrowser)) {
|
||||
return new ClientBrowser(callServer, options);
|
||||
}
|
||||
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
this.options = {
|
||||
reviver: typeof options.reviver !== 'undefined' ? options.reviver : null,
|
||||
replacer: typeof options.replacer !== 'undefined' ? options.replacer : null,
|
||||
generator: typeof options.generator !== 'undefined' ? options.generator : function() { return uuid(); },
|
||||
version: typeof options.version !== 'undefined' ? options.version : 2,
|
||||
notificationIdNull: typeof options.notificationIdNull === 'boolean' ? options.notificationIdNull : false,
|
||||
};
|
||||
|
||||
this.callServer = callServer;
|
||||
};
|
||||
|
||||
module.exports = ClientBrowser;
|
||||
|
||||
/**
|
||||
* Creates a request and dispatches it if given a callback.
|
||||
* @param {String|Array} method A batch request if passed an Array, or a method name if passed a String
|
||||
* @param {Array|Object} [params] Parameters for the method
|
||||
* @param {String|Number} [id] Optional id. If undefined an id will be generated. If null it creates a notification request
|
||||
* @param {Function} [callback] Request callback. If specified, executes the request rather than only returning it.
|
||||
* @throws {TypeError} Invalid parameters
|
||||
* @return {Object} JSON-RPC 1.0 or 2.0 compatible request
|
||||
*/
|
||||
ClientBrowser.prototype.request = function(method, params, id, callback) {
|
||||
const self = this;
|
||||
let request = null;
|
||||
|
||||
// is this a batch request?
|
||||
const isBatch = Array.isArray(method) && typeof params === 'function';
|
||||
|
||||
if (this.options.version === 1 && isBatch) {
|
||||
throw new TypeError('JSON-RPC 1.0 does not support batching');
|
||||
}
|
||||
|
||||
// is this a raw request?
|
||||
const isRaw = !isBatch && method && typeof method === 'object' && typeof params === 'function';
|
||||
|
||||
if(isBatch || isRaw) {
|
||||
callback = params;
|
||||
request = method;
|
||||
} else {
|
||||
if(typeof id === 'function') {
|
||||
callback = id;
|
||||
// specifically undefined because "null" is a notification request
|
||||
id = undefined;
|
||||
}
|
||||
|
||||
const hasCallback = typeof callback === 'function';
|
||||
|
||||
try {
|
||||
request = generateRequest(method, params, id, {
|
||||
generator: this.options.generator,
|
||||
version: this.options.version,
|
||||
notificationIdNull: this.options.notificationIdNull,
|
||||
});
|
||||
} catch(err) {
|
||||
if(hasCallback) {
|
||||
return callback(err);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
// no callback means we should just return a raw request
|
||||
if(!hasCallback) {
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let message;
|
||||
try {
|
||||
message = JSON.stringify(request, this.options.replacer);
|
||||
} catch(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
this.callServer(message, function(err, response) {
|
||||
self._parseResponse(err, response, callback);
|
||||
});
|
||||
|
||||
// always return the raw request
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a response from a server
|
||||
* @param {Object} err Error to pass on that is unrelated to the actual response
|
||||
* @param {String} responseText JSON-RPC 1.0 or 2.0 response
|
||||
* @param {Function} callback Callback that will receive different arguments depending on the amount of parameters
|
||||
* @private
|
||||
*/
|
||||
ClientBrowser.prototype._parseResponse = function(err, responseText, callback) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!responseText) {
|
||||
// empty response text, assume that is correct because it could be a
|
||||
// notification which jayson does not give any body for
|
||||
return callback();
|
||||
}
|
||||
|
||||
let response;
|
||||
try {
|
||||
response = JSON.parse(responseText, this.options.reviver);
|
||||
} catch(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if(callback.length === 3) {
|
||||
// if callback length is 3, we split callback arguments on error and response
|
||||
|
||||
// is batch response?
|
||||
if(Array.isArray(response)) {
|
||||
|
||||
// neccesary to split strictly on validity according to spec here
|
||||
const isError = function(res) {
|
||||
return typeof res.error !== 'undefined';
|
||||
};
|
||||
|
||||
const isNotError = function (res) {
|
||||
return !isError(res);
|
||||
};
|
||||
|
||||
return callback(null, response.filter(isError), response.filter(isNotError));
|
||||
|
||||
} else {
|
||||
|
||||
// split regardless of validity
|
||||
return callback(null, response.error, response.result);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
callback(null, response);
|
||||
};
|
||||
116
node_modules/jayson/lib/client/http.js
generated
vendored
Normal file
116
node_modules/jayson/lib/client/http.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
'use strict';
|
||||
|
||||
const http = require('http');
|
||||
const url = require('url');
|
||||
const utils = require('../utils');
|
||||
const Client = require('../client');
|
||||
const { version } = require('../../package.json');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson HTTP Client
|
||||
* @class ClientHttp
|
||||
* @constructor
|
||||
* @extends Client
|
||||
* @param {Object|String} [options] String interpreted as a URL
|
||||
* @param {String} [options.encoding="utf8"] Encoding to use
|
||||
* @return {ClientHttp}
|
||||
*/
|
||||
const ClientHttp = function(options) {
|
||||
// accept first parameter as a url string
|
||||
if(typeof(options) === 'string') {
|
||||
options = url.parse(options);
|
||||
}
|
||||
|
||||
if(!(this instanceof ClientHttp)) {
|
||||
return new ClientHttp(options);
|
||||
}
|
||||
Client.call(this, options);
|
||||
|
||||
const defaults = utils.merge(this.options, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
this.options = utils.merge(defaults, options || {});
|
||||
};
|
||||
require('util').inherits(ClientHttp, Client);
|
||||
|
||||
module.exports = ClientHttp;
|
||||
|
||||
ClientHttp.prototype._request = function(request, callback) {
|
||||
const self = this;
|
||||
// copies options so object can be modified in this context
|
||||
const options = utils.merge({}, this.options);
|
||||
|
||||
utils.JSON.stringify(request, options, function(err, body) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
options.method = options.method || 'POST';
|
||||
|
||||
const headers = {
|
||||
'Content-Length': Buffer.byteLength(body, options.encoding),
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
Accept: 'application/json',
|
||||
'User-Agent': `jayson-${version}`,
|
||||
};
|
||||
|
||||
// let user override the headers
|
||||
options.headers = utils.merge(headers, options.headers || {});
|
||||
|
||||
const req = self._getRequestStream(options);
|
||||
|
||||
self.emit('http request', req);
|
||||
|
||||
req.on('response', function(res) {
|
||||
self.emit('http response', res, req);
|
||||
|
||||
res.setEncoding(options.encoding);
|
||||
|
||||
let data = '';
|
||||
res.on('data', function(chunk) { data += chunk; });
|
||||
|
||||
res.on('end', function() {
|
||||
|
||||
// assume we have an error
|
||||
if(res.statusCode < 200 || res.statusCode >= 300) {
|
||||
// assume the server gave the reason in the body
|
||||
const err = new Error(data);
|
||||
err.code = res.statusCode;
|
||||
callback(err);
|
||||
} else {
|
||||
// empty reply
|
||||
if(!data || typeof(data) !== 'string') {
|
||||
return callback();
|
||||
}
|
||||
utils.JSON.parse(data, options, callback);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// abort on timeout
|
||||
req.on('timeout', function() {
|
||||
req.abort(); // req.abort causes "error" event
|
||||
});
|
||||
|
||||
// abort on error
|
||||
req.on('error', function(err) {
|
||||
self.emit('http error', err);
|
||||
callback(err);
|
||||
req.abort();
|
||||
});
|
||||
|
||||
req.end(body);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a stream interface to a http server
|
||||
* @param {Object} options An options object
|
||||
* @return {require('http').ClientRequest}
|
||||
* @private
|
||||
*/
|
||||
ClientHttp.prototype._getRequestStream = function(options) {
|
||||
return http.request(options || {});
|
||||
};
|
||||
34
node_modules/jayson/lib/client/https.js
generated
vendored
Normal file
34
node_modules/jayson/lib/client/https.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const https = require('https');
|
||||
const ClientHttp = require('./http');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson HTTPS Client
|
||||
* @class ClientHttps
|
||||
* @constructor
|
||||
* @extends ClientHttp
|
||||
* @param {Object|String} [options] String interpreted as a URL
|
||||
* @param {String} [options.encoding="utf8"] Encoding to use
|
||||
* @return {ClientHttps}
|
||||
*/
|
||||
const ClientHttps = function(options) {
|
||||
if(!(this instanceof ClientHttps)) {
|
||||
return new ClientHttps(options);
|
||||
}
|
||||
// just proxy to constructor for ClientHttp
|
||||
ClientHttp.call(this, options);
|
||||
};
|
||||
require('util').inherits(ClientHttps, ClientHttp);
|
||||
|
||||
module.exports = ClientHttps;
|
||||
|
||||
/**
|
||||
* Gets a stream interface to a https server
|
||||
* @param {Object} options An options object
|
||||
* @return {require('https').ClientRequest}
|
||||
* @private
|
||||
*/
|
||||
ClientHttps.prototype._getRequestStream = function(options) {
|
||||
return https.request(options || {});
|
||||
};
|
||||
220
node_modules/jayson/lib/client/index.js
generated
vendored
Normal file
220
node_modules/jayson/lib/client/index.js
generated
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
'use strict';
|
||||
|
||||
const events = require('events');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson Client
|
||||
* @class Client
|
||||
* @extends require('events').EventEmitter
|
||||
* @param {Server} [server] An instance of Server (a object with a "call" method")
|
||||
* @param {Object} [options]
|
||||
* @param {Function} [options.reviver] Reviver function for JSON
|
||||
* @param {Function} [options.replacer] Replacer function for JSON
|
||||
* @param {Number} [options.version=2] JSON-RPC version to use (1|2)
|
||||
* @param {Boolean} [options.notificationIdNull=false] When true, version 2 requests will set id to null instead of omitting it
|
||||
* @param {Function} [options.generator] Function to use for generating request IDs
|
||||
* @return {Client}
|
||||
*/
|
||||
const Client = function(server, options) {
|
||||
if(arguments.length === 1 && utils.isPlainObject(server)) {
|
||||
options = server;
|
||||
server = null;
|
||||
}
|
||||
|
||||
if(!(this instanceof Client)) {
|
||||
return new Client(server, options);
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
reviver: null,
|
||||
replacer: null,
|
||||
generator: utils.generateId,
|
||||
version: 2,
|
||||
notificationIdNull: false,
|
||||
};
|
||||
|
||||
this.options = utils.merge(defaults, options || {});
|
||||
|
||||
if(server) {
|
||||
this.server = server;
|
||||
}
|
||||
};
|
||||
require('util').inherits(Client, events.EventEmitter);
|
||||
|
||||
module.exports = Client;
|
||||
|
||||
/**
|
||||
* HTTP client constructor
|
||||
* @type ClientHttp
|
||||
* @static
|
||||
*/
|
||||
Client.http = require('./http');
|
||||
|
||||
/**
|
||||
* HTTPS client constructor
|
||||
* @type ClientHttps
|
||||
* @static
|
||||
*/
|
||||
Client.https = require('./https');
|
||||
|
||||
/**
|
||||
* TCP client constructor
|
||||
* @type ClientTcp
|
||||
* @static
|
||||
*/
|
||||
Client.tcp = require('./tcp');
|
||||
|
||||
/**
|
||||
* TLS client constructor
|
||||
* @type ClientTls
|
||||
* @static
|
||||
*/
|
||||
Client.tls = require('./tls');
|
||||
|
||||
/**
|
||||
* Browser client constructor
|
||||
* @type ClientBrowser
|
||||
* @static
|
||||
*/
|
||||
Client.browser = require('./browser');
|
||||
|
||||
/**
|
||||
* Websocket client constructor
|
||||
* @type ClientWebsocket
|
||||
* @static
|
||||
*/
|
||||
Client.websocket = require('./websocket');
|
||||
|
||||
/**
|
||||
* Creates a request and dispatches it if given a callback.
|
||||
* @param {String|Array} method A batch request if passed an Array, or a method name if passed a String
|
||||
* @param {Array|Object} params Parameters for the method
|
||||
* @param {String|Number} [id] Optional id. If undefined an id will be generated. If null it creates a notification request
|
||||
* @param {Function} [callback] Request callback. If specified, executes the request rather than only returning it.
|
||||
* @throws {TypeError} Invalid parameters
|
||||
* @return {Object} JSON-RPC 1.0 or 2.0 compatible request
|
||||
*/
|
||||
Client.prototype.request = function(method, params, id, callback) {
|
||||
const self = this;
|
||||
let request = null;
|
||||
|
||||
// is this a batch request?
|
||||
const isBatch = Array.isArray(method) && typeof(params) === 'function';
|
||||
|
||||
if (this.options.version === 1 && isBatch) {
|
||||
throw new TypeError('JSON-RPC 1.0 does not support batching');
|
||||
}
|
||||
|
||||
// is this a raw request?
|
||||
const isRaw = !isBatch && method && typeof(method) === 'object' && typeof(params) === 'function';
|
||||
|
||||
if(isBatch || isRaw) {
|
||||
callback = params;
|
||||
request = method;
|
||||
} else {
|
||||
if(typeof(id) === 'function') {
|
||||
callback = id;
|
||||
// specifically undefined because "null" is a notification request
|
||||
id = undefined;
|
||||
}
|
||||
|
||||
const hasCallback = typeof(callback) === 'function';
|
||||
|
||||
try {
|
||||
request = utils.request(method, params, id, {
|
||||
generator: this.options.generator,
|
||||
version: this.options.version,
|
||||
notificationIdNull: this.options.notificationIdNull,
|
||||
});
|
||||
} catch(err) {
|
||||
if(hasCallback) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
// no callback means we should just return a raw request before sending
|
||||
if(!hasCallback) {
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.emit('request', request);
|
||||
|
||||
this._request(request, function(err, response) {
|
||||
self.emit('response', request, response);
|
||||
self._parseResponse(err, response, callback);
|
||||
});
|
||||
|
||||
// always return the raw request
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes a request on a directly bound server
|
||||
* @param {Object} request A JSON-RPC 1.0 or 2.0 request
|
||||
* @param {Function} callback Request callback that will receive the server response as the second argument
|
||||
* @private
|
||||
*/
|
||||
Client.prototype._request = function(request, callback) {
|
||||
const self = this;
|
||||
|
||||
// serializes the request as a JSON string so that we get a copy and can run the replacer as intended
|
||||
utils.JSON.stringify(request, this.options, function(err, message) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
self.server.call(message, function(error, success) {
|
||||
const response = error || success;
|
||||
callback(null, response);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a response from a server, taking care of sugaring
|
||||
* @param {Object} err Error to pass on that is unrelated to the actual response
|
||||
* @param {Object} response JSON-RPC 1.0 or 2.0 response
|
||||
* @param {Function} callback Callback that will receive different arguments depending on the amount of parameters
|
||||
* @private
|
||||
*/
|
||||
Client.prototype._parseResponse = function(err, response, callback) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if(!response || typeof(response) !== 'object') {
|
||||
return callback();
|
||||
}
|
||||
|
||||
if(callback.length === 3) {
|
||||
// if callback length is 3, we split callback arguments on error and response
|
||||
|
||||
// is batch response?
|
||||
if(Array.isArray(response)) {
|
||||
|
||||
// neccesary to split strictly on validity according to spec here
|
||||
const isError = function(res) { return typeof(res.error) !== 'undefined'; };
|
||||
const isNotError = function(res) { return !isError(res); };
|
||||
|
||||
return callback(null, response.filter(isError), response.filter(isNotError));
|
||||
|
||||
} else {
|
||||
|
||||
// split regardless of validity
|
||||
return callback(null, response.error, response.result);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return callback(null, response);
|
||||
|
||||
};
|
||||
90
node_modules/jayson/lib/client/tcp.js
generated
vendored
Normal file
90
node_modules/jayson/lib/client/tcp.js
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
'use strict';
|
||||
|
||||
const net = require('net');
|
||||
const utils = require('../utils');
|
||||
const Client = require('../client');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson TCP Client
|
||||
* @class ClientTcp
|
||||
* @constructor
|
||||
* @extends Client
|
||||
* @param {Object|String} [options] Object goes into options for net.connect, String goes into options.path. String option argument is NOT recommended.
|
||||
* @return {ClientTcp}
|
||||
*/
|
||||
const ClientTcp = function(options) {
|
||||
if(typeof(options) === 'string') {
|
||||
options = {path: options};
|
||||
}
|
||||
|
||||
if(!(this instanceof ClientTcp)) {
|
||||
return new ClientTcp(options);
|
||||
}
|
||||
Client.call(this, options);
|
||||
|
||||
const defaults = utils.merge(this.options, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
this.options = utils.merge(defaults, options || {});
|
||||
};
|
||||
require('util').inherits(ClientTcp, Client);
|
||||
|
||||
module.exports = ClientTcp;
|
||||
|
||||
ClientTcp.prototype._request = function(request, callback) {
|
||||
const self = this;
|
||||
|
||||
// copies options so object can be modified in this context
|
||||
const options = utils.merge({}, this.options);
|
||||
const delimiter = options.delimiter || '\n';
|
||||
|
||||
utils.JSON.stringify(request, options, function(err, body) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let handled = false;
|
||||
|
||||
const conn = net.connect(options, function() {
|
||||
|
||||
conn.setEncoding(options.encoding);
|
||||
|
||||
// wont get anything for notifications, just end here
|
||||
if(utils.Request.isNotification(request)) {
|
||||
|
||||
handled = true;
|
||||
conn.end(body + delimiter);
|
||||
callback();
|
||||
|
||||
} else {
|
||||
|
||||
utils.parseStream(conn, options, function(err, response) {
|
||||
handled = true;
|
||||
conn.end();
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, response);
|
||||
});
|
||||
|
||||
conn.write(body + delimiter);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
self.emit('tcp socket', conn);
|
||||
|
||||
conn.on('error', function(err) {
|
||||
self.emit('tcp error', err);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
conn.on('end', function() {
|
||||
if(!handled) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
89
node_modules/jayson/lib/client/tls.js
generated
vendored
Normal file
89
node_modules/jayson/lib/client/tls.js
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
|
||||
const tls = require('tls');
|
||||
const utils = require('../utils');
|
||||
const Client = require('../client');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson TLS-encrypted TCP Client
|
||||
* @class ClientTls
|
||||
* @constructor
|
||||
* @extends Client
|
||||
* @param {Object|String} [options] Object goes into options for tls.connect, String goes into options.path. String option argument is NOT recommended.
|
||||
* @return {ClientTls}
|
||||
*/
|
||||
const ClientTls = function(options) {
|
||||
if(typeof(options) === 'string') {
|
||||
options = {path: options};
|
||||
}
|
||||
|
||||
if(!(this instanceof ClientTls)) {
|
||||
return new ClientTls(options);
|
||||
}
|
||||
Client.call(this, options);
|
||||
|
||||
const defaults = utils.merge(this.options, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
this.options = utils.merge(defaults, options || {});
|
||||
};
|
||||
require('util').inherits(ClientTls, Client);
|
||||
|
||||
module.exports = ClientTls;
|
||||
|
||||
ClientTls.prototype._request = function(request, callback) {
|
||||
const self = this;
|
||||
|
||||
// copies options so object can be modified in this context
|
||||
const options = utils.merge({}, this.options);
|
||||
|
||||
utils.JSON.stringify(request, options, function(err, body) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let handled = false;
|
||||
|
||||
const conn = tls.connect(options, function() {
|
||||
|
||||
conn.setEncoding(options.encoding);
|
||||
|
||||
// wont get anything for notifications, just end here
|
||||
if(utils.Request.isNotification(request)) {
|
||||
|
||||
handled = true;
|
||||
conn.end(body + '\n');
|
||||
callback();
|
||||
|
||||
} else {
|
||||
|
||||
utils.parseStream(conn, options, function(err, response) {
|
||||
handled = true;
|
||||
conn.end();
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, response);
|
||||
});
|
||||
|
||||
conn.write(body + '\n');
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
self.emit('tcp socket', conn);
|
||||
|
||||
conn.on('error', function(err) {
|
||||
self.emit('tcp error', err);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
conn.on('end', function() {
|
||||
if(!handled) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
135
node_modules/jayson/lib/client/websocket.js
generated
vendored
Normal file
135
node_modules/jayson/lib/client/websocket.js
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
'use strict';
|
||||
|
||||
const WebSocket = require('isomorphic-ws');
|
||||
const utils = require('../utils');
|
||||
const delay = require('delay');
|
||||
const Client = require('../client');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson Websocket Client
|
||||
* @class ClientWebsocket
|
||||
* @constructor
|
||||
* @extends Client
|
||||
* @param {Object} [options]
|
||||
* @param {String} [options.url] When options.ws not provided this will be the URL to open the websocket to
|
||||
* @param {ws.WebSocket} [options.ws] When not provided will create a WebSocket instance with options.url
|
||||
* @param {Number} [options.timeout] Will wait this long in ms until callbacking with an error
|
||||
* @return {ClientWebsocket}
|
||||
*/
|
||||
const ClientWebsocket = function(options) {
|
||||
if(!(this instanceof ClientWebsocket)) {
|
||||
return new ClientWebsocket(options);
|
||||
}
|
||||
Client.call(this, options);
|
||||
|
||||
const defaults = utils.merge(this.options, {});
|
||||
this.options = utils.merge(defaults, options || {});
|
||||
|
||||
const self = this;
|
||||
|
||||
this.ws = this.options.ws || new WebSocket(this.options.url);
|
||||
this.outstandingRequests = [];
|
||||
this.handlers = {};
|
||||
|
||||
this.handlers.message = function (str) {
|
||||
utils.JSON.parse(str, self.options, function(err, response) {
|
||||
if (err) {
|
||||
// invalid JSON is ignored
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(response)) {
|
||||
|
||||
// we have a batch reply
|
||||
const matchingRequest = self.outstandingRequests.find(function ([request]) {
|
||||
if (Array.isArray(request)) {
|
||||
// a batch is considered matching if at least one response id matches one request id
|
||||
return response.some(function (resp) {
|
||||
if (utils.Response.isValidResponse(resp)) {
|
||||
return request.some(function (req) {
|
||||
return req.id === resp.id;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (matchingRequest) {
|
||||
const [ , resolve ] = matchingRequest;
|
||||
return resolve(response);
|
||||
}
|
||||
|
||||
} else if (utils.Response.isValidResponse(response)) {
|
||||
|
||||
const matchingRequest = self.outstandingRequests.find(function ([request]) {
|
||||
return !Array.isArray(request) && request.id === response.id;
|
||||
});
|
||||
|
||||
if (matchingRequest) {
|
||||
const [ , resolve ] = matchingRequest;
|
||||
return resolve(response);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
this.ws.on('message', this.handlers.message);
|
||||
};
|
||||
require('util').inherits(ClientWebsocket, Client);
|
||||
|
||||
module.exports = ClientWebsocket;
|
||||
|
||||
/**
|
||||
* @desc Removes all event listeners from Websocket instance which cancels all outstanding requests too
|
||||
*/
|
||||
ClientWebsocket.prototype.unlisten = function () {
|
||||
for (const eventName in this.handlers) {
|
||||
this.ws.off(eventName, this.handlers[eventName]);
|
||||
}
|
||||
};
|
||||
|
||||
ClientWebsocket.prototype._request = function(request, callback) {
|
||||
const self = this;
|
||||
const { ws, options } = this;
|
||||
|
||||
// we have to remove the object representing this request when the promise resolves/rejects
|
||||
let outstandingItem;
|
||||
|
||||
Promise.race([
|
||||
options.timeout > 0 ? delay(options.timeout).then(function () {
|
||||
throw new Error('timeout reached after ' + options.timeout + ' ms');
|
||||
}) : null,
|
||||
new Promise(function (resolve, reject) {
|
||||
utils.JSON.stringify(request, options, function(err, body) {
|
||||
if (err) {
|
||||
return resolve(err);
|
||||
}
|
||||
|
||||
ws.send(body);
|
||||
|
||||
if (utils.Request.isNotification(request)) {
|
||||
// notifications callback immediately since they don't have a reply
|
||||
return resolve();
|
||||
}
|
||||
|
||||
outstandingItem = [request, resolve, reject];
|
||||
self.outstandingRequests.push(outstandingItem);
|
||||
});
|
||||
}),
|
||||
].filter(v => v !== null)).then(function (result) {
|
||||
removeOutstandingRequest();
|
||||
callback(null, result);
|
||||
}).catch(function (err) {
|
||||
removeOutstandingRequest();
|
||||
callback(err);
|
||||
});
|
||||
|
||||
function removeOutstandingRequest () {
|
||||
if (!outstandingItem) {
|
||||
return;
|
||||
}
|
||||
self.outstandingRequests = self.outstandingRequests.filter(v => v !== outstandingItem);
|
||||
}
|
||||
};
|
||||
63
node_modules/jayson/lib/generateRequest.js
generated
vendored
Normal file
63
node_modules/jayson/lib/generateRequest.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
|
||||
const uuid = require('uuid').v4;
|
||||
|
||||
/**
|
||||
* Generates a JSON-RPC 1.0 or 2.0 request
|
||||
* @param {String} method Name of method to call
|
||||
* @param {Array|Object} params Array of parameters passed to the method as specified, or an object of parameter names and corresponding value
|
||||
* @param {String|Number|null} [id] Request ID can be a string, number, null for explicit notification or left out for automatic generation
|
||||
* @param {Object} [options]
|
||||
* @param {Number} [options.version=2] JSON-RPC version to use (1 or 2)
|
||||
* @param {Boolean} [options.notificationIdNull=false] When true, version 2 requests will set id to null instead of omitting it
|
||||
* @param {Function} [options.generator] Passed the request, and the options object and is expected to return a request ID
|
||||
* @throws {TypeError} If any of the parameters are invalid
|
||||
* @return {Object} A JSON-RPC 1.0 or 2.0 request
|
||||
* @memberOf Utils
|
||||
*/
|
||||
const generateRequest = function(method, params, id, options) {
|
||||
if(typeof method !== 'string') {
|
||||
throw new TypeError(method + ' must be a string');
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
// check valid version provided
|
||||
const version = typeof options.version === 'number' ? options.version : 2;
|
||||
if (version !== 1 && version !== 2) {
|
||||
throw new TypeError(version + ' must be 1 or 2');
|
||||
}
|
||||
|
||||
const request = {
|
||||
method: method
|
||||
};
|
||||
|
||||
if(version === 2) {
|
||||
request.jsonrpc = '2.0';
|
||||
}
|
||||
|
||||
if(params) {
|
||||
// params given, but invalid?
|
||||
if(typeof params !== 'object' && !Array.isArray(params)) {
|
||||
throw new TypeError(params + ' must be an object, array or omitted');
|
||||
}
|
||||
request.params = params;
|
||||
}
|
||||
|
||||
// if id was left out, generate one (null means explicit notification)
|
||||
if(typeof(id) === 'undefined') {
|
||||
const generator = typeof options.generator === 'function' ? options.generator : function() { return uuid(); };
|
||||
request.id = generator(request, options);
|
||||
} else if (version === 2 && id === null) {
|
||||
// we have a version 2 notification
|
||||
if (options.notificationIdNull) {
|
||||
request.id = null; // id will not be set at all unless option provided
|
||||
}
|
||||
} else {
|
||||
request.id = id;
|
||||
}
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
module.exports = generateRequest;
|
||||
31
node_modules/jayson/lib/index.js
generated
vendored
Normal file
31
node_modules/jayson/lib/index.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Namespace available as require('jayson')
|
||||
* @namespace Jayson
|
||||
*/
|
||||
const Jayson = module.exports;
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @type Client
|
||||
*/
|
||||
Jayson.Client = Jayson.client = require('./client');
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @type Server
|
||||
*/
|
||||
Jayson.Server = Jayson.server = require('./server');
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @type Utils
|
||||
*/
|
||||
Jayson.Utils = Jayson.utils = require('./utils');
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @type Method
|
||||
*/
|
||||
Jayson.Method = Jayson.method = require('./method');
|
||||
120
node_modules/jayson/lib/method.js
generated
vendored
Normal file
120
node_modules/jayson/lib/method.js
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
'use strict';
|
||||
|
||||
const utils = require('./utils');
|
||||
|
||||
/**
|
||||
* @summary Constructor for a Jayson Method
|
||||
* @class Method
|
||||
* @param {Function} [handler] Function to set as handler
|
||||
* @param {Object} [options]
|
||||
* @param {Function} [options.handler] Same as separate handler
|
||||
* @param {Boolean} [options.useContext=false] When true, the handler expects a context object
|
||||
* @param {Array|Object} [options.params] Defines params that the handler accepts
|
||||
*/
|
||||
const Method = function(handler, options) {
|
||||
|
||||
if(!(this instanceof Method)) {
|
||||
return new Method(handler, options);
|
||||
}
|
||||
|
||||
// only got passed options
|
||||
if(utils.isPlainObject(handler)) {
|
||||
options = handler;
|
||||
handler = null;
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
useContext: false,
|
||||
};
|
||||
|
||||
options = options || {};
|
||||
|
||||
this.options = utils.merge(defaults, options);
|
||||
this.handler = handler || options.handler;
|
||||
};
|
||||
|
||||
module.exports = Method;
|
||||
|
||||
/**
|
||||
* @summary Returns the handler function associated with this method
|
||||
* @return {Function}
|
||||
*/
|
||||
Method.prototype.getHandler = function() {
|
||||
return this.handler;
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Sets the handler function associated with this method
|
||||
* @param {Function} handler
|
||||
*/
|
||||
Method.prototype.setHandler = function(handler) {
|
||||
this.handler = handler;
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Prepare parameters for the method handler
|
||||
* @private
|
||||
*/
|
||||
Method.prototype._getHandlerParams = function(params) {
|
||||
const options = this.options;
|
||||
|
||||
const isObjectParams = !Array.isArray(params) && utils.isPlainObject(params) && params;
|
||||
const isArrayParams = Array.isArray(params);
|
||||
|
||||
switch(true) {
|
||||
|
||||
// handler always gets an array
|
||||
case options.params === Array:
|
||||
return isArrayParams ? params : utils.toArray(params);
|
||||
|
||||
// handler always gets an object
|
||||
case options.params === Object:
|
||||
return isObjectParams ? params : utils.toPlainObject(params);
|
||||
|
||||
// handler gets a list of defined properties that should always be set
|
||||
case Array.isArray(options.params): {
|
||||
const undefinedParams = Object.keys(options.params).reduce(function (out, index) {
|
||||
const key = options.params[index];
|
||||
out[key] = undefined;
|
||||
return out;
|
||||
}, {});
|
||||
return {...undefinedParams, ...utils.pick(params, Object.keys(params))};
|
||||
}
|
||||
|
||||
// handler gets a map of defined properties and their default values
|
||||
case utils.isPlainObject(options.params):
|
||||
return {...options.params, ...utils.pick(params, Object.keys(params))};
|
||||
|
||||
// give params as is
|
||||
default:
|
||||
return params;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Executes this method in the context of a server
|
||||
* @param {Server} server
|
||||
* @param {Array|Object} requestParams
|
||||
* @param {Object} [context]
|
||||
* @param {Function} callback
|
||||
*/
|
||||
Method.prototype.execute = function(server, requestParams, context, callback) {
|
||||
if(typeof(context) === 'function') {
|
||||
callback = context;
|
||||
context = {};
|
||||
}
|
||||
|
||||
if(!context) {
|
||||
context = {};
|
||||
}
|
||||
|
||||
// when useContext is true, the handler gets a context object every time
|
||||
const useContext = Boolean(this.options.useContext);
|
||||
const handler = this.getHandler();
|
||||
const params = this._getHandlerParams(requestParams);
|
||||
|
||||
const args = useContext ? [params, context, callback] : [params, callback];
|
||||
return handler.call(server, ...args);
|
||||
};
|
||||
26
node_modules/jayson/lib/server/http.js
generated
vendored
Normal file
26
node_modules/jayson/lib/server/http.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const http = require('http');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson HTTP server
|
||||
* @class ServerHttp
|
||||
* @extends require('http').Server
|
||||
* @param {Server} server Server instance
|
||||
* @param {Object} [options] Options for this instance
|
||||
* @return {ServerHttp}
|
||||
*/
|
||||
const ServerHttp = function(server, options) {
|
||||
if(!(this instanceof ServerHttp)) {
|
||||
return new ServerHttp(server, options);
|
||||
}
|
||||
|
||||
this.options = utils.merge(server.options, options || {});
|
||||
|
||||
const listener = utils.getHttpListener(this, server);
|
||||
http.Server.call(this, listener);
|
||||
};
|
||||
require('util').inherits(ServerHttp, http.Server);
|
||||
|
||||
module.exports = ServerHttp;
|
||||
26
node_modules/jayson/lib/server/https.js
generated
vendored
Normal file
26
node_modules/jayson/lib/server/https.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const https = require('https');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson HTTPS server
|
||||
* @class ServerHttps
|
||||
* @extends require('https').Server
|
||||
* @param {Server} server Server instance
|
||||
* @param {Object} [options] Options for this instance
|
||||
* @return {ServerHttps}
|
||||
*/
|
||||
const ServerHttps = function(server, options) {
|
||||
if(!(this instanceof ServerHttps)) {
|
||||
return new ServerHttps(server, options);
|
||||
}
|
||||
|
||||
this.options = utils.merge(server.options, options || {});
|
||||
|
||||
const listener = utils.getHttpListener(this, server);
|
||||
https.Server.call(this, this.options, listener);
|
||||
};
|
||||
require('util').inherits(ServerHttps, https.Server);
|
||||
|
||||
module.exports = ServerHttps;
|
||||
473
node_modules/jayson/lib/server/index.js
generated
vendored
Normal file
473
node_modules/jayson/lib/server/index.js
generated
vendored
Normal file
@@ -0,0 +1,473 @@
|
||||
'use strict';
|
||||
|
||||
const events = require('events');
|
||||
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 {Number} [options.maxBatchLength=Infinity] Maximum requests allowed in a batch
|
||||
* @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,
|
||||
maxBatchLength: Infinity,
|
||||
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'),
|
||||
websocket: require('./websocket'),
|
||||
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,
|
||||
INVALID_REQUEST_MAX_BATCH_LENGTH_EXCEEDED: -32099,
|
||||
};
|
||||
|
||||
/*
|
||||
* 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';
|
||||
Server.errorMessages[Server.errors.INVALID_REQUEST_MAX_BATCH_LENGTH_EXCEEDED] = 'Invalid request: Maximum batch length exceeded';
|
||||
|
||||
/**
|
||||
* 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 = typeof definition === 'function';
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// verify number of batch requests does not exceed maximum allowed length
|
||||
if (self.options.maxBatchLength >= 0 && request.length > self.options.maxBatchLength) {
|
||||
error = self.error(Server.errors.INVALID_REQUEST_MAX_BATCH_LENGTH_EXCEEDED);
|
||||
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);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls a method on the server returning a promise
|
||||
* @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
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
Server.prototype.callp = function (...args) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
return self.call(...args, function (err, response) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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(typeof router !== 'function') {
|
||||
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(typeof resolved === 'function') {
|
||||
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);
|
||||
}
|
||||
}
|
||||
75
node_modules/jayson/lib/server/middleware.js
generated
vendored
Normal file
75
node_modules/jayson/lib/server/middleware.js
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
'use strict';
|
||||
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Creates a Connect/Express compatible middleware bound to a Server
|
||||
* @class ServerMiddleware
|
||||
* @param {Server} server Server instance
|
||||
* @param {Object} [outerOptions] Specific options for the middleware
|
||||
* @return {Function}
|
||||
*/
|
||||
const Middleware = function(server, outerOptions) {
|
||||
return function(req, res, next) {
|
||||
const options = utils.merge(server.options, outerOptions || {});
|
||||
|
||||
// default options.end to true
|
||||
if(typeof(options.end) !== 'boolean') {
|
||||
options.end = true;
|
||||
}
|
||||
|
||||
// 405 method not allowed if not POST
|
||||
if(!utils.isMethod(req, 'POST')) {
|
||||
return error(405, { 'Allow': 'POST' });
|
||||
}
|
||||
|
||||
// 415 unsupported media type if Content-Type is not correct
|
||||
if(!utils.isContentType(req, 'application/json')) {
|
||||
return error(415);
|
||||
}
|
||||
|
||||
// body does not appear to be parsed, 500 server error
|
||||
if(!req.body || typeof(req.body) !== 'object') {
|
||||
return next(new Error('Request body must be parsed'));
|
||||
}
|
||||
|
||||
server.call(req.body, function(error, success) {
|
||||
const response = error || success;
|
||||
|
||||
utils.JSON.stringify(response, options, function(err, body) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
// empty response?
|
||||
if(body) {
|
||||
const headers = {
|
||||
'content-length': Buffer.byteLength(body, options.encoding),
|
||||
'content-type': 'application/json; charset=utf-8'
|
||||
};
|
||||
|
||||
res.writeHead(200, headers);
|
||||
res.write(body);
|
||||
} else {
|
||||
res.writeHead(204);
|
||||
}
|
||||
|
||||
// if end is false, next request instead of ending it
|
||||
if(options.end) {
|
||||
res.end();
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// ends the request with an error code
|
||||
function error(code, headers) {
|
||||
res.writeHead(code, headers || {});
|
||||
res.end();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Middleware;
|
||||
72
node_modules/jayson/lib/server/tcp.js
generated
vendored
Normal file
72
node_modules/jayson/lib/server/tcp.js
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
'use strict';
|
||||
|
||||
const net = require('net');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson TCP server
|
||||
* @class ServerTcp
|
||||
* @extends require('net').Server
|
||||
* @param {Server} server Server instance
|
||||
* @param {Object} [options] Options for this instance
|
||||
* @return {ServerTcp}
|
||||
*/
|
||||
const ServerTcp = function(server, options) {
|
||||
if(!(this instanceof ServerTcp)) {
|
||||
return new ServerTcp(server, options);
|
||||
}
|
||||
|
||||
this.options = utils.merge(server.options, options || {});
|
||||
|
||||
net.Server.call(this, getTcpListener(this, server));
|
||||
};
|
||||
require('util').inherits(ServerTcp, net.Server);
|
||||
|
||||
module.exports = ServerTcp;
|
||||
|
||||
/**
|
||||
* Returns a TCP connection listener bound to the server in the argument.
|
||||
* @param {Server} server Instance of JaysonServer
|
||||
* @param {net.Server} self Instance of net.Server
|
||||
* @return {Function}
|
||||
* @private
|
||||
* @ignore
|
||||
*/
|
||||
function getTcpListener(self, server) {
|
||||
return function(conn) {
|
||||
const options = self.options || {};
|
||||
|
||||
utils.parseStream(conn, options, function(err, request) {
|
||||
if(err) {
|
||||
return respondError(err);
|
||||
}
|
||||
|
||||
server.call(request, function(error, success) {
|
||||
const response = error || success;
|
||||
if(response) {
|
||||
utils.JSON.stringify(response, options, function(err, body) {
|
||||
if(err) {
|
||||
return respondError(err);
|
||||
}
|
||||
conn.write(body);
|
||||
});
|
||||
} else {
|
||||
// no response received at all, must be a notification
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ends the request with an error code
|
||||
function respondError(err) {
|
||||
const error = server.error(-32700, null, String(err));
|
||||
const response = utils.response(error, undefined, undefined, self.options.version);
|
||||
utils.JSON.stringify(response, options, function(err, body) {
|
||||
if(err) {
|
||||
body = ''; // we tried our best.
|
||||
}
|
||||
conn.end(body);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
72
node_modules/jayson/lib/server/tls.js
generated
vendored
Normal file
72
node_modules/jayson/lib/server/tls.js
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
'use strict';
|
||||
|
||||
const tls = require('tls');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson TLS-encrypted TCP server
|
||||
* @class ServerTls
|
||||
* @extends require('tls').Server
|
||||
* @param {Server} server Server instance
|
||||
* @param {Object} [options] Options for this instance
|
||||
* @return {ServerTls}
|
||||
*/
|
||||
const ServerTls = function(server, options) {
|
||||
if(!(this instanceof ServerTls)) {
|
||||
return new ServerTls(server, options);
|
||||
}
|
||||
|
||||
this.options = utils.merge(server.options, options || {});
|
||||
|
||||
tls.Server.call(this, this.options, getTlsListener(this, server));
|
||||
};
|
||||
require('util').inherits(ServerTls, tls.Server);
|
||||
|
||||
module.exports = ServerTls;
|
||||
|
||||
/**
|
||||
* Returns a TLS-encrypted TCP connection listener bound to the server in the argument.
|
||||
* @param {Server} server Instance of JaysonServer
|
||||
* @param {tls.Server} self Instance of tls.Server
|
||||
* @return {Function}
|
||||
* @private
|
||||
* @ignore
|
||||
*/
|
||||
function getTlsListener(self, server) {
|
||||
return function(conn) {
|
||||
const options = self.options || {};
|
||||
|
||||
utils.parseStream(conn, options, function(err, request) {
|
||||
if(err) {
|
||||
return respondError(err);
|
||||
}
|
||||
|
||||
server.call(request, function(error, success) {
|
||||
const response = error || success;
|
||||
if(response) {
|
||||
utils.JSON.stringify(response, options, function(err, body) {
|
||||
if(err) {
|
||||
return respondError(err);
|
||||
}
|
||||
conn.write(body);
|
||||
});
|
||||
} else {
|
||||
// no response received at all, must be a notification
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ends the request with an error code
|
||||
function respondError(err) {
|
||||
const error = server.error(-32700, null, String(err));
|
||||
const response = utils.response(error, undefined, undefined, self.options.version);
|
||||
utils.JSON.stringify(response, options, function(err, body) {
|
||||
if(err) {
|
||||
body = ''; // we tried our best.
|
||||
}
|
||||
conn.end(body);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
62
node_modules/jayson/lib/server/websocket.js
generated
vendored
Normal file
62
node_modules/jayson/lib/server/websocket.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
const WebSocket = require('isomorphic-ws');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson Websocket Server
|
||||
* @name ServerWebsocket
|
||||
* @param {Server} server Server instance
|
||||
* @param {Object} [options] Options for this instance
|
||||
* @param {ws.Websocket.Server} [options.wss] When provided will not create a new ws.WebSocket.Server but use this one
|
||||
* @return {ws.WebSocket.Server}
|
||||
*/
|
||||
const ServerWebsocket = function(server, options) {
|
||||
const jaysonOptions = utils.merge(server.options, options || {});
|
||||
const wss = options.wss || new WebSocket.Server(options);
|
||||
|
||||
wss.on('connection', onConnection);
|
||||
|
||||
function onConnection (ws) {
|
||||
// every message received on the socket is handled as a JSON-RPC message
|
||||
ws.on('message', function (buf) {
|
||||
const str = Buffer.isBuffer(buf) ? buf.toString('utf8') : buf;
|
||||
utils.JSON.parse(str, jaysonOptions, function(err, request) {
|
||||
if (err) {
|
||||
return respondError(err);
|
||||
}
|
||||
|
||||
server.call(request, function(error, success) {
|
||||
const response = error || success;
|
||||
if (response) {
|
||||
utils.JSON.stringify(response, jaysonOptions, function (err, str) {
|
||||
if (err) {
|
||||
return respondError(err);
|
||||
}
|
||||
ws.send(str);
|
||||
});
|
||||
} else {
|
||||
// no response received at all, must be a notification which we do nothing about
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// writes an error message to the client
|
||||
function respondError (err) {
|
||||
const error = server.error(-32700, null, String(err));
|
||||
const response = utils.response(error, undefined, undefined, jaysonOptions.version);
|
||||
utils.JSON.stringify(response, jaysonOptions, function(err, str) {
|
||||
if(err) {
|
||||
// not much to do here, we couldn't even respond with an error
|
||||
throw err;
|
||||
}
|
||||
ws.send(str);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return wss;
|
||||
};
|
||||
|
||||
module.exports = ServerWebsocket;
|
||||
511
node_modules/jayson/lib/utils.js
generated
vendored
Normal file
511
node_modules/jayson/lib/utils.js
generated
vendored
Normal file
@@ -0,0 +1,511 @@
|
||||
'use strict';
|
||||
|
||||
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;
|
||||
result = typeof(result) === 'undefined' || result === null ? null : result;
|
||||
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} args Objects to be merged
|
||||
* @return {Object}
|
||||
* @private
|
||||
*/
|
||||
Utils.merge = function(...args) {
|
||||
return args.reduce(function (out, obj) {
|
||||
return {...out, ...obj};
|
||||
}, {});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = Utils.once(onRequest);
|
||||
const onSuccess = (...args) => onRequest(null, ...args);
|
||||
|
||||
const result = JSONStream.parse();
|
||||
|
||||
result.on('data', function(data) {
|
||||
|
||||
// apply reviver walk function to prevent stringify/parse again
|
||||
if(typeof options.reviver === 'function') {
|
||||
data = Utils.walk({'': data}, '', options.reviver);
|
||||
}
|
||||
|
||||
onSuccess(data);
|
||||
});
|
||||
|
||||
result.on('error', onError);
|
||||
stream.on('error', onError);
|
||||
|
||||
stream.pipe(result);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function that can only be called once
|
||||
* @param {Function} fn
|
||||
* @return {Function}
|
||||
*/
|
||||
Utils.once = function (fn) {
|
||||
let called = false;
|
||||
let lastRetval;
|
||||
return function (...args) {
|
||||
if (called) return lastRetval;
|
||||
called = true;
|
||||
lastRetval = fn.call(this, ...args);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if obj is a plain object (not null)
|
||||
* @param {*} obj
|
||||
* @return {Boolean}
|
||||
*/
|
||||
Utils.isPlainObject = function (obj) {
|
||||
return typeof obj === 'object' && obj !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an object to an array
|
||||
* @param {*} obj
|
||||
* @return {Array}
|
||||
*/
|
||||
Utils.toArray = function (obj) {
|
||||
if (Array.isArray(obj)) return obj;
|
||||
if (Utils.isPlainObject(obj)) return Object.keys(obj).map(function (key) {
|
||||
return obj[key];
|
||||
});
|
||||
if (!obj) return [];
|
||||
return Array.prototype.slice.call(obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an object to a plain object
|
||||
* @param {*} obj
|
||||
* @return {Object}
|
||||
*/
|
||||
Utils.toPlainObject = function (value) {
|
||||
value = Object(value);
|
||||
const result = {};
|
||||
for (const key in value) {
|
||||
result[key] = value[key];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Picks keys from obj
|
||||
* @param {Object} obj
|
||||
* @param {String[]} keys
|
||||
* @return {Object}
|
||||
*/
|
||||
Utils.pick = function (obj, keys) {
|
||||
return keys.reduce(function (out, key) {
|
||||
out[key] = obj[key];
|
||||
return out;
|
||||
}, {});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = Utils.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(typeof options.reviver === 'function') {
|
||||
reviver = options.reviver;
|
||||
}
|
||||
|
||||
try {
|
||||
obj = JSON.parse.apply(JSON, [str, reviver].filter(v => v));
|
||||
} 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(typeof options.replacer === 'function') {
|
||||
replacer = options.replacer;
|
||||
}
|
||||
|
||||
try {
|
||||
str = JSONstringify.apply(JSON, [obj, replacer].filter(v => v));
|
||||
} 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)
|
||||
)
|
||||
))
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user