websocket.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149
  1. /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Readable$" }] */
  2. 'use strict';
  3. const EventEmitter = require('events');
  4. const https = require('https');
  5. const http = require('http');
  6. const net = require('net');
  7. const tls = require('tls');
  8. const { randomBytes, createHash } = require('crypto');
  9. const { Readable } = require('stream');
  10. const { URL } = require('url');
  11. const PerMessageDeflate = require('./permessage-deflate');
  12. const Receiver = require('./receiver');
  13. const Sender = require('./sender');
  14. const {
  15. BINARY_TYPES,
  16. EMPTY_BUFFER,
  17. GUID,
  18. kForOnEventAttribute,
  19. kListener,
  20. kStatusCode,
  21. kWebSocket,
  22. NOOP
  23. } = require('./constants');
  24. const {
  25. EventTarget: { addEventListener, removeEventListener }
  26. } = require('./event-target');
  27. const { format, parse } = require('./extension');
  28. const { toBuffer } = require('./buffer-util');
  29. const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
  30. const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
  31. const protocolVersions = [8, 13];
  32. const closeTimeout = 30 * 1000;
  33. /**
  34. * Class representing a WebSocket.
  35. *
  36. * @extends EventEmitter
  37. */
  38. class WebSocket extends EventEmitter {
  39. /**
  40. * Create a new `WebSocket`.
  41. *
  42. * @param {(String|URL)} address The URL to which to connect
  43. * @param {(String|String[])} [protocols] The subprotocols
  44. * @param {Object} [options] Connection options
  45. */
  46. constructor(address, protocols, options) {
  47. super();
  48. this._binaryType = BINARY_TYPES[0];
  49. this._closeCode = 1006;
  50. this._closeFrameReceived = false;
  51. this._closeFrameSent = false;
  52. this._closeMessage = EMPTY_BUFFER;
  53. this._closeTimer = null;
  54. this._extensions = {};
  55. this._protocol = '';
  56. this._readyState = WebSocket.CONNECTING;
  57. this._receiver = null;
  58. this._sender = null;
  59. this._socket = null;
  60. if (address !== null) {
  61. this._bufferedAmount = 0;
  62. this._isServer = false;
  63. this._redirects = 0;
  64. if (protocols === undefined) {
  65. protocols = [];
  66. } else if (!Array.isArray(protocols)) {
  67. if (typeof protocols === 'object' && protocols !== null) {
  68. options = protocols;
  69. protocols = [];
  70. } else {
  71. protocols = [protocols];
  72. }
  73. }
  74. initAsClient(this, address, protocols, options);
  75. } else {
  76. this._isServer = true;
  77. }
  78. }
  79. /**
  80. * This deviates from the WHATWG interface since ws doesn't support the
  81. * required default "blob" type (instead we define a custom "nodebuffer"
  82. * type).
  83. *
  84. * @type {String}
  85. */
  86. get binaryType() {
  87. return this._binaryType;
  88. }
  89. set binaryType(type) {
  90. if (!BINARY_TYPES.includes(type)) return;
  91. this._binaryType = type;
  92. //
  93. // Allow to change `binaryType` on the fly.
  94. //
  95. if (this._receiver) this._receiver._binaryType = type;
  96. }
  97. /**
  98. * @type {Number}
  99. */
  100. get bufferedAmount() {
  101. if (!this._socket) return this._bufferedAmount;
  102. return this._socket._writableState.length + this._sender._bufferedBytes;
  103. }
  104. /**
  105. * @type {String}
  106. */
  107. get extensions() {
  108. return Object.keys(this._extensions).join();
  109. }
  110. /**
  111. * @type {Function}
  112. */
  113. /* istanbul ignore next */
  114. get onclose() {
  115. return null;
  116. }
  117. /**
  118. * @type {Function}
  119. */
  120. /* istanbul ignore next */
  121. get onerror() {
  122. return null;
  123. }
  124. /**
  125. * @type {Function}
  126. */
  127. /* istanbul ignore next */
  128. get onopen() {
  129. return null;
  130. }
  131. /**
  132. * @type {Function}
  133. */
  134. /* istanbul ignore next */
  135. get onmessage() {
  136. return null;
  137. }
  138. /**
  139. * @type {String}
  140. */
  141. get protocol() {
  142. return this._protocol;
  143. }
  144. /**
  145. * @type {Number}
  146. */
  147. get readyState() {
  148. return this._readyState;
  149. }
  150. /**
  151. * @type {String}
  152. */
  153. get url() {
  154. return this._url;
  155. }
  156. /**
  157. * Set up the socket and the internal resources.
  158. *
  159. * @param {(net.Socket|tls.Socket)} socket The network socket between the
  160. * server and client
  161. * @param {Buffer} head The first packet of the upgraded stream
  162. * @param {Object} options Options object
  163. * @param {Number} [options.maxPayload=0] The maximum allowed message size
  164. * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
  165. * not to skip UTF-8 validation for text and close messages
  166. * @private
  167. */
  168. setSocket(socket, head, options) {
  169. const receiver = new Receiver({
  170. binaryType: this.binaryType,
  171. extensions: this._extensions,
  172. isServer: this._isServer,
  173. maxPayload: options.maxPayload,
  174. skipUTF8Validation: options.skipUTF8Validation
  175. });
  176. this._sender = new Sender(socket, this._extensions);
  177. this._receiver = receiver;
  178. this._socket = socket;
  179. receiver[kWebSocket] = this;
  180. socket[kWebSocket] = this;
  181. receiver.on('conclude', receiverOnConclude);
  182. receiver.on('drain', receiverOnDrain);
  183. receiver.on('error', receiverOnError);
  184. receiver.on('message', receiverOnMessage);
  185. receiver.on('ping', receiverOnPing);
  186. receiver.on('pong', receiverOnPong);
  187. socket.setTimeout(0);
  188. socket.setNoDelay();
  189. if (head.length > 0) socket.unshift(head);
  190. socket.on('close', socketOnClose);
  191. socket.on('data', socketOnData);
  192. socket.on('end', socketOnEnd);
  193. socket.on('error', socketOnError);
  194. this._readyState = WebSocket.OPEN;
  195. this.emit('open');
  196. }
  197. /**
  198. * Emit the `'close'` event.
  199. *
  200. * @private
  201. */
  202. emitClose() {
  203. if (!this._socket) {
  204. this._readyState = WebSocket.CLOSED;
  205. this.emit('close', this._closeCode, this._closeMessage);
  206. return;
  207. }
  208. if (this._extensions[PerMessageDeflate.extensionName]) {
  209. this._extensions[PerMessageDeflate.extensionName].cleanup();
  210. }
  211. this._receiver.removeAllListeners();
  212. this._readyState = WebSocket.CLOSED;
  213. this.emit('close', this._closeCode, this._closeMessage);
  214. }
  215. /**
  216. * Start a closing handshake.
  217. *
  218. * +----------+ +-----------+ +----------+
  219. * - - -|ws.close()|-->|close frame|-->|ws.close()|- - -
  220. * | +----------+ +-----------+ +----------+ |
  221. * +----------+ +-----------+ |
  222. * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING
  223. * +----------+ +-----------+ |
  224. * | | | +---+ |
  225. * +------------------------+-->|fin| - - - -
  226. * | +---+ | +---+
  227. * - - - - -|fin|<---------------------+
  228. * +---+
  229. *
  230. * @param {Number} [code] Status code explaining why the connection is closing
  231. * @param {(String|Buffer)} [data] The reason why the connection is
  232. * closing
  233. * @public
  234. */
  235. close(code, data) {
  236. if (this.readyState === WebSocket.CLOSED) return;
  237. if (this.readyState === WebSocket.CONNECTING) {
  238. const msg = 'WebSocket was closed before the connection was established';
  239. return abortHandshake(this, this._req, msg);
  240. }
  241. if (this.readyState === WebSocket.CLOSING) {
  242. if (
  243. this._closeFrameSent &&
  244. (this._closeFrameReceived || this._receiver._writableState.errorEmitted)
  245. ) {
  246. this._socket.end();
  247. }
  248. return;
  249. }
  250. this._readyState = WebSocket.CLOSING;
  251. this._sender.close(code, data, !this._isServer, (err) => {
  252. //
  253. // This error is handled by the `'error'` listener on the socket. We only
  254. // want to know if the close frame has been sent here.
  255. //
  256. if (err) return;
  257. this._closeFrameSent = true;
  258. if (
  259. this._closeFrameReceived ||
  260. this._receiver._writableState.errorEmitted
  261. ) {
  262. this._socket.end();
  263. }
  264. });
  265. //
  266. // Specify a timeout for the closing handshake to complete.
  267. //
  268. this._closeTimer = setTimeout(
  269. this._socket.destroy.bind(this._socket),
  270. closeTimeout
  271. );
  272. }
  273. /**
  274. * Send a ping.
  275. *
  276. * @param {*} [data] The data to send
  277. * @param {Boolean} [mask] Indicates whether or not to mask `data`
  278. * @param {Function} [cb] Callback which is executed when the ping is sent
  279. * @public
  280. */
  281. ping(data, mask, cb) {
  282. if (this.readyState === WebSocket.CONNECTING) {
  283. throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
  284. }
  285. if (typeof data === 'function') {
  286. cb = data;
  287. data = mask = undefined;
  288. } else if (typeof mask === 'function') {
  289. cb = mask;
  290. mask = undefined;
  291. }
  292. if (typeof data === 'number') data = data.toString();
  293. if (this.readyState !== WebSocket.OPEN) {
  294. sendAfterClose(this, data, cb);
  295. return;
  296. }
  297. if (mask === undefined) mask = !this._isServer;
  298. this._sender.ping(data || EMPTY_BUFFER, mask, cb);
  299. }
  300. /**
  301. * Send a pong.
  302. *
  303. * @param {*} [data] The data to send
  304. * @param {Boolean} [mask] Indicates whether or not to mask `data`
  305. * @param {Function} [cb] Callback which is executed when the pong is sent
  306. * @public
  307. */
  308. pong(data, mask, cb) {
  309. if (this.readyState === WebSocket.CONNECTING) {
  310. throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
  311. }
  312. if (typeof data === 'function') {
  313. cb = data;
  314. data = mask = undefined;
  315. } else if (typeof mask === 'function') {
  316. cb = mask;
  317. mask = undefined;
  318. }
  319. if (typeof data === 'number') data = data.toString();
  320. if (this.readyState !== WebSocket.OPEN) {
  321. sendAfterClose(this, data, cb);
  322. return;
  323. }
  324. if (mask === undefined) mask = !this._isServer;
  325. this._sender.pong(data || EMPTY_BUFFER, mask, cb);
  326. }
  327. /**
  328. * Send a data message.
  329. *
  330. * @param {*} data The message to send
  331. * @param {Object} [options] Options object
  332. * @param {Boolean} [options.binary] Specifies whether `data` is binary or
  333. * text
  334. * @param {Boolean} [options.compress] Specifies whether or not to compress
  335. * `data`
  336. * @param {Boolean} [options.fin=true] Specifies whether the fragment is the
  337. * last one
  338. * @param {Boolean} [options.mask] Specifies whether or not to mask `data`
  339. * @param {Function} [cb] Callback which is executed when data is written out
  340. * @public
  341. */
  342. send(data, options, cb) {
  343. if (this.readyState === WebSocket.CONNECTING) {
  344. throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
  345. }
  346. if (typeof options === 'function') {
  347. cb = options;
  348. options = {};
  349. }
  350. if (typeof data === 'number') data = data.toString();
  351. if (this.readyState !== WebSocket.OPEN) {
  352. sendAfterClose(this, data, cb);
  353. return;
  354. }
  355. const opts = {
  356. binary: typeof data !== 'string',
  357. mask: !this._isServer,
  358. compress: true,
  359. fin: true,
  360. ...options
  361. };
  362. if (!this._extensions[PerMessageDeflate.extensionName]) {
  363. opts.compress = false;
  364. }
  365. this._sender.send(data || EMPTY_BUFFER, opts, cb);
  366. }
  367. /**
  368. * Forcibly close the connection.
  369. *
  370. * @public
  371. */
  372. terminate() {
  373. if (this.readyState === WebSocket.CLOSED) return;
  374. if (this.readyState === WebSocket.CONNECTING) {
  375. const msg = 'WebSocket was closed before the connection was established';
  376. return abortHandshake(this, this._req, msg);
  377. }
  378. if (this._socket) {
  379. this._readyState = WebSocket.CLOSING;
  380. this._socket.destroy();
  381. }
  382. }
  383. }
  384. /**
  385. * @constant {Number} CONNECTING
  386. * @memberof WebSocket
  387. */
  388. Object.defineProperty(WebSocket, 'CONNECTING', {
  389. enumerable: true,
  390. value: readyStates.indexOf('CONNECTING')
  391. });
  392. /**
  393. * @constant {Number} CONNECTING
  394. * @memberof WebSocket.prototype
  395. */
  396. Object.defineProperty(WebSocket.prototype, 'CONNECTING', {
  397. enumerable: true,
  398. value: readyStates.indexOf('CONNECTING')
  399. });
  400. /**
  401. * @constant {Number} OPEN
  402. * @memberof WebSocket
  403. */
  404. Object.defineProperty(WebSocket, 'OPEN', {
  405. enumerable: true,
  406. value: readyStates.indexOf('OPEN')
  407. });
  408. /**
  409. * @constant {Number} OPEN
  410. * @memberof WebSocket.prototype
  411. */
  412. Object.defineProperty(WebSocket.prototype, 'OPEN', {
  413. enumerable: true,
  414. value: readyStates.indexOf('OPEN')
  415. });
  416. /**
  417. * @constant {Number} CLOSING
  418. * @memberof WebSocket
  419. */
  420. Object.defineProperty(WebSocket, 'CLOSING', {
  421. enumerable: true,
  422. value: readyStates.indexOf('CLOSING')
  423. });
  424. /**
  425. * @constant {Number} CLOSING
  426. * @memberof WebSocket.prototype
  427. */
  428. Object.defineProperty(WebSocket.prototype, 'CLOSING', {
  429. enumerable: true,
  430. value: readyStates.indexOf('CLOSING')
  431. });
  432. /**
  433. * @constant {Number} CLOSED
  434. * @memberof WebSocket
  435. */
  436. Object.defineProperty(WebSocket, 'CLOSED', {
  437. enumerable: true,
  438. value: readyStates.indexOf('CLOSED')
  439. });
  440. /**
  441. * @constant {Number} CLOSED
  442. * @memberof WebSocket.prototype
  443. */
  444. Object.defineProperty(WebSocket.prototype, 'CLOSED', {
  445. enumerable: true,
  446. value: readyStates.indexOf('CLOSED')
  447. });
  448. [
  449. 'binaryType',
  450. 'bufferedAmount',
  451. 'extensions',
  452. 'protocol',
  453. 'readyState',
  454. 'url'
  455. ].forEach((property) => {
  456. Object.defineProperty(WebSocket.prototype, property, { enumerable: true });
  457. });
  458. //
  459. // Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes.
  460. // See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface
  461. //
  462. ['open', 'error', 'close', 'message'].forEach((method) => {
  463. Object.defineProperty(WebSocket.prototype, `on${method}`, {
  464. enumerable: true,
  465. get() {
  466. for (const listener of this.listeners(method)) {
  467. if (listener[kForOnEventAttribute]) return listener[kListener];
  468. }
  469. return null;
  470. },
  471. set(handler) {
  472. for (const listener of this.listeners(method)) {
  473. if (listener[kForOnEventAttribute]) {
  474. this.removeListener(method, listener);
  475. break;
  476. }
  477. }
  478. if (typeof handler !== 'function') return;
  479. this.addEventListener(method, handler, {
  480. [kForOnEventAttribute]: true
  481. });
  482. }
  483. });
  484. });
  485. WebSocket.prototype.addEventListener = addEventListener;
  486. WebSocket.prototype.removeEventListener = removeEventListener;
  487. module.exports = WebSocket;
  488. /**
  489. * Initialize a WebSocket client.
  490. *
  491. * @param {WebSocket} websocket The client to initialize
  492. * @param {(String|URL)} address The URL to which to connect
  493. * @param {Array} protocols The subprotocols
  494. * @param {Object} [options] Connection options
  495. * @param {Boolean} [options.followRedirects=false] Whether or not to follow
  496. * redirects
  497. * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
  498. * handshake request
  499. * @param {Number} [options.maxPayload=104857600] The maximum allowed message
  500. * size
  501. * @param {Number} [options.maxRedirects=10] The maximum number of redirects
  502. * allowed
  503. * @param {String} [options.origin] Value of the `Origin` or
  504. * `Sec-WebSocket-Origin` header
  505. * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
  506. * permessage-deflate
  507. * @param {Number} [options.protocolVersion=13] Value of the
  508. * `Sec-WebSocket-Version` header
  509. * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
  510. * not to skip UTF-8 validation for text and close messages
  511. * @private
  512. */
  513. function initAsClient(websocket, address, protocols, options) {
  514. const opts = {
  515. protocolVersion: protocolVersions[1],
  516. maxPayload: 100 * 1024 * 1024,
  517. skipUTF8Validation: false,
  518. perMessageDeflate: true,
  519. followRedirects: false,
  520. maxRedirects: 10,
  521. ...options,
  522. createConnection: undefined,
  523. socketPath: undefined,
  524. hostname: undefined,
  525. protocol: undefined,
  526. timeout: undefined,
  527. method: undefined,
  528. host: undefined,
  529. path: undefined,
  530. port: undefined
  531. };
  532. if (!protocolVersions.includes(opts.protocolVersion)) {
  533. throw new RangeError(
  534. `Unsupported protocol version: ${opts.protocolVersion} ` +
  535. `(supported versions: ${protocolVersions.join(', ')})`
  536. );
  537. }
  538. let parsedUrl;
  539. if (address instanceof URL) {
  540. parsedUrl = address;
  541. websocket._url = address.href;
  542. } else {
  543. try {
  544. parsedUrl = new URL(address);
  545. } catch (e) {
  546. throw new SyntaxError(`Invalid URL: ${address}`);
  547. }
  548. websocket._url = address;
  549. }
  550. const isSecure = parsedUrl.protocol === 'wss:';
  551. const isUnixSocket = parsedUrl.protocol === 'ws+unix:';
  552. if (parsedUrl.protocol !== 'ws:' && !isSecure && !isUnixSocket) {
  553. throw new SyntaxError(
  554. 'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"'
  555. );
  556. }
  557. if (isUnixSocket && !parsedUrl.pathname) {
  558. throw new SyntaxError("The URL's pathname is empty");
  559. }
  560. if (parsedUrl.hash) {
  561. throw new SyntaxError('The URL contains a fragment identifier');
  562. }
  563. const defaultPort = isSecure ? 443 : 80;
  564. const key = randomBytes(16).toString('base64');
  565. const get = isSecure ? https.get : http.get;
  566. const protocolSet = new Set();
  567. let perMessageDeflate;
  568. opts.createConnection = isSecure ? tlsConnect : netConnect;
  569. opts.defaultPort = opts.defaultPort || defaultPort;
  570. opts.port = parsedUrl.port || defaultPort;
  571. opts.host = parsedUrl.hostname.startsWith('[')
  572. ? parsedUrl.hostname.slice(1, -1)
  573. : parsedUrl.hostname;
  574. opts.headers = {
  575. 'Sec-WebSocket-Version': opts.protocolVersion,
  576. 'Sec-WebSocket-Key': key,
  577. Connection: 'Upgrade',
  578. Upgrade: 'websocket',
  579. ...opts.headers
  580. };
  581. opts.path = parsedUrl.pathname + parsedUrl.search;
  582. opts.timeout = opts.handshakeTimeout;
  583. if (opts.perMessageDeflate) {
  584. perMessageDeflate = new PerMessageDeflate(
  585. opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
  586. false,
  587. opts.maxPayload
  588. );
  589. opts.headers['Sec-WebSocket-Extensions'] = format({
  590. [PerMessageDeflate.extensionName]: perMessageDeflate.offer()
  591. });
  592. }
  593. if (protocols.length) {
  594. for (const protocol of protocols) {
  595. if (
  596. typeof protocol !== 'string' ||
  597. !subprotocolRegex.test(protocol) ||
  598. protocolSet.has(protocol)
  599. ) {
  600. throw new SyntaxError(
  601. 'An invalid or duplicated subprotocol was specified'
  602. );
  603. }
  604. protocolSet.add(protocol);
  605. }
  606. opts.headers['Sec-WebSocket-Protocol'] = protocols.join(',');
  607. }
  608. if (opts.origin) {
  609. if (opts.protocolVersion < 13) {
  610. opts.headers['Sec-WebSocket-Origin'] = opts.origin;
  611. } else {
  612. opts.headers.Origin = opts.origin;
  613. }
  614. }
  615. if (parsedUrl.username || parsedUrl.password) {
  616. opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
  617. }
  618. if (isUnixSocket) {
  619. const parts = opts.path.split(':');
  620. opts.socketPath = parts[0];
  621. opts.path = parts[1];
  622. }
  623. let req = (websocket._req = get(opts));
  624. if (opts.timeout) {
  625. req.on('timeout', () => {
  626. abortHandshake(websocket, req, 'Opening handshake has timed out');
  627. });
  628. }
  629. req.on('error', (err) => {
  630. if (req === null || req.aborted) return;
  631. req = websocket._req = null;
  632. websocket._readyState = WebSocket.CLOSING;
  633. websocket.emit('error', err);
  634. websocket.emitClose();
  635. });
  636. req.on('response', (res) => {
  637. const location = res.headers.location;
  638. const statusCode = res.statusCode;
  639. if (
  640. location &&
  641. opts.followRedirects &&
  642. statusCode >= 300 &&
  643. statusCode < 400
  644. ) {
  645. if (++websocket._redirects > opts.maxRedirects) {
  646. abortHandshake(websocket, req, 'Maximum redirects exceeded');
  647. return;
  648. }
  649. req.abort();
  650. const addr = new URL(location, address);
  651. initAsClient(websocket, addr, protocols, options);
  652. } else if (!websocket.emit('unexpected-response', req, res)) {
  653. abortHandshake(
  654. websocket,
  655. req,
  656. `Unexpected server response: ${res.statusCode}`
  657. );
  658. }
  659. });
  660. req.on('upgrade', (res, socket, head) => {
  661. websocket.emit('upgrade', res);
  662. //
  663. // The user may have closed the connection from a listener of the `upgrade`
  664. // event.
  665. //
  666. if (websocket.readyState !== WebSocket.CONNECTING) return;
  667. req = websocket._req = null;
  668. const digest = createHash('sha1')
  669. .update(key + GUID)
  670. .digest('base64');
  671. if (res.headers['sec-websocket-accept'] !== digest) {
  672. abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header');
  673. return;
  674. }
  675. const serverProt = res.headers['sec-websocket-protocol'];
  676. let protError;
  677. if (serverProt !== undefined) {
  678. if (!protocolSet.size) {
  679. protError = 'Server sent a subprotocol but none was requested';
  680. } else if (!protocolSet.has(serverProt)) {
  681. protError = 'Server sent an invalid subprotocol';
  682. }
  683. } else if (protocolSet.size) {
  684. protError = 'Server sent no subprotocol';
  685. }
  686. if (protError) {
  687. abortHandshake(websocket, socket, protError);
  688. return;
  689. }
  690. if (serverProt) websocket._protocol = serverProt;
  691. const secWebSocketExtensions = res.headers['sec-websocket-extensions'];
  692. if (secWebSocketExtensions !== undefined) {
  693. if (!perMessageDeflate) {
  694. const message =
  695. 'Server sent a Sec-WebSocket-Extensions header but no extension ' +
  696. 'was requested';
  697. abortHandshake(websocket, socket, message);
  698. return;
  699. }
  700. let extensions;
  701. try {
  702. extensions = parse(secWebSocketExtensions);
  703. } catch (err) {
  704. const message = 'Invalid Sec-WebSocket-Extensions header';
  705. abortHandshake(websocket, socket, message);
  706. return;
  707. }
  708. const extensionNames = Object.keys(extensions);
  709. if (
  710. extensionNames.length !== 1 ||
  711. extensionNames[0] !== PerMessageDeflate.extensionName
  712. ) {
  713. const message = 'Server indicated an extension that was not requested';
  714. abortHandshake(websocket, socket, message);
  715. return;
  716. }
  717. try {
  718. perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
  719. } catch (err) {
  720. const message = 'Invalid Sec-WebSocket-Extensions header';
  721. abortHandshake(websocket, socket, message);
  722. return;
  723. }
  724. websocket._extensions[PerMessageDeflate.extensionName] =
  725. perMessageDeflate;
  726. }
  727. websocket.setSocket(socket, head, {
  728. maxPayload: opts.maxPayload,
  729. skipUTF8Validation: opts.skipUTF8Validation
  730. });
  731. });
  732. }
  733. /**
  734. * Create a `net.Socket` and initiate a connection.
  735. *
  736. * @param {Object} options Connection options
  737. * @return {net.Socket} The newly created socket used to start the connection
  738. * @private
  739. */
  740. function netConnect(options) {
  741. options.path = options.socketPath;
  742. return net.connect(options);
  743. }
  744. /**
  745. * Create a `tls.TLSSocket` and initiate a connection.
  746. *
  747. * @param {Object} options Connection options
  748. * @return {tls.TLSSocket} The newly created socket used to start the connection
  749. * @private
  750. */
  751. function tlsConnect(options) {
  752. options.path = undefined;
  753. if (!options.servername && options.servername !== '') {
  754. options.servername = net.isIP(options.host) ? '' : options.host;
  755. }
  756. return tls.connect(options);
  757. }
  758. /**
  759. * Abort the handshake and emit an error.
  760. *
  761. * @param {WebSocket} websocket The WebSocket instance
  762. * @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to
  763. * abort or the socket to destroy
  764. * @param {String} message The error message
  765. * @private
  766. */
  767. function abortHandshake(websocket, stream, message) {
  768. websocket._readyState = WebSocket.CLOSING;
  769. const err = new Error(message);
  770. Error.captureStackTrace(err, abortHandshake);
  771. if (stream.setHeader) {
  772. stream.abort();
  773. if (stream.socket && !stream.socket.destroyed) {
  774. //
  775. // On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if
  776. // called after the request completed. See
  777. // https://github.com/websockets/ws/issues/1869.
  778. //
  779. stream.socket.destroy();
  780. }
  781. stream.once('abort', websocket.emitClose.bind(websocket));
  782. websocket.emit('error', err);
  783. } else {
  784. stream.destroy(err);
  785. stream.once('error', websocket.emit.bind(websocket, 'error'));
  786. stream.once('close', websocket.emitClose.bind(websocket));
  787. }
  788. }
  789. /**
  790. * Handle cases where the `ping()`, `pong()`, or `send()` methods are called
  791. * when the `readyState` attribute is `CLOSING` or `CLOSED`.
  792. *
  793. * @param {WebSocket} websocket The WebSocket instance
  794. * @param {*} [data] The data to send
  795. * @param {Function} [cb] Callback
  796. * @private
  797. */
  798. function sendAfterClose(websocket, data, cb) {
  799. if (data) {
  800. const length = toBuffer(data).length;
  801. //
  802. // The `_bufferedAmount` property is used only when the peer is a client and
  803. // the opening handshake fails. Under these circumstances, in fact, the
  804. // `setSocket()` method is not called, so the `_socket` and `_sender`
  805. // properties are set to `null`.
  806. //
  807. if (websocket._socket) websocket._sender._bufferedBytes += length;
  808. else websocket._bufferedAmount += length;
  809. }
  810. if (cb) {
  811. const err = new Error(
  812. `WebSocket is not open: readyState ${websocket.readyState} ` +
  813. `(${readyStates[websocket.readyState]})`
  814. );
  815. cb(err);
  816. }
  817. }
  818. /**
  819. * The listener of the `Receiver` `'conclude'` event.
  820. *
  821. * @param {Number} code The status code
  822. * @param {Buffer} reason The reason for closing
  823. * @private
  824. */
  825. function receiverOnConclude(code, reason) {
  826. const websocket = this[kWebSocket];
  827. websocket._closeFrameReceived = true;
  828. websocket._closeMessage = reason;
  829. websocket._closeCode = code;
  830. if (websocket._socket[kWebSocket] === undefined) return;
  831. websocket._socket.removeListener('data', socketOnData);
  832. process.nextTick(resume, websocket._socket);
  833. if (code === 1005) websocket.close();
  834. else websocket.close(code, reason);
  835. }
  836. /**
  837. * The listener of the `Receiver` `'drain'` event.
  838. *
  839. * @private
  840. */
  841. function receiverOnDrain() {
  842. this[kWebSocket]._socket.resume();
  843. }
  844. /**
  845. * The listener of the `Receiver` `'error'` event.
  846. *
  847. * @param {(RangeError|Error)} err The emitted error
  848. * @private
  849. */
  850. function receiverOnError(err) {
  851. const websocket = this[kWebSocket];
  852. if (websocket._socket[kWebSocket] !== undefined) {
  853. websocket._socket.removeListener('data', socketOnData);
  854. //
  855. // On Node.js < 14.0.0 the `'error'` event is emitted synchronously. See
  856. // https://github.com/websockets/ws/issues/1940.
  857. //
  858. process.nextTick(resume, websocket._socket);
  859. websocket.close(err[kStatusCode]);
  860. }
  861. websocket.emit('error', err);
  862. }
  863. /**
  864. * The listener of the `Receiver` `'finish'` event.
  865. *
  866. * @private
  867. */
  868. function receiverOnFinish() {
  869. this[kWebSocket].emitClose();
  870. }
  871. /**
  872. * The listener of the `Receiver` `'message'` event.
  873. *
  874. * @param {Buffer|ArrayBuffer|Buffer[])} data The message
  875. * @param {Boolean} isBinary Specifies whether the message is binary or not
  876. * @private
  877. */
  878. function receiverOnMessage(data, isBinary) {
  879. this[kWebSocket].emit('message', data, isBinary);
  880. }
  881. /**
  882. * The listener of the `Receiver` `'ping'` event.
  883. *
  884. * @param {Buffer} data The data included in the ping frame
  885. * @private
  886. */
  887. function receiverOnPing(data) {
  888. const websocket = this[kWebSocket];
  889. websocket.pong(data, !websocket._isServer, NOOP);
  890. websocket.emit('ping', data);
  891. }
  892. /**
  893. * The listener of the `Receiver` `'pong'` event.
  894. *
  895. * @param {Buffer} data The data included in the pong frame
  896. * @private
  897. */
  898. function receiverOnPong(data) {
  899. this[kWebSocket].emit('pong', data);
  900. }
  901. /**
  902. * Resume a readable stream
  903. *
  904. * @param {Readable} stream The readable stream
  905. * @private
  906. */
  907. function resume(stream) {
  908. stream.resume();
  909. }
  910. /**
  911. * The listener of the `net.Socket` `'close'` event.
  912. *
  913. * @private
  914. */
  915. function socketOnClose() {
  916. const websocket = this[kWebSocket];
  917. this.removeListener('close', socketOnClose);
  918. this.removeListener('data', socketOnData);
  919. this.removeListener('end', socketOnEnd);
  920. websocket._readyState = WebSocket.CLOSING;
  921. let chunk;
  922. //
  923. // The close frame might not have been received or the `'end'` event emitted,
  924. // for example, if the socket was destroyed due to an error. Ensure that the
  925. // `receiver` stream is closed after writing any remaining buffered data to
  926. // it. If the readable side of the socket is in flowing mode then there is no
  927. // buffered data as everything has been already written and `readable.read()`
  928. // will return `null`. If instead, the socket is paused, any possible buffered
  929. // data will be read as a single chunk.
  930. //
  931. if (
  932. !this._readableState.endEmitted &&
  933. !websocket._closeFrameReceived &&
  934. !websocket._receiver._writableState.errorEmitted &&
  935. (chunk = websocket._socket.read()) !== null
  936. ) {
  937. websocket._receiver.write(chunk);
  938. }
  939. websocket._receiver.end();
  940. this[kWebSocket] = undefined;
  941. clearTimeout(websocket._closeTimer);
  942. if (
  943. websocket._receiver._writableState.finished ||
  944. websocket._receiver._writableState.errorEmitted
  945. ) {
  946. websocket.emitClose();
  947. } else {
  948. websocket._receiver.on('error', receiverOnFinish);
  949. websocket._receiver.on('finish', receiverOnFinish);
  950. }
  951. }
  952. /**
  953. * The listener of the `net.Socket` `'data'` event.
  954. *
  955. * @param {Buffer} chunk A chunk of data
  956. * @private
  957. */
  958. function socketOnData(chunk) {
  959. if (!this[kWebSocket]._receiver.write(chunk)) {
  960. this.pause();
  961. }
  962. }
  963. /**
  964. * The listener of the `net.Socket` `'end'` event.
  965. *
  966. * @private
  967. */
  968. function socketOnEnd() {
  969. const websocket = this[kWebSocket];
  970. websocket._readyState = WebSocket.CLOSING;
  971. websocket._receiver.end();
  972. this.end();
  973. }
  974. /**
  975. * The listener of the `net.Socket` `'error'` event.
  976. *
  977. * @private
  978. */
  979. function socketOnError() {
  980. const websocket = this[kWebSocket];
  981. this.removeListener('error', socketOnError);
  982. this.on('error', NOOP);
  983. if (websocket) {
  984. websocket._readyState = WebSocket.CLOSING;
  985. this.destroy();
  986. }
  987. }