Skip to content
Browse files

Merge branch 'events'

  • Loading branch information...
2 parents 6fbdbcc + da93be2 commit b3896a94b89c2ffa94ee2adac4c0d7d9ebdf7332 @tedeh committed Sep 21, 2012
View
12 README.md
@@ -207,6 +207,14 @@ Every client supports these options:
* `reviver` -> Function to use as a JSON reviver
* `replacer` -> Function to use as a JSON replacer
+* `generator` -> Function to generate request ids with. If omitted, Jayson will just generate a "random" number like this: `Math.round(Math.random() * Math.pow(2, 24))`
+
+#### Client events
+
+A client will emit the following events (in addition to any special ones emitted by a specific interface):
+
+* `request` Emitted when a client is just about to dispatch a request. First argument is the request object.
+* `response` Emitted when a client has just received a reponse. First argument is the request object, second argument is the response as received.
##### Client.http
@@ -282,7 +290,7 @@ https.listen(443, function() {
Passing an instance of a client as a method to the server makes the server relay incoming requests to wherever the client is pointing to. This might be used to delegate computationally expensive functions into a separate fork/server or to abstract a cluster of servers behind a common interface.
-Public server in `examples/relay/server_public.js` listening on *:3000:
+Public server in `examples/relay/server_public.js` listening on `*:3000`:
```javascript
var jayson = require(__dirname + '/../..');
@@ -320,7 +328,7 @@ server.http().listen(3001, '127.0.0.1', function() {
Every request to `add` on the public server will now relay the request to the private server. See the client example in `examples/relay/client.js`.
-#### Events
+#### Server events
In addition to events that are specific to a certain interface, all servers will emit the following events:
View
21 lib/client/index.js → lib/client.js
@@ -1,5 +1,5 @@
-var Server = require('../server');
-var utils = require('../utils');
+var Server = require('./server');
+var utils = require('./utils');
var events = require('events');
/**
@@ -20,7 +20,8 @@ var Client = function(server, options) {
var defaults = {
reviver: null,
- replacer: null
+ replacer: null,
+ generator: utils.generateId
};
this.options = utils.merge(defaults, options || {});
@@ -36,14 +37,14 @@ module.exports = Client;
* @type HttpClient
* @static
*/
-Client.http = require('./http');
+Client.http = require('./client/http');
/**
* Fork client constructor
* @type ForkClient
* @static
*/
-Client.fork = require('./fork');
+Client.fork = require('./client/fork');
/**
* Creates a request and dispatches it if given a callback.
@@ -55,6 +56,8 @@ Client.fork = require('./fork');
* @api public
*/
Client.prototype.request = function(method, params, id, callback) {
+ var self = this;
+
// is this a batch request?
var isBatch = Array.isArray(method) && typeof(params) === 'function';
@@ -73,7 +76,9 @@ Client.prototype.request = function(method, params, id, callback) {
var hasCallback = typeof(callback) === 'function';
try {
- var request = utils.request(method, params, id);
+ var request = utils.request(method, params, id, {
+ generator: this.options.generator
+ });
} catch(err) {
if(hasCallback) return callback(err);
throw err;
@@ -83,9 +88,13 @@ Client.prototype.request = function(method, params, id, callback) {
if(!hasCallback) return request;
}
+ this.emit('request', request);
+
this._request(request, function(err, response) {
if(err) return callback(err);
+ self.emit('response', request, response);
+
if(!response) return callback();
var isBatchResponse = Array.isArray(response);
var isError = function(res) { return typeof(res.error) !== 'undefined'; };
View
2 lib/client/fork.js
@@ -1,5 +1,5 @@
var utils = require('../utils');
-var Client = require('./');
+var Client = require('../client');
/**
* Constructor for a Jayson HTTP Client
View
2 lib/client/http.js
@@ -1,6 +1,6 @@
var http = require('http');
var utils = require('../utils');
-var Client = require('./index');
+var Client = require('../client');
/**
* Constructor for a Jayson HTTP Client
View
12 lib/client/jquery.js
@@ -15,6 +15,7 @@
dataType: 'json',
type: 'POST',
processData: false,
+ generator: generateId,
headers: { 'content-type': 'application/json' }
};
@@ -51,7 +52,7 @@
$.fn.jayson = function(options) {
options = options || {};
var client = new JqueryClient(options);
- client.request(options.method, options.params, options.id, function(err, data) {
+ client.request(options.method, options.params, options.id, {generator: options.generator}, function(err, data) {
if(err) {
if($.isFunction(options.error)) {
return options.error.apply(options.error, err);
@@ -77,7 +78,7 @@
* Generates a JSON-RPC 2.0 request
* @see Utils.request
*/
- function generateRequest(method, params, id) {
+ function generateRequest(method, params, id, options) {
if($.type(method) !== 'string') {
throw new TypeError(method + ' must be a string');
}
@@ -86,15 +87,18 @@
throw new TypeError(params + ' must be an object or an array');
}
+ options = options || {};
+
var request = {
jsonrpc: "2.0",
params: params,
method: method
- }
+ };
// if id was left out, generate one (null means explicit notification)
if($.type(id) === 'undefined') {
- request.id = generateId();
+ var generator = typeof(options.generator) === 'function' ? options.generator : generateId;
+ request.id = generator(request);
} else {
request.id = id;
}
View
28 lib/server/index.js → lib/server.js
@@ -1,6 +1,6 @@
-var jayson = require('../');
+var jayson = require('./');
var events = require('events');
-var utils = require('../utils');
+var utils = require('./utils');
/**
* Constructor for a Jayson server
@@ -56,17 +56,17 @@ module.exports = Server;
* @static
*/
Server.interfaces = {
- http: require('./http'),
- https: require('./https'),
- middleware: require('./middleware')
+ http: require('./server/http'),
+ https: require('./server/https'),
+ middleware: require('./server/middleware')
};
/**
* Fork server constructor
* @type ForkServer
* @static
*/
-Server.fork = require('./fork');
+Server.fork = require('./server/fork');
/**
* JSON-RPC specification errors that map to a integer code
@@ -170,10 +170,19 @@ Server.prototype.error = function(code, message, data) {
* @return {void}
* @api public
*/
-Server.prototype.call = function(request, callback) {
+Server.prototype.call = function(request, originalCallback) {
var self = this;
- if(typeof(callback) !== 'function') callback = function() {};
+ this.emit('request', request);
+
+ if(typeof(originalCallback) !== 'function') originalCallback = function() {};
+
+ // compose the callback so that we may emit an event on every response
+ var callback = function(error, response) {
+ var emit = self.emit.bind(self, 'response');
+ self.emit('response', request, response || error);
+ originalCallback.apply(null, arguments);
+ };
// if passed a string, assume that it should be parsed
if(typeof(request) === 'string') {
@@ -199,13 +208,10 @@ Server.prototype.call = function(request, callback) {
return callback(utils.response(this.error(Server.errors.INVALID_REQUEST)));
}
- this.emit('request', request);
-
// from now on we are "notification"-aware and can deliberately ignore errors for such requests
var respond = function(error, result) {
if(isNotification(request)) return callback();
var response = utils.response(error, result, request.id);
- self.emit('response', request, response);
if(response.error) callback(response);
else callback(null, response);
};
View
2 lib/server/fork.js
@@ -1,7 +1,7 @@
var utils = require('../utils');
var fork = require('child_process').fork;
var path = require('path');
-var JaysonServer = require('./index');
+var JaysonServer = require('../server');
/**
* Constructor for a Jayson JSON-RPC Fork Server
View
12 lib/utils.js
@@ -8,11 +8,12 @@ var Utils = module.exports;
* @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] Optional name => value pairs of settings
* @throws {TypeError} If any of the parameters are invalid
* @return {Object} A JSON-RPC 2.0 request
* @api public
*/
-Utils.request = function(method, params, id) {
+Utils.request = function(method, params, id, options) {
if(typeof(method) !== 'string') {
throw new TypeError(method + ' must be a string');
}
@@ -21,6 +22,8 @@ Utils.request = function(method, params, id) {
throw new TypeError(params + ' must be an object or an array');
}
+ options = options || {};
+
var request = {
jsonrpc: "2.0",
params: params,
@@ -29,7 +32,8 @@ Utils.request = function(method, params, id) {
// if id was left out, generate one (null means explicit notification)
if(typeof(id) === 'undefined') {
- request.id = Utils.generateId();
+ var generator = typeof(options.generator) === 'function' ? options.generator : Utils.generateId;
+ request.id = generator(request);
} else {
request.id = id;
}
@@ -46,7 +50,7 @@ Utils.request = function(method, params, id) {
* @api public
*/
Utils.response = function(error, result, id) {
- id = id || null;
+ id = typeof(id) === 'undefined' || id === null ? null : id;
var response = { jsonrpc: "2.0", id: id };
// one or the other with precedence for errors
if(error) response.error = error;
@@ -109,7 +113,7 @@ Utils.parseBody = function(req, reviver, callback) {
/**
* Wraps a server instance around a HTTP request listener (used by the HTTP and HTTPS server modules)
* @param {JaysonServer} server Instance of JaysonServer (typically jayson.Server.http or jayson.Server.https)
- * @param {Object} [options] Optional name => value pair of settings
+ * @param {Object} [options] Optional name => value pairs of settings
* @return {Function}
* @api private
*/
View
46 test/client.test.js
@@ -40,7 +40,7 @@ describe('jayson client instance', function() {
it('should support reviving and replacing', support.clientReviveReplace(client));
- it('should return the response as received if given a two-param callback', function(done) {
+ it('should return the response as received if given a callback with arity 2', function(done) {
var a = 11, b = 9;
client.request('add', [a, b], function(err, response) {
arguments.length.should.equal(2);
@@ -52,6 +52,50 @@ describe('jayson client instance', function() {
});
});
+ it('should support specifying a request id generator', function(done) {
+ var ordinal = 0, a = 9, b = 2;
+ client.options.generator = function(request) { return ordinal++; };
+ client.request('add', [a, b], function(err, response) {
+ should.not.exist(err);
+ should.exist(response);
+ response.should.have.property('result', a + b);
+ response.should.have.property('id', 0);
+ ordinal.should.equal(1);
+ delete client.options.generator;
+ done();
+ });
+ });
+
+ it('should emit "request" when a request is dispatched', function(done) {
+ var a = 6, b = 9, hasFired = false;
+ client.once('request', function(request) {
+ hasFired = true;
+ should.exist(request);
+ request.params.should.include(a).and.include(b).and.have.lengthOf(2);
+ });
+ client.request('add', [a, b], function(err) {
+ if(err) return done(err);
+ hasFired.should.be.ok;
+ done();
+ });
+ });
+
+ it('should emit "response" when a response is received', function(done) {
+ var a = 8, b = 10, hasFired = false;
+ client.once('response', function(request, response) {
+ hasFired = true;
+ should.exist(request);
+ request.params.should.include(a).and.include(b).and.have.lengthOf(2);
+ should.exist(response);
+ response.should.have.property('result', a + b);
+ });
+ client.request('add', [a, b], function(err) {
+ if(err) return done(err);
+ hasFired.should.be.ok;
+ done();
+ });
+ });
+
it('should be able to execute a notification request', function(done) {
var a = 3, b = 4;
client.request('add', [a, b], null, function(err, response) {

0 comments on commit b3896a9

Please sign in to comment.
Something went wrong with that request. Please try again.