Skip to content
This repository has been archived by the owner on Mar 7, 2023. It is now read-only.

Commit

Permalink
Merge pull request #164 from blockchain/george-ws
Browse files Browse the repository at this point in the history
fix(webSocket): ping every 15 secs and wait for a pong for 5 secs.
  • Loading branch information
Sjors committed Apr 1, 2016
2 parents b69eebe + d697af8 commit 3eef814
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 23 deletions.
61 changes: 38 additions & 23 deletions src/blockchain-socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ function BlockchainSocket () {
this.wsUrl = 'wss://blockchain.info/inv';
this.headers = { 'Origin': 'https://blockchain.info' };
this.socket;
this.reconnectInterval;
this.pingInterval;
this.reconnect;
this.reconnect = null;
// websocket pings the server every pingInterval
this.pingInterval = 15000; // 15 secs
this.pingIntervalPID = null;
// ping has a timeout of pingTimeout
this.pingTimeout = 5000; // 5 secs
this.pingTimeoutPID = null;
}

// hack to browserify websocket library
Expand All @@ -28,37 +32,49 @@ if (!(typeof window === 'undefined')) {
};
}


BlockchainSocket.prototype.connect = function (onOpen, onMessage, onClose) {
if(Helpers.tor()) return;

this.reconnect = function () {
var connect = this.connectOnce.bind(this, onOpen, onMessage, onClose);
if (!this.socket || this.socket.readyState === 3) connect();
var connect = this._initialize.bind(this, onOpen, onMessage, onClose);
connect();
}.bind(this);
var pingSocket = function () { this.send(this.msgPing()); }.bind(this);
this.reconnect();
this.reconnectInterval = setInterval(this.reconnect, 20000);
this.pingInterval = setInterval(pingSocket, 30013);
};

BlockchainSocket.prototype.connectOnce = function (onOpen, onMessage, onClose) {
try {
this.socket = new WebSocket(this.wsUrl, [], { headers: this.headers });
this.socket.on('open', onOpen);
this.socket.on('message', onMessage);
this.socket.on('close', onClose);
} catch (e) {
console.log('Failed to connect to websocket');
BlockchainSocket.prototype._initialize = function (onOpen, onMessage, onClose) {

if (!this.socket || this.socket.readyState === 3) {
try {
this.pingIntervalPID = setInterval(this.ping.bind(this), this.pingInterval);
this.socket = new WebSocket(this.wsUrl, [], { headers: this.headers });
this.socket.on('open', onOpen);
this.socket.on('message', onMessage);
this.socket.on('close', onClose);
} catch (e) {
console.log('Failed to connect to websocket');
}
}
};

BlockchainSocket.prototype.ping = function (){
this.send(this.msgPing());
var connect = this.reconnect.bind(this);
var close = this.close.bind(this);
this.pingTimeoutPID = setTimeout(connect.compose(close), this.pingTimeout);
};

BlockchainSocket.prototype.close = function (){
if (this.socket) { this.socket.close();}
this.socket = null;
clearInterval(this.pingIntervalPID);
clearTimeout(this.pingTimeoutPID);
};

BlockchainSocket.prototype.send = function (message) {
if(Helpers.tor()) return;

this.reconnect();
var send = function () {this.socket.send(message); }.bind(this);
if (this.socket && this.socket.readyState === 1) { send();}
if (this.socket && this.socket.readyState === 1) {
this.socket.send(message);
}
};

BlockchainSocket.prototype.msgWalletSub = function (myGUID) {
Expand Down Expand Up @@ -104,5 +120,4 @@ BlockchainSocket.prototype.msgOnOpen = function (guid, addresses, xpubs) {
this.msgXPUBSub(xpubs);
};


module.exports = BlockchainSocket;
3 changes: 3 additions & 0 deletions src/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ function socketConnect () {
var sendOnBlock = WalletStore.sendEvent.bind(null, 'on_block');
MyWallet.wallet.getHistory().then(sendOnBlock);
MyWallet.wallet.latestBlock = obj.x;

} else if (obj.op == 'pong') {
clearTimeout(MyWallet.ws.pingTimeoutPID);
}
}

Expand Down
20 changes: 20 additions & 0 deletions tests/blockchain_socket.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ describe "Websocket", ->
{
on: (event, callback) ->
send: (message) ->
close: () ->
readyState: 1
url: url
}
Expand Down Expand Up @@ -73,6 +74,25 @@ describe "Websocket", ->
# ws.socket is not defined, so nothing to spy on
expect(() -> ws.send(message)).not.toThrow()

describe "close()", ->
beforeEach ->
ws.connect()

it "should clear interval and timeout", ->
ws.close()
expect(ws.pingTimeoutPID).toEqual(null)
expect(ws.socket).toEqual(null)

describe "ping()", ->
beforeEach ->
ws.connect()

it "should clear interval and timeout", ->
spyOn(ws, "send")
ws.ping()
expected = JSON.stringify({op: "ping"})
expect(ws.send).toHaveBeenCalledWith(expected)

describe "msgWalletSub()", ->
it "should subscribe to a guid", ->
res = ws.msgWalletSub("1234")
Expand Down

0 comments on commit 3eef814

Please sign in to comment.