index.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.decodePayload = exports.decodePacket = exports.encodePayload = exports.encodePacket = exports.protocol = exports.createPacketDecoderStream = exports.createPacketEncoderStream = void 0;
  4. const encodePacket_js_1 = require("./encodePacket.js");
  5. Object.defineProperty(exports, "encodePacket", { enumerable: true, get: function () { return encodePacket_js_1.encodePacket; } });
  6. const decodePacket_js_1 = require("./decodePacket.js");
  7. Object.defineProperty(exports, "decodePacket", { enumerable: true, get: function () { return decodePacket_js_1.decodePacket; } });
  8. const commons_js_1 = require("./commons.js");
  9. const SEPARATOR = String.fromCharCode(30); // see https://en.wikipedia.org/wiki/Delimiter#ASCII_delimited_text
  10. const encodePayload = (packets, callback) => {
  11. // some packets may be added to the array while encoding, so the initial length must be saved
  12. const length = packets.length;
  13. const encodedPackets = new Array(length);
  14. let count = 0;
  15. packets.forEach((packet, i) => {
  16. // force base64 encoding for binary packets
  17. (0, encodePacket_js_1.encodePacket)(packet, false, (encodedPacket) => {
  18. encodedPackets[i] = encodedPacket;
  19. if (++count === length) {
  20. callback(encodedPackets.join(SEPARATOR));
  21. }
  22. });
  23. });
  24. };
  25. exports.encodePayload = encodePayload;
  26. const decodePayload = (encodedPayload, binaryType) => {
  27. const encodedPackets = encodedPayload.split(SEPARATOR);
  28. const packets = [];
  29. for (let i = 0; i < encodedPackets.length; i++) {
  30. const decodedPacket = (0, decodePacket_js_1.decodePacket)(encodedPackets[i], binaryType);
  31. packets.push(decodedPacket);
  32. if (decodedPacket.type === "error") {
  33. break;
  34. }
  35. }
  36. return packets;
  37. };
  38. exports.decodePayload = decodePayload;
  39. function createPacketEncoderStream() {
  40. // @ts-expect-error
  41. return new TransformStream({
  42. transform(packet, controller) {
  43. (0, encodePacket_js_1.encodePacketToBinary)(packet, (encodedPacket) => {
  44. const payloadLength = encodedPacket.length;
  45. let header;
  46. // inspired by the WebSocket format: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#decoding_payload_length
  47. if (payloadLength < 126) {
  48. header = new Uint8Array(1);
  49. new DataView(header.buffer).setUint8(0, payloadLength);
  50. }
  51. else if (payloadLength < 65536) {
  52. header = new Uint8Array(3);
  53. const view = new DataView(header.buffer);
  54. view.setUint8(0, 126);
  55. view.setUint16(1, payloadLength);
  56. }
  57. else {
  58. header = new Uint8Array(9);
  59. const view = new DataView(header.buffer);
  60. view.setUint8(0, 127);
  61. view.setBigUint64(1, BigInt(payloadLength));
  62. }
  63. // first bit indicates whether the payload is plain text (0) or binary (1)
  64. if (packet.data && typeof packet.data !== "string") {
  65. header[0] |= 0x80;
  66. }
  67. controller.enqueue(header);
  68. controller.enqueue(encodedPacket);
  69. });
  70. },
  71. });
  72. }
  73. exports.createPacketEncoderStream = createPacketEncoderStream;
  74. let TEXT_DECODER;
  75. function totalLength(chunks) {
  76. return chunks.reduce((acc, chunk) => acc + chunk.length, 0);
  77. }
  78. function concatChunks(chunks, size) {
  79. if (chunks[0].length === size) {
  80. return chunks.shift();
  81. }
  82. const buffer = new Uint8Array(size);
  83. let j = 0;
  84. for (let i = 0; i < size; i++) {
  85. buffer[i] = chunks[0][j++];
  86. if (j === chunks[0].length) {
  87. chunks.shift();
  88. j = 0;
  89. }
  90. }
  91. if (chunks.length && j < chunks[0].length) {
  92. chunks[0] = chunks[0].slice(j);
  93. }
  94. return buffer;
  95. }
  96. function createPacketDecoderStream(maxPayload, binaryType) {
  97. if (!TEXT_DECODER) {
  98. TEXT_DECODER = new TextDecoder();
  99. }
  100. const chunks = [];
  101. let state = 0 /* READ_HEADER */;
  102. let expectedLength = -1;
  103. let isBinary = false;
  104. // @ts-expect-error
  105. return new TransformStream({
  106. transform(chunk, controller) {
  107. chunks.push(chunk);
  108. while (true) {
  109. if (state === 0 /* READ_HEADER */) {
  110. if (totalLength(chunks) < 1) {
  111. break;
  112. }
  113. const header = concatChunks(chunks, 1);
  114. isBinary = (header[0] & 0x80) === 0x80;
  115. expectedLength = header[0] & 0x7f;
  116. if (expectedLength < 126) {
  117. state = 3 /* READ_PAYLOAD */;
  118. }
  119. else if (expectedLength === 126) {
  120. state = 1 /* READ_EXTENDED_LENGTH_16 */;
  121. }
  122. else {
  123. state = 2 /* READ_EXTENDED_LENGTH_64 */;
  124. }
  125. }
  126. else if (state === 1 /* READ_EXTENDED_LENGTH_16 */) {
  127. if (totalLength(chunks) < 2) {
  128. break;
  129. }
  130. const headerArray = concatChunks(chunks, 2);
  131. expectedLength = new DataView(headerArray.buffer, headerArray.byteOffset, headerArray.length).getUint16(0);
  132. state = 3 /* READ_PAYLOAD */;
  133. }
  134. else if (state === 2 /* READ_EXTENDED_LENGTH_64 */) {
  135. if (totalLength(chunks) < 8) {
  136. break;
  137. }
  138. const headerArray = concatChunks(chunks, 8);
  139. const view = new DataView(headerArray.buffer, headerArray.byteOffset, headerArray.length);
  140. const n = view.getUint32(0);
  141. if (n > Math.pow(2, 53 - 32) - 1) {
  142. // the maximum safe integer in JavaScript is 2^53 - 1
  143. controller.enqueue(commons_js_1.ERROR_PACKET);
  144. break;
  145. }
  146. expectedLength = n * Math.pow(2, 32) + view.getUint32(4);
  147. state = 3 /* READ_PAYLOAD */;
  148. }
  149. else {
  150. if (totalLength(chunks) < expectedLength) {
  151. break;
  152. }
  153. const data = concatChunks(chunks, expectedLength);
  154. controller.enqueue((0, decodePacket_js_1.decodePacket)(isBinary ? data : TEXT_DECODER.decode(data), binaryType));
  155. state = 0 /* READ_HEADER */;
  156. }
  157. if (expectedLength === 0 || expectedLength > maxPayload) {
  158. controller.enqueue(commons_js_1.ERROR_PACKET);
  159. break;
  160. }
  161. }
  162. },
  163. });
  164. }
  165. exports.createPacketDecoderStream = createPacketDecoderStream;
  166. exports.protocol = 4;