mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 20:25:48 +00:00
[FIX] fix multiple reconnections issue
This commit is contained in:
@@ -35,6 +35,8 @@ class Connection extends EventEmitter {
|
|||||||
this._ledgerVersion = null;
|
this._ledgerVersion = null;
|
||||||
this._availableLedgerVersions = new RangeSet();
|
this._availableLedgerVersions = new RangeSet();
|
||||||
this._nextRequestID = 1;
|
this._nextRequestID = 1;
|
||||||
|
this._retry = 0;
|
||||||
|
this._retryTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateLedgerVersions(data) {
|
_updateLedgerVersions(data) {
|
||||||
@@ -95,14 +97,44 @@ class Connection extends EventEmitter {
|
|||||||
return this._state === WebSocket.OPEN && this._isReady;
|
return this._state === WebSocket.OPEN && this._isReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onUnexpectedClose(resolve = function() {}, reject = function() {}) {
|
_onUnexpectedClose(resolve, reject) {
|
||||||
if (this._onOpenErrorBound) {
|
if (this._onOpenErrorBound) {
|
||||||
this._ws.removeListener('error', this._onOpenErrorBound);
|
this._ws.removeListener('error', this._onOpenErrorBound);
|
||||||
this._onOpenErrorBound = null;
|
this._onOpenErrorBound = null;
|
||||||
}
|
}
|
||||||
this._ws = null;
|
this._ws = null;
|
||||||
this._isReady = false;
|
this._isReady = false;
|
||||||
|
if (_.isFunction(resolve)) {
|
||||||
|
// connection was closed before it was properly opened, so we must return
|
||||||
|
// error to connect's caller
|
||||||
this.connect().then(resolve, reject);
|
this.connect().then(resolve, reject);
|
||||||
|
} else {
|
||||||
|
this.emit('disconnected', true);
|
||||||
|
this._retryConnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_retryConnect() {
|
||||||
|
this._retry += 1;
|
||||||
|
const retryTimeout = (this._retry < 40)
|
||||||
|
// First, for 2 seconds: 20 times per second
|
||||||
|
? (1000 / 20)
|
||||||
|
: (this._retry < 40 + 60)
|
||||||
|
// Then, for 1 minute: once per second
|
||||||
|
? (1000)
|
||||||
|
: (this._retry < 40 + 60 + 60)
|
||||||
|
// Then, for 10 minutes: once every 10 seconds
|
||||||
|
? (10 * 1000)
|
||||||
|
// Then: once every 30 seconds
|
||||||
|
: (30 * 1000);
|
||||||
|
this._retryTimer = setTimeout(() => {
|
||||||
|
this.connect().catch(this._retryConnect.bind(this));
|
||||||
|
}, retryTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
_clearReconnectTimer() {
|
||||||
|
clearTimeout(this._retryTimer);
|
||||||
|
this._retryTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onOpen() {
|
_onOpen() {
|
||||||
@@ -112,6 +144,7 @@ class Connection extends EventEmitter {
|
|||||||
|
|
||||||
this._ws.removeListener('error', this._onOpenErrorBound);
|
this._ws.removeListener('error', this._onOpenErrorBound);
|
||||||
this._onOpenErrorBound = null;
|
this._onOpenErrorBound = null;
|
||||||
|
this._retry = 0;
|
||||||
this._ws.on('error', error =>
|
this._ws.on('error', error =>
|
||||||
this.emit('error', 'websocket', error.message, error));
|
this.emit('error', 'websocket', error.message, error));
|
||||||
|
|
||||||
@@ -127,6 +160,7 @@ class Connection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onOpenError(reject, error) {
|
_onOpenError(reject, error) {
|
||||||
|
this._onOpenErrorBound = null;
|
||||||
reject(new NotConnectedError(error && error.message));
|
reject(new NotConnectedError(error && error.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +208,7 @@ class Connection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
|
this._clearReconnectTimer();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!this._url) {
|
if (!this._url) {
|
||||||
reject(new ConnectionError(
|
reject(new ConnectionError(
|
||||||
@@ -208,6 +243,8 @@ class Connection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
|
this._clearReconnectTimer();
|
||||||
|
this._retry = 0;
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (this._state === WebSocket.CLOSED) {
|
if (this._state === WebSocket.CLOSED) {
|
||||||
resolve();
|
resolve();
|
||||||
@@ -218,6 +255,7 @@ class Connection extends EventEmitter {
|
|||||||
this._ws.once('close', () => {
|
this._ws.once('close', () => {
|
||||||
this._ws = null;
|
this._ws = null;
|
||||||
this._isReady = false;
|
this._isReady = false;
|
||||||
|
this.emit('disconnected', false);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
this._ws.close();
|
this._ws.close();
|
||||||
|
|||||||
@@ -193,6 +193,46 @@ describe('Connection', function() {
|
|||||||
}, 1);
|
}, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('reconnect on several unexpected close', function(done) {
|
||||||
|
if (process.browser) {
|
||||||
|
// can't be tested in browser this way, so skipping
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.timeout(7000);
|
||||||
|
const self = this;
|
||||||
|
function breakConnection() {
|
||||||
|
setTimeout(() => {
|
||||||
|
self.mockRippled.close();
|
||||||
|
setTimeout(() => {
|
||||||
|
self.mockRippled = setupAPI.createMockRippled(self._mockedServerPort);
|
||||||
|
}, 1500);
|
||||||
|
}, 21);
|
||||||
|
}
|
||||||
|
|
||||||
|
let connectsCount = 0;
|
||||||
|
let disconnectsCount = 0;
|
||||||
|
this.api.connection.on('disconnected', () => {
|
||||||
|
disconnectsCount += 1;
|
||||||
|
});
|
||||||
|
this.api.connection.on('connected', () => {
|
||||||
|
connectsCount += 1;
|
||||||
|
if (connectsCount < 3) {
|
||||||
|
breakConnection();
|
||||||
|
}
|
||||||
|
if (connectsCount === 3) {
|
||||||
|
if (disconnectsCount !== 3) {
|
||||||
|
done(new Error('disconnectsCount must be equal to 3 (got ' +
|
||||||
|
disconnectsCount + ' instead)'));
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
breakConnection();
|
||||||
|
});
|
||||||
|
|
||||||
it('Multiply connect calls', function() {
|
it('Multiply connect calls', function() {
|
||||||
return this.api.connect().then(() => {
|
return this.api.connect().then(() => {
|
||||||
return this.api.connect();
|
return this.api.connect();
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use strict';
|
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
const RippleAPI = require('ripple-api').RippleAPI;
|
const RippleAPI = require('ripple-api').RippleAPI;
|
||||||
const RippleAPIBroadcast = require('ripple-api').RippleAPIBroadcast;
|
const RippleAPIBroadcast = require('ripple-api').RippleAPIBroadcast;
|
||||||
@@ -27,6 +26,7 @@ function getFreePort() {
|
|||||||
function setupMockRippledConnection(testcase, port) {
|
function setupMockRippledConnection(testcase, port) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
testcase.mockRippled = createMockRippled(port);
|
testcase.mockRippled = createMockRippled(port);
|
||||||
|
testcase._mockedServerPort = port;
|
||||||
testcase.api = new RippleAPI({server: 'ws://localhost:' + port});
|
testcase.api = new RippleAPI({server: 'ws://localhost:' + port});
|
||||||
testcase.api.connect().then(() => {
|
testcase.api.connect().then(() => {
|
||||||
testcase.api.once('ledger', () => resolve());
|
testcase.api.once('ledger', () => resolve());
|
||||||
@@ -73,5 +73,6 @@ function teardown(done) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
setup: setup,
|
setup: setup,
|
||||||
teardown: teardown,
|
teardown: teardown,
|
||||||
setupBroadcast: setupBroadcast
|
setupBroadcast: setupBroadcast,
|
||||||
|
createMockRippled: createMockRippled
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user