Update websocket.js
This commit is contained in:
parent
091f4a8f41
commit
3db08b859b
1 changed files with 343 additions and 113 deletions
454
node_modules/ws/lib/websocket.js
generated
vendored
454
node_modules/ws/lib/websocket.js
generated
vendored
|
@ -1,3 +1,5 @@
|
|||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Readable$" }] */
|
||||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events');
|
||||
|
@ -6,6 +8,7 @@ const http = require('http');
|
|||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const { randomBytes, createHash } = require('crypto');
|
||||
const { Readable } = require('stream');
|
||||
const { URL } = require('url');
|
||||
|
||||
const PerMessageDeflate = require('./permessage-deflate');
|
||||
|
@ -36,23 +39,22 @@ class WebSocket extends EventEmitter {
|
|||
/**
|
||||
* Create a new `WebSocket`.
|
||||
*
|
||||
* @param {(String|url.URL)} address The URL to which to connect
|
||||
* @param {(String|String[])} protocols The subprotocols
|
||||
* @param {Object} options Connection options
|
||||
* @param {(String|URL)} address The URL to which to connect
|
||||
* @param {(String|String[])} [protocols] The subprotocols
|
||||
* @param {Object} [options] Connection options
|
||||
*/
|
||||
constructor(address, protocols, options) {
|
||||
super();
|
||||
|
||||
this.readyState = WebSocket.CONNECTING;
|
||||
this.protocol = '';
|
||||
|
||||
this._binaryType = BINARY_TYPES[0];
|
||||
this._closeCode = 1006;
|
||||
this._closeFrameReceived = false;
|
||||
this._closeFrameSent = false;
|
||||
this._closeMessage = '';
|
||||
this._closeTimer = null;
|
||||
this._closeCode = 1006;
|
||||
this._extensions = {};
|
||||
this._protocol = '';
|
||||
this._readyState = WebSocket.CONNECTING;
|
||||
this._receiver = null;
|
||||
this._sender = null;
|
||||
this._socket = null;
|
||||
|
@ -75,19 +77,6 @@ class WebSocket extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
get CONNECTING() {
|
||||
return WebSocket.CONNECTING;
|
||||
}
|
||||
get CLOSING() {
|
||||
return WebSocket.CLOSING;
|
||||
}
|
||||
get CLOSED() {
|
||||
return WebSocket.CLOSED;
|
||||
}
|
||||
get OPEN() {
|
||||
return WebSocket.OPEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* This deviates from the WHATWG interface since ws doesn't support the
|
||||
* required default "blob" type (instead we define a custom "nodebuffer"
|
||||
|
@ -126,17 +115,83 @@ class WebSocket extends EventEmitter {
|
|||
return Object.keys(this._extensions).join();
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Function}
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
get onclose() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
set onclose(listener) {}
|
||||
|
||||
/**
|
||||
* @type {Function}
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
get onerror() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
set onerror(listener) {}
|
||||
|
||||
/**
|
||||
* @type {Function}
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
get onopen() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
set onopen(listener) {}
|
||||
|
||||
/**
|
||||
* @type {Function}
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
get onmessage() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
set onmessage(listener) {}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get protocol() {
|
||||
return this._protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
get readyState() {
|
||||
return this._readyState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get url() {
|
||||
return this._url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the socket and the internal resources.
|
||||
*
|
||||
* @param {net.Socket} socket The network socket between the server and client
|
||||
* @param {(net.Socket|tls.Socket)} socket The network socket between the
|
||||
* server and client
|
||||
* @param {Buffer} head The first packet of the upgraded stream
|
||||
* @param {Number} maxPayload The maximum allowed message size
|
||||
* @param {Number} [maxPayload=0] The maximum allowed message size
|
||||
* @private
|
||||
*/
|
||||
setSocket(socket, head, maxPayload) {
|
||||
const receiver = new Receiver(
|
||||
this._binaryType,
|
||||
this.binaryType,
|
||||
this._extensions,
|
||||
this._isServer,
|
||||
maxPayload
|
||||
|
@ -166,7 +221,7 @@ class WebSocket extends EventEmitter {
|
|||
socket.on('end', socketOnEnd);
|
||||
socket.on('error', socketOnError);
|
||||
|
||||
this.readyState = WebSocket.OPEN;
|
||||
this._readyState = WebSocket.OPEN;
|
||||
this.emit('open');
|
||||
}
|
||||
|
||||
|
@ -177,7 +232,7 @@ class WebSocket extends EventEmitter {
|
|||
*/
|
||||
emitClose() {
|
||||
if (!this._socket) {
|
||||
this.readyState = WebSocket.CLOSED;
|
||||
this._readyState = WebSocket.CLOSED;
|
||||
this.emit('close', this._closeCode, this._closeMessage);
|
||||
return;
|
||||
}
|
||||
|
@ -187,7 +242,7 @@ class WebSocket extends EventEmitter {
|
|||
}
|
||||
|
||||
this._receiver.removeAllListeners();
|
||||
this.readyState = WebSocket.CLOSED;
|
||||
this._readyState = WebSocket.CLOSED;
|
||||
this.emit('close', this._closeCode, this._closeMessage);
|
||||
}
|
||||
|
||||
|
@ -206,8 +261,8 @@ class WebSocket extends EventEmitter {
|
|||
* - - - - -|fin|<---------------------+
|
||||
* +---+
|
||||
*
|
||||
* @param {Number} code Status code explaining why the connection is closing
|
||||
* @param {String} data A string explaining why the connection is closing
|
||||
* @param {Number} [code] Status code explaining why the connection is closing
|
||||
* @param {String} [data] A string explaining why the connection is closing
|
||||
* @public
|
||||
*/
|
||||
close(code, data) {
|
||||
|
@ -218,11 +273,17 @@ class WebSocket extends EventEmitter {
|
|||
}
|
||||
|
||||
if (this.readyState === WebSocket.CLOSING) {
|
||||
if (this._closeFrameSent && this._closeFrameReceived) this._socket.end();
|
||||
if (
|
||||
this._closeFrameSent &&
|
||||
(this._closeFrameReceived || this._receiver._writableState.errorEmitted)
|
||||
) {
|
||||
this._socket.end();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.readyState = WebSocket.CLOSING;
|
||||
this._readyState = WebSocket.CLOSING;
|
||||
this._sender.close(code, data, !this._isServer, (err) => {
|
||||
//
|
||||
// This error is handled by the `'error'` listener on the socket. We only
|
||||
|
@ -231,7 +292,13 @@ class WebSocket extends EventEmitter {
|
|||
if (err) return;
|
||||
|
||||
this._closeFrameSent = true;
|
||||
if (this._closeFrameReceived) this._socket.end();
|
||||
|
||||
if (
|
||||
this._closeFrameReceived ||
|
||||
this._receiver._writableState.errorEmitted
|
||||
) {
|
||||
this._socket.end();
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
|
@ -246,9 +313,9 @@ class WebSocket extends EventEmitter {
|
|||
/**
|
||||
* Send a ping.
|
||||
*
|
||||
* @param {*} data The data to send
|
||||
* @param {Boolean} mask Indicates whether or not to mask `data`
|
||||
* @param {Function} cb Callback which is executed when the ping is sent
|
||||
* @param {*} [data] The data to send
|
||||
* @param {Boolean} [mask] Indicates whether or not to mask `data`
|
||||
* @param {Function} [cb] Callback which is executed when the ping is sent
|
||||
* @public
|
||||
*/
|
||||
ping(data, mask, cb) {
|
||||
|
@ -278,9 +345,9 @@ class WebSocket extends EventEmitter {
|
|||
/**
|
||||
* Send a pong.
|
||||
*
|
||||
* @param {*} data The data to send
|
||||
* @param {Boolean} mask Indicates whether or not to mask `data`
|
||||
* @param {Function} cb Callback which is executed when the pong is sent
|
||||
* @param {*} [data] The data to send
|
||||
* @param {Boolean} [mask] Indicates whether or not to mask `data`
|
||||
* @param {Function} [cb] Callback which is executed when the pong is sent
|
||||
* @public
|
||||
*/
|
||||
pong(data, mask, cb) {
|
||||
|
@ -311,13 +378,15 @@ class WebSocket extends EventEmitter {
|
|||
* Send a data message.
|
||||
*
|
||||
* @param {*} data The message to send
|
||||
* @param {Object} options Options object
|
||||
* @param {Boolean} options.compress Specifies whether or not to compress
|
||||
* @param {Object} [options] Options object
|
||||
* @param {Boolean} [options.compress] Specifies whether or not to compress
|
||||
* `data`
|
||||
* @param {Boolean} options.binary Specifies whether `data` is binary or text
|
||||
* @param {Boolean} options.fin Specifies whether the fragment is the last one
|
||||
* @param {Boolean} options.mask Specifies whether or not to mask `data`
|
||||
* @param {Function} cb Callback which is executed when data is written out
|
||||
* @param {Boolean} [options.binary] Specifies whether `data` is binary or
|
||||
* text
|
||||
* @param {Boolean} [options.fin=true] Specifies whether the fragment is the
|
||||
* last one
|
||||
* @param {Boolean} [options.mask] Specifies whether or not to mask `data`
|
||||
* @param {Function} [cb] Callback which is executed when data is written out
|
||||
* @public
|
||||
*/
|
||||
send(data, options, cb) {
|
||||
|
@ -365,14 +434,93 @@ class WebSocket extends EventEmitter {
|
|||
}
|
||||
|
||||
if (this._socket) {
|
||||
this.readyState = WebSocket.CLOSING;
|
||||
this._readyState = WebSocket.CLOSING;
|
||||
this._socket.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readyStates.forEach((readyState, i) => {
|
||||
WebSocket[readyState] = i;
|
||||
/**
|
||||
* @constant {Number} CONNECTING
|
||||
* @memberof WebSocket
|
||||
*/
|
||||
Object.defineProperty(WebSocket, 'CONNECTING', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CONNECTING')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} CONNECTING
|
||||
* @memberof WebSocket.prototype
|
||||
*/
|
||||
Object.defineProperty(WebSocket.prototype, 'CONNECTING', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CONNECTING')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} OPEN
|
||||
* @memberof WebSocket
|
||||
*/
|
||||
Object.defineProperty(WebSocket, 'OPEN', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('OPEN')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} OPEN
|
||||
* @memberof WebSocket.prototype
|
||||
*/
|
||||
Object.defineProperty(WebSocket.prototype, 'OPEN', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('OPEN')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} CLOSING
|
||||
* @memberof WebSocket
|
||||
*/
|
||||
Object.defineProperty(WebSocket, 'CLOSING', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CLOSING')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} CLOSING
|
||||
* @memberof WebSocket.prototype
|
||||
*/
|
||||
Object.defineProperty(WebSocket.prototype, 'CLOSING', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CLOSING')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} CLOSED
|
||||
* @memberof WebSocket
|
||||
*/
|
||||
Object.defineProperty(WebSocket, 'CLOSED', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CLOSED')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} CLOSED
|
||||
* @memberof WebSocket.prototype
|
||||
*/
|
||||
Object.defineProperty(WebSocket.prototype, 'CLOSED', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CLOSED')
|
||||
});
|
||||
|
||||
[
|
||||
'binaryType',
|
||||
'bufferedAmount',
|
||||
'extensions',
|
||||
'protocol',
|
||||
'readyState',
|
||||
'url'
|
||||
].forEach((property) => {
|
||||
Object.defineProperty(WebSocket.prototype, property, { enumerable: true });
|
||||
});
|
||||
|
||||
//
|
||||
|
@ -381,12 +529,7 @@ readyStates.forEach((readyState, i) => {
|
|||
//
|
||||
['open', 'error', 'close', 'message'].forEach((method) => {
|
||||
Object.defineProperty(WebSocket.prototype, `on${method}`, {
|
||||
/**
|
||||
* Return the listener of the event.
|
||||
*
|
||||
* @return {(Function|undefined)} The event listener or `undefined`
|
||||
* @public
|
||||
*/
|
||||
enumerable: true,
|
||||
get() {
|
||||
const listeners = this.listeners(method);
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
|
@ -395,12 +538,6 @@ readyStates.forEach((readyState, i) => {
|
|||
|
||||
return undefined;
|
||||
},
|
||||
/**
|
||||
* Add a listener for the event.
|
||||
*
|
||||
* @param {Function} listener The listener to add
|
||||
* @public
|
||||
*/
|
||||
set(listener) {
|
||||
const listeners = this.listeners(method);
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
|
@ -423,20 +560,23 @@ module.exports = WebSocket;
|
|||
* Initialize a WebSocket client.
|
||||
*
|
||||
* @param {WebSocket} websocket The client to initialize
|
||||
* @param {(String|url.URL)} address The URL to which to connect
|
||||
* @param {String} protocols The subprotocols
|
||||
* @param {Object} options Connection options
|
||||
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable
|
||||
* @param {(String|URL)} address The URL to which to connect
|
||||
* @param {String} [protocols] The subprotocols
|
||||
* @param {Object} [options] Connection options
|
||||
* @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
|
||||
* permessage-deflate
|
||||
* @param {Number} options.handshakeTimeout Timeout in milliseconds for the
|
||||
* @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
|
||||
* handshake request
|
||||
* @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version`
|
||||
* header
|
||||
* @param {String} options.origin Value of the `Origin` or
|
||||
* @param {Number} [options.protocolVersion=13] Value of the
|
||||
* `Sec-WebSocket-Version` header
|
||||
* @param {String} [options.origin] Value of the `Origin` or
|
||||
* `Sec-WebSocket-Origin` header
|
||||
* @param {Number} options.maxPayload The maximum allowed message size
|
||||
* @param {Boolean} options.followRedirects Whether or not to follow redirects
|
||||
* @param {Number} options.maxRedirects The maximum number of redirects allowed
|
||||
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
||||
* size
|
||||
* @param {Boolean} [options.followRedirects=false] Whether or not to follow
|
||||
* redirects
|
||||
* @param {Number} [options.maxRedirects=10] The maximum number of redirects
|
||||
* allowed
|
||||
* @private
|
||||
*/
|
||||
function initAsClient(websocket, address, protocols, options) {
|
||||
|
@ -469,16 +609,23 @@ function initAsClient(websocket, address, protocols, options) {
|
|||
|
||||
if (address instanceof URL) {
|
||||
parsedUrl = address;
|
||||
websocket.url = address.href;
|
||||
websocket._url = address.href;
|
||||
} else {
|
||||
parsedUrl = new URL(address);
|
||||
websocket.url = address;
|
||||
websocket._url = address;
|
||||
}
|
||||
|
||||
const isUnixSocket = parsedUrl.protocol === 'ws+unix:';
|
||||
|
||||
if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) {
|
||||
throw new Error(`Invalid URL: ${websocket.url}`);
|
||||
const err = new Error(`Invalid URL: ${websocket.url}`);
|
||||
|
||||
if (websocket._redirects === 0) {
|
||||
throw err;
|
||||
} else {
|
||||
emitErrorAndClose(websocket, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const isSecure =
|
||||
|
@ -544,12 +691,10 @@ function initAsClient(websocket, address, protocols, options) {
|
|||
}
|
||||
|
||||
req.on('error', (err) => {
|
||||
if (websocket._req.aborted) return;
|
||||
if (req === null || req.aborted) return;
|
||||
|
||||
req = websocket._req = null;
|
||||
websocket.readyState = WebSocket.CLOSING;
|
||||
websocket.emit('error', err);
|
||||
websocket.emitClose();
|
||||
emitErrorAndClose(websocket, err);
|
||||
});
|
||||
|
||||
req.on('response', (res) => {
|
||||
|
@ -569,7 +714,14 @@ function initAsClient(websocket, address, protocols, options) {
|
|||
|
||||
req.abort();
|
||||
|
||||
const addr = new URL(location, address);
|
||||
let addr;
|
||||
|
||||
try {
|
||||
addr = new URL(location, address);
|
||||
} catch (err) {
|
||||
emitErrorAndClose(websocket, err);
|
||||
return;
|
||||
}
|
||||
|
||||
initAsClient(websocket, addr, protocols, options);
|
||||
} else if (!websocket.emit('unexpected-response', req, res)) {
|
||||
|
@ -618,32 +770,72 @@ function initAsClient(websocket, address, protocols, options) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (serverProt) websocket.protocol = serverProt;
|
||||
if (serverProt) websocket._protocol = serverProt;
|
||||
|
||||
if (perMessageDeflate) {
|
||||
try {
|
||||
const extensions = parse(res.headers['sec-websocket-extensions']);
|
||||
const secWebSocketExtensions = res.headers['sec-websocket-extensions'];
|
||||
|
||||
if (extensions[PerMessageDeflate.extensionName]) {
|
||||
perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
|
||||
websocket._extensions[
|
||||
PerMessageDeflate.extensionName
|
||||
] = perMessageDeflate;
|
||||
}
|
||||
} catch (err) {
|
||||
abortHandshake(
|
||||
websocket,
|
||||
socket,
|
||||
'Invalid Sec-WebSocket-Extensions header'
|
||||
);
|
||||
if (secWebSocketExtensions !== undefined) {
|
||||
if (!perMessageDeflate) {
|
||||
const message =
|
||||
'Server sent a Sec-WebSocket-Extensions header but no extension ' +
|
||||
'was requested';
|
||||
abortHandshake(websocket, socket, message);
|
||||
return;
|
||||
}
|
||||
|
||||
let extensions;
|
||||
|
||||
try {
|
||||
extensions = parse(secWebSocketExtensions);
|
||||
} catch (err) {
|
||||
const message = 'Invalid Sec-WebSocket-Extensions header';
|
||||
abortHandshake(websocket, socket, message);
|
||||
return;
|
||||
}
|
||||
|
||||
const extensionNames = Object.keys(extensions);
|
||||
|
||||
if (extensionNames.length) {
|
||||
if (
|
||||
extensionNames.length !== 1 ||
|
||||
extensionNames[0] !== PerMessageDeflate.extensionName
|
||||
) {
|
||||
const message =
|
||||
'Server indicated an extension that was not requested';
|
||||
abortHandshake(websocket, socket, message);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
|
||||
} catch (err) {
|
||||
const message = 'Invalid Sec-WebSocket-Extensions header';
|
||||
abortHandshake(websocket, socket, message);
|
||||
return;
|
||||
}
|
||||
|
||||
websocket._extensions[PerMessageDeflate.extensionName] =
|
||||
perMessageDeflate;
|
||||
}
|
||||
}
|
||||
|
||||
websocket.setSocket(socket, head, opts.maxPayload);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the `'error'` and `'close'` event.
|
||||
*
|
||||
* @param {WebSocket} websocket The WebSocket instance
|
||||
* @param {Error} The error to emit
|
||||
* @private
|
||||
*/
|
||||
function emitErrorAndClose(websocket, err) {
|
||||
websocket._readyState = WebSocket.CLOSING;
|
||||
websocket.emit('error', err);
|
||||
websocket.emitClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `net.Socket` and initiate a connection.
|
||||
*
|
||||
|
@ -667,7 +859,7 @@ function tlsConnect(options) {
|
|||
options.path = undefined;
|
||||
|
||||
if (!options.servername && options.servername !== '') {
|
||||
options.servername = options.host;
|
||||
options.servername = net.isIP(options.host) ? '' : options.host;
|
||||
}
|
||||
|
||||
return tls.connect(options);
|
||||
|
@ -677,19 +869,29 @@ function tlsConnect(options) {
|
|||
* Abort the handshake and emit an error.
|
||||
*
|
||||
* @param {WebSocket} websocket The WebSocket instance
|
||||
* @param {(http.ClientRequest|net.Socket)} stream The request to abort or the
|
||||
* socket to destroy
|
||||
* @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to
|
||||
* abort or the socket to destroy
|
||||
* @param {String} message The error message
|
||||
* @private
|
||||
*/
|
||||
function abortHandshake(websocket, stream, message) {
|
||||
websocket.readyState = WebSocket.CLOSING;
|
||||
websocket._readyState = WebSocket.CLOSING;
|
||||
|
||||
const err = new Error(message);
|
||||
Error.captureStackTrace(err, abortHandshake);
|
||||
|
||||
if (stream.setHeader) {
|
||||
stream.abort();
|
||||
|
||||
if (stream.socket && !stream.socket.destroyed) {
|
||||
//
|
||||
// On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if
|
||||
// called after the request completed. See
|
||||
// https://github.com/websockets/ws/issues/1869.
|
||||
//
|
||||
stream.socket.destroy();
|
||||
}
|
||||
|
||||
stream.once('abort', websocket.emitClose.bind(websocket));
|
||||
websocket.emit('error', err);
|
||||
} else {
|
||||
|
@ -704,8 +906,8 @@ function abortHandshake(websocket, stream, message) {
|
|||
* when the `readyState` attribute is `CLOSING` or `CLOSED`.
|
||||
*
|
||||
* @param {WebSocket} websocket The WebSocket instance
|
||||
* @param {*} data The data to send
|
||||
* @param {Function} cb Callback
|
||||
* @param {*} [data] The data to send
|
||||
* @param {Function} [cb] Callback
|
||||
* @private
|
||||
*/
|
||||
function sendAfterClose(websocket, data, cb) {
|
||||
|
@ -741,13 +943,15 @@ function sendAfterClose(websocket, data, cb) {
|
|||
function receiverOnConclude(code, reason) {
|
||||
const websocket = this[kWebSocket];
|
||||
|
||||
websocket._socket.removeListener('data', socketOnData);
|
||||
websocket._socket.resume();
|
||||
|
||||
websocket._closeFrameReceived = true;
|
||||
websocket._closeMessage = reason;
|
||||
websocket._closeCode = code;
|
||||
|
||||
if (websocket._socket[kWebSocket] === undefined) return;
|
||||
|
||||
websocket._socket.removeListener('data', socketOnData);
|
||||
process.nextTick(resume, websocket._socket);
|
||||
|
||||
if (code === 1005) websocket.close();
|
||||
else websocket.close(code, reason);
|
||||
}
|
||||
|
@ -770,12 +974,19 @@ function receiverOnDrain() {
|
|||
function receiverOnError(err) {
|
||||
const websocket = this[kWebSocket];
|
||||
|
||||
if (websocket._socket[kWebSocket] !== undefined) {
|
||||
websocket._socket.removeListener('data', socketOnData);
|
||||
|
||||
websocket.readyState = WebSocket.CLOSING;
|
||||
websocket._closeCode = err[kStatusCode];
|
||||
//
|
||||
// On Node.js < 14.0.0 the `'error'` event is emitted synchronously. See
|
||||
// https://github.com/websockets/ws/issues/1940.
|
||||
//
|
||||
process.nextTick(resume, websocket._socket);
|
||||
|
||||
websocket.close(err[kStatusCode]);
|
||||
}
|
||||
|
||||
websocket.emit('error', err);
|
||||
websocket._socket.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -820,6 +1031,16 @@ function receiverOnPong(data) {
|
|||
this[kWebSocket].emit('pong', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume a readable stream
|
||||
*
|
||||
* @param {Readable} stream The readable stream
|
||||
* @private
|
||||
*/
|
||||
function resume(stream) {
|
||||
stream.resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener of the `net.Socket` `'close'` event.
|
||||
*
|
||||
|
@ -829,9 +1050,12 @@ function socketOnClose() {
|
|||
const websocket = this[kWebSocket];
|
||||
|
||||
this.removeListener('close', socketOnClose);
|
||||
this.removeListener('data', socketOnData);
|
||||
this.removeListener('end', socketOnEnd);
|
||||
|
||||
websocket.readyState = WebSocket.CLOSING;
|
||||
websocket._readyState = WebSocket.CLOSING;
|
||||
|
||||
let chunk;
|
||||
|
||||
//
|
||||
// The close frame might not have been received or the `'end'` event emitted,
|
||||
|
@ -840,13 +1064,19 @@ function socketOnClose() {
|
|||
// it. If the readable side of the socket is in flowing mode then there is no
|
||||
// buffered data as everything has been already written and `readable.read()`
|
||||
// will return `null`. If instead, the socket is paused, any possible buffered
|
||||
// data will be read as a single chunk and emitted synchronously in a single
|
||||
// `'data'` event.
|
||||
// data will be read as a single chunk.
|
||||
//
|
||||
websocket._socket.read();
|
||||
if (
|
||||
!this._readableState.endEmitted &&
|
||||
!websocket._closeFrameReceived &&
|
||||
!websocket._receiver._writableState.errorEmitted &&
|
||||
(chunk = websocket._socket.read()) !== null
|
||||
) {
|
||||
websocket._receiver.write(chunk);
|
||||
}
|
||||
|
||||
websocket._receiver.end();
|
||||
|
||||
this.removeListener('data', socketOnData);
|
||||
this[kWebSocket] = undefined;
|
||||
|
||||
clearTimeout(websocket._closeTimer);
|
||||
|
@ -882,7 +1112,7 @@ function socketOnData(chunk) {
|
|||
function socketOnEnd() {
|
||||
const websocket = this[kWebSocket];
|
||||
|
||||
websocket.readyState = WebSocket.CLOSING;
|
||||
websocket._readyState = WebSocket.CLOSING;
|
||||
websocket._receiver.end();
|
||||
this.end();
|
||||
}
|
||||
|
@ -899,7 +1129,7 @@ function socketOnError() {
|
|||
this.on('error', NOOP);
|
||||
|
||||
if (websocket) {
|
||||
websocket.readyState = WebSocket.CLOSING;
|
||||
websocket._readyState = WebSocket.CLOSING;
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue