Skip to content
This repository has been archived by the owner on Jan 20, 2020. It is now read-only.

Commit

Permalink
Add heartbeat option for WebSocket connections (#76)
Browse files Browse the repository at this point in the history
* Added heartbeat option

* Added heartbeat option

* fix OrderbookSync test

* fix websocket tests

* update docs

* remove default parameter

* new test

* cleanup

* makes it backward compatible

* improve tests and readme
  • Loading branch information
amejiarosario authored and fb55 committed Sep 19, 2017
1 parent bbbb072 commit 86c2987
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 12 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,20 @@ websocket.on('error', err => { /* handle error */ });
websocket.on('close', () => { /* ... */ });
```

Optionally set the heartbeat mode or websocket URI.
```javascript
var websocket = new Gdax.WebsocketClient(
['BTC-USD','ETH-USD'],
'https://api-public.sandbox.gdax.com',
{
key: 'suchkey',
secret: 'suchsecret',
passphrase: 'muchpassphrase',
},
{ heartbeat: true }
);
```

The following events can be emitted from the `WebsocketClient`:

- `open`
Expand Down
29 changes: 22 additions & 7 deletions lib/clients/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ const { signRequest } = require('../../lib/request_signer');
* @param {Object} [auth] - An optional object containing your API key details (key, secret & passphrase)
*/
class WebsocketClient extends EventEmitter {
constructor(productIDs, websocketURI = 'wss://ws-feed.gdax.com', auth) {
constructor(
productIDs,
websocketURI = 'wss://ws-feed.gdax.com',
auth = null,
{ heartbeat = false } = {}
) {
super();
this.productIDs = Utils.determineProductIDs(productIDs);
this.websocketURI = websocketURI;
Expand All @@ -20,6 +25,7 @@ class WebsocketClient extends EventEmitter {
);
}
this.auth = auth || {};
this.heartbeat = heartbeat;
this.connect();
}

Expand Down Expand Up @@ -63,12 +69,21 @@ class WebsocketClient extends EventEmitter {

this.socket.send(JSON.stringify(subscribeMessage));

// Set a 30 second ping to keep connection alive
this.pinger = setInterval(() => {
if (this.socket) {
this.socket.ping('keepalive');
}
}, 30000);
if (this.heartbeat) {
// send heartbeat
var heartbeatMessage = {
type: 'heartbeat',
on: true,
};
this.socket.send(JSON.stringify(heartbeatMessage));
} else {
// Set a 30 second ping to keep connection alive
this.pinger = setInterval(() => {
if (this.socket) {
this.socket.ping('keepalive');
}
}, 30000);
}
}

onClose() {
Expand Down
11 changes: 6 additions & 5 deletions lib/orderbook_sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class OrderbookSync extends WebsocketClient {
productIDs,
apiURI = 'https://api.gdax.com',
websocketURI = 'wss://ws-feed.gdax.com',
authenticatedClient
authenticatedClient = null,
{ heartbeat = false } = {}
) {
let auth = null;
if (authenticatedClient) {
Expand All @@ -19,7 +20,7 @@ class OrderbookSync extends WebsocketClient {
};
}

super(productIDs, websocketURI, auth);
super(productIDs, websocketURI, auth, { heartbeat });
this.apiURI = apiURI;
this.authenticatedClient = authenticatedClient;

Expand All @@ -46,9 +47,9 @@ class OrderbookSync extends WebsocketClient {
this._queues[product_id].push(data);

if (this._sequences[product_id] === -2) {
// Start first resync
this._sequences[product_id] = -1;
this.loadOrderbook(product_id);
// Start first resync
this._sequences[product_id] = -1;
this.loadOrderbook(product_id);
}
} else {
this.processMessage(data);
Expand Down
67 changes: 67 additions & 0 deletions tests/websocket.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,70 @@ suite('WebsocketClient', () => {
});
});
});

test('passes heartbeat details through', done => {
let calls = 0;
const server = testserver(++port, () => {
new Gdax.WebsocketClient(
'ETH-USD',
'ws://localhost:' + port,
{
key: 'suchkey',
secret: 'suchsecret',
passphrase: 'muchpassphrase',
},
{ heartbeat: true }
);
});
server.on('connection', socket => {
socket.on('message', data => {
const msg = JSON.parse(data);
calls++;

if (msg.type === 'subscribe') {
assert.equal(msg.key, 'suchkey');
assert.equal(msg.passphrase, 'muchpassphrase');
assert(msg.timestamp);
assert(msg.signature);
} else {
assert.equal(msg.type, 'heartbeat');
assert.equal(msg.on, true);
}

if (calls > 1) {
server.close();
done();
}
});
});
});

test('passes heartbeat details through without authentication details', done => {
let calls = 0;
const server = testserver(++port, () => {
new Gdax.WebsocketClient(
['BTC-USD', 'ETH-USD'],
'ws://localhost:' + port,
null,
{ heartbeat: true }
);
});
server.on('connection', socket => {
socket.on('message', data => {
const msg = JSON.parse(data);
calls++;

if (msg.type === 'subscribe') {
assert.deepEqual(msg.product_ids, ['BTC-USD', 'ETH-USD']);
} else {
assert.equal(msg.type, 'heartbeat');
assert.equal(msg.on, true);
}

if (calls > 1) {
server.close();
done();
}
});
});
});

0 comments on commit 86c2987

Please sign in to comment.