websocket.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  1. 'use strict';
  2. const EventEmitter = require('events');
  3. const crypto = require('crypto');
  4. const https = require('https');
  5. const http = require('http');
  6. const net = require('net');
  7. const tls = require('tls');
  8. const url = require('url');
  9. const PerMessageDeflate = require('./permessage-deflate');
  10. const EventTarget = require('./event-target');
  11. const extension = require('./extension');
  12. const constants = require('./constants');
  13. const Receiver = require('./receiver');
  14. const Sender = require('./sender');
  15. const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
  16. const kWebSocket = constants.kWebSocket;
  17. const protocolVersions = [8, 13];
  18. const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.
  19. /**
  20. * Class representing a WebSocket.
  21. *
  22. * @extends EventEmitter
  23. */
  24. class WebSocket extends EventEmitter {
  25. /**
  26. * Create a new `WebSocket`.
  27. *
  28. * @param {(String|url.Url|url.URL)} address The URL to which to connect
  29. * @param {(String|String[])} protocols The subprotocols
  30. * @param {Object} options Connection options
  31. */
  32. constructor(address, protocols, options) {
  33. super();
  34. this.readyState = WebSocket.CONNECTING;
  35. this.protocol = '';
  36. this._binaryType = constants.BINARY_TYPES[0];
  37. this._closeFrameReceived = false;
  38. this._closeFrameSent = false;
  39. this._closeMessage = '';
  40. this._closeTimer = null;
  41. this._closeCode = 1006;
  42. this._extensions = {};
  43. this._isServer = true;
  44. this._receiver = null;
  45. this._sender = null;
  46. this._socket = null;
  47. if (address !== null) {
  48. if (Array.isArray(protocols)) {
  49. protocols = protocols.join(', ');
  50. } else if (typeof protocols === 'object' && protocols !== null) {
  51. options = protocols;
  52. protocols = undefined;
  53. }
  54. initAsClient.call(this, address, protocols, options);
  55. }
  56. }
  57. get CONNECTING() {
  58. return WebSocket.CONNECTING;
  59. }
  60. get CLOSING() {
  61. return WebSocket.CLOSING;
  62. }
  63. get CLOSED() {
  64. return WebSocket.CLOSED;
  65. }
  66. get OPEN() {
  67. return WebSocket.OPEN;
  68. }
  69. /**
  70. * This deviates from the WHATWG interface since ws doesn't support the required
  71. * default "blob" type (instead we define a custom "nodebuffer" type).
  72. *
  73. * @type {String}
  74. */
  75. get binaryType() {
  76. return this._binaryType;
  77. }
  78. set binaryType(type) {
  79. if (!constants.BINARY_TYPES.includes(type)) return;
  80. this._binaryType = type;
  81. //
  82. // Allow to change `binaryType` on the fly.
  83. //
  84. if (this._receiver) this._receiver._binaryType = type;
  85. }
  86. /**
  87. * @type {Number}
  88. */
  89. get bufferedAmount() {
  90. if (!this._socket) return 0;
  91. //
  92. // `socket.bufferSize` is `undefined` if the socket is closed.
  93. //
  94. return (this._socket.bufferSize || 0) + this._sender._bufferedBytes;
  95. }
  96. /**
  97. * @type {String}
  98. */
  99. get extensions() {
  100. return Object.keys(this._extensions).join();
  101. }
  102. /**
  103. * Set up the socket and the internal resources.
  104. *
  105. * @param {net.Socket} socket The network socket between the server and client
  106. * @param {Buffer} head The first packet of the upgraded stream
  107. * @param {Number} maxPayload The maximum allowed message size
  108. * @private
  109. */
  110. setSocket(socket, head, maxPayload) {
  111. const receiver = new Receiver(
  112. this._binaryType,
  113. this._extensions,
  114. maxPayload
  115. );
  116. this._sender = new Sender(socket, this._extensions);
  117. this._receiver = receiver;
  118. this._socket = socket;
  119. receiver[kWebSocket] = this;
  120. socket[kWebSocket] = this;
  121. receiver.on('conclude', receiverOnConclude);
  122. receiver.on('drain', receiverOnDrain);
  123. receiver.on('error', receiverOnError);
  124. receiver.on('message', receiverOnMessage);
  125. receiver.on('ping', receiverOnPing);
  126. receiver.on('pong', receiverOnPong);
  127. socket.setTimeout(0);
  128. socket.setNoDelay();
  129. if (head.length > 0) socket.unshift(head);
  130. socket.on('close', socketOnClose);
  131. socket.on('data', socketOnData);
  132. socket.on('end', socketOnEnd);
  133. socket.on('error', socketOnError);
  134. this.readyState = WebSocket.OPEN;
  135. this.emit('open');
  136. }
  137. /**
  138. * Emit the `'close'` event.
  139. *
  140. * @private
  141. */
  142. emitClose() {
  143. this.readyState = WebSocket.CLOSED;
  144. if (!this._socket) {
  145. this.emit('close', this._closeCode, this._closeMessage);
  146. return;
  147. }
  148. if (this._extensions[PerMessageDeflate.extensionName]) {
  149. this._extensions[PerMessageDeflate.extensionName].cleanup();
  150. }
  151. this._receiver.removeAllListeners();
  152. this.emit('close', this._closeCode, this._closeMessage);
  153. }
  154. /**
  155. * Start a closing handshake.
  156. *
  157. * +----------+ +-----------+ +----------+
  158. * - - -|ws.close()|-->|close frame|-->|ws.close()|- - -
  159. * | +----------+ +-----------+ +----------+ |
  160. * +----------+ +-----------+ |
  161. * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING
  162. * +----------+ +-----------+ |
  163. * | | | +---+ |
  164. * +------------------------+-->|fin| - - - -
  165. * | +---+ | +---+
  166. * - - - - -|fin|<---------------------+
  167. * +---+
  168. *
  169. * @param {Number} code Status code explaining why the connection is closing
  170. * @param {String} data A string explaining why the connection is closing
  171. * @public
  172. */
  173. close(code, data) {
  174. if (this.readyState === WebSocket.CLOSED) return;
  175. if (this.readyState === WebSocket.CONNECTING) {
  176. const msg = 'WebSocket was closed before the connection was established';
  177. return abortHandshake(this, this._req, msg);
  178. }
  179. if (this.readyState === WebSocket.CLOSING) {
  180. if (this._closeFrameSent && this._closeFrameReceived) this._socket.end();
  181. return;
  182. }
  183. this.readyState = WebSocket.CLOSING;
  184. this._sender.close(code, data, !this._isServer, (err) => {
  185. //
  186. // This error is handled by the `'error'` listener on the socket. We only
  187. // want to know if the close frame has been sent here.
  188. //
  189. if (err) return;
  190. this._closeFrameSent = true;
  191. if (this._socket.writable) {
  192. if (this._closeFrameReceived) this._socket.end();
  193. //
  194. // Ensure that the connection is closed even if the closing handshake
  195. // fails.
  196. //
  197. this._closeTimer = setTimeout(
  198. this._socket.destroy.bind(this._socket),
  199. closeTimeout
  200. );
  201. }
  202. });
  203. }
  204. /**
  205. * Send a ping.
  206. *
  207. * @param {*} data The data to send
  208. * @param {Boolean} mask Indicates whether or not to mask `data`
  209. * @param {Function} cb Callback which is executed when the ping is sent
  210. * @public
  211. */
  212. ping(data, mask, cb) {
  213. if (typeof data === 'function') {
  214. cb = data;
  215. data = mask = undefined;
  216. } else if (typeof mask === 'function') {
  217. cb = mask;
  218. mask = undefined;
  219. }
  220. if (this.readyState !== WebSocket.OPEN) {
  221. const err = new Error(
  222. `WebSocket is not open: readyState ${this.readyState} ` +
  223. `(${readyStates[this.readyState]})`
  224. );
  225. if (cb) return cb(err);
  226. throw err;
  227. }
  228. if (typeof data === 'number') data = data.toString();
  229. if (mask === undefined) mask = !this._isServer;
  230. this._sender.ping(data || constants.EMPTY_BUFFER, mask, cb);
  231. }
  232. /**
  233. * Send a pong.
  234. *
  235. * @param {*} data The data to send
  236. * @param {Boolean} mask Indicates whether or not to mask `data`
  237. * @param {Function} cb Callback which is executed when the pong is sent
  238. * @public
  239. */
  240. pong(data, mask, cb) {
  241. if (typeof data === 'function') {
  242. cb = data;
  243. data = mask = undefined;
  244. } else if (typeof mask === 'function') {
  245. cb = mask;
  246. mask = undefined;
  247. }
  248. if (this.readyState !== WebSocket.OPEN) {
  249. const err = new Error(
  250. `WebSocket is not open: readyState ${this.readyState} ` +
  251. `(${readyStates[this.readyState]})`
  252. );
  253. if (cb) return cb(err);
  254. throw err;
  255. }
  256. if (typeof data === 'number') data = data.toString();
  257. if (mask === undefined) mask = !this._isServer;
  258. this._sender.pong(data || constants.EMPTY_BUFFER, mask, cb);
  259. }
  260. /**
  261. * Send a data message.
  262. *
  263. * @param {*} data The message to send
  264. * @param {Object} options Options object
  265. * @param {Boolean} options.compress Specifies whether or not to compress `data`
  266. * @param {Boolean} options.binary Specifies whether `data` is binary or text
  267. * @param {Boolean} options.fin Specifies whether the fragment is the last one
  268. * @param {Boolean} options.mask Specifies whether or not to mask `data`
  269. * @param {Function} cb Callback which is executed when data is written out
  270. * @public
  271. */
  272. send(data, options, cb) {
  273. if (typeof options === 'function') {
  274. cb = options;
  275. options = {};
  276. }
  277. if (this.readyState !== WebSocket.OPEN) {
  278. const err = new Error(
  279. `WebSocket is not open: readyState ${this.readyState} ` +
  280. `(${readyStates[this.readyState]})`
  281. );
  282. if (cb) return cb(err);
  283. throw err;
  284. }
  285. if (typeof data === 'number') data = data.toString();
  286. const opts = Object.assign(
  287. {
  288. binary: typeof data !== 'string',
  289. mask: !this._isServer,
  290. compress: true,
  291. fin: true
  292. },
  293. options
  294. );
  295. if (!this._extensions[PerMessageDeflate.extensionName]) {
  296. opts.compress = false;
  297. }
  298. this._sender.send(data || constants.EMPTY_BUFFER, opts, cb);
  299. }
  300. /**
  301. * Forcibly close the connection.
  302. *
  303. * @public
  304. */
  305. terminate() {
  306. if (this.readyState === WebSocket.CLOSED) return;
  307. if (this.readyState === WebSocket.CONNECTING) {
  308. const msg = 'WebSocket was closed before the connection was established';
  309. return abortHandshake(this, this._req, msg);
  310. }
  311. if (this._socket) {
  312. this.readyState = WebSocket.CLOSING;
  313. this._socket.destroy();
  314. }
  315. }
  316. }
  317. readyStates.forEach((readyState, i) => {
  318. WebSocket[readyState] = i;
  319. });
  320. //
  321. // Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes.
  322. // See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface
  323. //
  324. ['open', 'error', 'close', 'message'].forEach((method) => {
  325. Object.defineProperty(WebSocket.prototype, `on${method}`, {
  326. /**
  327. * Return the listener of the event.
  328. *
  329. * @return {(Function|undefined)} The event listener or `undefined`
  330. * @public
  331. */
  332. get() {
  333. const listeners = this.listeners(method);
  334. for (var i = 0; i < listeners.length; i++) {
  335. if (listeners[i]._listener) return listeners[i]._listener;
  336. }
  337. return undefined;
  338. },
  339. /**
  340. * Add a listener for the event.
  341. *
  342. * @param {Function} listener The listener to add
  343. * @public
  344. */
  345. set(listener) {
  346. const listeners = this.listeners(method);
  347. for (var i = 0; i < listeners.length; i++) {
  348. //
  349. // Remove only the listeners added via `addEventListener`.
  350. //
  351. if (listeners[i]._listener) this.removeListener(method, listeners[i]);
  352. }
  353. this.addEventListener(method, listener);
  354. }
  355. });
  356. });
  357. WebSocket.prototype.addEventListener = EventTarget.addEventListener;
  358. WebSocket.prototype.removeEventListener = EventTarget.removeEventListener;
  359. module.exports = WebSocket;
  360. /**
  361. * Initialize a WebSocket client.
  362. *
  363. * @param {(String|url.Url|url.URL)} address The URL to which to connect
  364. * @param {String} protocols The subprotocols
  365. * @param {Object} options Connection options
  366. * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate
  367. * @param {Number} options.handshakeTimeout Timeout in milliseconds for the handshake request
  368. * @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` header
  369. * @param {String} options.origin Value of the `Origin` or `Sec-WebSocket-Origin` header
  370. * @param {Number} options.maxPayload The maximum allowed message size
  371. * @private
  372. */
  373. function initAsClient(address, protocols, options) {
  374. options = Object.assign(
  375. {
  376. protocolVersion: protocolVersions[1],
  377. perMessageDeflate: true,
  378. maxPayload: 100 * 1024 * 1024
  379. },
  380. options,
  381. {
  382. createConnection: undefined,
  383. socketPath: undefined,
  384. hostname: undefined,
  385. protocol: undefined,
  386. timeout: undefined,
  387. method: undefined,
  388. auth: undefined,
  389. host: undefined,
  390. path: undefined,
  391. port: undefined
  392. }
  393. );
  394. if (!protocolVersions.includes(options.protocolVersion)) {
  395. throw new RangeError(
  396. `Unsupported protocol version: ${options.protocolVersion} ` +
  397. `(supported versions: ${protocolVersions.join(', ')})`
  398. );
  399. }
  400. this._isServer = false;
  401. var parsedUrl;
  402. if (typeof address === 'object' && address.href !== undefined) {
  403. parsedUrl = address;
  404. this.url = address.href;
  405. } else {
  406. //
  407. // The WHATWG URL constructor is not available on Node.js < 6.13.0
  408. //
  409. parsedUrl = url.URL ? new url.URL(address) : url.parse(address);
  410. this.url = address;
  411. }
  412. const isUnixSocket = parsedUrl.protocol === 'ws+unix:';
  413. if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) {
  414. throw new Error(`Invalid URL: ${this.url}`);
  415. }
  416. const isSecure =
  417. parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:';
  418. const defaultPort = isSecure ? 443 : 80;
  419. const key = crypto.randomBytes(16).toString('base64');
  420. const httpObj = isSecure ? https : http;
  421. const path = parsedUrl.search
  422. ? `${parsedUrl.pathname || '/'}${parsedUrl.search}`
  423. : parsedUrl.pathname || '/';
  424. var perMessageDeflate;
  425. options.createConnection = isSecure ? tlsConnect : netConnect;
  426. options.defaultPort = options.defaultPort || defaultPort;
  427. options.port = parsedUrl.port || defaultPort;
  428. options.host = parsedUrl.hostname.startsWith('[')
  429. ? parsedUrl.hostname.slice(1, -1)
  430. : parsedUrl.hostname;
  431. options.headers = Object.assign(
  432. {
  433. 'Sec-WebSocket-Version': options.protocolVersion,
  434. 'Sec-WebSocket-Key': key,
  435. Connection: 'Upgrade',
  436. Upgrade: 'websocket'
  437. },
  438. options.headers
  439. );
  440. options.path = path;
  441. options.timeout = options.handshakeTimeout;
  442. if (options.perMessageDeflate) {
  443. perMessageDeflate = new PerMessageDeflate(
  444. options.perMessageDeflate !== true ? options.perMessageDeflate : {},
  445. false,
  446. options.maxPayload
  447. );
  448. options.headers['Sec-WebSocket-Extensions'] = extension.format({
  449. [PerMessageDeflate.extensionName]: perMessageDeflate.offer()
  450. });
  451. }
  452. if (protocols) {
  453. options.headers['Sec-WebSocket-Protocol'] = protocols;
  454. }
  455. if (options.origin) {
  456. if (options.protocolVersion < 13) {
  457. options.headers['Sec-WebSocket-Origin'] = options.origin;
  458. } else {
  459. options.headers.Origin = options.origin;
  460. }
  461. }
  462. if (parsedUrl.auth) {
  463. options.auth = parsedUrl.auth;
  464. } else if (parsedUrl.username || parsedUrl.password) {
  465. options.auth = `${parsedUrl.username}:${parsedUrl.password}`;
  466. }
  467. if (isUnixSocket) {
  468. const parts = path.split(':');
  469. options.socketPath = parts[0];
  470. options.path = parts[1];
  471. }
  472. var req = (this._req = httpObj.get(options));
  473. if (options.handshakeTimeout) {
  474. req.on('timeout', () => {
  475. abortHandshake(this, req, 'Opening handshake has timed out');
  476. });
  477. }
  478. req.on('error', (err) => {
  479. if (this._req.aborted) return;
  480. req = this._req = null;
  481. this.readyState = WebSocket.CLOSING;
  482. this.emit('error', err);
  483. this.emitClose();
  484. });
  485. req.on('response', (res) => {
  486. if (this.emit('unexpected-response', req, res)) return;
  487. abortHandshake(this, req, `Unexpected server response: ${res.statusCode}`);
  488. });
  489. req.on('upgrade', (res, socket, head) => {
  490. this.emit('upgrade', res);
  491. //
  492. // The user may have closed the connection from a listener of the `upgrade`
  493. // event.
  494. //
  495. if (this.readyState !== WebSocket.CONNECTING) return;
  496. req = this._req = null;
  497. const digest = crypto
  498. .createHash('sha1')
  499. .update(key + constants.GUID, 'binary')
  500. .digest('base64');
  501. if (res.headers['sec-websocket-accept'] !== digest) {
  502. abortHandshake(this, socket, 'Invalid Sec-WebSocket-Accept header');
  503. return;
  504. }
  505. const serverProt = res.headers['sec-websocket-protocol'];
  506. const protList = (protocols || '').split(/, */);
  507. var protError;
  508. if (!protocols && serverProt) {
  509. protError = 'Server sent a subprotocol but none was requested';
  510. } else if (protocols && !serverProt) {
  511. protError = 'Server sent no subprotocol';
  512. } else if (serverProt && !protList.includes(serverProt)) {
  513. protError = 'Server sent an invalid subprotocol';
  514. }
  515. if (protError) {
  516. abortHandshake(this, socket, protError);
  517. return;
  518. }
  519. if (serverProt) this.protocol = serverProt;
  520. if (perMessageDeflate) {
  521. try {
  522. const extensions = extension.parse(
  523. res.headers['sec-websocket-extensions']
  524. );
  525. if (extensions[PerMessageDeflate.extensionName]) {
  526. perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
  527. this._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
  528. }
  529. } catch (err) {
  530. abortHandshake(this, socket, 'Invalid Sec-WebSocket-Extensions header');
  531. return;
  532. }
  533. }
  534. this.setSocket(socket, head, options.maxPayload);
  535. });
  536. }
  537. /**
  538. * Create a `net.Socket` and initiate a connection.
  539. *
  540. * @param {Object} options Connection options
  541. * @return {net.Socket} The newly created socket used to start the connection
  542. * @private
  543. */
  544. function netConnect(options) {
  545. //
  546. // Override `options.path` only if `options` is a copy of the original options
  547. // object. This is always true on Node.js >= 8 but not on Node.js 6 where
  548. // `options.socketPath` might be `undefined` even if the `socketPath` option
  549. // was originally set.
  550. //
  551. if (options.protocolVersion) options.path = options.socketPath;
  552. return net.connect(options);
  553. }
  554. /**
  555. * Create a `tls.TLSSocket` and initiate a connection.
  556. *
  557. * @param {Object} options Connection options
  558. * @return {tls.TLSSocket} The newly created socket used to start the connection
  559. * @private
  560. */
  561. function tlsConnect(options) {
  562. options.path = undefined;
  563. options.servername = options.servername || options.host;
  564. return tls.connect(options);
  565. }
  566. /**
  567. * Abort the handshake and emit an error.
  568. *
  569. * @param {WebSocket} websocket The WebSocket instance
  570. * @param {(http.ClientRequest|net.Socket)} stream The request to abort or the
  571. * socket to destroy
  572. * @param {String} message The error message
  573. * @private
  574. */
  575. function abortHandshake(websocket, stream, message) {
  576. websocket.readyState = WebSocket.CLOSING;
  577. const err = new Error(message);
  578. Error.captureStackTrace(err, abortHandshake);
  579. if (stream.setHeader) {
  580. stream.abort();
  581. stream.once('abort', websocket.emitClose.bind(websocket));
  582. websocket.emit('error', err);
  583. } else {
  584. stream.destroy(err);
  585. stream.once('error', websocket.emit.bind(websocket, 'error'));
  586. stream.once('close', websocket.emitClose.bind(websocket));
  587. }
  588. }
  589. /**
  590. * The listener of the `Receiver` `'conclude'` event.
  591. *
  592. * @param {Number} code The status code
  593. * @param {String} reason The reason for closing
  594. * @private
  595. */
  596. function receiverOnConclude(code, reason) {
  597. const websocket = this[kWebSocket];
  598. websocket._socket.removeListener('data', socketOnData);
  599. websocket._socket.resume();
  600. websocket._closeFrameReceived = true;
  601. websocket._closeMessage = reason;
  602. websocket._closeCode = code;
  603. if (code === 1005) websocket.close();
  604. else websocket.close(code, reason);
  605. }
  606. /**
  607. * The listener of the `Receiver` `'drain'` event.
  608. *
  609. * @private
  610. */
  611. function receiverOnDrain() {
  612. this[kWebSocket]._socket.resume();
  613. }
  614. /**
  615. * The listener of the `Receiver` `'error'` event.
  616. *
  617. * @param {(RangeError|Error)} err The emitted error
  618. * @private
  619. */
  620. function receiverOnError(err) {
  621. const websocket = this[kWebSocket];
  622. websocket._socket.removeListener('data', socketOnData);
  623. websocket.readyState = WebSocket.CLOSING;
  624. websocket._closeCode = err[constants.kStatusCode];
  625. websocket.emit('error', err);
  626. websocket._socket.destroy();
  627. }
  628. /**
  629. * The listener of the `Receiver` `'finish'` event.
  630. *
  631. * @private
  632. */
  633. function receiverOnFinish() {
  634. this[kWebSocket].emitClose();
  635. }
  636. /**
  637. * The listener of the `Receiver` `'message'` event.
  638. *
  639. * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The message
  640. * @private
  641. */
  642. function receiverOnMessage(data) {
  643. this[kWebSocket].emit('message', data);
  644. }
  645. /**
  646. * The listener of the `Receiver` `'ping'` event.
  647. *
  648. * @param {Buffer} data The data included in the ping frame
  649. * @private
  650. */
  651. function receiverOnPing(data) {
  652. const websocket = this[kWebSocket];
  653. websocket.pong(data, !websocket._isServer, constants.NOOP);
  654. websocket.emit('ping', data);
  655. }
  656. /**
  657. * The listener of the `Receiver` `'pong'` event.
  658. *
  659. * @param {Buffer} data The data included in the pong frame
  660. * @private
  661. */
  662. function receiverOnPong(data) {
  663. this[kWebSocket].emit('pong', data);
  664. }
  665. /**
  666. * The listener of the `net.Socket` `'close'` event.
  667. *
  668. * @private
  669. */
  670. function socketOnClose() {
  671. const websocket = this[kWebSocket];
  672. this.removeListener('close', socketOnClose);
  673. this.removeListener('end', socketOnEnd);
  674. websocket.readyState = WebSocket.CLOSING;
  675. //
  676. // The close frame might not have been received or the `'end'` event emitted,
  677. // for example, if the socket was destroyed due to an error. Ensure that the
  678. // `receiver` stream is closed after writing any remaining buffered data to
  679. // it. If the readable side of the socket is in flowing mode then there is no
  680. // buffered data as everything has been already written and `readable.read()`
  681. // will return `null`. If instead, the socket is paused, any possible buffered
  682. // data will be read as a single chunk and emitted synchronously in a single
  683. // `'data'` event.
  684. //
  685. websocket._socket.read();
  686. websocket._receiver.end();
  687. this.removeListener('data', socketOnData);
  688. this[kWebSocket] = undefined;
  689. clearTimeout(websocket._closeTimer);
  690. if (
  691. websocket._receiver._writableState.finished ||
  692. websocket._receiver._writableState.errorEmitted
  693. ) {
  694. websocket.emitClose();
  695. } else {
  696. websocket._receiver.on('error', receiverOnFinish);
  697. websocket._receiver.on('finish', receiverOnFinish);
  698. }
  699. }
  700. /**
  701. * The listener of the `net.Socket` `'data'` event.
  702. *
  703. * @param {Buffer} chunk A chunk of data
  704. * @private
  705. */
  706. function socketOnData(chunk) {
  707. if (!this[kWebSocket]._receiver.write(chunk)) {
  708. this.pause();
  709. }
  710. }
  711. /**
  712. * The listener of the `net.Socket` `'end'` event.
  713. *
  714. * @private
  715. */
  716. function socketOnEnd() {
  717. const websocket = this[kWebSocket];
  718. websocket.readyState = WebSocket.CLOSING;
  719. websocket._receiver.end();
  720. this.end();
  721. }
  722. /**
  723. * The listener of the `net.Socket` `'error'` event.
  724. *
  725. * @private
  726. */
  727. function socketOnError() {
  728. const websocket = this[kWebSocket];
  729. this.removeListener('error', socketOnError);
  730. this.on('error', constants.NOOP);
  731. if (websocket) {
  732. websocket.readyState = WebSocket.CLOSING;
  733. this.destroy();
  734. }
  735. }