Skip to content

Commit 30c9f71

Browse files
committedJan 5, 2018
[major] Make WebSocket#p{i,o}ng() accept an optional callback
Fixes #599
1 parent 63e275e commit 30c9f71

File tree

5 files changed

+101
-62
lines changed

5 files changed

+101
-62
lines changed
 

‎README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ const WebSocket = require('ws');
298298

299299
const wss = new WebSocket.Server({ port: 8080 });
300300

301+
function noop() {}
302+
301303
function heartbeat() {
302304
this.isAlive = true;
303305
}
@@ -312,7 +314,7 @@ const interval = setInterval(function ping() {
312314
if (ws.isAlive === false) return ws.terminate();
313315

314316
ws.isAlive = false;
315-
ws.ping('', false, true);
317+
ws.ping(noop);
316318
});
317319
}, 30000);
318320
```

‎doc/ws.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -355,23 +355,23 @@ receives an `OpenEvent` named "open".
355355

356356
Pause the socket.
357357

358-
### websocket.ping([data[, mask[, failSilently]]])
358+
### websocket.ping([data[, mask]][, callback])
359359

360360
- `data` {Any} The data to send in the ping frame.
361361
- `mask` {Boolean} Specifies whether `data` should be masked or not. Defaults
362362
to `true` when `websocket` is not a server client.
363-
- `failSilently` {Boolean} Specifies whether or not to throw an error if the
364-
connection is not open.
363+
- `callback` {Function} An optional callback which is invoked when the ping
364+
frame is written out.
365365

366366
Send a ping.
367367

368-
### websocket.pong([data[, mask[, failSilently]]])
368+
### websocket.pong([data[, mask]][, callback])
369369

370370
- `data` {Any} The data to send in the pong frame.
371371
- `mask` {Boolean} Specifies whether `data` should be masked or not. Defaults
372372
to `true` when `websocket` is not a server client.
373-
- `failSilently` {Boolean} Specifies whether or not to throw an error if the
374-
connection is not open.
373+
- `callback` {Function} An optional callback which is invoked when the pong
374+
frame is written out.
375375

376376
Send a pong.
377377

@@ -404,7 +404,7 @@ Removes an event listener emulating the `EventTarget` interface.
404404

405405
Resume the socket.
406406

407-
### websocket.send(data, [options][, callback])
407+
### websocket.send(data[, options][, callback])
408408

409409
- `data` {Any} The data to send.
410410
- `options` {Object}

‎lib/sender.js

+14-10
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,10 @@ class Sender {
158158
*
159159
* @param {*} data The message to send
160160
* @param {Boolean} mask Specifies whether or not to mask `data`
161+
* @param {Function} cb Callback
161162
* @public
162163
*/
163-
ping (data, mask) {
164+
ping (data, mask, cb) {
164165
var readOnly = true;
165166

166167
if (!Buffer.isBuffer(data)) {
@@ -175,9 +176,9 @@ class Sender {
175176
}
176177

177178
if (this._deflating) {
178-
this.enqueue([this.doPing, data, mask, readOnly]);
179+
this.enqueue([this.doPing, data, mask, readOnly, cb]);
179180
} else {
180-
this.doPing(data, mask, readOnly);
181+
this.doPing(data, mask, readOnly, cb);
181182
}
182183
}
183184

@@ -187,26 +188,28 @@ class Sender {
187188
* @param {*} data The message to send
188189
* @param {Boolean} mask Specifies whether or not to mask `data`
189190
* @param {Boolean} readOnly Specifies whether `data` can be modified
191+
* @param {Function} cb Callback
190192
* @private
191193
*/
192-
doPing (data, mask, readOnly) {
194+
doPing (data, mask, readOnly, cb) {
193195
this.sendFrame(Sender.frame(data, {
194196
fin: true,
195197
rsv1: false,
196198
opcode: 0x09,
197199
mask,
198200
readOnly
199-
}));
201+
}), cb);
200202
}
201203

202204
/**
203205
* Sends a pong message to the other peer.
204206
*
205207
* @param {*} data The message to send
206208
* @param {Boolean} mask Specifies whether or not to mask `data`
209+
* @param {Function} cb Callback
207210
* @public
208211
*/
209-
pong (data, mask) {
212+
pong (data, mask, cb) {
210213
var readOnly = true;
211214

212215
if (!Buffer.isBuffer(data)) {
@@ -221,9 +224,9 @@ class Sender {
221224
}
222225

223226
if (this._deflating) {
224-
this.enqueue([this.doPong, data, mask, readOnly]);
227+
this.enqueue([this.doPong, data, mask, readOnly, cb]);
225228
} else {
226-
this.doPong(data, mask, readOnly);
229+
this.doPong(data, mask, readOnly, cb);
227230
}
228231
}
229232

@@ -233,16 +236,17 @@ class Sender {
233236
* @param {*} data The message to send
234237
* @param {Boolean} mask Specifies whether or not to mask `data`
235238
* @param {Boolean} readOnly Specifies whether `data` can be modified
239+
* @param {Function} cb Callback
236240
* @private
237241
*/
238-
doPong (data, mask, readOnly) {
242+
doPong (data, mask, readOnly, cb) {
239243
this.sendFrame(Sender.frame(data, {
240244
fin: true,
241245
rsv1: false,
242246
opcode: 0x0a,
243247
mask,
244248
readOnly
245-
}));
249+
}), cb);
246250
}
247251

248252
/**

‎lib/websocket.js

+37-18
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ class WebSocket extends EventEmitter {
141141

142142
this._receiver.onmessage = (data) => this.emit('message', data);
143143
this._receiver.onping = (data) => {
144-
this.pong(data, !this._isServer, true);
144+
this.pong(data, !this._isServer, constants.NOOP);
145145
this.emit('ping', data);
146146
};
147147
this._receiver.onpong = (data) => this.emit('pong', data);
@@ -317,47 +317,67 @@ class WebSocket extends EventEmitter {
317317
}
318318

319319
/**
320-
* Send a ping message.
320+
* Send a ping.
321321
*
322-
* @param {*} data The message to send
322+
* @param {*} data The data to send
323323
* @param {Boolean} mask Indicates whether or not to mask `data`
324-
* @param {Boolean} failSilently Indicates whether or not to throw if `readyState` isn't `OPEN`
324+
* @param {Function} cb Callback which is executed when the ping is sent
325325
* @public
326326
*/
327-
ping (data, mask, failSilently) {
327+
ping (data, mask, cb) {
328+
if (typeof data === 'function') {
329+
cb = data;
330+
data = mask = undefined;
331+
} else if (typeof mask === 'function') {
332+
cb = mask;
333+
mask = undefined;
334+
}
335+
328336
if (this.readyState !== WebSocket.OPEN) {
329-
if (failSilently) return;
330-
throw new Error(
337+
const err = new Error(
331338
`WebSocket is not open: readyState ${this.readyState} ` +
332339
`(${readyStates[this.readyState]})`
333340
);
341+
342+
if (cb) return cb(err);
343+
throw err;
334344
}
335345

336346
if (typeof data === 'number') data = data.toString();
337347
if (mask === undefined) mask = !this._isServer;
338-
this._sender.ping(data || constants.EMPTY_BUFFER, mask);
348+
this._sender.ping(data || constants.EMPTY_BUFFER, mask, cb);
339349
}
340350

341351
/**
342-
* Send a pong message.
352+
* Send a pong.
343353
*
344-
* @param {*} data The message to send
354+
* @param {*} data The data to send
345355
* @param {Boolean} mask Indicates whether or not to mask `data`
346-
* @param {Boolean} failSilently Indicates whether or not to throw if `readyState` isn't `OPEN`
356+
* @param {Function} cb Callback which is executed when the pong is sent
347357
* @public
348358
*/
349-
pong (data, mask, failSilently) {
359+
pong (data, mask, cb) {
360+
if (typeof data === 'function') {
361+
cb = data;
362+
data = mask = undefined;
363+
} else if (typeof mask === 'function') {
364+
cb = mask;
365+
mask = undefined;
366+
}
367+
350368
if (this.readyState !== WebSocket.OPEN) {
351-
if (failSilently) return;
352-
throw new Error(
369+
const err = new Error(
353370
`WebSocket is not open: readyState ${this.readyState} ` +
354371
`(${readyStates[this.readyState]})`
355372
);
373+
374+
if (cb) return cb(err);
375+
throw err;
356376
}
357377

358378
if (typeof data === 'number') data = data.toString();
359379
if (mask === undefined) mask = !this._isServer;
360-
this._sender.pong(data || constants.EMPTY_BUFFER, mask);
380+
this._sender.pong(data || constants.EMPTY_BUFFER, mask, cb);
361381
}
362382

363383
/**
@@ -384,9 +404,8 @@ class WebSocket extends EventEmitter {
384404
`(${readyStates[this.readyState]})`
385405
);
386406

387-
if (cb) cb(err);
388-
else throw err;
389-
return;
407+
if (cb) return cb(err);
408+
throw err;
390409
}
391410

392411
if (typeof data === 'number') data = data.toString();

‎test/websocket.test.js

+40-26
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ describe('WebSocket', function () {
663663
});
664664

665665
describe('#ping', function () {
666-
it('before connect should fail', function () {
666+
it('throws an error if `readyState` is not `OPEN`', function (done) {
667667
const ws = new WebSocket('ws://localhost', {
668668
agent: new CustomAgent()
669669
});
@@ -672,37 +672,44 @@ describe('WebSocket', function () {
672672
() => ws.ping(),
673673
/^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/
674674
);
675-
});
676675

677-
it('before connect can silently fail', function () {
678-
const ws = new WebSocket('ws://localhost', {
679-
agent: new CustomAgent()
676+
ws.ping((err) => {
677+
assert.ok(err instanceof Error);
678+
assert.strictEqual(
679+
err.message,
680+
'WebSocket is not open: readyState 0 (CONNECTING)'
681+
);
682+
done();
680683
});
681-
682-
assert.doesNotThrow(() => ws.ping('', true, true));
683684
});
684685

685-
it('without message is successfully transmitted to the server', function (done) {
686+
it('can send a ping with no data', function (done) {
686687
const wss = new WebSocket.Server({ port: 0 }, () => {
687688
const port = wss._server.address().port;
688689
const ws = new WebSocket(`ws://localhost:${port}`);
689690

690-
ws.on('open', () => ws.ping());
691+
ws.on('open', () => {
692+
ws.ping(() => ws.ping());
693+
});
691694
});
692695

693696
wss.on('connection', (ws) => {
694-
ws.on('ping', () => wss.close(done));
697+
let pings = 0;
698+
ws.on('ping', (data) => {
699+
assert.ok(Buffer.isBuffer(data));
700+
assert.strictEqual(data.length, 0);
701+
if (++pings === 2) wss.close(done);
702+
});
695703
});
696704
});
697705

698-
it('with message is successfully transmitted to the server', function (done) {
706+
it('can send a ping with data', function (done) {
699707
const wss = new WebSocket.Server({ port: 0 }, () => {
700708
const port = wss._server.address().port;
701709
const ws = new WebSocket(`ws://localhost:${port}`);
702710

703711
ws.on('open', () => {
704-
ws.ping('hi', true);
705-
ws.ping('hi');
712+
ws.ping('hi', () => ws.ping('hi', true));
706713
});
707714
});
708715

@@ -733,7 +740,7 @@ describe('WebSocket', function () {
733740
});
734741

735742
describe('#pong', function () {
736-
it('before connect should fail', () => {
743+
it('throws an error if `readyState` is not `OPEN`', (done) => {
737744
const ws = new WebSocket('ws://localhost', {
738745
agent: new CustomAgent()
739746
});
@@ -742,37 +749,44 @@ describe('WebSocket', function () {
742749
() => ws.pong(),
743750
/^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/
744751
);
745-
});
746752

747-
it('before connect can silently fail', function () {
748-
const ws = new WebSocket('ws://localhost', {
749-
agent: new CustomAgent()
753+
ws.pong((err) => {
754+
assert.ok(err instanceof Error);
755+
assert.strictEqual(
756+
err.message,
757+
'WebSocket is not open: readyState 0 (CONNECTING)'
758+
);
759+
done();
750760
});
751-
752-
assert.doesNotThrow(() => ws.pong('', true, true));
753761
});
754762

755-
it('without message is successfully transmitted to the server', function (done) {
763+
it('can send a pong with no data', function (done) {
756764
const wss = new WebSocket.Server({ port: 0 }, () => {
757765
const port = wss._server.address().port;
758766
const ws = new WebSocket(`ws://localhost:${port}`);
759767

760-
ws.on('open', () => ws.pong());
768+
ws.on('open', () => {
769+
ws.pong(() => ws.pong());
770+
});
761771
});
762772

763773
wss.on('connection', (ws) => {
764-
ws.on('pong', () => wss.close(done));
774+
let pongs = 0;
775+
ws.on('pong', (data) => {
776+
assert.ok(Buffer.isBuffer(data));
777+
assert.strictEqual(data.length, 0);
778+
if (++pongs === 2) wss.close(done);
779+
});
765780
});
766781
});
767782

768-
it('with message is successfully transmitted to the server', function (done) {
783+
it('can send a pong with data', function (done) {
769784
const wss = new WebSocket.Server({ port: 0 }, () => {
770785
const port = wss._server.address().port;
771786
const ws = new WebSocket(`ws://localhost:${port}`);
772787

773788
ws.on('open', () => {
774-
ws.pong('hi', true);
775-
ws.pong('hi');
789+
ws.pong('hi', () => ws.pong('hi', true));
776790
});
777791
});
778792

0 commit comments

Comments
 (0)
Please sign in to comment.