From 38a7eedeb97924f3cf830961df9110dc0ca610c1 Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Sat, 21 Oct 2017 09:32:37 +0300 Subject: [PATCH 1/2] Simplify request/response association using EventEmitter's once() --- index.js | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/index.js b/index.js index f65d3e3..c0bd016 100644 --- a/index.js +++ b/index.js @@ -2,9 +2,10 @@ const path = require('path'); const net = require('net'); +const {EventEmitter} = require('events'); const _ = require('lodash'); -class LightningClient { +class LightningClient extends EventEmitter { constructor(rpcPath) { if (!path.isAbsolute(rpcPath)) { throw new Error('The rpcPath must be an absolute path'); @@ -14,6 +15,7 @@ class LightningClient { console.log(`Connecting to ${rpcPath}`); + super(); this.rpcPath = rpcPath; this.reconnectWait = 0.5; this.reconnectTimeout = null; @@ -41,8 +43,6 @@ class LightningClient { }); }); - this.waitingFor = {}; - this.client.on('data', data => { _.each(LightningClient.splitJSON(data.toString()), str => { let dataObject = {}; @@ -52,12 +52,7 @@ class LightningClient { return; } - if (!_.isFunction(_self.waitingFor[dataObject.id])) { - return; - } - - _self.waitingFor[dataObject.id].call(_self, dataObject); - delete _self.waitingFor[dataObject.id]; + _self.emit('res:' + dataObject.id, dataObject); }); }); } @@ -131,22 +126,20 @@ class LightningClient { // Wait for the client to connect return this.clientConnectionPromise - .then(() => { + .then(() => new Promise((resolve, reject) => { // Wait for a response - return new Promise((resolve, reject) => { - this.waitingFor[callInt] = response => { - if (_.isNil(response.error)) { - resolve(response.result); - return; - } - - reject(new Error(response.error)); - }; - - // Send the command - _self.client.write(JSON.stringify(sendObj)); + this.once('res:' + callInt, response => { + if (_.isNil(response.error)) { + resolve(response.result); + return; + } + + reject(new Error(response.error)); }); - }); + + // Send the command + _self.client.write(JSON.stringify(sendObj)); + })); } devBlockheight() { From d543474ef9ca36c6e3f3b162a9a4cd175e27ff54 Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Sat, 21 Oct 2017 12:19:27 +0300 Subject: [PATCH 2/2] Use readline to separate JSON response objects - JSON responses are newline-delimited by c-lighting and can't contain newlines, so this achieves the same in a simpler and more efficient manner. - The previous method would break with something like { "key": "}" } - The previous method would break if a single JSON response is split across multiple `data` events (possible with large responses). --- index.js | 49 +++++++++---------------------------------------- 1 file changed, 9 insertions(+), 40 deletions(-) diff --git a/index.js b/index.js index c0bd016..cc35202 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ const path = require('path'); const net = require('net'); +const readline = require('readline'); const {EventEmitter} = require('events'); const _ = require('lodash'); @@ -43,48 +44,16 @@ class LightningClient extends EventEmitter { }); }); - this.client.on('data', data => { - _.each(LightningClient.splitJSON(data.toString()), str => { - let dataObject = {}; - try { - dataObject = JSON.parse(str); - } catch (err) { - return; - } - - _self.emit('res:' + dataObject.id, dataObject); - }); - }); - } - - static splitJSON(str) { - const parts = []; - - let openCount = 0; - let lastSplit = 0; - - for (let i = 0; i < str.length; i++) { - if (i > 0 && str.charCodeAt(i - 1) === 115) { // 115 => backslash, ignore this character - continue; + readline.createInterface({input: this.client}).on('line', str => { + let data; + try { + data = JSON.parse(str); + } catch (err) { + return _self.emit('error', 'Invalid JSON: ' + str); } - if (str[i] === '{') { - openCount++; - } else if (str[i] === '}') { - openCount--; - - if (openCount === 0) { - const start = lastSplit; - const end = i + 1 === str.length ? undefined : i + 1; - - parts.push(str.slice(start, end)); - - lastSplit = end; - } - } - } - - return parts.length === 0 ? [str] : parts; + _self.emit('res:' + data.id, data); + }); } increaseWaitTime() {