1 | import { encodePacket, encodePacketToBinary } from "./encodePacket.js";
|
2 | import { decodePacket } from "./decodePacket.js";
|
3 | import { ERROR_PACKET, } from "./commons.js";
|
4 | const SEPARATOR = String.fromCharCode(30);
|
5 | const encodePayload = (packets, callback) => {
|
6 |
|
7 | const length = packets.length;
|
8 | const encodedPackets = new Array(length);
|
9 | let count = 0;
|
10 | packets.forEach((packet, i) => {
|
11 |
|
12 | encodePacket(packet, false, (encodedPacket) => {
|
13 | encodedPackets[i] = encodedPacket;
|
14 | if (++count === length) {
|
15 | callback(encodedPackets.join(SEPARATOR));
|
16 | }
|
17 | });
|
18 | });
|
19 | };
|
20 | const decodePayload = (encodedPayload, binaryType) => {
|
21 | const encodedPackets = encodedPayload.split(SEPARATOR);
|
22 | const packets = [];
|
23 | for (let i = 0; i < encodedPackets.length; i++) {
|
24 | const decodedPacket = decodePacket(encodedPackets[i], binaryType);
|
25 | packets.push(decodedPacket);
|
26 | if (decodedPacket.type === "error") {
|
27 | break;
|
28 | }
|
29 | }
|
30 | return packets;
|
31 | };
|
32 | export function createPacketEncoderStream() {
|
33 | return new TransformStream({
|
34 | transform(packet, controller) {
|
35 | encodePacketToBinary(packet, (encodedPacket) => {
|
36 | const payloadLength = encodedPacket.length;
|
37 | let header;
|
38 |
|
39 | if (payloadLength < 126) {
|
40 | header = new Uint8Array(1);
|
41 | new DataView(header.buffer).setUint8(0, payloadLength);
|
42 | }
|
43 | else if (payloadLength < 65536) {
|
44 | header = new Uint8Array(3);
|
45 | const view = new DataView(header.buffer);
|
46 | view.setUint8(0, 126);
|
47 | view.setUint16(1, payloadLength);
|
48 | }
|
49 | else {
|
50 | header = new Uint8Array(9);
|
51 | const view = new DataView(header.buffer);
|
52 | view.setUint8(0, 127);
|
53 | view.setBigUint64(1, BigInt(payloadLength));
|
54 | }
|
55 |
|
56 | if (packet.data && typeof packet.data !== "string") {
|
57 | header[0] |= 0x80;
|
58 | }
|
59 | controller.enqueue(header);
|
60 | controller.enqueue(encodedPacket);
|
61 | });
|
62 | },
|
63 | });
|
64 | }
|
65 | let TEXT_DECODER;
|
66 | function totalLength(chunks) {
|
67 | return chunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
68 | }
|
69 | function concatChunks(chunks, size) {
|
70 | if (chunks[0].length === size) {
|
71 | return chunks.shift();
|
72 | }
|
73 | const buffer = new Uint8Array(size);
|
74 | let j = 0;
|
75 | for (let i = 0; i < size; i++) {
|
76 | buffer[i] = chunks[0][j++];
|
77 | if (j === chunks[0].length) {
|
78 | chunks.shift();
|
79 | j = 0;
|
80 | }
|
81 | }
|
82 | if (chunks.length && j < chunks[0].length) {
|
83 | chunks[0] = chunks[0].slice(j);
|
84 | }
|
85 | return buffer;
|
86 | }
|
87 | export function createPacketDecoderStream(maxPayload, binaryType) {
|
88 | if (!TEXT_DECODER) {
|
89 | TEXT_DECODER = new TextDecoder();
|
90 | }
|
91 | const chunks = [];
|
92 | let state = 0 ;
|
93 | let expectedLength = -1;
|
94 | let isBinary = false;
|
95 | return new TransformStream({
|
96 | transform(chunk, controller) {
|
97 | chunks.push(chunk);
|
98 | while (true) {
|
99 | if (state === 0 ) {
|
100 | if (totalLength(chunks) < 1) {
|
101 | break;
|
102 | }
|
103 | const header = concatChunks(chunks, 1);
|
104 | isBinary = (header[0] & 0x80) === 0x80;
|
105 | expectedLength = header[0] & 0x7f;
|
106 | if (expectedLength < 126) {
|
107 | state = 3 ;
|
108 | }
|
109 | else if (expectedLength === 126) {
|
110 | state = 1 ;
|
111 | }
|
112 | else {
|
113 | state = 2 ;
|
114 | }
|
115 | }
|
116 | else if (state === 1 ) {
|
117 | if (totalLength(chunks) < 2) {
|
118 | break;
|
119 | }
|
120 | const headerArray = concatChunks(chunks, 2);
|
121 | expectedLength = new DataView(headerArray.buffer, headerArray.byteOffset, headerArray.length).getUint16(0);
|
122 | state = 3 ;
|
123 | }
|
124 | else if (state === 2 ) {
|
125 | if (totalLength(chunks) < 8) {
|
126 | break;
|
127 | }
|
128 | const headerArray = concatChunks(chunks, 8);
|
129 | const view = new DataView(headerArray.buffer, headerArray.byteOffset, headerArray.length);
|
130 | const n = view.getUint32(0);
|
131 | if (n > Math.pow(2, 53 - 32) - 1) {
|
132 |
|
133 | controller.enqueue(ERROR_PACKET);
|
134 | break;
|
135 | }
|
136 | expectedLength = n * Math.pow(2, 32) + view.getUint32(4);
|
137 | state = 3 ;
|
138 | }
|
139 | else {
|
140 | if (totalLength(chunks) < expectedLength) {
|
141 | break;
|
142 | }
|
143 | const data = concatChunks(chunks, expectedLength);
|
144 | controller.enqueue(decodePacket(isBinary ? data : TEXT_DECODER.decode(data), binaryType));
|
145 | state = 0 ;
|
146 | }
|
147 | if (expectedLength === 0 || expectedLength > maxPayload) {
|
148 | controller.enqueue(ERROR_PACKET);
|
149 | break;
|
150 | }
|
151 | }
|
152 | },
|
153 | });
|
154 | }
|
155 | export const protocol = 4;
|
156 | export { encodePacket, encodePayload, decodePacket, decodePayload, };
|