Skip to content

Commit a4050db

Browse files
committedMar 1, 2018
[major] Do not re-emit net.Socket errors
1 parent aa2c423 commit a4050db

File tree

3 files changed

+45
-66
lines changed

3 files changed

+45
-66
lines changed
 

Diff for: ‎doc/ws.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,7 @@ human-readable string explaining why the connection has been closed.
244244

245245
- `error` {Error}
246246

247-
Emitted when an error occurs. Errors from the underlying `net.Socket` are
248-
forwarded here.
247+
Emitted when an error occurs.
249248

250249
### Event: 'message'
251250

Diff for: ‎lib/websocket.js

+22-33
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
1717
const kWebSocket = constants.kWebSocket;
1818
const protocolVersions = [8, 13];
1919
const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.
20-
const NOOP = constants.NOOP;
2120

2221
/**
2322
* Class representing a WebSocket.
@@ -49,7 +48,6 @@ class WebSocket extends EventEmitter {
4948
this._receiver = null;
5049
this._sender = null;
5150
this._socket = null;
52-
this._error = null;
5351

5452
if (address !== null) {
5553
if (!protocols) {
@@ -132,10 +130,10 @@ class WebSocket extends EventEmitter {
132130
receiver[kWebSocket] = this;
133131
socket[kWebSocket] = this;
134132

135-
receiver.on('message', receiverOnMessage);
136133
receiver.on('close', receiverOnClose);
137134
receiver.on('drain', receiverOnDrain);
138135
receiver.on('error', receiverOnError);
136+
receiver.on('message', receiverOnMessage);
139137
receiver.on('ping', receiverOnPing);
140138
receiver.on('pong', receiverOnPong);
141139

@@ -148,7 +146,6 @@ class WebSocket extends EventEmitter {
148146
socket.on('data', socketOnData);
149147
socket.on('end', socketOnEnd);
150148
socket.on('error', socketOnError);
151-
socket.on('error', NOOP);
152149

153150
this.readyState = WebSocket.OPEN;
154151
this.emit('open');
@@ -167,17 +164,11 @@ class WebSocket extends EventEmitter {
167164
return;
168165
}
169166

170-
const err = this._error;
171-
172-
if (err) {
173-
this._error = null;
174-
this.emit('error', err);
175-
}
176-
177167
if (this._extensions[PerMessageDeflate.extensionName]) {
178168
this._extensions[PerMessageDeflate.extensionName].cleanup();
179169
}
180170

171+
this._receiver.removeAllListeners();
181172
this.emit('close', this._closeCode, this._closeMessage);
182173
}
183174

@@ -359,7 +350,6 @@ class WebSocket extends EventEmitter {
359350

360351
if (this._socket) {
361352
this.readyState = WebSocket.CLOSING;
362-
this._socket.removeListener('error', socketOnError);
363353
this._socket.destroy();
364354
}
365355
}
@@ -726,15 +716,21 @@ function receiverOnDrain () {
726716
function receiverOnError (err) {
727717
const websocket = this[kWebSocket];
728718

729-
if (websocket._error) return;
730-
731719
websocket.readyState = WebSocket.CLOSING;
732-
websocket._socket.removeListener('error', socketOnError);
733720
websocket._closeCode = err[constants.kStatusCode];
734721
websocket.emit('error', err);
735722
websocket._socket.destroy();
736723
}
737724

725+
/**
726+
* The listener of the `Receiver` `'finish'` event.
727+
*
728+
* @private
729+
*/
730+
function receiverOnFinish () {
731+
this[kWebSocket].emitClose();
732+
}
733+
738734
/**
739735
* The listener of the `Receiver` `'message'` event.
740736
*
@@ -754,7 +750,7 @@ function receiverOnMessage (data) {
754750
function receiverOnPing (data) {
755751
const websocket = this[kWebSocket];
756752

757-
websocket.pong(data, !websocket._isServer, NOOP);
753+
websocket.pong(data, !websocket._isServer, constants.NOOP);
758754
websocket.emit('ping', data);
759755
}
760756

@@ -776,7 +772,8 @@ function receiverOnPong (data) {
776772
function socketOnClose () {
777773
const websocket = this[kWebSocket];
778774

779-
this.removeListener('error', socketOnError);
775+
this.removeListener('close', socketOnClose);
776+
this.removeListener('data', socketOnData);
780777
this.removeListener('end', socketOnEnd);
781778
this[kWebSocket] = undefined;
782779

@@ -799,13 +796,8 @@ function socketOnClose () {
799796
) {
800797
websocket.emitClose();
801798
} else {
802-
const emitClose = () => {
803-
websocket._receiver.removeAllListeners();
804-
websocket.emitClose();
805-
};
806-
807-
websocket._receiver.on('error', emitClose);
808-
websocket._receiver.on('finish', emitClose);
799+
websocket._receiver.on('error', receiverOnFinish);
800+
websocket._receiver.on('finish', receiverOnFinish);
809801
}
810802
}
811803

@@ -830,26 +822,23 @@ function socketOnEnd () {
830822
const websocket = this[kWebSocket];
831823

832824
websocket.readyState = WebSocket.CLOSING;
833-
this.removeListener('error', socketOnError);
834825
websocket._receiver.end();
835826
this.end();
836827
}
837828

838829
/**
839830
* The listener of the `net.Socket` `'error'` event.
840831
*
841-
* @param {Error} err The emitted error
842832
* @private
843833
*/
844-
function socketOnError (err) {
834+
function socketOnError () {
845835
const websocket = this[kWebSocket];
846836

847-
websocket.readyState = WebSocket.CLOSING;
848837
this.removeListener('error', socketOnError);
838+
this.on('error', constants.NOOP);
849839

850-
//
851-
// There might be valid buffered data in the socket waiting to be read so we
852-
// can't re-emit this error immediately.
853-
//
854-
websocket._error = err;
840+
if (websocket) {
841+
websocket.readyState = WebSocket.CLOSING;
842+
this.destroy();
843+
}
855844
}

Diff for: ‎test/websocket.test.js

+22-31
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const crypto = require('crypto');
88
const https = require('https');
99
const http = require('http');
1010
const dns = require('dns');
11-
const net = require('net');
1211
const fs = require('fs');
1312
const os = require('os');
1413

@@ -393,6 +392,28 @@ describe('WebSocket', function () {
393392
});
394393
});
395394

395+
it('does not re-emit `net.Socket` errors', function (done) {
396+
const wss = new WebSocket.Server({ port: 0 }, () => {
397+
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
398+
399+
ws.on('open', () => {
400+
ws._socket.on('error', (err) => {
401+
assert.ok(err instanceof Error);
402+
assert.ok(err.message.startsWith('write E'));
403+
ws.on('close', (code, message) => {
404+
assert.strictEqual(message, '');
405+
assert.strictEqual(code, 1006);
406+
wss.close(done);
407+
});
408+
});
409+
410+
for (const client of wss.clients) client.terminate();
411+
ws.send('foo');
412+
ws.send('bar');
413+
});
414+
});
415+
});
416+
396417
it("emits an 'upgrade' event", function (done) {
397418
const wss = new WebSocket.Server({ port: 0 }, () => {
398419
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
@@ -1160,36 +1181,6 @@ describe('WebSocket', function () {
11601181
});
11611182
});
11621183

1163-
it('emits an error if the close frame can not be sent', function (done) {
1164-
const wss = new WebSocket.Server({ port: 0 }, () => {
1165-
const socket = net.createConnection(wss.address().port, () => {
1166-
socket.write(
1167-
'GET / HTTP/1.1\r\n' +
1168-
'Host: localhost\r\n' +
1169-
'Upgrade: websocket\r\n' +
1170-
'Connection: Upgrade\r\n' +
1171-
'Sec-WebSocket-Key: qqFVFwaCnSMXiqfezY/AZQ==\r\n' +
1172-
'Sec-WebSocket-Version: 13\r\n' +
1173-
'\r\n'
1174-
);
1175-
socket.destroy();
1176-
});
1177-
1178-
wss.on('connection', (ws) => {
1179-
ws.on('error', (err) => {
1180-
assert.ok(err instanceof Error);
1181-
assert.ok(err.message.startsWith('write E'));
1182-
ws.on('close', (code, message) => {
1183-
assert.strictEqual(message, '');
1184-
assert.strictEqual(code, 1006);
1185-
wss.close(done);
1186-
});
1187-
});
1188-
ws.close();
1189-
});
1190-
});
1191-
});
1192-
11931184
it('sends the close status code only when necessary', function (done) {
11941185
let sent;
11951186
const wss = new WebSocket.Server({ port: 0 }, () => {

0 commit comments

Comments
 (0)
Please sign in to comment.