WebSocket.js 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. /*!
  2. * ws: a node.js websocket client
  3. * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
  4. * MIT Licensed
  5. */
  6. 'use strict';
  7. const EventEmitter = require('events');
  8. const crypto = require('crypto');
  9. const Ultron = require('ultron');
  10. const https = require('https');
  11. const http = require('http');
  12. const url = require('url');
  13. const PerMessageDeflate = require('./PerMessageDeflate');
  14. const EventTarget = require('./EventTarget');
  15. const Extensions = require('./Extensions');
  16. const constants = require('./Constants');
  17. const Receiver = require('./Receiver');
  18. const Sender = require('./Sender');
  19. const protocolVersions = [8, 13];
  20. const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.
  21. /**
  22. * Class representing a WebSocket.
  23. *
  24. * @extends EventEmitter
  25. */
  26. class WebSocket extends EventEmitter {
  27. /**
  28. * Create a new `WebSocket`.
  29. *
  30. * @param {String} address The URL to which to connect
  31. * @param {(String|String[])} protocols The subprotocols
  32. * @param {Object} options Connection options
  33. */
  34. constructor (address, protocols, options) {
  35. super();
  36. if (!protocols) {
  37. protocols = [];
  38. } else if (typeof protocols === 'string') {
  39. protocols = [protocols];
  40. } else if (!Array.isArray(protocols)) {
  41. options = protocols;
  42. protocols = [];
  43. }
  44. this.readyState = WebSocket.CONNECTING;
  45. this.bytesReceived = 0;
  46. this.extensions = {};
  47. this.protocol = '';
  48. this._binaryType = constants.BINARY_TYPES[0];
  49. this._finalize = this.finalize.bind(this);
  50. this._finalizeCalled = false;
  51. this._closeMessage = null;
  52. this._closeTimer = null;
  53. this._closeCode = null;
  54. this._receiver = null;
  55. this._sender = null;
  56. this._socket = null;
  57. this._ultron = null;
  58. if (Array.isArray(address)) {
  59. initAsServerClient.call(this, address[0], address[1], options);
  60. } else {
  61. initAsClient.call(this, address, protocols, options);
  62. }
  63. }
  64. get CONNECTING () { return WebSocket.CONNECTING; }
  65. get CLOSING () { return WebSocket.CLOSING; }
  66. get CLOSED () { return WebSocket.CLOSED; }
  67. get OPEN () { return WebSocket.OPEN; }
  68. /**
  69. * @type {Number}
  70. */
  71. get bufferedAmount () {
  72. var amount = 0;
  73. if (this._socket) {
  74. amount = this._socket.bufferSize + this._sender._bufferedBytes;
  75. }
  76. return amount;
  77. }
  78. /**
  79. * This deviates from the WHATWG interface since ws doesn't support the required
  80. * default "blob" type (instead we define a custom "nodebuffer" type).
  81. *
  82. * @type {String}
  83. */
  84. get binaryType () {
  85. return this._binaryType;
  86. }
  87. set binaryType (type) {
  88. if (constants.BINARY_TYPES.indexOf(type) < 0) return;
  89. this._binaryType = type;
  90. //
  91. // Allow to change `binaryType` on the fly.
  92. //
  93. if (this._receiver) this._receiver._binaryType = type;
  94. }
  95. /**
  96. * Set up the socket and the internal resources.
  97. *
  98. * @param {net.Socket} socket The network socket between the server and client
  99. * @param {Buffer} head The first packet of the upgraded stream
  100. * @private
  101. */
  102. setSocket (socket, head) {
  103. socket.setTimeout(0);
  104. socket.setNoDelay();
  105. this._receiver = new Receiver(this.extensions, this._maxPayload, this.binaryType);
  106. this._sender = new Sender(socket, this.extensions);
  107. this._ultron = new Ultron(socket);
  108. this._socket = socket;
  109. // socket cleanup handlers
  110. this._ultron.on('close', this._finalize);
  111. this._ultron.on('error', this._finalize);
  112. this._ultron.on('end', this._finalize);
  113. // ensure that the head is added to the receiver
  114. if (head.length > 0) socket.unshift(head);
  115. // subsequent packets are pushed to the receiver
  116. this._ultron.on('data', (data) => {
  117. this.bytesReceived += data.length;
  118. this._receiver.add(data);
  119. });
  120. // receiver event handlers
  121. this._receiver.onmessage = (data) => this.emit('message', data);
  122. this._receiver.onping = (data) => {
  123. this.pong(data, !this._isServer, true);
  124. this.emit('ping', data);
  125. };
  126. this._receiver.onpong = (data) => this.emit('pong', data);
  127. this._receiver.onclose = (code, reason) => {
  128. this._closeMessage = reason;
  129. this._closeCode = code;
  130. this.close(code, reason);
  131. };
  132. this._receiver.onerror = (error, code) => {
  133. // close the connection when the receiver reports a HyBi error code
  134. this.close(code, '');
  135. this.emit('error', error);
  136. };
  137. this.readyState = WebSocket.OPEN;
  138. this.emit('open');
  139. }
  140. /**
  141. * Clean up and release internal resources.
  142. *
  143. * @param {(Boolean|Error)} Indicates whether or not an error occurred
  144. * @private
  145. */
  146. finalize (error) {
  147. if (this._finalizeCalled) return;
  148. this.readyState = WebSocket.CLOSING;
  149. this._finalizeCalled = true;
  150. clearTimeout(this._closeTimer);
  151. this._closeTimer = null;
  152. //
  153. // If the connection was closed abnormally (with an error), or if the close
  154. // control frame was malformed or not received then the close code must be
  155. // 1006.
  156. //
  157. if (error) this._closeCode = 1006;
  158. if (this._socket) {
  159. this._ultron.destroy();
  160. this._socket.on('error', function onerror () {
  161. this.destroy();
  162. });
  163. if (!error) this._socket.end();
  164. else this._socket.destroy();
  165. this._receiver.cleanup(() => this.emitClose());
  166. this._receiver = null;
  167. this._sender = null;
  168. this._socket = null;
  169. this._ultron = null;
  170. } else {
  171. this.emitClose();
  172. }
  173. }
  174. /**
  175. * Emit the `close` event.
  176. *
  177. * @private
  178. */
  179. emitClose () {
  180. this.readyState = WebSocket.CLOSED;
  181. this.emit('close', this._closeCode || 1006, this._closeMessage || '');
  182. if (this.extensions[PerMessageDeflate.extensionName]) {
  183. this.extensions[PerMessageDeflate.extensionName].cleanup();
  184. }
  185. this.extensions = null;
  186. this.removeAllListeners();
  187. this.on('error', constants.NOOP); // Catch all errors after this.
  188. }
  189. /**
  190. * Pause the socket stream.
  191. *
  192. * @public
  193. */
  194. pause () {
  195. if (this.readyState !== WebSocket.OPEN) throw new Error('not opened');
  196. this._socket.pause();
  197. }
  198. /**
  199. * Resume the socket stream
  200. *
  201. * @public
  202. */
  203. resume () {
  204. if (this.readyState !== WebSocket.OPEN) throw new Error('not opened');
  205. this._socket.resume();
  206. }
  207. /**
  208. * Start a closing handshake.
  209. *
  210. * @param {Number} code Status code explaining why the connection is closing
  211. * @param {String} data A string explaining why the connection is closing
  212. * @public
  213. */
  214. close (code, data) {
  215. if (this.readyState === WebSocket.CLOSED) return;
  216. if (this.readyState === WebSocket.CONNECTING) {
  217. if (this._req && !this._req.aborted) {
  218. this._req.abort();
  219. this.emit('error', new Error('closed before the connection is established'));
  220. this.finalize(true);
  221. }
  222. return;
  223. }
  224. if (this.readyState === WebSocket.CLOSING) {
  225. if (this._closeCode && this._socket) this._socket.end();
  226. return;
  227. }
  228. this.readyState = WebSocket.CLOSING;
  229. this._sender.close(code, data, !this._isServer, (err) => {
  230. if (err) this.emit('error', err);
  231. if (this._socket) {
  232. if (this._closeCode) this._socket.end();
  233. //
  234. // Ensure that the connection is cleaned up even when the closing
  235. // handshake fails.
  236. //
  237. clearTimeout(this._closeTimer);
  238. this._closeTimer = setTimeout(this._finalize, closeTimeout, true);
  239. }
  240. });
  241. }
  242. /**
  243. * Send a ping message.
  244. *
  245. * @param {*} data The message to send
  246. * @param {Boolean} mask Indicates whether or not to mask `data`
  247. * @param {Boolean} failSilently Indicates whether or not to throw if `readyState` isn't `OPEN`
  248. * @public
  249. */
  250. ping (data, mask, failSilently) {
  251. if (this.readyState !== WebSocket.OPEN) {
  252. if (failSilently) return;
  253. throw new Error('not opened');
  254. }
  255. if (typeof data === 'number') data = data.toString();
  256. if (mask === undefined) mask = !this._isServer;
  257. this._sender.ping(data || constants.EMPTY_BUFFER, mask);
  258. }
  259. /**
  260. * Send a pong message.
  261. *
  262. * @param {*} data The message to send
  263. * @param {Boolean} mask Indicates whether or not to mask `data`
  264. * @param {Boolean} failSilently Indicates whether or not to throw if `readyState` isn't `OPEN`
  265. * @public
  266. */
  267. pong (data, mask, failSilently) {
  268. if (this.readyState !== WebSocket.OPEN) {
  269. if (failSilently) return;
  270. throw new Error('not opened');
  271. }
  272. if (typeof data === 'number') data = data.toString();
  273. if (mask === undefined) mask = !this._isServer;
  274. this._sender.pong(data || constants.EMPTY_BUFFER, mask);
  275. }
  276. /**
  277. * Send a data message.
  278. *
  279. * @param {*} data The message to send
  280. * @param {Object} options Options object
  281. * @param {Boolean} options.compress Specifies whether or not to compress `data`
  282. * @param {Boolean} options.binary Specifies whether `data` is binary or text
  283. * @param {Boolean} options.fin Specifies whether the fragment is the last one
  284. * @param {Boolean} options.mask Specifies whether or not to mask `data`
  285. * @param {Function} cb Callback which is executed when data is written out
  286. * @public
  287. */
  288. send (data, options, cb) {
  289. if (typeof options === 'function') {
  290. cb = options;
  291. options = {};
  292. }
  293. if (this.readyState !== WebSocket.OPEN) {
  294. if (cb) cb(new Error('not opened'));
  295. else throw new Error('not opened');
  296. return;
  297. }
  298. if (typeof data === 'number') data = data.toString();
  299. const opts = Object.assign({
  300. binary: typeof data !== 'string',
  301. mask: !this._isServer,
  302. compress: true,
  303. fin: true
  304. }, options);
  305. if (!this.extensions[PerMessageDeflate.extensionName]) {
  306. opts.compress = false;
  307. }
  308. this._sender.send(data || constants.EMPTY_BUFFER, opts, cb);
  309. }
  310. /**
  311. * Forcibly close the connection.
  312. *
  313. * @public
  314. */
  315. terminate () {
  316. if (this.readyState === WebSocket.CLOSED) return;
  317. if (this.readyState === WebSocket.CONNECTING) {
  318. if (this._req && !this._req.aborted) {
  319. this._req.abort();
  320. this.emit('error', new Error('closed before the connection is established'));
  321. this.finalize(true);
  322. }
  323. return;
  324. }
  325. this.finalize(true);
  326. }
  327. }
  328. WebSocket.CONNECTING = 0;
  329. WebSocket.OPEN = 1;
  330. WebSocket.CLOSING = 2;
  331. WebSocket.CLOSED = 3;
  332. //
  333. // Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes.
  334. // See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface
  335. //
  336. ['open', 'error', 'close', 'message'].forEach((method) => {
  337. Object.defineProperty(WebSocket.prototype, `on${method}`, {
  338. /**
  339. * Return the listener of the event.
  340. *
  341. * @return {(Function|undefined)} The event listener or `undefined`
  342. * @public
  343. */
  344. get () {
  345. const listeners = this.listeners(method);
  346. for (var i = 0; i < listeners.length; i++) {
  347. if (listeners[i]._listener) return listeners[i]._listener;
  348. }
  349. },
  350. /**
  351. * Add a listener for the event.
  352. *
  353. * @param {Function} listener The listener to add
  354. * @public
  355. */
  356. set (listener) {
  357. const listeners = this.listeners(method);
  358. for (var i = 0; i < listeners.length; i++) {
  359. //
  360. // Remove only the listeners added via `addEventListener`.
  361. //
  362. if (listeners[i]._listener) this.removeListener(method, listeners[i]);
  363. }
  364. this.addEventListener(method, listener);
  365. }
  366. });
  367. });
  368. WebSocket.prototype.addEventListener = EventTarget.addEventListener;
  369. WebSocket.prototype.removeEventListener = EventTarget.removeEventListener;
  370. module.exports = WebSocket;
  371. /**
  372. * Initialize a WebSocket server client.
  373. *
  374. * @param {http.IncomingMessage} req The request object
  375. * @param {net.Socket} socket The network socket between the server and client
  376. * @param {Buffer} head The first packet of the upgraded stream
  377. * @param {Object} options WebSocket attributes
  378. * @param {Number} options.protocolVersion The WebSocket protocol version
  379. * @param {Object} options.extensions The negotiated extensions
  380. * @param {Number} options.maxPayload The maximum allowed message size
  381. * @param {String} options.protocol The chosen subprotocol
  382. * @private
  383. */
  384. function initAsServerClient (socket, head, options) {
  385. this.protocolVersion = options.protocolVersion;
  386. this._maxPayload = options.maxPayload;
  387. this.extensions = options.extensions;
  388. this.protocol = options.protocol;
  389. this._isServer = true;
  390. this.setSocket(socket, head);
  391. }
  392. /**
  393. * Initialize a WebSocket client.
  394. *
  395. * @param {String} address The URL to which to connect
  396. * @param {String[]} protocols The list of subprotocols
  397. * @param {Object} options Connection options
  398. * @param {String} options.protocol Value of the `Sec-WebSocket-Protocol` header
  399. * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate
  400. * @param {Number} options.handshakeTimeout Timeout in milliseconds for the handshake request
  401. * @param {String} options.localAddress Local interface to bind for network connections
  402. * @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` header
  403. * @param {Object} options.headers An object containing request headers
  404. * @param {String} options.origin Value of the `Origin` or `Sec-WebSocket-Origin` header
  405. * @param {http.Agent} options.agent Use the specified Agent
  406. * @param {String} options.host Value of the `Host` header
  407. * @param {Number} options.family IP address family to use during hostname lookup (4 or 6).
  408. * @param {Function} options.checkServerIdentity A function to validate the server hostname
  409. * @param {Boolean} options.rejectUnauthorized Verify or not the server certificate
  410. * @param {String} options.passphrase The passphrase for the private key or pfx
  411. * @param {String} options.ciphers The ciphers to use or exclude
  412. * @param {String} options.ecdhCurve The curves for ECDH key agreement to use or exclude
  413. * @param {(String|String[]|Buffer|Buffer[])} options.cert The certificate key
  414. * @param {(String|String[]|Buffer|Buffer[])} options.key The private key
  415. * @param {(String|Buffer)} options.pfx The private key, certificate, and CA certs
  416. * @param {(String|String[]|Buffer|Buffer[])} options.ca Trusted certificates
  417. * @private
  418. */
  419. function initAsClient (address, protocols, options) {
  420. options = Object.assign({
  421. protocolVersion: protocolVersions[1],
  422. protocol: protocols.join(','),
  423. perMessageDeflate: true,
  424. handshakeTimeout: null,
  425. localAddress: null,
  426. headers: null,
  427. family: null,
  428. origin: null,
  429. agent: null,
  430. host: null,
  431. //
  432. // SSL options.
  433. //
  434. checkServerIdentity: null,
  435. rejectUnauthorized: null,
  436. passphrase: null,
  437. ciphers: null,
  438. ecdhCurve: null,
  439. cert: null,
  440. key: null,
  441. pfx: null,
  442. ca: null
  443. }, options);
  444. if (protocolVersions.indexOf(options.protocolVersion) === -1) {
  445. throw new Error(
  446. `unsupported protocol version: ${options.protocolVersion} ` +
  447. `(supported versions: ${protocolVersions.join(', ')})`
  448. );
  449. }
  450. this.protocolVersion = options.protocolVersion;
  451. this._isServer = false;
  452. this.url = address;
  453. const serverUrl = url.parse(address);
  454. const isUnixSocket = serverUrl.protocol === 'ws+unix:';
  455. if (!serverUrl.host && (!isUnixSocket || !serverUrl.path)) {
  456. throw new Error('invalid url');
  457. }
  458. const isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:';
  459. const key = crypto.randomBytes(16).toString('base64');
  460. const httpObj = isSecure ? https : http;
  461. var perMessageDeflate;
  462. const requestOptions = {
  463. port: serverUrl.port || (isSecure ? 443 : 80),
  464. host: serverUrl.hostname,
  465. path: '/',
  466. headers: {
  467. 'Sec-WebSocket-Version': options.protocolVersion,
  468. 'Sec-WebSocket-Key': key,
  469. 'Connection': 'Upgrade',
  470. 'Upgrade': 'websocket'
  471. }
  472. };
  473. if (options.headers) Object.assign(requestOptions.headers, options.headers);
  474. if (options.perMessageDeflate) {
  475. perMessageDeflate = new PerMessageDeflate(
  476. options.perMessageDeflate !== true ? options.perMessageDeflate : {},
  477. false
  478. );
  479. requestOptions.headers['Sec-WebSocket-Extensions'] = Extensions.format({
  480. [PerMessageDeflate.extensionName]: perMessageDeflate.offer()
  481. });
  482. }
  483. if (options.protocol) {
  484. requestOptions.headers['Sec-WebSocket-Protocol'] = options.protocol;
  485. }
  486. if (options.origin) {
  487. if (options.protocolVersion < 13) {
  488. requestOptions.headers['Sec-WebSocket-Origin'] = options.origin;
  489. } else {
  490. requestOptions.headers.Origin = options.origin;
  491. }
  492. }
  493. if (options.host) requestOptions.headers.Host = options.host;
  494. if (serverUrl.auth) requestOptions.auth = serverUrl.auth;
  495. if (options.localAddress) requestOptions.localAddress = options.localAddress;
  496. if (options.family) requestOptions.family = options.family;
  497. if (isUnixSocket) {
  498. const parts = serverUrl.path.split(':');
  499. requestOptions.socketPath = parts[0];
  500. requestOptions.path = parts[1];
  501. } else if (serverUrl.path) {
  502. //
  503. // Make sure that path starts with `/`.
  504. //
  505. if (serverUrl.path.charAt(0) !== '/') {
  506. requestOptions.path = `/${serverUrl.path}`;
  507. } else {
  508. requestOptions.path = serverUrl.path;
  509. }
  510. }
  511. var agent = options.agent;
  512. //
  513. // A custom agent is required for these options.
  514. //
  515. if (
  516. options.rejectUnauthorized != null ||
  517. options.checkServerIdentity ||
  518. options.passphrase ||
  519. options.ciphers ||
  520. options.ecdhCurve ||
  521. options.cert ||
  522. options.key ||
  523. options.pfx ||
  524. options.ca
  525. ) {
  526. if (options.passphrase) requestOptions.passphrase = options.passphrase;
  527. if (options.ciphers) requestOptions.ciphers = options.ciphers;
  528. if (options.ecdhCurve) requestOptions.ecdhCurve = options.ecdhCurve;
  529. if (options.cert) requestOptions.cert = options.cert;
  530. if (options.key) requestOptions.key = options.key;
  531. if (options.pfx) requestOptions.pfx = options.pfx;
  532. if (options.ca) requestOptions.ca = options.ca;
  533. if (options.checkServerIdentity) {
  534. requestOptions.checkServerIdentity = options.checkServerIdentity;
  535. }
  536. if (options.rejectUnauthorized != null) {
  537. requestOptions.rejectUnauthorized = options.rejectUnauthorized;
  538. }
  539. if (!agent) agent = new httpObj.Agent(requestOptions);
  540. }
  541. if (agent) requestOptions.agent = agent;
  542. this._req = httpObj.get(requestOptions);
  543. if (options.handshakeTimeout) {
  544. this._req.setTimeout(options.handshakeTimeout, () => {
  545. this._req.abort();
  546. this.emit('error', new Error('opening handshake has timed out'));
  547. this.finalize(true);
  548. });
  549. }
  550. this._req.on('error', (error) => {
  551. if (this._req.aborted) return;
  552. this._req = null;
  553. this.emit('error', error);
  554. this.finalize(true);
  555. });
  556. this._req.on('response', (res) => {
  557. if (!this.emit('unexpected-response', this._req, res)) {
  558. this._req.abort();
  559. this.emit('error', new Error(`unexpected server response (${res.statusCode})`));
  560. this.finalize(true);
  561. }
  562. });
  563. this._req.on('upgrade', (res, socket, head) => {
  564. this.emit('headers', res.headers, res);
  565. //
  566. // The user may have closed the connection from a listener of the `headers`
  567. // event.
  568. //
  569. if (this.readyState !== WebSocket.CONNECTING) return;
  570. this._req = null;
  571. const digest = crypto.createHash('sha1')
  572. .update(key + constants.GUID, 'binary')
  573. .digest('base64');
  574. if (res.headers['sec-websocket-accept'] !== digest) {
  575. socket.destroy();
  576. this.emit('error', new Error('invalid server key'));
  577. return this.finalize(true);
  578. }
  579. const serverProt = res.headers['sec-websocket-protocol'];
  580. const protList = (options.protocol || '').split(/, */);
  581. var protError;
  582. if (!options.protocol && serverProt) {
  583. protError = 'server sent a subprotocol even though none requested';
  584. } else if (options.protocol && !serverProt) {
  585. protError = 'server sent no subprotocol even though requested';
  586. } else if (serverProt && protList.indexOf(serverProt) === -1) {
  587. protError = 'server responded with an invalid protocol';
  588. }
  589. if (protError) {
  590. socket.destroy();
  591. this.emit('error', new Error(protError));
  592. return this.finalize(true);
  593. }
  594. if (serverProt) this.protocol = serverProt;
  595. if (perMessageDeflate) {
  596. try {
  597. const serverExtensions = Extensions.parse(
  598. res.headers['sec-websocket-extensions']
  599. );
  600. if (serverExtensions[PerMessageDeflate.extensionName]) {
  601. perMessageDeflate.accept(
  602. serverExtensions[PerMessageDeflate.extensionName]
  603. );
  604. this.extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
  605. }
  606. } catch (err) {
  607. socket.destroy();
  608. this.emit('error', new Error('invalid Sec-WebSocket-Extensions header'));
  609. return this.finalize(true);
  610. }
  611. }
  612. this.setSocket(socket, head);
  613. });
  614. }