Comparing version 3.3.2 to 3.3.3
@@ -58,3 +58,3 @@ 'use strict'; | ||
this.wasClean = code === undefined || code === 1000 || (code >= 3000 && code <= 4999); | ||
this.wasClean = target._closeFrameReceived && target._closeFrameSent; | ||
this.reason = reason; | ||
@@ -61,0 +61,0 @@ this.code = code; |
@@ -15,2 +15,3 @@ /*! | ||
const ErrorCodes = require('./ErrorCodes'); | ||
const constants = require('./Constants'); | ||
@@ -116,11 +117,23 @@ const Buffer = safeBuffer.Buffer; | ||
close (code, data, mask, cb) { | ||
if (code !== undefined && (typeof code !== 'number' || !ErrorCodes.isValidErrorCode(code))) { | ||
var buf; | ||
if (code === undefined) { | ||
code = 1000; | ||
} else if (typeof code !== 'number' || !ErrorCodes.isValidErrorCode(code)) { | ||
throw new Error('first argument must be a valid error code number'); | ||
} | ||
const buf = Buffer.allocUnsafe(2 + (data ? Buffer.byteLength(data) : 0)); | ||
if (data === undefined || data === '') { | ||
if (code === 1000) { | ||
buf = constants.EMPTY_BUFFER; | ||
} else { | ||
buf = Buffer.allocUnsafe(2); | ||
buf.writeUInt16BE(code, 0, true); | ||
} | ||
} else { | ||
buf = Buffer.allocUnsafe(2 + Buffer.byteLength(data)); | ||
buf.writeUInt16BE(code, 0, true); | ||
buf.write(data, 2); | ||
} | ||
buf.writeUInt16BE(code || 1000, 0, true); | ||
if (buf.length > 2) buf.write(data, 2); | ||
if (this._deflating) { | ||
@@ -127,0 +140,0 @@ this.enqueue([this.doClose, buf, mask, cb]); |
@@ -58,6 +58,8 @@ /*! | ||
this._finalize = this.finalize.bind(this); | ||
this._finalizeCalled = false; | ||
this._closeMessage = null; | ||
this._closeFrameReceived = false; | ||
this._closeFrameSent = false; | ||
this._closeMessage = ''; | ||
this._closeTimer = null; | ||
this._closeCode = null; | ||
this._finalized = false; | ||
this._closeCode = 1006; | ||
this._receiver = null; | ||
@@ -129,3 +131,2 @@ this._sender = null; | ||
// socket cleanup handlers | ||
this._ultron.on('close', this._finalize); | ||
@@ -135,6 +136,4 @@ this._ultron.on('error', this._finalize); | ||
// ensure that the head is added to the receiver | ||
if (head.length > 0) socket.unshift(head); | ||
// subsequent packets are pushed to the receiver | ||
this._ultron.on('data', (data) => { | ||
@@ -145,3 +144,2 @@ this.bytesReceived += data.length; | ||
// receiver event handlers | ||
this._receiver.onmessage = (data) => this.emit('message', data); | ||
@@ -154,10 +152,18 @@ this._receiver.onping = (data) => { | ||
this._receiver.onclose = (code, reason) => { | ||
this._closeFrameReceived = true; | ||
this._closeMessage = reason; | ||
this._closeCode = code; | ||
this.close(code, reason); | ||
if (!this._finalized) this.close(code, reason); | ||
}; | ||
this._receiver.onerror = (error, code) => { | ||
// close the connection when the receiver reports a HyBi error code | ||
this.close(code, ''); | ||
this._closeMessage = ''; | ||
this._closeCode = code; | ||
// | ||
// Ensure that the error is emitted even if `WebSocket#finalize()` has | ||
// already been called. | ||
// | ||
this.readyState = WebSocket.CLOSING; | ||
this.emit('error', error); | ||
this.finalize(true); | ||
}; | ||
@@ -172,39 +178,30 @@ | ||
* | ||
* @param {(Boolean|Error)} Indicates whether or not an error occurred | ||
* @param {(Boolean|Error)} error Indicates whether or not an error occurred | ||
* @private | ||
*/ | ||
finalize (error) { | ||
if (this._finalizeCalled) return; | ||
if (this._finalized) return; | ||
this.readyState = WebSocket.CLOSING; | ||
this._finalizeCalled = true; | ||
this._finalized = true; | ||
if (typeof error === 'object') this.emit('error', error); | ||
if (!this._socket) return this.emitClose(); | ||
clearTimeout(this._closeTimer); | ||
this._closeTimer = null; | ||
// | ||
// If the connection was closed abnormally (with an error), or if the close | ||
// control frame was malformed or not received then the close code must be | ||
// 1006. | ||
// | ||
if (error) this._closeCode = 1006; | ||
this._ultron.destroy(); | ||
this._ultron = null; | ||
if (this._socket) { | ||
this._ultron.destroy(); | ||
this._socket.on('error', function onerror () { | ||
this.destroy(); | ||
}); | ||
this._socket.on('error', constants.NOOP); | ||
if (!error) this._socket.end(); | ||
else this._socket.destroy(); | ||
if (!error) this._socket.end(); | ||
else this._socket.destroy(); | ||
this._receiver.cleanup(() => this.emitClose()); | ||
this._socket = null; | ||
this._sender = null; | ||
this._receiver = null; | ||
this._sender = null; | ||
this._socket = null; | ||
this._ultron = null; | ||
} else { | ||
this.emitClose(); | ||
} | ||
this._receiver.cleanup(() => this.emitClose()); | ||
this._receiver = null; | ||
} | ||
@@ -219,4 +216,5 @@ | ||
this.readyState = WebSocket.CLOSED; | ||
this.emit('close', this._closeCode || 1006, this._closeMessage || ''); | ||
this.emit('close', this._closeCode, this._closeMessage); | ||
if (this.extensions[PerMessageDeflate.extensionName]) { | ||
@@ -229,3 +227,2 @@ this.extensions[PerMessageDeflate.extensionName].cleanup(); | ||
this.removeAllListeners(); | ||
this.on('error', constants.NOOP); // Catch all errors after this. | ||
} | ||
@@ -258,2 +255,18 @@ | ||
* | ||
* +----------+ +-----------+ +----------+ | ||
* + - - -|ws.close()|---->|close frame|-->|ws.close()|- - - - | ||
* +----------+ +-----------+ +----------+ | | ||
* | +----------+ +-----------+ | | ||
* |ws.close()|<----|close frame|<--------+ | | ||
* +----------+ +-----------+ | | ||
* CLOSING | +---+ | CLOSING | ||
* | +---|fin|<------------+ | ||
* | | | +---+ | | ||
* | | +---+ +-------------+ | ||
* | +----------+-->|fin|----->|ws.finalize()| - - + | ||
* | +---+ +-------------+ | ||
* | +-------------+ | | ||
* - - -|ws.finalize()|<--+ | ||
* +-------------+ | ||
* | ||
* @param {Number} code Status code explaining why the connection is closing | ||
@@ -266,7 +279,4 @@ * @param {String} data A string explaining why the connection is closing | ||
if (this.readyState === WebSocket.CONNECTING) { | ||
if (this._req && !this._req.aborted) { | ||
this._req.abort(); | ||
this.emit('error', new Error('closed before the connection is established')); | ||
this.finalize(true); | ||
} | ||
this._req.abort(); | ||
this.finalize(new Error('closed before the connection is established')); | ||
return; | ||
@@ -276,3 +286,3 @@ } | ||
if (this.readyState === WebSocket.CLOSING) { | ||
if (this._closeCode && this._socket) this._socket.end(); | ||
if (this._closeFrameSent && this._closeFrameReceived) this._socket.end(); | ||
return; | ||
@@ -283,6 +293,13 @@ } | ||
this._sender.close(code, data, !this._isServer, (err) => { | ||
if (err) this.emit('error', err); | ||
// | ||
// This error is handled by the `'error'` listener on the socket. We only | ||
// want to know if the close frame has been sent here. | ||
// | ||
if (err) return; | ||
if (this._socket) { | ||
if (this._closeCode) this._socket.end(); | ||
this._closeFrameSent = true; | ||
if (!this._finalized) { | ||
if (this._closeFrameReceived) this._socket.end(); | ||
// | ||
@@ -292,3 +309,2 @@ // Ensure that the connection is cleaned up even when the closing | ||
// | ||
clearTimeout(this._closeTimer); | ||
this._closeTimer = setTimeout(this._finalize, closeTimeout, true); | ||
@@ -385,7 +401,4 @@ } | ||
if (this.readyState === WebSocket.CONNECTING) { | ||
if (this._req && !this._req.aborted) { | ||
this._req.abort(); | ||
this.emit('error', new Error('closed before the connection is established')); | ||
this.finalize(true); | ||
} | ||
this._req.abort(); | ||
this.finalize(new Error('closed before the connection is established')); | ||
return; | ||
@@ -640,4 +653,3 @@ } | ||
this._req.abort(); | ||
this.emit('error', new Error('opening handshake has timed out')); | ||
this.finalize(true); | ||
this.finalize(new Error('opening handshake has timed out')); | ||
}); | ||
@@ -650,4 +662,3 @@ } | ||
this._req = null; | ||
this.emit('error', error); | ||
this.finalize(true); | ||
this.finalize(error); | ||
}); | ||
@@ -658,4 +669,3 @@ | ||
this._req.abort(); | ||
this.emit('error', new Error(`unexpected server response (${res.statusCode})`)); | ||
this.finalize(true); | ||
this.finalize(new Error(`unexpected server response (${res.statusCode})`)); | ||
} | ||
@@ -681,4 +691,3 @@ }); | ||
socket.destroy(); | ||
this.emit('error', new Error('invalid server key')); | ||
return this.finalize(true); | ||
return this.finalize(new Error('invalid server key')); | ||
} | ||
@@ -700,4 +709,3 @@ | ||
socket.destroy(); | ||
this.emit('error', new Error(protError)); | ||
return this.finalize(true); | ||
return this.finalize(new Error(protError)); | ||
} | ||
@@ -721,4 +729,4 @@ | ||
socket.destroy(); | ||
this.emit('error', new Error('invalid Sec-WebSocket-Extensions header')); | ||
return this.finalize(true); | ||
this.finalize(new Error('invalid Sec-WebSocket-Extensions header')); | ||
return; | ||
} | ||
@@ -725,0 +733,0 @@ } |
@@ -76,3 +76,2 @@ /*! | ||
}); | ||
this._server.allowHalfOpen = false; | ||
this._server.listen(options.port, options.host, options.backlog, callback); | ||
@@ -79,0 +78,0 @@ } else if (options.server) { |
{ | ||
"name": "ws", | ||
"version": "3.3.2", | ||
"version": "3.3.3", | ||
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js", | ||
@@ -36,3 +36,3 @@ "keywords": [ | ||
"bufferutil": "~3.0.0", | ||
"eslint": "~4.11.0", | ||
"eslint": "~4.13.0", | ||
"eslint-config-standard": "~10.2.0", | ||
@@ -45,4 +45,4 @@ "eslint-plugin-import": "~2.8.0", | ||
"nyc": "~11.3.0", | ||
"utf-8-validate": "~3.0.0" | ||
"utf-8-validate": "~4.0.0" | ||
} | ||
} |
@@ -11,4 +11,4 @@ # ws: a Node.js WebSocket library | ||
Passes the quite extensive Autobahn test suite. See http://websockets.github.io/ws/ | ||
for the full reports. | ||
Passes the quite extensive Autobahn test suite: [server][server-report], | ||
[client][client-report]. | ||
@@ -339,3 +339,5 @@ **Note**: This module does not work in the browser. The client in the docs is a | ||
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent | ||
[client-report]: http://websockets.github.io/ws/autobahn/clients/ | ||
[server-report]: http://websockets.github.io/ws/autobahn/servers/ | ||
[permessage-deflate]: https://tools.ietf.org/html/rfc7692 | ||
[changelog]: https://github.com/websockets/ws/releases |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
100745
2614
342