diff --git a/Readme.md b/Readme.md index cc5abca..6938a12 100644 --- a/Readme.md +++ b/Readme.md @@ -64,3 +64,19 @@ client.cmd('getbalance', '*', 6, function(err, balance){ console.log('Balance:', balance); }); ``` + +### Batch multiple RPC calls into single HTTP request + +```js +var batch = []; +for (var i = 0; i < 10; ++i) { + batch.push({ + method: 'getnewaddress', + params: ['myaccount'] + }); +} +client.cmd(batch, function(err, address) { + if (err) return console.log(err); + console.log('Address:', address); +}); +``` diff --git a/lib/jsonrpc.js b/lib/jsonrpc.js index 55396d1..05c69b0 100644 --- a/lib/jsonrpc.js +++ b/lib/jsonrpc.js @@ -14,13 +14,31 @@ var Client = function(port, host, user, password) { this.password = password; this.call = function(method, params, callback, errback, path) { + + var time = Date.now(); + var requestJSON; + + if (Array.isArray(method)) { + // multiple rpc batch call + requestJSON = []; + method.forEach(function(batchCall, i) { + requestJSON.push({ + id: time + '-' + i, + method: batchCall.method, + params: batchCall.params + }); + }); + } else { + // single rpc call + requestJSON = { + id: time, + method: method, + params: params + }; + } // First we encode the request into JSON - var requestJSON = JSON.stringify({ - 'id': '' + (new Date()).getTime(), - 'method': method, - 'params': params - }); + var requestJSON = JSON.stringify(requestJSON); // prepare request options var requestOptions = { @@ -71,32 +89,39 @@ var Client = function(port, host, user, password) { return; } - if (decoded.hasOwnProperty('error') && decoded.error != null) { - if (errback) { - err = new Error(decoded.error.message || ''); - if (decoded.error.code) { - err.code = decoded.error.code; - } - errback(err); - } - } else if (decoded.hasOwnProperty('result')) { - if (callback) { - callback(decoded.result); - } - } else { - if (errback) { - err = new Error(decoded.error.message || ''); - if (decoded.error.code) { - err.code = decoded.error.code; - } - errback(err); - } + if (!Array.isArray(decoded)) { + decoded = [decoded]; } + // iterate over each response, normally there will be just one + // unless a batch rpc call response is being processed + decoded.forEach(function(decodedResponse, i) { + if (decodedResponse.hasOwnProperty('error') && decodedResponse.error != null) { + if (errback) { + err = new Error(decodedResponse.error.message || ''); + if (decodedResponse.error.code) { + err.code = decodedResponse.error.code; + } + errback(err); + } + } else if (decodedResponse.hasOwnProperty('result')) { + if (callback) { + callback(decodedResponse.result); + } + } else { + if (errback) { + err = new Error(decodedResponse.error.message || ''); + if (decodedResponse.error.code) { + err.code = decodedResponse.error.code; + } + errback(err); + } + } + }); + }); }); - request.write(requestJSON); - request.end(); + request.end(requestJSON); }; } diff --git a/test/api.js b/test/api.js index 12c32fe..6af58e5 100644 --- a/test/api.js +++ b/test/api.js @@ -25,6 +25,8 @@ function notEmpty(data) { assert.ok(data); } +var batchCallbackCount = 0; + vows.describe('api').addBatch({ '': { topic: makeClient, @@ -134,6 +136,29 @@ vows.describe('api').addBatch({ assert.equal(expectedValue, undefined); }, }, + 'running batch of rpc calls': { + topic: function(client) { + // create batch of calls to get 10 new addresses + var batch = []; + for (var i = 0; i < 10; ++i) { + batch.push({ + method: 'getnewaddress', + params: [test.account] + }); + } + var self = this; + client.cmd(batch, function(err, address) { + assert.isTrue(++batchCallbackCount <= 10); + if (batchCallbackCount === 10) { + self.callback(err, address); + } + }); + }, + 'should receive new address': function(err, address){ + assert.equal(err, null); + assert.ok(address); + } + } }, 'invalid credentials': { topic: function() {