From bdae0ac460961ddb4c70e1a32f690331cf3eb9e1 Mon Sep 17 00:00:00 2001 From: fiddlerwoaroof Date: Mon, 25 Jul 2011 14:15:46 -0700 Subject: [PATCH 1/9] added jsonrpc:2.0 --- src/jsonrpc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jsonrpc.js b/src/jsonrpc.js index 23ef0f4..978f10d 100644 --- a/src/jsonrpc.js +++ b/src/jsonrpc.js @@ -19,6 +19,7 @@ var Client = function(port, host, user, password) { // First we encode the request into JSON var requestJSON = JSON.stringify({ + 'jsonrpc': '2.0', 'id': '' + (new Date()).getTime(), 'method': method, 'params': params From faa006481f2732b09ffd6a6817476fcd8a533378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Schr=C3=B6er?= Date: Wed, 17 Aug 2011 19:47:24 +0200 Subject: [PATCH 2/9] Added JSON-RPC version declaration in onFailure and onSuccess handlers. --- src/jsonrpc.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jsonrpc.js b/src/jsonrpc.js index 978f10d..fa15f8f 100644 --- a/src/jsonrpc.js +++ b/src/jsonrpc.js @@ -174,6 +174,7 @@ Server.prototype.handlePOST = function(req, res) { JSON.stringify(funcResp)); var encoded = JSON.stringify({ + 'jsonrpc': '2.0', 'result': funcResp, 'error': null, 'id': decoded.id @@ -188,6 +189,7 @@ Server.prototype.handlePOST = function(req, res) { var onFailure = function(failure) { Server.trace('-->', 'failure: ' + JSON.stringify(failure)); var encoded = JSON.stringify({ + 'jsonrpc': '2.0', 'result': null, 'error': failure || 'Unspecified Failure', 'id': decoded.id From 5111e669e70fb81931e12d859d18793761dd44fc Mon Sep 17 00:00:00 2001 From: Bruno Bigras Date: Mon, 17 Oct 2011 23:15:02 +0800 Subject: [PATCH 3/9] Report errors from the http client --- src/jsonrpc.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/jsonrpc.js b/src/jsonrpc.js index fa15f8f..cb50752 100644 --- a/src/jsonrpc.js +++ b/src/jsonrpc.js @@ -38,6 +38,11 @@ var Client = function(port, host, user, password) { headers['Host'] = host; headers['Content-Length'] = requestJSON.length; + // Report errors from the http client. This also prevents crashes since an exception is thrown if we don't handle this event. + client.on('error', function(err) { + callback(err); + }); + // Now we'll make a request to the server var request = client.request('POST', path || '/', headers); request.write(requestJSON); From bbe42d77b6b0239ffa078ca13dda094c35b0a17d Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 18 Oct 2011 14:49:51 +0800 Subject: [PATCH 4/9] Bump version to 0.0.7. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1608f12..f3c8396 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jsonrpc2", - "version": "0.0.6", + "version": "0.0.7", "description": "JSON-RPC server and client library", "main": "./src/jsonrpc", "keywords": [ From cae518b8ffc985cdaaceca2d001c5e3974a9f83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Schr=C3=B6er?= Date: Thu, 18 Aug 2011 21:34:54 +0800 Subject: [PATCH 5/9] Added true JSON-RPC compatibility & improved error handling * Removed handleInvalidRequest() * Added handleError with JSON error object * Now returning JSON-RPC as it is in the specs (http://groups.google.com/group/json-rpc/web/json-rpc-2-0?pli=1) * Prevented server crashes because of invalid JSON * Changed callback syntax used by the server functions (look at README) * Allowing extra error callback or error parameter in success callback at the client --- README.md | 2 +- examples/client.js | 13 ++++- examples/server.js | 21 +++++--- src/jsonrpc.js | 119 +++++++++++++++++++++++++------------------ test/jsonrpc-test.js | 13 +++-- 5 files changed, 103 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index e0d2d3e..c8b61e2 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ var rpc = require('jsonrpc2'); var server = new rpc.Server(); function add(args, opt, callback) { - callback(null, args[0] + args[1]); + callback(args[0] + args[1]); } server.expose('add', add); diff --git a/examples/client.js b/examples/client.js index 0481c7c..0cd4ea1 100644 --- a/examples/client.js +++ b/examples/client.js @@ -17,12 +17,21 @@ client.call('math.power', [3, 3], function (err, result) { }); // We can handle errors the same way as anywhere else in Node -client.call('add', [1, 1], function (err, result) { +client.call('wrong', [1, 1], function (err, result) { if (err) { sys.puts('RPC Error: '+ sys.inspect(err)); return; } - sys.puts(' 1 + 1 = ' + result + ', dummy!'); + sys.puts(result); +}); + +// If you want to seperate the errors from your callback, +// then define an extra error callback +client.call('wrong', [1, 1], function (err, result) { + sys.puts(result); +}, +function(err){ + sys.puts('RPC Error: ' + sys.inspect(err)); }); /* These calls should each take 1.5 seconds to complete. */ diff --git a/examples/server.js b/examples/server.js index bee3090..25e0483 100644 --- a/examples/server.js +++ b/examples/server.js @@ -4,11 +4,11 @@ var server = new rpc.Server(); /* Create two simple functions */ function add(args, opts, callback) { - callback(null, args[0]+args[1]); + callback(args[0]+args[1]); } function multiply(args, opts, callback) { - callback(null, args[0]*args[1]); + callback(args[0]*args[1]); } /* Expose those methods */ @@ -18,10 +18,10 @@ server.expose('multiply', multiply); /* We can expose entire modules easily */ var math = { power: function(args, opts, callback) { - callback(null, Math.pow(args[0], args[1])); + callback(Math.pow(args[0], args[1])); }, sqrt: function(args, opts, callback) { - callback(null, Math.sqrt(args[0])); + callback(Math.sqrt(args[0])); } } server.exposeModule('math', math); @@ -36,7 +36,7 @@ var delayed = { var data = args[0]; var delay = args[1]; setTimeout(function() { - callback(null, data); + callback(data); }, delay); }, @@ -45,9 +45,18 @@ var delayed = { var second = args[1]; var delay = args[2]; setTimeout(function() { - callback(null, first + second); + callback(first + second); }, delay); } } server.exposeModule('delayed', delayed); + + +// We can also add error parameters to our callback +// if something went wrong +function wrong(arg, opts, callback) { + callback(null, "This will ever go wrong.") +} +server.expose('wrong', wrong); + diff --git a/src/jsonrpc.js b/src/jsonrpc.js index cb50752..7e59d56 100644 --- a/src/jsonrpc.js +++ b/src/jsonrpc.js @@ -1,10 +1,6 @@ var sys = require('sys'); var http = require('http'); -var METHOD_NOT_ALLOWED = "Method Not Allowed\n"; -var INVALID_REQUEST = "Invalid Request\n"; - - //===----------------------------------------------------------------------===// // Server Client //===----------------------------------------------------------------------===// @@ -56,15 +52,19 @@ var Client = function(port, host, user, password) { // depending on whether it's got a result or an error, we call // emitSuccess or emitError on the promise. response.on('end', function() { - var decoded = JSON.parse(buffer); + var decoded = JSON.parse(buffer); // TODO: Check for invalid response from server if(decoded.hasOwnProperty('result')) { - if (callback) + if (callback) callback(null, decoded.result); - } - else { - if (errback) - errback(decoded.error); - } + + } else { + // Call error handler if it is set, otherwise call callback with error parameters + if (errback) { + errback(decoded.error); + } else if(callback) { + callback(decoded.error, null); + } + } }); }); }; @@ -143,17 +143,6 @@ Server.prototype.listen = function(port, host) { } -//===----------------------------------------------------------------------===// -// handleInvalidRequest -//===----------------------------------------------------------------------===// -Server.handleInvalidRequest = function(req, res) { - res.writeHead(400, {'Content-Type': 'text/plain', - 'Content-Length': INVALID_REQUEST.length}); - res.write(INVALID_REQUEST); - res.end(); -} - - //===----------------------------------------------------------------------===// // handlePOST //===----------------------------------------------------------------------===// @@ -161,44 +150,42 @@ Server.prototype.handlePOST = function(req, res) { var buffer = ''; var self = this; var handle = function (buf) { - var decoded = JSON.parse(buf); + + var decoded = ""; + try { + decoded = JSON.parse(buf); + } catch (e) { + return Server.handleError(-32700, "Parse Error", null, req, res); + } + // Check for the required fields, and if they aren't there, then - // dispatch to the handleInvalidRequest function. + // dispatch to the handleError function. if(!(decoded.method && decoded.params && decoded.id)) { - return Server.handleInvalidRequest(req, res); + + if (typeof(id) == "undefined") { + var id = null; + } + + return Server.handleError(-32600, "Invalid Request", decoded.id, req, res); } if(!self.functions.hasOwnProperty(decoded.method)) { - return Server.handleInvalidRequest(req, res); + return Server.handleError(-32601, "Method not found", decoded.id, req, res); } // Build our success handler var onSuccess = function(funcResp) { Server.trace('-->', 'response (id ' + decoded.id + '): ' + JSON.stringify(funcResp)); - - var encoded = JSON.stringify({ + + var encoded = JSON.stringify({ 'jsonrpc': '2.0', 'result': funcResp, 'error': null, 'id': decoded.id }); - res.writeHead(200, {'Content-Type': 'application/json', - 'Content-Length': encoded.length}); - 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)); - var encoded = JSON.stringify({ - 'jsonrpc': '2.0', - 'result': null, - 'error': failure || 'Unspecified Failure', - 'id': decoded.id - }); + res.writeHead(200, {'Content-Type': 'application/json', 'Content-Length': encoded.length}); res.write(encoded); @@ -211,9 +198,9 @@ Server.prototype.handlePOST = function(req, res) { // Try to call the method, but intercept errors and call our // onFailure handler. var method = self.functions[decoded.method]; - var callback = function(err, result) { - if (err) { - onFailure(err); + var callback = function(result, errormessage) { + if (errormessage) { + Server.handleError(-32602, errormessage, decoded.id, req, res); } else { onSuccess(result); } @@ -230,7 +217,7 @@ Server.prototype.handlePOST = function(req, res) { try { method.call(scope, decoded.params, opt, callback); } catch (err) { - return onFailure(err); + return Server.handleError(-32603, err, decoded.id, req, res); } } // function handle(buf) @@ -244,15 +231,49 @@ Server.prototype.handlePOST = function(req, res) { }); } +//===----------------------------------------------------------------------===// +// handleError +//===----------------------------------------------------------------------===// +Server.handleError = function(code, message, id, req, res) { + + var encoded = JSON.stringify({ + 'jsonrpc': '2.0', + 'error': { + 'code':code, + 'message':message + }, + 'id': id + }); + + res.writeHead(400, {'Content-Type': 'text/plain', + 'Content-Length': encoded.length, + 'Allow': 'POST'}); + + res.write(encoded); + res.end(); + + Server.trace('-->', 'Failure: ' + code + ': ' + message); +} + //===----------------------------------------------------------------------===// // handleNonPOST //===----------------------------------------------------------------------===// Server.handleNonPOST = function(req, res) { + + var encoded = JSON.stringify({ + 'jsonrpc': '2.0', + 'error': { + 'code':-32600, + 'message':"Only POST is allowed." + }, + 'id': null + }); + res.writeHead(405, {'Content-Type': 'text/plain', - 'Content-Length': METHOD_NOT_ALLOWED.length, + 'Content-Length': encoded.length, 'Allow': 'POST'}); - res.write(METHOD_NOT_ALLOWED); + res.write(encoded); res.end(); } diff --git a/test/jsonrpc-test.js b/test/jsonrpc-test.js index fbab78e..2bfb4fa 100644 --- a/test/jsonrpc-test.js +++ b/test/jsonrpc-test.js @@ -38,7 +38,7 @@ var TestModule = { test('Server.expose', function() { var echo = function(args, opts, callback) { - callback(null, args[0]); + callback(args[0], null); }; server.expose('echo', echo); assert(server.functions.echo === echo); @@ -66,7 +66,6 @@ function testBadRequest(testJSON) { server.handlePOST(req, res); req.emit('data', testJSON); req.emit('end'); - sys.puts(res.httpCode); assert(res.httpCode === 400); } @@ -99,6 +98,7 @@ test('Simple synchronous echo', function() { 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); @@ -126,7 +126,7 @@ test('Using promise', function() { // yet. assert(res['httpCode'] == null); // We can force the promise to emit a success code, with a message. - callbackRef(null, 'Hello, World!'); + callbackRef('Hello, World!', null); // Aha, now that the promise has finished, our request has finished as well. assert(res.httpCode === 200); var decoded = JSON.parse(res.httpBody); @@ -146,13 +146,12 @@ test('Triggering an errback', function() { 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); + callbackRef(null, 'This is an error'); + assert(res.httpCode === 400); var decoded = JSON.parse(res.httpBody); assert(decoded.id === 1); - assert(decoded.error == 'This is an error'); + assert(decoded.error.message == 'This is an error'); assert(decoded.result == null); }) From c8f720c0156d270a894bfdd2390d33e0106eec9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Schr=C3=B6er?= Date: Thu, 25 Aug 2011 10:16:50 +0800 Subject: [PATCH 6/9] Added note to installation with NPM. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c8b61e2..b2c4690 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ To install node-jsonrpc2 in the current directory, run: npm install jsonrpc2 +** Important: If you run this command, you will get not this repository, but [bitconjs repository](https://github.com/bitcoinjs/node-jsonrpc2)** ## Usage Firing up an efficient JSON-RPC server becomes extremely simple: From b61cd08e3737df09633242ce92ac7b41e32d5f48 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Sun, 4 Dec 2011 09:26:14 +0800 Subject: [PATCH 7/9] Bump version to 0.0.8. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3c8396..03ced4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jsonrpc2", - "version": "0.0.7", + "version": "0.0.8", "description": "JSON-RPC server and client library", "main": "./src/jsonrpc", "keywords": [ From 6f81dbaf36e861d1b169b4cb411112f81c6f928d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Schr=C3=B6er?= Date: Thu, 25 Aug 2011 10:17:59 +0800 Subject: [PATCH 8/9] Tried to correct the markdown code. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b2c4690..d9b9874 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ To install node-jsonrpc2 in the current directory, run: npm install jsonrpc2 -** Important: If you run this command, you will get not this repository, but [bitconjs repository](https://github.com/bitcoinjs/node-jsonrpc2)** +**Important**: If you run this command, you will get not this repository, but [bitconjs repository](https://github.com/bitcoinjs/node-jsonrpc2). ## Usage Firing up an efficient JSON-RPC server becomes extremely simple: From aab2f7c253bc69988d21bb3a7ceb48820b3b890a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 19:40:48 +0000 Subject: [PATCH 9/9] Upgrade to GitHub-native Dependabot --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..4872c5a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: npm + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10