Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed README, test cases and examples.

  • Loading branch information...
commit 4be3cb0270e3cebb08defa794cda1c892f66b528 1 parent 1dca507
@justmoon justmoon authored
View
14 README.md
@@ -16,12 +16,14 @@ Firing up an efficient JSON-RPC server becomes extremely simple:
``` javascript
var rpc = require('jsonrpc2');
-function add(first, second) {
- return first + second;
+var server = new rpc.Server();
+
+function add(args, opt, callback) {
+ callback(null, args[0] + args[1]);
}
-rpc.expose('add', add);
+server.expose('add', add);
-rpc.listen(8000, 'localhost');
+server.listen(8000, 'localhost');
```
And creating a client to speak to that server is easy too:
@@ -30,9 +32,9 @@ And creating a client to speak to that server is easy too:
var rpc = require('jsonrpc2');
var sys = require('sys');
-var client = rpc.getClient(8000, 'localhost');
+var client = new rpc.Client(8000, 'localhost');
-client.call('add', [1, 2], function(result) {
+client.call('add', [1, 2], function(err, result) {
sys.puts('1 + 2 = ' + result);
});
```
View
35 examples/client.js
@@ -1,32 +1,35 @@
var sys = require('sys');
var rpc = require('../src/jsonrpc');
-var client = rpc.getClient(8000, 'localhost');
+var client = new rpc.Client(8088, 'localhost');
-client.call('add', [1, 2], function(result) {
- sys.puts(' 1 + 2 = ' + result);
+client.call('add', [1, 2], function (err, result) {
+ sys.puts(' 1 + 2 = ' + result);
});
-client.call('multiply', [199, 2], function(result) {
- sys.puts('199 * 2 = ' + result);
+client.call('multiply', [199, 2], function (err, result) {
+ sys.puts('199 * 2 = ' + result);
});
// Accessing modules is as simple as dot-prefixing.
-client.call('math.power', [3, 3], function(result) {
- sys.puts(' 3 ^ 3 = ' + result);
+client.call('math.power', [3, 3], function (err, result) {
+ sys.puts(' 3 ^ 3 = ' + result);
});
-// Call simply returns a promise, so we can add callbacks or errbacks at will.
-var promise = client.call('add', [1, 1]);
-promise.addCallback(function(result) {
- sys.puts(' 1 + 1 = ' + result + ', dummy!');
+// We can handle errors the same way as anywhere else in Node
+client.call('add', [1, 1], function (err, result) {
+ if (err) {
+ sys.puts('RPC Error: '+ sys.inspect(err));
+ return;
+ }
+ sys.puts(' 1 + 1 = ' + result + ', dummy!');
});
/* These calls should each take 1.5 seconds to complete. */
-client.call('delayed.add', [1, 1, 1500], function(result) {
- sys.puts(result);
+client.call('delayed.add', [1, 1, 1500], function (err, result) {
+ sys.puts(result);
});
-client.call('delayed.echo', ['Echo.', 1500], function(result) {
- sys.puts(result);
-});
+client.call('delayed.echo', ['Echo.', 1500], function (err, result) {
+ sys.puts(result);
+});
View
65 examples/server.js
@@ -1,46 +1,53 @@
var rpc = require('../src/jsonrpc');
+var server = new rpc.Server();
+
/* Create two simple functions */
-function add(first, second) {
- return first + second;
+function add(args, opts, callback) {
+ callback(null, args[0]+args[1]);
}
-function multiply(first, second) {
- return first * second;
+function multiply(args, opts, callback) {
+ callback(null, args[0]*args[1]);
}
/* Expose those methods */
-rpc.expose('add', add);
-rpc.expose('multiply', multiply);
+server.expose('add', add);
+server.expose('multiply', multiply);
/* We can expose entire modules easily */
var math = {
- power: function(first, second) { return Math.pow(first, second); },
- sqrt: function(num) { return Math.sqrt(num); }
+ power: function(args, opts, callback) {
+ callback(null, Math.pow(args[0], args[1]));
+ },
+ sqrt: function(args, opts, callback) {
+ callback(null, Math.sqrt(args[0]));
+ }
}
-rpc.exposeModule('math', math);
+server.exposeModule('math', math);
-/* Listen on port 8000 */
-rpc.listen(8000, 'localhost');
+/* Listen on port 8088 */
+server.listen(8088, 'localhost');
-/* By returning a promise, we can delay our response indefinitely, leaving the
- request hanging until the promise emits success. */
+/* By using a callback, we can delay our response indefinitely, leaving the
+ request hanging until the callback emits success. */
var delayed = {
- echo: function(data, delay) {
- var promise = new process.Promise();
- setTimeout(function() {
- promise.emitSuccess(data);
- }, delay);
- return promise;
- },
-
- add: function(first, second, delay) {
- var promise = new process.Promise();
- setTimeout(function() {
- promise.emitSuccess(first + second);
- }, delay);
- return promise;
- }
+ echo: function(args, opts, callback) {
+ var data = args[0];
+ var delay = args[1];
+ setTimeout(function() {
+ callback(null, data);
+ }, delay);
+ },
+
+ add: function(args, opts, callback) {
+ var first = args[0];
+ var second = args[1];
+ var delay = args[2];
+ setTimeout(function() {
+ callback(null, first + second);
+ }, delay);
+ }
}
-rpc.exposeModule('delayed', delayed);
+server.exposeModule('delayed', delayed);
View
2  package.json
@@ -1,6 +1,6 @@
{
"name": "jsonrpc2",
- "version": "0.0.5",
+ "version": "0.0.6",
"description": "JSON-RPC server and client library",
"main": "./src/jsonrpc",
"keywords": [
View
10 src/jsonrpc.js
@@ -156,7 +156,7 @@ Server.prototype.handlePOST = function(req, res) {
var self = this;
var handle = function (buf) {
var decoded = JSON.parse(buf);
-
+
// Check for the required fields, and if they aren't there, then
// dispatch to the handleInvalidRequest function.
if(!(decoded.method && decoded.params && decoded.id)) {
@@ -166,7 +166,7 @@ Server.prototype.handlePOST = function(req, res) {
if(!self.functions.hasOwnProperty(decoded.method)) {
return Server.handleInvalidRequest(req, res);
}
-
+
// Build our success handler
var onSuccess = function(funcResp) {
Server.trace('-->', 'response (id ' + decoded.id + '): ' +
@@ -182,7 +182,7 @@ Server.prototype.handlePOST = function(req, res) {
res.write(encoded);
res.end();
};
-
+
// Build our failure handler (note that error must not be null)
var onFailure = function(failure) {
Server.trace('-->', 'failure: ' + JSON.stringify(failure));
@@ -196,10 +196,10 @@ Server.prototype.handlePOST = function(req, res) {
res.write(encoded);
res.end();
};
-
+
Server.trace('<--', 'request (id ' + decoded.id + '): ' +
decoded.method + '(' + decoded.params.join(', ') + ')');
-
+
// Try to call the method, but intercept errors and call our
// onFailure handler.
var method = self.functions[decoded.method];
View
212 test/jsonrpc-test.js
@@ -1,156 +1,158 @@
-process.mixin(GLOBAL, require('./test'));
+require('./test').extend(global);
var sys = require('sys');
-var jsonrpc = require('../src/jsonrpc');
+var rpc = require('../src/jsonrpc');
+
+var server = new rpc.Server();
// MOCK REQUEST/RESPONSE OBJECTS
var MockRequest = function(method) {
- this.method = method;
- process.EventEmitter.call(this);
+ this.method = method;
+ process.EventEmitter.call(this);
};
sys.inherits(MockRequest, process.EventEmitter);
var MockResponse = function() {
- process.EventEmitter.call(this);
- this.sendHeader = function(httpCode, httpHeaders) {
- this.httpCode = httpCode;
- this.httpHeaders = httpCode;
- };
- this.sendBody = function(httpBody) {
- this.httpBody = httpBody;
- };
- this.finish = function() {};
+ process.EventEmitter.call(this);
+ this.writeHead = this.sendHeader = function(httpCode, httpHeaders) {
+ this.httpCode = httpCode;
+ this.httpHeaders = httpCode;
+ };
+ this.write = this.sendBody = function(httpBody) {
+ this.httpBody = httpBody;
+ };
+ this.end = this.finish = function() {};
};
sys.inherits(MockResponse, process.EventEmitter);
// A SIMPLE MODULE
var TestModule = {
- foo: function (a, b) {
- return ['foo', 'bar', a, b];
- },
+ foo: function (a, b) {
+ return ['foo', 'bar', a, b];
+ },
- other: 'hello'
+ other: 'hello'
};
// EXPOSING FUNCTIONS
-test('jsonrpc.expose', function() {
- var echo = function(data) {
- return data;
- };
- jsonrpc.expose('echo', echo);
- assert(jsonrpc.functions.echo === echo);
+test('Server.expose', function() {
+ var echo = function(args, opts, callback) {
+ callback(null, args[0]);
+ };
+ server.expose('echo', echo);
+ assert(server.functions.echo === echo);
})
-test('jsonrpc.exposeModule', function() {
- jsonrpc.exposeModule('test', TestModule);
- sys.puts(jsonrpc.functions['test.foo']);
- sys.puts(TestModule.foo);
- assert(jsonrpc.functions['test.foo'] == TestModule.foo);
+test('Server.exposeModule', function() {
+ server.exposeModule('test', TestModule);
+ sys.puts(server.functions['test.foo']);
+ sys.puts(TestModule.foo);
+ assert(server.functions['test.foo'] == TestModule.foo);
});
// INVALID REQUEST
-test('GET jsonrpc.handleRequest', function() {
- var req = new MockRequest('GET');
- var res = new MockResponse();
- jsonrpc.handleRequest(req, res);
- assert(res.httpCode === 405);
+test('GET Server.handleNonPOST', function() {
+ var req = new MockRequest('GET');
+ var res = new MockResponse();
+ rpc.Server.handleNonPOST(req, res);
+ assert(res.httpCode === 405);
});
function testBadRequest(testJSON) {
- var req = new MockRequest('POST');
- var res = new MockResponse();
- jsonrpc.handleRequest(req, res);
- req.emit('body', testJSON);
- req.emit('complete');
- sys.puts(res.httpCode);
- assert(res.httpCode === 400);
+ var req = new MockRequest('POST');
+ var res = new MockResponse();
+ server.handlePOST(req, res);
+ req.emit('data', testJSON);
+ req.emit('end');
+ sys.puts(res.httpCode);
+ assert(res.httpCode === 400);
}
test('Missing object attribute (method)', function() {
- var testJSON = '{ "params": ["Hello, World!"], "id": 1 }';
- testBadRequest(testJSON);
+ var testJSON = '{ "params": ["Hello, World!"], "id": 1 }';
+ testBadRequest(testJSON);
});
test('Missing object attribute (params)', function() {
- var testJSON = '{ "method": "echo", "id": 1 }';
- testBadRequest(testJSON);
+ var testJSON = '{ "method": "echo", "id": 1 }';
+ testBadRequest(testJSON);
});
test('Missing object attribute (id)', function() {
- var testJSON = '{ "method": "echo", "params": ["Hello, World!"] }';
- testBadRequest(testJSON);
+ var testJSON = '{ "method": "echo", "params": ["Hello, World!"] }';
+ testBadRequest(testJSON);
});
test('Unregistered method', function() {
- var testJSON = '{ "method": "notRegistered", "params": ["Hello, World!"], "id": 1 }';
- testBadRequest(testJSON);
+ var testJSON = '{ "method": "notRegistered", "params": ["Hello, World!"], "id": 1 }';
+ testBadRequest(testJSON);
});
// VALID REQUEST
test('Simple synchronous echo', function() {
- var testJSON = '{ "method": "echo", "params": ["Hello, World!"], "id": 1 }';
- var req = new MockRequest('POST');
- var res = new MockResponse();
- jsonrpc.handleRequest(req, res);
- req.emit('body', testJSON);
- req.emit('complete');
- assert(res.httpCode === 200);
- var decoded = JSON.parse(res.httpBody);
- assert(decoded.id === 1);
- assert(decoded.error === null);
- assert(decoded.result == 'Hello, World!');
+ var testJSON = '{ "method": "echo", "params": ["Hello, World!"], "id": 1 }';
+ var req = new MockRequest('POST');
+ var res = new MockResponse();
+ server.handlePOST(req, res);
+ req.emit('data', testJSON);
+ req.emit('end');
+ assert(res.httpCode === 200);
+ var decoded = JSON.parse(res.httpBody);
+ assert(decoded.id === 1);
+ assert(decoded.error === null);
+ assert(decoded.result == 'Hello, World!');
});
test('Using promise', function() {
- // Expose a function that just returns a promise that we can control.
- var promise = new process.Promise();
- jsonrpc.expose('promiseEcho', function(data) {
- return promise;
- });
- // Build a request to call that function
- var testJSON = '{ "method": "promiseEcho", "params": ["Hello, World!"], "id": 1 }';
- var req = new MockRequest('POST');
- var res = new MockResponse();
- // Have the server handle that request
- jsonrpc.handleRequest(req, res);
- req.emit('body', testJSON);
- req.emit('complete');
- // Now the request has completed, and in the above synchronous test, we
- // would be finished. However, this function is smarter and only completes
- // when the promise completes. Therefore, we should not have a response
- // yet.
- assert(res['httpCode'] == null);
- // We can force the promise to emit a success code, with a message.
- promise.emitSuccess('Hello, World!');
- // Aha, now that the promise has finished, our request has finished as well.
- assert(res.httpCode === 200);
- var decoded = JSON.parse(res.httpBody);
- assert(decoded.id === 1);
- assert(decoded.error === null);
- assert(decoded.result == 'Hello, World!');
+ // Expose a function that just returns a promise that we can control.
+ var callbackRef = null;
+ server.expose('promiseEcho', function(args, opts, callback) {
+ callbackRef = callback;
+ });
+ // Build a request to call that function
+ var testJSON = '{ "method": "promiseEcho", "params": ["Hello, World!"], "id": 1 }';
+ var req = new MockRequest('POST');
+ var res = new MockResponse();
+ // Have the server handle that request
+ server.handlePOST(req, res);
+ req.emit('data', testJSON);
+ req.emit('end');
+ // Now the request has completed, and in the above synchronous test, we
+ // would be finished. However, this function is smarter and only completes
+ // when the promise completes. Therefore, we should not have a response
+ // yet.
+ assert(res['httpCode'] == null);
+ // We can force the promise to emit a success code, with a message.
+ callbackRef(null, 'Hello, World!');
+ // Aha, now that the promise has finished, our request has finished as well.
+ assert(res.httpCode === 200);
+ var decoded = JSON.parse(res.httpBody);
+ assert(decoded.id === 1);
+ assert(decoded.error === null);
+ assert(decoded.result == 'Hello, World!');
});
test('Triggering an errback', function() {
- var promise = new process.Promise();
- jsonrpc.expose('errbackEcho', function(data) {
- return promise;
- });
- var testJSON = '{ "method": "errbackEcho", "params": ["Hello, World!"], "id": 1 }';
- var req = new MockRequest('POST');
- var res = new MockResponse();
- jsonrpc.handleRequest(req, res);
- req.emit('body', testJSON);
- req.emit('complete');
- assert(res['httpCode'] == null);
- // This time, unlike the above test, we trigger an error and expect to see
- // it in the error attribute of the object returned.
- promise.emitError('This is an error');
- assert(res.httpCode === 200);
- var decoded = JSON.parse(res.httpBody);
- assert(decoded.id === 1);
- assert(decoded.error == 'This is an error');
- assert(decoded.result == null);
-})
+ var callbackRef = null;
+ server.expose('errbackEcho', function(args, opts, callback) {
+ callbackRef = callback;
+ });
+ var testJSON = '{ "method": "errbackEcho", "params": ["Hello, World!"], "id": 1 }';
+ var req = new MockRequest('POST');
+ var res = new MockResponse();
+ server.handlePOST(req, res);
+ req.emit('data', testJSON);
+ req.emit('end');
+ assert(res['httpCode'] == null);
+ // This time, unlike the above test, we trigger an error and expect to see
+ // it in the error attribute of the object returned.
+ callbackRef('This is an error');
+ assert(res.httpCode === 200);
+ var decoded = JSON.parse(res.httpBody);
+ assert(decoded.id === 1);
+ assert(decoded.error == 'This is an error');
+ assert(decoded.result == null);
+})
View
132 test/test.js
@@ -1,77 +1,81 @@
var sys = require('sys');
-TEST = {
- passed: 0,
- failed: 0,
- assertions: 0,
+var TEST = module.exports = {
+ passed: 0,
+ failed: 0,
+ assertions: 0,
- test: function (desc, block) {
- var _puts = sys.puts,
- output = "",
- result = '?',
- _boom = null;
- sys.puts = function (s) { output += s + "\n"; }
- try {
- sys.print(" " + desc + " ...");
- block();
- result = '.';
- } catch(boom) {
- if ( boom == 'FAIL' ) {
- result = 'F';
- } else {
- result = 'E';
- _boom = boom;
- sys.puts(boom.toString());
- }
- }
- sys.puts = _puts;
- if ( result == '.' ) {
- sys.print(" OK\n");
- TEST.passed += 1;
+ test: function (desc, block) {
+ var _puts = sys.puts,
+ output = "",
+ result = '?',
+ _boom = null;
+ sys.puts = function (s) { output += s + "\n"; }
+ try {
+ sys.print(" " + desc + " ...");
+ block();
+ result = '.';
+ } catch(boom) {
+ if ( boom == 'FAIL' ) {
+ result = 'F';
} else {
- sys.print(" FAIL\n");
- sys.print(output.replace(/^/, " ") + "\n");
- TEST.failed += 1;
- if ( _boom ) throw _boom;
+ result = 'E';
+ _boom = boom;
+ sys.puts(boom.toString());
}
- },
+ }
+ sys.puts = _puts;
+ if ( result == '.' ) {
+ sys.print(" OK\n");
+ TEST.passed += 1;
+ } else {
+ sys.print(" FAIL\n");
+ sys.print(output.replace(/^/, " ") + "\n");
+ TEST.failed += 1;
+ if ( _boom ) throw _boom;
+ }
+ },
- assert: function (value, desc) {
- TEST.assertions += 1;
- if ( desc ) sys.puts("ASSERT: " + desc);
- if ( !value ) throw 'FAIL';
- },
+ assert: function (value, desc) {
+ TEST.assertions += 1;
+ if ( desc ) sys.puts("ASSERT: " + desc);
+ if ( !value ) throw 'FAIL';
+ },
- assert_equal: function (expect, is) {
- assert(
- expect == is,
- sys.inspect(expect) + " == " + sys.inspect(is)
- );
- },
+ assert_equal: function (expect, is) {
+ assert(
+ expect == is,
+ sys.inspect(expect) + " == " + sys.inspect(is)
+ );
+ },
- assert_boom: function (message, block) {
- var error = null;
- try { block() }
- catch (boom) { error = boom }
+ assert_boom: function (message, block) {
+ var error = null;
+ try { block() }
+ catch (boom) { error = boom }
- if ( !error ) {
- sys.puts('NO BOOM');
- throw 'FAIL'
- }
- if ( error != message ) {
- sys.puts('BOOM: ' + sys.inspect(error) +
- ' [' + sys.inspect(message) + ' expected]');
- throw 'FAIL'
- }
- }
-};
+ if ( !error ) {
+ sys.puts('NO BOOM');
+ throw 'FAIL'
+ }
+ if ( error != message ) {
+ sys.puts('BOOM: ' + sys.inspect(error) +
+ ' [' + sys.inspect(message) + ' expected]');
+ throw 'FAIL'
+ }
+ },
-process.mixin(exports, TEST);
+ extend: function (scope) {
+ Object.keys(TEST).forEach(function (key) {
+ scope[key] = TEST[key];
+ });
+ }
+};
process.addListener('exit', function (code) {
- if ( !TEST.exit ) {
- TEST.exit = true;
- sys.puts("" + TEST.passed + " passed, " + TEST.failed + " failed");
- if ( TEST.failed > 0 ) { process.exit(1) };
- }
+ if ( !TEST.exit ) {
+ TEST.exit = true;
+ sys.puts("" + TEST.passed + " passed, " + TEST.failed + " failed");
+ if ( TEST.failed > 0 ) { process.exit(1) };
+ }
});
Please sign in to comment.
Something went wrong with that request. Please try again.