Front end of the Slack clone application.

Receiver.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  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 safeBuffer = require('safe-buffer');
  8. const PerMessageDeflate = require('./PerMessageDeflate');
  9. const isValidUTF8 = require('./Validation');
  10. const bufferUtil = require('./BufferUtil');
  11. const ErrorCodes = require('./ErrorCodes');
  12. const constants = require('./Constants');
  13. const Buffer = safeBuffer.Buffer;
  14. const GET_INFO = 0;
  15. const GET_PAYLOAD_LENGTH_16 = 1;
  16. const GET_PAYLOAD_LENGTH_64 = 2;
  17. const GET_MASK = 3;
  18. const GET_DATA = 4;
  19. const INFLATING = 5;
  20. /**
  21. * HyBi Receiver implementation.
  22. */
  23. class Receiver {
  24. /**
  25. * Creates a Receiver instance.
  26. *
  27. * @param {Object} extensions An object containing the negotiated extensions
  28. * @param {Number} maxPayload The maximum allowed message length
  29. * @param {String} binaryType The type for binary data
  30. */
  31. constructor (extensions, maxPayload, binaryType) {
  32. this._binaryType = binaryType || constants.BINARY_TYPES[0];
  33. this._extensions = extensions || {};
  34. this._maxPayload = maxPayload | 0;
  35. this._bufferedBytes = 0;
  36. this._buffers = [];
  37. this._compressed = false;
  38. this._payloadLength = 0;
  39. this._fragmented = 0;
  40. this._masked = false;
  41. this._fin = false;
  42. this._mask = null;
  43. this._opcode = 0;
  44. this._totalPayloadLength = 0;
  45. this._messageLength = 0;
  46. this._fragments = [];
  47. this._cleanupCallback = null;
  48. this._hadError = false;
  49. this._dead = false;
  50. this._loop = false;
  51. this.onmessage = null;
  52. this.onclose = null;
  53. this.onerror = null;
  54. this.onping = null;
  55. this.onpong = null;
  56. this._state = GET_INFO;
  57. }
  58. /**
  59. * Consumes bytes from the available buffered data.
  60. *
  61. * @param {Number} bytes The number of bytes to consume
  62. * @return {Buffer} Consumed bytes
  63. * @private
  64. */
  65. readBuffer (bytes) {
  66. var offset = 0;
  67. var dst;
  68. var l;
  69. this._bufferedBytes -= bytes;
  70. if (bytes === this._buffers[0].length) return this._buffers.shift();
  71. if (bytes < this._buffers[0].length) {
  72. dst = this._buffers[0].slice(0, bytes);
  73. this._buffers[0] = this._buffers[0].slice(bytes);
  74. return dst;
  75. }
  76. dst = Buffer.allocUnsafe(bytes);
  77. while (bytes > 0) {
  78. l = this._buffers[0].length;
  79. if (bytes >= l) {
  80. this._buffers[0].copy(dst, offset);
  81. offset += l;
  82. this._buffers.shift();
  83. } else {
  84. this._buffers[0].copy(dst, offset, 0, bytes);
  85. this._buffers[0] = this._buffers[0].slice(bytes);
  86. }
  87. bytes -= l;
  88. }
  89. return dst;
  90. }
  91. /**
  92. * Checks if the number of buffered bytes is bigger or equal than `n` and
  93. * calls `cleanup` if necessary.
  94. *
  95. * @param {Number} n The number of bytes to check against
  96. * @return {Boolean} `true` if `bufferedBytes >= n`, else `false`
  97. * @private
  98. */
  99. hasBufferedBytes (n) {
  100. if (this._bufferedBytes >= n) return true;
  101. this._loop = false;
  102. if (this._dead) this.cleanup(this._cleanupCallback);
  103. return false;
  104. }
  105. /**
  106. * Adds new data to the parser.
  107. *
  108. * @public
  109. */
  110. add (data) {
  111. if (this._dead) return;
  112. this._bufferedBytes += data.length;
  113. this._buffers.push(data);
  114. this.startLoop();
  115. }
  116. /**
  117. * Starts the parsing loop.
  118. *
  119. * @private
  120. */
  121. startLoop () {
  122. this._loop = true;
  123. while (this._loop) {
  124. switch (this._state) {
  125. case GET_INFO:
  126. this.getInfo();
  127. break;
  128. case GET_PAYLOAD_LENGTH_16:
  129. this.getPayloadLength16();
  130. break;
  131. case GET_PAYLOAD_LENGTH_64:
  132. this.getPayloadLength64();
  133. break;
  134. case GET_MASK:
  135. this.getMask();
  136. break;
  137. case GET_DATA:
  138. this.getData();
  139. break;
  140. default: // `INFLATING`
  141. this._loop = false;
  142. }
  143. }
  144. }
  145. /**
  146. * Reads the first two bytes of a frame.
  147. *
  148. * @private
  149. */
  150. getInfo () {
  151. if (!this.hasBufferedBytes(2)) return;
  152. const buf = this.readBuffer(2);
  153. if ((buf[0] & 0x30) !== 0x00) {
  154. this.error(new Error('RSV2 and RSV3 must be clear'), 1002);
  155. return;
  156. }
  157. const compressed = (buf[0] & 0x40) === 0x40;
  158. if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
  159. this.error(new Error('RSV1 must be clear'), 1002);
  160. return;
  161. }
  162. this._fin = (buf[0] & 0x80) === 0x80;
  163. this._opcode = buf[0] & 0x0f;
  164. this._payloadLength = buf[1] & 0x7f;
  165. if (this._opcode === 0x00) {
  166. if (compressed) {
  167. this.error(new Error('RSV1 must be clear'), 1002);
  168. return;
  169. }
  170. if (!this._fragmented) {
  171. this.error(new Error(`invalid opcode: ${this._opcode}`), 1002);
  172. return;
  173. } else {
  174. this._opcode = this._fragmented;
  175. }
  176. } else if (this._opcode === 0x01 || this._opcode === 0x02) {
  177. if (this._fragmented) {
  178. this.error(new Error(`invalid opcode: ${this._opcode}`), 1002);
  179. return;
  180. }
  181. this._compressed = compressed;
  182. } else if (this._opcode > 0x07 && this._opcode < 0x0b) {
  183. if (!this._fin) {
  184. this.error(new Error('FIN must be set'), 1002);
  185. return;
  186. }
  187. if (compressed) {
  188. this.error(new Error('RSV1 must be clear'), 1002);
  189. return;
  190. }
  191. if (this._payloadLength > 0x7d) {
  192. this.error(new Error('invalid payload length'), 1002);
  193. return;
  194. }
  195. } else {
  196. this.error(new Error(`invalid opcode: ${this._opcode}`), 1002);
  197. return;
  198. }
  199. if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
  200. this._masked = (buf[1] & 0x80) === 0x80;
  201. if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
  202. else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
  203. else this.haveLength();
  204. }
  205. /**
  206. * Gets extended payload length (7+16).
  207. *
  208. * @private
  209. */
  210. getPayloadLength16 () {
  211. if (!this.hasBufferedBytes(2)) return;
  212. this._payloadLength = this.readBuffer(2).readUInt16BE(0, true);
  213. this.haveLength();
  214. }
  215. /**
  216. * Gets extended payload length (7+64).
  217. *
  218. * @private
  219. */
  220. getPayloadLength64 () {
  221. if (!this.hasBufferedBytes(8)) return;
  222. const buf = this.readBuffer(8);
  223. const num = buf.readUInt32BE(0, true);
  224. //
  225. // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned
  226. // if payload length is greater than this number.
  227. //
  228. if (num > Math.pow(2, 53 - 32) - 1) {
  229. this.error(new Error('max payload size exceeded'), 1009);
  230. return;
  231. }
  232. this._payloadLength = (num * Math.pow(2, 32)) + buf.readUInt32BE(4, true);
  233. this.haveLength();
  234. }
  235. /**
  236. * Payload length has been read.
  237. *
  238. * @private
  239. */
  240. haveLength () {
  241. if (this._opcode < 0x08 && this.maxPayloadExceeded(this._payloadLength)) {
  242. return;
  243. }
  244. if (this._masked) this._state = GET_MASK;
  245. else this._state = GET_DATA;
  246. }
  247. /**
  248. * Reads mask bytes.
  249. *
  250. * @private
  251. */
  252. getMask () {
  253. if (!this.hasBufferedBytes(4)) return;
  254. this._mask = this.readBuffer(4);
  255. this._state = GET_DATA;
  256. }
  257. /**
  258. * Reads data bytes.
  259. *
  260. * @private
  261. */
  262. getData () {
  263. var data = constants.EMPTY_BUFFER;
  264. if (this._payloadLength) {
  265. if (!this.hasBufferedBytes(this._payloadLength)) return;
  266. data = this.readBuffer(this._payloadLength);
  267. if (this._masked) bufferUtil.unmask(data, this._mask);
  268. }
  269. if (this._opcode > 0x07) {
  270. this.controlMessage(data);
  271. } else if (this._compressed) {
  272. this._state = INFLATING;
  273. this.decompress(data);
  274. } else if (this.pushFragment(data)) {
  275. this.dataMessage();
  276. }
  277. }
  278. /**
  279. * Decompresses data.
  280. *
  281. * @param {Buffer} data Compressed data
  282. * @private
  283. */
  284. decompress (data) {
  285. const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
  286. perMessageDeflate.decompress(data, this._fin, (err, buf) => {
  287. if (err) {
  288. this.error(err, err.closeCode === 1009 ? 1009 : 1007);
  289. return;
  290. }
  291. if (this.pushFragment(buf)) this.dataMessage();
  292. this.startLoop();
  293. });
  294. }
  295. /**
  296. * Handles a data message.
  297. *
  298. * @private
  299. */
  300. dataMessage () {
  301. if (this._fin) {
  302. const messageLength = this._messageLength;
  303. const fragments = this._fragments;
  304. this._totalPayloadLength = 0;
  305. this._messageLength = 0;
  306. this._fragmented = 0;
  307. this._fragments = [];
  308. if (this._opcode === 2) {
  309. var data;
  310. if (this._binaryType === 'nodebuffer') {
  311. data = toBuffer(fragments, messageLength);
  312. } else if (this._binaryType === 'arraybuffer') {
  313. data = toArrayBuffer(toBuffer(fragments, messageLength));
  314. } else {
  315. data = fragments;
  316. }
  317. this.onmessage(data);
  318. } else {
  319. const buf = toBuffer(fragments, messageLength);
  320. if (!isValidUTF8(buf)) {
  321. this.error(new Error('invalid utf8 sequence'), 1007);
  322. return;
  323. }
  324. this.onmessage(buf.toString());
  325. }
  326. }
  327. this._state = GET_INFO;
  328. }
  329. /**
  330. * Handles a control message.
  331. *
  332. * @param {Buffer} data Data to handle
  333. * @private
  334. */
  335. controlMessage (data) {
  336. if (this._opcode === 0x08) {
  337. if (data.length === 0) {
  338. this.onclose(1000, '');
  339. this._loop = false;
  340. this.cleanup(this._cleanupCallback);
  341. } else if (data.length === 1) {
  342. this.error(new Error('invalid payload length'), 1002);
  343. } else {
  344. const code = data.readUInt16BE(0, true);
  345. if (!ErrorCodes.isValidErrorCode(code)) {
  346. this.error(new Error(`invalid status code: ${code}`), 1002);
  347. return;
  348. }
  349. const buf = data.slice(2);
  350. if (!isValidUTF8(buf)) {
  351. this.error(new Error('invalid utf8 sequence'), 1007);
  352. return;
  353. }
  354. this.onclose(code, buf.toString());
  355. this._loop = false;
  356. this.cleanup(this._cleanupCallback);
  357. }
  358. return;
  359. }
  360. if (this._opcode === 0x09) this.onping(data);
  361. else this.onpong(data);
  362. this._state = GET_INFO;
  363. }
  364. /**
  365. * Handles an error.
  366. *
  367. * @param {Error} err The error
  368. * @param {Number} code Close code
  369. * @private
  370. */
  371. error (err, code) {
  372. this.onerror(err, code);
  373. this._hadError = true;
  374. this._loop = false;
  375. this.cleanup(this._cleanupCallback);
  376. }
  377. /**
  378. * Checks payload size, disconnects socket when it exceeds `maxPayload`.
  379. *
  380. * @param {Number} length Payload length
  381. * @private
  382. */
  383. maxPayloadExceeded (length) {
  384. if (length === 0 || this._maxPayload < 1) return false;
  385. const fullLength = this._totalPayloadLength + length;
  386. if (fullLength <= this._maxPayload) {
  387. this._totalPayloadLength = fullLength;
  388. return false;
  389. }
  390. this.error(new Error('max payload size exceeded'), 1009);
  391. return true;
  392. }
  393. /**
  394. * Appends a fragment in the fragments array after checking that the sum of
  395. * fragment lengths does not exceed `maxPayload`.
  396. *
  397. * @param {Buffer} fragment The fragment to add
  398. * @return {Boolean} `true` if `maxPayload` is not exceeded, else `false`
  399. * @private
  400. */
  401. pushFragment (fragment) {
  402. if (fragment.length === 0) return true;
  403. const totalLength = this._messageLength + fragment.length;
  404. if (this._maxPayload < 1 || totalLength <= this._maxPayload) {
  405. this._messageLength = totalLength;
  406. this._fragments.push(fragment);
  407. return true;
  408. }
  409. this.error(new Error('max payload size exceeded'), 1009);
  410. return false;
  411. }
  412. /**
  413. * Releases resources used by the receiver.
  414. *
  415. * @param {Function} cb Callback
  416. * @public
  417. */
  418. cleanup (cb) {
  419. this._dead = true;
  420. if (!this._hadError && (this._loop || this._state === INFLATING)) {
  421. this._cleanupCallback = cb;
  422. } else {
  423. this._extensions = null;
  424. this._fragments = null;
  425. this._buffers = null;
  426. this._mask = null;
  427. this._cleanupCallback = null;
  428. this.onmessage = null;
  429. this.onclose = null;
  430. this.onerror = null;
  431. this.onping = null;
  432. this.onpong = null;
  433. if (cb) cb();
  434. }
  435. }
  436. }
  437. module.exports = Receiver;
  438. /**
  439. * Makes a buffer from a list of fragments.
  440. *
  441. * @param {Buffer[]} fragments The list of fragments composing the message
  442. * @param {Number} messageLength The length of the message
  443. * @return {Buffer}
  444. * @private
  445. */
  446. function toBuffer (fragments, messageLength) {
  447. if (fragments.length === 1) return fragments[0];
  448. if (fragments.length > 1) return bufferUtil.concat(fragments, messageLength);
  449. return constants.EMPTY_BUFFER;
  450. }
  451. /**
  452. * Converts a buffer to an `ArrayBuffer`.
  453. *
  454. * @param {Buffer} The buffer to convert
  455. * @return {ArrayBuffer} Converted buffer
  456. */
  457. function toArrayBuffer (buf) {
  458. if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {
  459. return buf.buffer;
  460. }
  461. return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
  462. }