Created
October 25, 2019 04:53
-
-
Save Hugome/84fcf5c57ccb608b92c24a3773f97778 to your computer and use it in GitHub Desktop.
Ugly WS fix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
// Why this file ? | |
// Some weird things happend on socket close : | |
// The 'close' event is call twice in some case... | |
// To search | |
const EventEmitter = require('events'); | |
const crypto = require('crypto'); | |
const https = require('https'); | |
const http = require('http'); | |
const net = require('net'); | |
const tls = require('tls'); | |
const url = require('url'); | |
const PerMessageDeflate = require('./permessage-deflate'); | |
const EventTarget = require('./event-target'); | |
const extension = require('./extension'); | |
const constants = require('./constants'); | |
const Receiver = require('./receiver'); | |
const Sender = require('./sender'); | |
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']; | |
const kWebSocket = constants.kWebSocket; | |
const protocolVersions = [8, 13]; | |
const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly. | |
/** | |
* Class representing a WebSocket. | |
* | |
* @extends EventEmitter | |
*/ | |
class WebSocket extends EventEmitter { | |
/** | |
* Create a new `WebSocket`. | |
* | |
* @param {(String|url.Url|url.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 = constants.BINARY_TYPES[0]; | |
this._closeFrameReceived = false; | |
this._closeFrameSent = false; | |
this._closeMessage = ''; | |
this._closeTimer = null; | |
this._closeCode = 1006; | |
this._extensions = {}; | |
this._isServer = true; | |
this._receiver = null; | |
this._sender = null; | |
this._socket = null; | |
if (address !== null) { | |
if (Array.isArray(protocols)) { | |
protocols = protocols.join(', '); | |
} else if (typeof protocols === 'object' && protocols !== null) { | |
options = protocols; | |
protocols = undefined; | |
} | |
initAsClient.call(this, address, protocols, options); | |
} | |
} | |
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" type). | |
* | |
* @type {String} | |
*/ | |
get binaryType () { | |
return this._binaryType; | |
} | |
set binaryType (type) { | |
if (constants.BINARY_TYPES.indexOf(type) < 0) return; | |
this._binaryType = type; | |
// | |
// Allow to change `binaryType` on the fly. | |
// | |
if (this._receiver) this._receiver._binaryType = type; | |
} | |
/** | |
* @type {Number} | |
*/ | |
get bufferedAmount () { | |
if (!this._socket) return 0; | |
// | |
// `socket.bufferSize` is `undefined` if the socket is closed. | |
// | |
return (this._socket.bufferSize || 0) + this._sender._bufferedBytes; | |
} | |
/** | |
* @type {String} | |
*/ | |
get extensions () { | |
return Object.keys(this._extensions).join(); | |
} | |
/** | |
* Set up the socket and the internal resources. | |
* | |
* @param {net.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 | |
* @private | |
*/ | |
setSocket (socket, head, maxPayload) { | |
const receiver = new Receiver( | |
this._binaryType, | |
this._extensions, | |
maxPayload | |
); | |
this._sender = new Sender(socket, this._extensions); | |
this._receiver = receiver; | |
this._socket = socket; | |
receiver[kWebSocket] = this; | |
socket[kWebSocket] = this; | |
receiver.on('conclude', receiverOnConclude); | |
receiver.on('drain', receiverOnDrain); | |
receiver.on('error', receiverOnError); | |
receiver.on('message', receiverOnMessage); | |
receiver.on('ping', receiverOnPing); | |
receiver.on('pong', receiverOnPong); | |
socket.setTimeout(0); | |
socket.setNoDelay(); | |
if (head.length > 0) socket.unshift(head); | |
socket.on('close', socketOnClose); | |
socket.on('data', socketOnData); | |
socket.on('end', socketOnEnd); | |
socket.on('error', socketOnError); | |
this.readyState = WebSocket.OPEN; | |
this.emit('open'); | |
} | |
/** | |
* Emit the `'close'` event. | |
* | |
* @private | |
*/ | |
emitClose () { | |
this.readyState = WebSocket.CLOSED; | |
if (!this._socket) { | |
this.emit('close', this._closeCode, this._closeMessage); | |
return; | |
} | |
if (this._extensions[PerMessageDeflate.extensionName]) { | |
this._extensions[PerMessageDeflate.extensionName].cleanup(); | |
} | |
this._receiver.removeAllListeners(); | |
this.emit('close', this._closeCode, this._closeMessage); | |
} | |
/** | |
* Start a closing handshake. | |
* | |
* +----------+ +-----------+ +----------+ | |
* - - -|ws.close()|-->|close frame|-->|ws.close()|- - - | |
* | +----------+ +-----------+ +----------+ | | |
* +----------+ +-----------+ | | |
* CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING | |
* +----------+ +-----------+ | | |
* | | | +---+ | | |
* +------------------------+-->|fin| - - - - | |
* | +---+ | +---+ | |
* - - - - -|fin|<---------------------+ | |
* +---+ | |
* | |
* @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) { | |
if (this.readyState === WebSocket.CLOSED) return; | |
if (this.readyState === WebSocket.CONNECTING) { | |
const msg = 'WebSocket was closed before the connection was established'; | |
return abortHandshake(this, this._req, msg); | |
} | |
if (this.readyState === WebSocket.CLOSING) { | |
if (this._closeFrameSent && this._closeFrameReceived) this._socket.end(); | |
return; | |
} | |
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 | |
// want to know if the close frame has been sent here. | |
// | |
if (err) return; | |
this._closeFrameSent = true; | |
if (this._socket.writable) { | |
if (this._closeFrameReceived) this._socket.end(); | |
// | |
// Ensure that the connection is closed even if the closing handshake | |
// fails. | |
// | |
this._closeTimer = setTimeout( | |
this._socket.destroy.bind(this._socket), | |
closeTimeout | |
); | |
} | |
}); | |
} | |
/** | |
* 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 | |
* @public | |
*/ | |
ping (data, mask, cb) { | |
if (typeof data === 'function') { | |
cb = data; | |
data = mask = undefined; | |
} else if (typeof mask === 'function') { | |
cb = mask; | |
mask = undefined; | |
} | |
if (this.readyState !== WebSocket.OPEN) { | |
const err = new Error( | |
`WebSocket is not open: readyState ${this.readyState} ` + | |
`(${readyStates[this.readyState]})` | |
); | |
if (cb) return cb(err); | |
throw err; | |
} | |
if (typeof data === 'number') data = data.toString(); | |
if (mask === undefined) mask = !this._isServer; | |
this._sender.ping(data || constants.EMPTY_BUFFER, mask, cb); | |
} | |
/** | |
* 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 | |
* @public | |
*/ | |
pong (data, mask, cb) { | |
if (typeof data === 'function') { | |
cb = data; | |
data = mask = undefined; | |
} else if (typeof mask === 'function') { | |
cb = mask; | |
mask = undefined; | |
} | |
if (this.readyState !== WebSocket.OPEN) { | |
const err = new Error( | |
`WebSocket is not open: readyState ${this.readyState} ` + | |
`(${readyStates[this.readyState]})` | |
); | |
if (cb) return cb(err); | |
throw err; | |
} | |
if (typeof data === 'number') data = data.toString(); | |
if (mask === undefined) mask = !this._isServer; | |
this._sender.pong(data || constants.EMPTY_BUFFER, mask, cb); | |
} | |
/** | |
* 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 `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 | |
* @public | |
*/ | |
send (data, options, cb) { | |
if (typeof options === 'function') { | |
cb = options; | |
options = {}; | |
} | |
if (this.readyState !== WebSocket.OPEN) { | |
const err = new Error( | |
`WebSocket is not open: readyState ${this.readyState} ` + | |
`(${readyStates[this.readyState]})` | |
); | |
if (cb) return cb(err); | |
throw err; | |
} | |
if (typeof data === 'number') data = data.toString(); | |
const opts = Object.assign({ | |
binary: typeof data !== 'string', | |
mask: !this._isServer, | |
compress: true, | |
fin: true | |
}, options); | |
if (!this._extensions[PerMessageDeflate.extensionName]) { | |
opts.compress = false; | |
} | |
this._sender.send(data || constants.EMPTY_BUFFER, opts, cb); | |
} | |
/** | |
* Forcibly close the connection. | |
* | |
* @public | |
*/ | |
terminate () { | |
if (this.readyState === WebSocket.CLOSED) return; | |
if (this.readyState === WebSocket.CONNECTING) { | |
const msg = 'WebSocket was closed before the connection was established'; | |
return abortHandshake(this, this._req, msg); | |
} | |
if (this._socket) { | |
this.readyState = WebSocket.CLOSING; | |
this._socket.destroy(); | |
} | |
} | |
} | |
readyStates.forEach((readyState, i) => { | |
WebSocket[readyStates[i]] = i; | |
}); | |
// | |
// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes. | |
// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface | |
// | |
['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 | |
*/ | |
get () { | |
const listeners = this.listeners(method); | |
for (var i = 0; i < listeners.length; i++) { | |
if (listeners[i]._listener) return listeners[i]._listener; | |
} | |
}, | |
/** | |
* Add a listener for the event. | |
* | |
* @param {Function} listener The listener to add | |
* @public | |
*/ | |
set (listener) { | |
const listeners = this.listeners(method); | |
for (var i = 0; i < listeners.length; i++) { | |
// | |
// Remove only the listeners added via `addEventListener`. | |
// | |
if (listeners[i]._listener) this.removeListener(method, listeners[i]); | |
} | |
this.addEventListener(method, listener); | |
} | |
}); | |
}); | |
WebSocket.prototype.addEventListener = EventTarget.addEventListener; | |
WebSocket.prototype.removeEventListener = EventTarget.removeEventListener; | |
module.exports = WebSocket; | |
/** | |
* Initialize a WebSocket client. | |
* | |
* @param {(String|url.Url|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 permessage-deflate | |
* @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 `Sec-WebSocket-Origin` header | |
* @private | |
*/ | |
function initAsClient (address, protocols, options) { | |
options = Object.assign({ | |
protocolVersion: protocolVersions[1], | |
perMessageDeflate: true | |
}, options, { | |
createConnection: undefined, | |
socketPath: undefined, | |
hostname: undefined, | |
protocol: undefined, | |
timeout: undefined, | |
method: undefined, | |
auth: undefined, | |
host: undefined, | |
path: undefined, | |
port: undefined | |
}); | |
if (protocolVersions.indexOf(options.protocolVersion) === -1) { | |
throw new RangeError( | |
`Unsupported protocol version: ${options.protocolVersion} ` + | |
`(supported versions: ${protocolVersions.join(', ')})` | |
); | |
} | |
this._isServer = false; | |
var parsedUrl; | |
if (typeof address === 'object' && address.href !== undefined) { | |
parsedUrl = address; | |
this.url = address.href; | |
} else { | |
parsedUrl = url.parse(address); | |
this.url = address; | |
} | |
const isUnixSocket = parsedUrl.protocol === 'ws+unix:'; | |
if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) { | |
throw new Error(`Invalid URL: ${this.url}`); | |
} | |
const isSecure = parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:'; | |
const key = crypto.randomBytes(16).toString('base64'); | |
const httpObj = isSecure ? https : http; | |
const path = parsedUrl.search | |
? `${parsedUrl.pathname || '/'}${parsedUrl.search}` | |
: parsedUrl.pathname || '/'; | |
var perMessageDeflate; | |
options.createConnection = isSecure ? tlsConnect : netConnect; | |
options.port = parsedUrl.port || (isSecure ? 443 : 80); | |
options.host = parsedUrl.hostname.startsWith('[') | |
? parsedUrl.hostname.slice(1, -1) | |
: parsedUrl.hostname; | |
options.headers = Object.assign({ | |
'Sec-WebSocket-Version': options.protocolVersion, | |
'Sec-WebSocket-Key': key, | |
'Connection': 'Upgrade', | |
'Upgrade': 'websocket' | |
}, options.headers); | |
options.path = path; | |
if (options.perMessageDeflate) { | |
perMessageDeflate = new PerMessageDeflate( | |
options.perMessageDeflate !== true ? options.perMessageDeflate : {}, | |
false | |
); | |
options.headers['Sec-WebSocket-Extensions'] = extension.format({ | |
[PerMessageDeflate.extensionName]: perMessageDeflate.offer() | |
}); | |
} | |
if (protocols) { | |
options.headers['Sec-WebSocket-Protocol'] = protocols; | |
} | |
if (options.origin) { | |
if (options.protocolVersion < 13) { | |
options.headers['Sec-WebSocket-Origin'] = options.origin; | |
} else { | |
options.headers.Origin = options.origin; | |
} | |
} | |
if (parsedUrl.auth) { | |
options.auth = parsedUrl.auth; | |
} else if (parsedUrl.username || parsedUrl.password) { | |
options.auth = `${parsedUrl.username}:${parsedUrl.password}`; | |
} | |
if (isUnixSocket) { | |
const parts = path.split(':'); | |
if (options.agent == null && process.versions.modules < 57) { | |
// | |
// Setting `socketPath` in conjunction with `createConnection` without an | |
// agent throws an error on Node.js < 8. Work around the issue by using a | |
// different property. | |
// | |
options._socketPath = parts[0]; | |
} else { | |
options.socketPath = parts[0]; | |
} | |
options.path = parts[1]; | |
} | |
var req = this._req = httpObj.get(options); | |
if (options.handshakeTimeout) { | |
req.setTimeout( | |
options.handshakeTimeout, | |
() => abortHandshake(this, req, 'Opening handshake has timed out') | |
); | |
} | |
req.on('error', (err) => { | |
if (this._req.aborted) return; | |
req = this._req = null; | |
this.readyState = WebSocket.CLOSING; | |
this.emit('error', err); | |
this.emitClose(); | |
}); | |
req.on('response', (res) => { | |
if (this.emit('unexpected-response', req, res)) return; | |
abortHandshake(this, req, `Unexpected server response: ${res.statusCode}`); | |
}); | |
req.on('upgrade', (res, socket, head) => { | |
this.emit('upgrade', res); | |
// | |
// The user may have closed the connection from a listener of the `upgrade` | |
// event. | |
// | |
if (this.readyState !== WebSocket.CONNECTING) return; | |
req = this._req = null; | |
const digest = crypto.createHash('sha1') | |
.update(key + constants.GUID, 'binary') | |
.digest('base64'); | |
if (res.headers['sec-websocket-accept'] !== digest) { | |
abortHandshake(this, socket, 'Invalid Sec-WebSocket-Accept header'); | |
return; | |
} | |
const serverProt = res.headers['sec-websocket-protocol']; | |
const protList = (protocols || '').split(/, */); | |
var protError; | |
if (!protocols && serverProt) { | |
protError = 'Server sent a subprotocol but none was requested'; | |
} else if (protocols && !serverProt) { | |
protError = 'Server sent no subprotocol'; | |
} else if (serverProt && protList.indexOf(serverProt) === -1) { | |
protError = 'Server sent an invalid subprotocol'; | |
} | |
if (protError) { | |
abortHandshake(this, socket, protError); | |
return; | |
} | |
if (serverProt) this.protocol = serverProt; | |
if (perMessageDeflate) { | |
try { | |
const extensions = extension.parse( | |
res.headers['sec-websocket-extensions'] | |
); | |
if (extensions[PerMessageDeflate.extensionName]) { | |
perMessageDeflate.accept( | |
extensions[PerMessageDeflate.extensionName] | |
); | |
this._extensions[PerMessageDeflate.extensionName] = perMessageDeflate; | |
} | |
} catch (err) { | |
abortHandshake(this, socket, 'Invalid Sec-WebSocket-Extensions header'); | |
return; | |
} | |
} | |
this.setSocket(socket, head, 0); | |
}); | |
} | |
/** | |
* Create a `net.Socket` and initiate a connection. | |
* | |
* @param {Object} options Connection options | |
* @return {net.Socket} The newly created socket used to start the connection | |
* @private | |
*/ | |
function netConnect (options) { | |
options.path = options.socketPath || options._socketPath || undefined; | |
return net.connect(options); | |
} | |
/** | |
* Create a `tls.TLSSocket` and initiate a connection. | |
* | |
* @param {Object} options Connection options | |
* @return {tls.TLSSocket} The newly created socket used to start the connection | |
* @private | |
*/ | |
function tlsConnect (options) { | |
options.path = options.socketPath || options._socketPath || undefined; | |
options.servername = options.servername || options.host; | |
return tls.connect(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 {String} message The error message | |
* @private | |
*/ | |
function abortHandshake (websocket, stream, message) { | |
websocket.readyState = WebSocket.CLOSING; | |
const err = new Error(message); | |
Error.captureStackTrace(err, abortHandshake); | |
if (stream.setHeader) { | |
stream.abort(); | |
stream.once('abort', websocket.emitClose.bind(websocket)); | |
websocket.emit('error', err); | |
} else { | |
stream.destroy(err); | |
stream.once('error', websocket.emit.bind(websocket, 'error')); | |
stream.once('close', websocket.emitClose.bind(websocket)); | |
} | |
} | |
/** | |
* The listener of the `Receiver` `'conclude'` event. | |
* | |
* @param {Number} code The status code | |
* @param {String} reason The reason for closing | |
* @private | |
*/ | |
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 (code === 1005) websocket.close(); | |
else websocket.close(code, reason); | |
} | |
/** | |
* The listener of the `Receiver` `'drain'` event. | |
* | |
* @private | |
*/ | |
function receiverOnDrain () { | |
this[kWebSocket]._socket.resume(); | |
} | |
/** | |
* The listener of the `Receiver` `'error'` event. | |
* | |
* @param {(RangeError|Error)} err The emitted error | |
* @private | |
*/ | |
function receiverOnError (err) { | |
const websocket = this[kWebSocket]; | |
websocket._socket.removeListener('data', socketOnData); | |
websocket.readyState = WebSocket.CLOSING; | |
websocket._closeCode = err[constants.kStatusCode]; | |
websocket.emit('error', err); | |
websocket._socket.destroy(); | |
} | |
/** | |
* The listener of the `Receiver` `'finish'` event. | |
* | |
* @private | |
*/ | |
function receiverOnFinish () { | |
this[kWebSocket].emitClose(); | |
} | |
/** | |
* The listener of the `Receiver` `'message'` event. | |
* | |
* @param {(String|Buffer|ArrayBuffer|Buffer[])} data The message | |
* @private | |
*/ | |
function receiverOnMessage (data) { | |
this[kWebSocket].emit('message', data); | |
} | |
/** | |
* The listener of the `Receiver` `'ping'` event. | |
* | |
* @param {Buffer} data The data included in the ping frame | |
* @private | |
*/ | |
function receiverOnPing (data) { | |
const websocket = this[kWebSocket]; | |
websocket.pong(data, !websocket._isServer, constants.NOOP); | |
websocket.emit('ping', data); | |
} | |
/** | |
* The listener of the `Receiver` `'pong'` event. | |
* | |
* @param {Buffer} data The data included in the pong frame | |
* @private | |
*/ | |
function receiverOnPong (data) { | |
this[kWebSocket].emit('pong', data); | |
} | |
/** | |
* The listener of the `net.Socket` `'close'` event. | |
* | |
* @private | |
*/ | |
function socketOnClose () { | |
const websocket = this[kWebSocket]; | |
if (websocket) { | |
this.removeListener('close', socketOnClose); | |
this.removeListener('end', socketOnEnd); | |
websocket.readyState = WebSocket.CLOSING; | |
// | |
// The close frame might not have been received or the `'end'` event emitted, | |
// for example, if the socket was destroyed due to an error. Ensure that the | |
// `receiver` stream is closed after writing any remaining buffered data to | |
// 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. | |
// | |
websocket._socket.read(); | |
websocket._receiver.end(); | |
this.removeListener('data', socketOnData); | |
this[kWebSocket] = undefined; | |
clearTimeout(websocket._closeTimer); | |
if ( | |
websocket._receiver._writableState.finished || | |
websocket._receiver._writableState.errorEmitted | |
) { | |
websocket.emitClose(); | |
} else { | |
websocket._receiver.on('error', receiverOnFinish); | |
websocket._receiver.on('finish', receiverOnFinish); | |
} | |
} else { | |
console.log('SOCKET CLOSE WEIRDLY') | |
console.log(new Error().stack) | |
} | |
} | |
/** | |
* The listener of the `net.Socket` `'data'` event. | |
* | |
* @param {Buffer} chunk A chunk of data | |
* @private | |
*/ | |
function socketOnData (chunk) { | |
if (!this[kWebSocket]._receiver.write(chunk)) { | |
this.pause(); | |
} | |
} | |
/** | |
* The listener of the `net.Socket` `'end'` event. | |
* | |
* @private | |
*/ | |
function socketOnEnd () { | |
const websocket = this[kWebSocket]; | |
websocket.readyState = WebSocket.CLOSING; | |
websocket._receiver.end(); | |
this.end(); | |
} | |
/** | |
* The listener of the `net.Socket` `'error'` event. | |
* | |
* @private | |
*/ | |
function socketOnError () { | |
const websocket = this[kWebSocket]; | |
this.removeListener('error', socketOnError); | |
this.on('error', constants.NOOP); | |
if (websocket) { | |
websocket.readyState = WebSocket.CLOSING; | |
this.destroy(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment