index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. "use strict";
  2. // imported from https://github.com/socketio/engine.io-parser/tree/2.2.x
  3. Object.defineProperty(exports, "__esModule", { value: true });
  4. exports.decodePayloadAsBinary = exports.encodePayloadAsBinary = exports.decodePayload = exports.encodePayload = exports.decodeBase64Packet = exports.decodePacket = exports.encodeBase64Packet = exports.encodePacket = exports.packets = exports.protocol = void 0;
  5. /**
  6. * Module dependencies.
  7. */
  8. var utf8 = require('./utf8');
  9. /**
  10. * Current protocol version.
  11. */
  12. exports.protocol = 3;
  13. const hasBinary = (packets) => {
  14. for (const packet of packets) {
  15. if (packet.data instanceof ArrayBuffer || ArrayBuffer.isView(packet.data)) {
  16. return true;
  17. }
  18. }
  19. return false;
  20. };
  21. /**
  22. * Packet types.
  23. */
  24. exports.packets = {
  25. open: 0 // non-ws
  26. ,
  27. close: 1 // non-ws
  28. ,
  29. ping: 2,
  30. pong: 3,
  31. message: 4,
  32. upgrade: 5,
  33. noop: 6
  34. };
  35. var packetslist = Object.keys(exports.packets);
  36. /**
  37. * Premade error packet.
  38. */
  39. var err = { type: 'error', data: 'parser error' };
  40. const EMPTY_BUFFER = Buffer.concat([]);
  41. /**
  42. * Encodes a packet.
  43. *
  44. * <packet type id> [ <data> ]
  45. *
  46. * Example:
  47. *
  48. * 5hello world
  49. * 3
  50. * 4
  51. *
  52. * Binary is encoded in an identical principle
  53. *
  54. * @api private
  55. */
  56. function encodePacket(packet, supportsBinary, utf8encode, callback) {
  57. if (typeof supportsBinary === 'function') {
  58. callback = supportsBinary;
  59. supportsBinary = null;
  60. }
  61. if (typeof utf8encode === 'function') {
  62. callback = utf8encode;
  63. utf8encode = null;
  64. }
  65. if (Buffer.isBuffer(packet.data)) {
  66. return encodeBuffer(packet, supportsBinary, callback);
  67. }
  68. else if (packet.data && (packet.data.buffer || packet.data) instanceof ArrayBuffer) {
  69. return encodeBuffer({ type: packet.type, data: arrayBufferToBuffer(packet.data) }, supportsBinary, callback);
  70. }
  71. // Sending data as a utf-8 string
  72. var encoded = exports.packets[packet.type];
  73. // data fragment is optional
  74. if (undefined !== packet.data) {
  75. encoded += utf8encode ? utf8.encode(String(packet.data), { strict: false }) : String(packet.data);
  76. }
  77. return callback('' + encoded);
  78. }
  79. exports.encodePacket = encodePacket;
  80. ;
  81. /**
  82. * Encode Buffer data
  83. */
  84. function encodeBuffer(packet, supportsBinary, callback) {
  85. if (!supportsBinary) {
  86. return encodeBase64Packet(packet, callback);
  87. }
  88. var data = packet.data;
  89. var typeBuffer = Buffer.allocUnsafe(1);
  90. typeBuffer[0] = exports.packets[packet.type];
  91. return callback(Buffer.concat([typeBuffer, data]));
  92. }
  93. /**
  94. * Encodes a packet with binary data in a base64 string
  95. *
  96. * @param {Object} packet, has `type` and `data`
  97. * @return {String} base64 encoded message
  98. */
  99. function encodeBase64Packet(packet, callback) {
  100. var data = Buffer.isBuffer(packet.data) ? packet.data : arrayBufferToBuffer(packet.data);
  101. var message = 'b' + exports.packets[packet.type];
  102. message += data.toString('base64');
  103. return callback(message);
  104. }
  105. exports.encodeBase64Packet = encodeBase64Packet;
  106. ;
  107. /**
  108. * Decodes a packet. Data also available as an ArrayBuffer if requested.
  109. *
  110. * @return {Object} with `type` and `data` (if any)
  111. * @api private
  112. */
  113. function decodePacket(data, binaryType, utf8decode) {
  114. if (data === undefined) {
  115. return err;
  116. }
  117. var type;
  118. // String data
  119. if (typeof data === 'string') {
  120. type = data.charAt(0);
  121. if (type === 'b') {
  122. return decodeBase64Packet(data.substr(1), binaryType);
  123. }
  124. if (utf8decode) {
  125. data = tryDecode(data);
  126. if (data === false) {
  127. return err;
  128. }
  129. }
  130. if (Number(type) != type || !packetslist[type]) {
  131. return err;
  132. }
  133. if (data.length > 1) {
  134. return { type: packetslist[type], data: data.substring(1) };
  135. }
  136. else {
  137. return { type: packetslist[type] };
  138. }
  139. }
  140. // Binary data
  141. if (binaryType === 'arraybuffer') {
  142. // wrap Buffer/ArrayBuffer data into an Uint8Array
  143. var intArray = new Uint8Array(data);
  144. type = intArray[0];
  145. return { type: packetslist[type], data: intArray.buffer.slice(1) };
  146. }
  147. if (data instanceof ArrayBuffer) {
  148. data = arrayBufferToBuffer(data);
  149. }
  150. type = data[0];
  151. return { type: packetslist[type], data: data.slice(1) };
  152. }
  153. exports.decodePacket = decodePacket;
  154. ;
  155. function tryDecode(data) {
  156. try {
  157. data = utf8.decode(data, { strict: false });
  158. }
  159. catch (e) {
  160. return false;
  161. }
  162. return data;
  163. }
  164. /**
  165. * Decodes a packet encoded in a base64 string.
  166. *
  167. * @param {String} base64 encoded message
  168. * @return {Object} with `type` and `data` (if any)
  169. */
  170. function decodeBase64Packet(msg, binaryType) {
  171. var type = packetslist[msg.charAt(0)];
  172. var data = Buffer.from(msg.substr(1), 'base64');
  173. if (binaryType === 'arraybuffer') {
  174. var abv = new Uint8Array(data.length);
  175. for (var i = 0; i < abv.length; i++) {
  176. abv[i] = data[i];
  177. }
  178. // @ts-ignore
  179. data = abv.buffer;
  180. }
  181. return { type: type, data: data };
  182. }
  183. exports.decodeBase64Packet = decodeBase64Packet;
  184. ;
  185. /**
  186. * Encodes multiple messages (payload).
  187. *
  188. * <length>:data
  189. *
  190. * Example:
  191. *
  192. * 11:hello world2:hi
  193. *
  194. * If any contents are binary, they will be encoded as base64 strings. Base64
  195. * encoded strings are marked with a b before the length specifier
  196. *
  197. * @param {Array} packets
  198. * @api private
  199. */
  200. function encodePayload(packets, supportsBinary, callback) {
  201. if (typeof supportsBinary === 'function') {
  202. callback = supportsBinary;
  203. supportsBinary = null;
  204. }
  205. if (supportsBinary && hasBinary(packets)) {
  206. return encodePayloadAsBinary(packets, callback);
  207. }
  208. if (!packets.length) {
  209. return callback('0:');
  210. }
  211. function encodeOne(packet, doneCallback) {
  212. encodePacket(packet, supportsBinary, false, function (message) {
  213. doneCallback(null, setLengthHeader(message));
  214. });
  215. }
  216. map(packets, encodeOne, function (err, results) {
  217. return callback(results.join(''));
  218. });
  219. }
  220. exports.encodePayload = encodePayload;
  221. ;
  222. function setLengthHeader(message) {
  223. return message.length + ':' + message;
  224. }
  225. /**
  226. * Async array map using after
  227. */
  228. function map(ary, each, done) {
  229. const results = new Array(ary.length);
  230. let count = 0;
  231. for (let i = 0; i < ary.length; i++) {
  232. each(ary[i], (error, msg) => {
  233. results[i] = msg;
  234. if (++count === ary.length) {
  235. done(null, results);
  236. }
  237. });
  238. }
  239. }
  240. /*
  241. * Decodes data when a payload is maybe expected. Possible binary contents are
  242. * decoded from their base64 representation
  243. *
  244. * @param {String} data, callback method
  245. * @api public
  246. */
  247. function decodePayload(data, binaryType, callback) {
  248. if (typeof data !== 'string') {
  249. return decodePayloadAsBinary(data, binaryType, callback);
  250. }
  251. if (typeof binaryType === 'function') {
  252. callback = binaryType;
  253. binaryType = null;
  254. }
  255. if (data === '') {
  256. // parser error - ignoring payload
  257. return callback(err, 0, 1);
  258. }
  259. var length = '', n, msg, packet;
  260. for (var i = 0, l = data.length; i < l; i++) {
  261. var chr = data.charAt(i);
  262. if (chr !== ':') {
  263. length += chr;
  264. continue;
  265. }
  266. // @ts-ignore
  267. if (length === '' || (length != (n = Number(length)))) {
  268. // parser error - ignoring payload
  269. return callback(err, 0, 1);
  270. }
  271. msg = data.substr(i + 1, n);
  272. if (length != msg.length) {
  273. // parser error - ignoring payload
  274. return callback(err, 0, 1);
  275. }
  276. if (msg.length) {
  277. packet = decodePacket(msg, binaryType, false);
  278. if (err.type === packet.type && err.data === packet.data) {
  279. // parser error in individual packet - ignoring payload
  280. return callback(err, 0, 1);
  281. }
  282. var more = callback(packet, i + n, l);
  283. if (false === more)
  284. return;
  285. }
  286. // advance cursor
  287. i += n;
  288. length = '';
  289. }
  290. if (length !== '') {
  291. // parser error - ignoring payload
  292. return callback(err, 0, 1);
  293. }
  294. }
  295. exports.decodePayload = decodePayload;
  296. ;
  297. /**
  298. *
  299. * Converts a buffer to a utf8.js encoded string
  300. *
  301. * @api private
  302. */
  303. function bufferToString(buffer) {
  304. var str = '';
  305. for (var i = 0, l = buffer.length; i < l; i++) {
  306. str += String.fromCharCode(buffer[i]);
  307. }
  308. return str;
  309. }
  310. /**
  311. *
  312. * Converts a utf8.js encoded string to a buffer
  313. *
  314. * @api private
  315. */
  316. function stringToBuffer(string) {
  317. var buf = Buffer.allocUnsafe(string.length);
  318. for (var i = 0, l = string.length; i < l; i++) {
  319. buf.writeUInt8(string.charCodeAt(i), i);
  320. }
  321. return buf;
  322. }
  323. /**
  324. *
  325. * Converts an ArrayBuffer to a Buffer
  326. *
  327. * @api private
  328. */
  329. function arrayBufferToBuffer(data) {
  330. // data is either an ArrayBuffer or ArrayBufferView.
  331. var length = data.byteLength || data.length;
  332. var offset = data.byteOffset || 0;
  333. return Buffer.from(data.buffer || data, offset, length);
  334. }
  335. /**
  336. * Encodes multiple messages (payload) as binary.
  337. *
  338. * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
  339. * 255><data>
  340. *
  341. * Example:
  342. * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
  343. *
  344. * @param {Array} packets
  345. * @return {Buffer} encoded payload
  346. * @api private
  347. */
  348. function encodePayloadAsBinary(packets, callback) {
  349. if (!packets.length) {
  350. return callback(EMPTY_BUFFER);
  351. }
  352. map(packets, encodeOneBinaryPacket, function (err, results) {
  353. return callback(Buffer.concat(results));
  354. });
  355. }
  356. exports.encodePayloadAsBinary = encodePayloadAsBinary;
  357. ;
  358. function encodeOneBinaryPacket(p, doneCallback) {
  359. function onBinaryPacketEncode(packet) {
  360. var encodingLength = '' + packet.length;
  361. var sizeBuffer;
  362. if (typeof packet === 'string') {
  363. sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2);
  364. sizeBuffer[0] = 0; // is a string (not true binary = 0)
  365. for (var i = 0; i < encodingLength.length; i++) {
  366. sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
  367. }
  368. sizeBuffer[sizeBuffer.length - 1] = 255;
  369. return doneCallback(null, Buffer.concat([sizeBuffer, stringToBuffer(packet)]));
  370. }
  371. sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2);
  372. sizeBuffer[0] = 1; // is binary (true binary = 1)
  373. for (var i = 0; i < encodingLength.length; i++) {
  374. sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
  375. }
  376. sizeBuffer[sizeBuffer.length - 1] = 255;
  377. doneCallback(null, Buffer.concat([sizeBuffer, packet]));
  378. }
  379. encodePacket(p, true, true, onBinaryPacketEncode);
  380. }
  381. /*
  382. * Decodes data when a payload is maybe expected. Strings are decoded by
  383. * interpreting each byte as a key code for entries marked to start with 0. See
  384. * description of encodePayloadAsBinary
  385. * @param {Buffer} data, callback method
  386. * @api public
  387. */
  388. function decodePayloadAsBinary(data, binaryType, callback) {
  389. if (typeof binaryType === 'function') {
  390. callback = binaryType;
  391. binaryType = null;
  392. }
  393. var bufferTail = data;
  394. var buffers = [];
  395. var i;
  396. while (bufferTail.length > 0) {
  397. var strLen = '';
  398. var isString = bufferTail[0] === 0;
  399. for (i = 1;; i++) {
  400. if (bufferTail[i] === 255)
  401. break;
  402. // 310 = char length of Number.MAX_VALUE
  403. if (strLen.length > 310) {
  404. return callback(err, 0, 1);
  405. }
  406. strLen += '' + bufferTail[i];
  407. }
  408. bufferTail = bufferTail.slice(strLen.length + 1);
  409. var msgLength = parseInt(strLen, 10);
  410. var msg = bufferTail.slice(1, msgLength + 1);
  411. if (isString)
  412. msg = bufferToString(msg);
  413. buffers.push(msg);
  414. bufferTail = bufferTail.slice(msgLength + 1);
  415. }
  416. var total = buffers.length;
  417. for (i = 0; i < total; i++) {
  418. var buffer = buffers[i];
  419. callback(decodePacket(buffer, binaryType, true), i, total);
  420. }
  421. }
  422. exports.decodePayloadAsBinary = decodePayloadAsBinary;
  423. ;