diff --git a/app/src/renderer/node.js b/app/src/renderer/node.js index ddc99e6d59..40985784fb 100644 --- a/app/src/renderer/node.js +++ b/app/src/renderer/node.js @@ -20,6 +20,12 @@ module.exports = function (nodeIP) { rpcOpen: true, initRPC (nodeIP) { if (node.rpc) { + console.log('removing old websocket') + + // ignore disconnect error + node.rpc.removeAllListeners('error') + node.rpc.on('error', () => {}) + node.rpc.ws.destroy() } @@ -29,15 +35,15 @@ module.exports = function (nodeIP) { // we need to check immediately if he connection fails. later we will not be able to check this error newRpc.on('error', err => { console.log('rpc error', err) - if (err.code === 'ECONNREFUSED') { + if (err.code === 'ECONNREFUSED' || err.code === 'ENETUNREACH') { node.rpcOpen = false } }) node.rpc = newRpc }, - rpcReconnect: async (rpcConnecting = node.rpcConnecting) => { - if (rpcConnecting) return + rpcReconnect: async (alreadyConnecting = node.rpcConnecting) => { + if (alreadyConnecting) return null node.rpcConnecting = true console.log('trying to reconnect') diff --git a/app/src/renderer/vuex/modules/node.js b/app/src/renderer/vuex/modules/node.js index a8736f9708..264a116e3f 100644 --- a/app/src/renderer/vuex/modules/node.js +++ b/app/src/renderer/vuex/modules/node.js @@ -1,3 +1,5 @@ +import { setTimeout } from 'timers' + 'use strict' export default function ({ node }) { @@ -24,7 +26,8 @@ export default function ({ node }) { state.lastHeader = header dispatch('maybeUpdateValidators', header) }, - async reconnect ({dispatch}) { + async reconnect ({commit, dispatch}) { + commit('setConnected', false) await node.rpcReconnect() dispatch('nodeSubscribe') }, @@ -58,6 +61,8 @@ export default function ({ node }) { commit('setConnected', true) dispatch('setLastHeader', event.data.data.header) }) + + dispatch('pollRPCConnection') }, async checkConnection ({ commit }) { try { @@ -67,6 +72,25 @@ export default function ({ node }) { commit('notifyError', {title: 'Critical Error', body: `Couldn't initialize blockchain connector`}) return false } + }, + pollRPCConnection ({state, commit, dispatch}, timeout = 3000) { + if (state.nodeTimeout) return + + state.nodeTimeout = setTimeout(() => { + // clear timeout doesn't work + if (state.nodeTimeout) { + state.nodeTimeout = null + dispatch('reconnect') + } + }, timeout) + node.rpc.status((err, res) => { + if (!err) { + state.nodeTimeout = null + setTimeout(() => { + dispatch('pollRPCConnection') + }, timeout) + } + }) } } diff --git a/test/unit/specs/store/node.spec.js b/test/unit/specs/store/node.spec.js index a0b5f20140..2d2409a440 100644 --- a/test/unit/specs/store/node.spec.js +++ b/test/unit/specs/store/node.spec.js @@ -52,7 +52,7 @@ describe('Module: Node', () => { it('reacts to rpc disconnection with reconnect', done => { let failed = false - node.rpc.status = () => done() + node.rpcReconnect = () => done() node.rpc.on = jest.fn((value, cb) => { if (value === 'error' && !failed) { failed = true @@ -116,4 +116,16 @@ describe('Module: Node', () => { } store.dispatch('nodeSubscribe') }) + + it('should ping the node to check connection status', done => { + node.rpc.status = () => done() + store.dispatch('pollRPCConnection') + expect(store.state.node.nodeTimeout).toBeDefined() + }) + + it('should reconnect if pinging node fails', done => { + node.rpcReconnect = () => done() + node.rpc.status = (cb) => {} + store.dispatch('pollRPCConnection', 0) + }) })