Skip to content

Commit

Permalink
Fix heartbeat channel subscriptions
Browse files Browse the repository at this point in the history
- Automatically adds the `heartbeat` channel if not already subscribed
- Removes redundant `keepalive` ping on the socket

Fixes coinbase#113
  • Loading branch information
rmm5t committed Dec 25, 2017
1 parent 17619ca commit d2a29cb
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 115 deletions.
3 changes: 1 addition & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,6 @@ declare module 'gdax' {
}

interface WebsocketClientOptions {
heartbeat?: boolean;
channels?: string[];
}

Expand All @@ -252,7 +251,7 @@ declare module 'gdax' {
productIds: string[],
websocketURI?: string,
auth?: {key:string, secret:string, passphrase:string},
{ heartbeat, channels }?: WebsocketClientOptions );
{ channels }?: WebsocketClientOptions );

on(event: 'message', eventHandler: (data:object) => void);
on(event: 'error', eventHandler: (err) => void);
Expand Down
29 changes: 6 additions & 23 deletions lib/clients/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class WebsocketClient extends EventEmitter {
productIDs,
websocketURI = 'wss://ws-feed.gdax.com',
auth = null,
{ heartbeat = false, channels = null } = {}
{ channels = null } = {}
) {
super();
this.productIDs = Utils.determineProductIDs(productIDs);
Expand All @@ -24,9 +24,11 @@ class WebsocketClient extends EventEmitter {
'Invalid or incomplete authentication credentials. You should either provide all of the secret, key and passphrase fields, or leave auth null'
);
}
this.channels = channels;
this.auth = auth || {};
this.heartbeat = heartbeat;
this.channels = channels || ['full'];
if (!this.channels.includes('heartbeat')) {
this.channels.push('heartbeat');
}
this.connect();
}

Expand Down Expand Up @@ -60,12 +62,9 @@ class WebsocketClient extends EventEmitter {
const subscribeMessage = {
type: 'subscribe',
product_ids: this.productIDs,
channels: this.channels,
};

if (this.channels) {
subscribeMessage.channels = this.channels;
}

// Add Signature
if (this.auth.secret) {
let sig = signRequest(
Expand All @@ -77,22 +76,6 @@ class WebsocketClient extends EventEmitter {
}

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

if (this.heartbeat) {
// send heartbeat
const 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
104 changes: 14 additions & 90 deletions tests/websocket.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,16 @@ suite('WebsocketClient', () => {
});
});

test('subscribes to the default product (BTC-USD) if undefined', done => {
test('subscribes to the default product (BTC-USD) and default channel (full) if undefined', done => {
const server = testserver(++port, () => {
new Gdax.WebsocketClient(null, 'ws://localhost:' + port);
});
server.on('connection', socket => {
socket.on('message', data => {
const msg = JSON.parse(data);
assert.deepEqual(msg, {
type: 'subscribe',
product_ids: ['BTC-USD'],
});
assert.equal(msg.type, 'subscribe');
assert.deepEqual(msg.product_ids, ['BTC-USD']);
assert.deepEqual(msg.channels, ['full', 'heartbeat']);

server.close();
done();
Expand All @@ -43,10 +42,8 @@ suite('WebsocketClient', () => {
server.on('connection', socket => {
socket.on('message', data => {
const msg = JSON.parse(data);
assert.deepEqual(msg, {
type: 'subscribe',
product_ids: ['BTC-USD'],
});
assert.equal(msg.type, 'subscribe');
assert.deepEqual(msg.product_ids, ['BTC-USD']);

server.close();
done();
Expand All @@ -61,10 +58,8 @@ suite('WebsocketClient', () => {
server.on('connection', socket => {
socket.on('message', data => {
const msg = JSON.parse(data);
assert.deepEqual(msg, {
type: 'subscribe',
product_ids: ['BTC-USD'],
});
assert.equal(msg.type, 'subscribe');
assert.deepEqual(msg.product_ids, ['BTC-USD']);

server.close();
done();
Expand All @@ -79,10 +74,8 @@ suite('WebsocketClient', () => {
server.on('connection', socket => {
socket.on('message', data => {
const msg = JSON.parse(data);
assert.deepEqual(msg, {
type: 'subscribe',
product_ids: ['BTC-EUR'],
});
assert.equal(msg.type, 'subscribe');
assert.deepEqual(msg.product_ids, ['BTC-EUR']);

server.close();
done();
Expand All @@ -97,10 +90,8 @@ suite('WebsocketClient', () => {
server.on('connection', socket => {
socket.on('message', data => {
const msg = JSON.parse(data);
assert.deepEqual(msg, {
type: 'subscribe',
product_ids: ['ETH-USD'],
});
assert.equal(msg.type, 'subscribe');
assert.deepEqual(msg.product_ids, ['ETH-USD']);

server.close();
done();
Expand Down Expand Up @@ -131,7 +122,7 @@ suite('WebsocketClient', () => {
});
});

test('passes channels through', done => {
test('passes channels through with heartbeat added', done => {
const server = testserver(++port, () => {
new Gdax.WebsocketClient(
'ETH-USD',
Expand All @@ -150,7 +141,7 @@ suite('WebsocketClient', () => {
assert.equal(msg.type, 'subscribe');
assert.equal(msg.key, 'suchkey');
assert.equal(msg.passphrase, 'muchpassphrase');
assert.deepEqual(msg.channels, ['user', 'ticker']);
assert.deepEqual(msg.channels, ['user', 'ticker', 'heartbeat']);
assert(msg.timestamp);
assert(msg.signature);
server.close();
Expand All @@ -159,70 +150,3 @@ 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 d2a29cb

Please sign in to comment.