grpc-libp2p-client 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dc-http2/frame.cjs.js +1468 -0
- package/dist/dc-http2/frame.cjs.js.map +1 -0
- package/dist/dc-http2/frame.d.ts +34 -0
- package/dist/dc-http2/frame.esm.js +1466 -0
- package/dist/dc-http2/frame.esm.js.map +1 -0
- package/dist/dc-http2/hpack.cjs.js +1201 -0
- package/dist/dc-http2/hpack.cjs.js.map +1 -0
- package/dist/dc-http2/hpack.d.ts +45 -0
- package/dist/dc-http2/hpack.esm.js +1199 -0
- package/dist/dc-http2/hpack.esm.js.map +1 -0
- package/dist/dc-http2/parser.cjs.js +1755 -0
- package/dist/dc-http2/parser.cjs.js.map +1 -0
- package/dist/dc-http2/parser.d.ts +87 -0
- package/dist/dc-http2/parser.esm.js +1753 -0
- package/dist/dc-http2/parser.esm.js.map +1 -0
- package/dist/dc-http2/stream.cjs.js +250 -0
- package/dist/dc-http2/stream.cjs.js.map +1 -0
- package/dist/dc-http2/stream.d.ts +42 -0
- package/dist/dc-http2/stream.esm.js +248 -0
- package/dist/dc-http2/stream.esm.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1468 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function coerce(o) {
|
|
4
|
+
if (o instanceof Uint8Array && o.constructor.name === 'Uint8Array')
|
|
5
|
+
return o;
|
|
6
|
+
if (o instanceof ArrayBuffer)
|
|
7
|
+
return new Uint8Array(o);
|
|
8
|
+
if (ArrayBuffer.isView(o)) {
|
|
9
|
+
return new Uint8Array(o.buffer, o.byteOffset, o.byteLength);
|
|
10
|
+
}
|
|
11
|
+
throw new Error('Unknown type, must be binary type');
|
|
12
|
+
}
|
|
13
|
+
function fromString(str) {
|
|
14
|
+
return new TextEncoder().encode(str);
|
|
15
|
+
}
|
|
16
|
+
function toString$1(b) {
|
|
17
|
+
return new TextDecoder().decode(b);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* eslint-disable */
|
|
21
|
+
// base-x encoding / decoding
|
|
22
|
+
// Copyright (c) 2018 base-x contributors
|
|
23
|
+
// Copyright (c) 2014-2018 The Bitcoin Core developers (base58.cpp)
|
|
24
|
+
// Distributed under the MIT software license, see the accompanying
|
|
25
|
+
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} ALPHABET
|
|
28
|
+
* @param {any} name
|
|
29
|
+
*/
|
|
30
|
+
function base(ALPHABET, name) {
|
|
31
|
+
if (ALPHABET.length >= 255) {
|
|
32
|
+
throw new TypeError('Alphabet too long');
|
|
33
|
+
}
|
|
34
|
+
var BASE_MAP = new Uint8Array(256);
|
|
35
|
+
for (var j = 0; j < BASE_MAP.length; j++) {
|
|
36
|
+
BASE_MAP[j] = 255;
|
|
37
|
+
}
|
|
38
|
+
for (var i = 0; i < ALPHABET.length; i++) {
|
|
39
|
+
var x = ALPHABET.charAt(i);
|
|
40
|
+
var xc = x.charCodeAt(0);
|
|
41
|
+
if (BASE_MAP[xc] !== 255) {
|
|
42
|
+
throw new TypeError(x + ' is ambiguous');
|
|
43
|
+
}
|
|
44
|
+
BASE_MAP[xc] = i;
|
|
45
|
+
}
|
|
46
|
+
var BASE = ALPHABET.length;
|
|
47
|
+
var LEADER = ALPHABET.charAt(0);
|
|
48
|
+
var FACTOR = Math.log(BASE) / Math.log(256); // log(BASE) / log(256), rounded up
|
|
49
|
+
var iFACTOR = Math.log(256) / Math.log(BASE); // log(256) / log(BASE), rounded up
|
|
50
|
+
/**
|
|
51
|
+
* @param {any[] | Iterable<number>} source
|
|
52
|
+
*/
|
|
53
|
+
function encode(source) {
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
if (source instanceof Uint8Array)
|
|
56
|
+
;
|
|
57
|
+
else if (ArrayBuffer.isView(source)) {
|
|
58
|
+
source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
|
|
59
|
+
}
|
|
60
|
+
else if (Array.isArray(source)) {
|
|
61
|
+
source = Uint8Array.from(source);
|
|
62
|
+
}
|
|
63
|
+
if (!(source instanceof Uint8Array)) {
|
|
64
|
+
throw new TypeError('Expected Uint8Array');
|
|
65
|
+
}
|
|
66
|
+
if (source.length === 0) {
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
69
|
+
// Skip & count leading zeroes.
|
|
70
|
+
var zeroes = 0;
|
|
71
|
+
var length = 0;
|
|
72
|
+
var pbegin = 0;
|
|
73
|
+
var pend = source.length;
|
|
74
|
+
while (pbegin !== pend && source[pbegin] === 0) {
|
|
75
|
+
pbegin++;
|
|
76
|
+
zeroes++;
|
|
77
|
+
}
|
|
78
|
+
// Allocate enough space in big-endian base58 representation.
|
|
79
|
+
var size = ((pend - pbegin) * iFACTOR + 1) >>> 0;
|
|
80
|
+
var b58 = new Uint8Array(size);
|
|
81
|
+
// Process the bytes.
|
|
82
|
+
while (pbegin !== pend) {
|
|
83
|
+
var carry = source[pbegin];
|
|
84
|
+
// Apply "b58 = b58 * 256 + ch".
|
|
85
|
+
var i = 0;
|
|
86
|
+
for (var it1 = size - 1; (carry !== 0 || i < length) && (it1 !== -1); it1--, i++) {
|
|
87
|
+
carry += (256 * b58[it1]) >>> 0;
|
|
88
|
+
b58[it1] = (carry % BASE) >>> 0;
|
|
89
|
+
carry = (carry / BASE) >>> 0;
|
|
90
|
+
}
|
|
91
|
+
if (carry !== 0) {
|
|
92
|
+
throw new Error('Non-zero carry');
|
|
93
|
+
}
|
|
94
|
+
length = i;
|
|
95
|
+
pbegin++;
|
|
96
|
+
}
|
|
97
|
+
// Skip leading zeroes in base58 result.
|
|
98
|
+
var it2 = size - length;
|
|
99
|
+
while (it2 !== size && b58[it2] === 0) {
|
|
100
|
+
it2++;
|
|
101
|
+
}
|
|
102
|
+
// Translate the result into a string.
|
|
103
|
+
var str = LEADER.repeat(zeroes);
|
|
104
|
+
for (; it2 < size; ++it2) {
|
|
105
|
+
str += ALPHABET.charAt(b58[it2]);
|
|
106
|
+
}
|
|
107
|
+
return str;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @param {string | string[]} source
|
|
111
|
+
*/
|
|
112
|
+
function decodeUnsafe(source) {
|
|
113
|
+
if (typeof source !== 'string') {
|
|
114
|
+
throw new TypeError('Expected String');
|
|
115
|
+
}
|
|
116
|
+
if (source.length === 0) {
|
|
117
|
+
return new Uint8Array();
|
|
118
|
+
}
|
|
119
|
+
var psz = 0;
|
|
120
|
+
// Skip leading spaces.
|
|
121
|
+
if (source[psz] === ' ') {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Skip and count leading '1's.
|
|
125
|
+
var zeroes = 0;
|
|
126
|
+
var length = 0;
|
|
127
|
+
while (source[psz] === LEADER) {
|
|
128
|
+
zeroes++;
|
|
129
|
+
psz++;
|
|
130
|
+
}
|
|
131
|
+
// Allocate enough space in big-endian base256 representation.
|
|
132
|
+
var size = (((source.length - psz) * FACTOR) + 1) >>> 0; // log(58) / log(256), rounded up.
|
|
133
|
+
var b256 = new Uint8Array(size);
|
|
134
|
+
// Process the characters.
|
|
135
|
+
while (source[psz]) {
|
|
136
|
+
// Decode character
|
|
137
|
+
var carry = BASE_MAP[source.charCodeAt(psz)];
|
|
138
|
+
// Invalid character
|
|
139
|
+
if (carry === 255) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
var i = 0;
|
|
143
|
+
for (var it3 = size - 1; (carry !== 0 || i < length) && (it3 !== -1); it3--, i++) {
|
|
144
|
+
carry += (BASE * b256[it3]) >>> 0;
|
|
145
|
+
b256[it3] = (carry % 256) >>> 0;
|
|
146
|
+
carry = (carry / 256) >>> 0;
|
|
147
|
+
}
|
|
148
|
+
if (carry !== 0) {
|
|
149
|
+
throw new Error('Non-zero carry');
|
|
150
|
+
}
|
|
151
|
+
length = i;
|
|
152
|
+
psz++;
|
|
153
|
+
}
|
|
154
|
+
// Skip trailing spaces.
|
|
155
|
+
if (source[psz] === ' ') {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// Skip leading zeroes in b256.
|
|
159
|
+
var it4 = size - length;
|
|
160
|
+
while (it4 !== size && b256[it4] === 0) {
|
|
161
|
+
it4++;
|
|
162
|
+
}
|
|
163
|
+
var vch = new Uint8Array(zeroes + (size - it4));
|
|
164
|
+
var j = zeroes;
|
|
165
|
+
while (it4 !== size) {
|
|
166
|
+
vch[j++] = b256[it4++];
|
|
167
|
+
}
|
|
168
|
+
return vch;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* @param {string | string[]} string
|
|
172
|
+
*/
|
|
173
|
+
function decode(string) {
|
|
174
|
+
var buffer = decodeUnsafe(string);
|
|
175
|
+
if (buffer) {
|
|
176
|
+
return buffer;
|
|
177
|
+
}
|
|
178
|
+
throw new Error(`Non-${name} character`);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
encode: encode,
|
|
182
|
+
decodeUnsafe: decodeUnsafe,
|
|
183
|
+
decode: decode
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
var src = base;
|
|
187
|
+
var _brrp__multiformats_scope_baseX = src;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Class represents both BaseEncoder and MultibaseEncoder meaning it
|
|
191
|
+
* can be used to encode to multibase or base encode without multibase
|
|
192
|
+
* prefix.
|
|
193
|
+
*/
|
|
194
|
+
class Encoder {
|
|
195
|
+
name;
|
|
196
|
+
prefix;
|
|
197
|
+
baseEncode;
|
|
198
|
+
constructor(name, prefix, baseEncode) {
|
|
199
|
+
this.name = name;
|
|
200
|
+
this.prefix = prefix;
|
|
201
|
+
this.baseEncode = baseEncode;
|
|
202
|
+
}
|
|
203
|
+
encode(bytes) {
|
|
204
|
+
if (bytes instanceof Uint8Array) {
|
|
205
|
+
return `${this.prefix}${this.baseEncode(bytes)}`;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
throw Error('Unknown type, must be binary type');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Class represents both BaseDecoder and MultibaseDecoder so it could be used
|
|
214
|
+
* to decode multibases (with matching prefix) or just base decode strings
|
|
215
|
+
* with corresponding base encoding.
|
|
216
|
+
*/
|
|
217
|
+
class Decoder {
|
|
218
|
+
name;
|
|
219
|
+
prefix;
|
|
220
|
+
baseDecode;
|
|
221
|
+
prefixCodePoint;
|
|
222
|
+
constructor(name, prefix, baseDecode) {
|
|
223
|
+
this.name = name;
|
|
224
|
+
this.prefix = prefix;
|
|
225
|
+
const prefixCodePoint = prefix.codePointAt(0);
|
|
226
|
+
/* c8 ignore next 3 */
|
|
227
|
+
if (prefixCodePoint === undefined) {
|
|
228
|
+
throw new Error('Invalid prefix character');
|
|
229
|
+
}
|
|
230
|
+
this.prefixCodePoint = prefixCodePoint;
|
|
231
|
+
this.baseDecode = baseDecode;
|
|
232
|
+
}
|
|
233
|
+
decode(text) {
|
|
234
|
+
if (typeof text === 'string') {
|
|
235
|
+
if (text.codePointAt(0) !== this.prefixCodePoint) {
|
|
236
|
+
throw Error(`Unable to decode multibase string ${JSON.stringify(text)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`);
|
|
237
|
+
}
|
|
238
|
+
return this.baseDecode(text.slice(this.prefix.length));
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
throw Error('Can only multibase decode strings');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
or(decoder) {
|
|
245
|
+
return or(this, decoder);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
class ComposedDecoder {
|
|
249
|
+
decoders;
|
|
250
|
+
constructor(decoders) {
|
|
251
|
+
this.decoders = decoders;
|
|
252
|
+
}
|
|
253
|
+
or(decoder) {
|
|
254
|
+
return or(this, decoder);
|
|
255
|
+
}
|
|
256
|
+
decode(input) {
|
|
257
|
+
const prefix = input[0];
|
|
258
|
+
const decoder = this.decoders[prefix];
|
|
259
|
+
if (decoder != null) {
|
|
260
|
+
return decoder.decode(input);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
throw RangeError(`Unable to decode multibase string ${JSON.stringify(input)}, only inputs prefixed with ${Object.keys(this.decoders)} are supported`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function or(left, right) {
|
|
268
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
269
|
+
return new ComposedDecoder({
|
|
270
|
+
...(left.decoders ?? { [left.prefix]: left }),
|
|
271
|
+
...(right.decoders ?? { [right.prefix]: right })
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
class Codec {
|
|
275
|
+
name;
|
|
276
|
+
prefix;
|
|
277
|
+
baseEncode;
|
|
278
|
+
baseDecode;
|
|
279
|
+
encoder;
|
|
280
|
+
decoder;
|
|
281
|
+
constructor(name, prefix, baseEncode, baseDecode) {
|
|
282
|
+
this.name = name;
|
|
283
|
+
this.prefix = prefix;
|
|
284
|
+
this.baseEncode = baseEncode;
|
|
285
|
+
this.baseDecode = baseDecode;
|
|
286
|
+
this.encoder = new Encoder(name, prefix, baseEncode);
|
|
287
|
+
this.decoder = new Decoder(name, prefix, baseDecode);
|
|
288
|
+
}
|
|
289
|
+
encode(input) {
|
|
290
|
+
return this.encoder.encode(input);
|
|
291
|
+
}
|
|
292
|
+
decode(input) {
|
|
293
|
+
return this.decoder.decode(input);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function from({ name, prefix, encode, decode }) {
|
|
297
|
+
return new Codec(name, prefix, encode, decode);
|
|
298
|
+
}
|
|
299
|
+
function baseX({ name, prefix, alphabet }) {
|
|
300
|
+
const { encode, decode } = _brrp__multiformats_scope_baseX(alphabet, name);
|
|
301
|
+
return from({
|
|
302
|
+
prefix,
|
|
303
|
+
name,
|
|
304
|
+
encode,
|
|
305
|
+
decode: (text) => coerce(decode(text))
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
function decode$1(string, alphabet, bitsPerChar, name) {
|
|
309
|
+
// Build the character lookup table:
|
|
310
|
+
const codes = {};
|
|
311
|
+
for (let i = 0; i < alphabet.length; ++i) {
|
|
312
|
+
codes[alphabet[i]] = i;
|
|
313
|
+
}
|
|
314
|
+
// Count the padding bytes:
|
|
315
|
+
let end = string.length;
|
|
316
|
+
while (string[end - 1] === '=') {
|
|
317
|
+
--end;
|
|
318
|
+
}
|
|
319
|
+
// Allocate the output:
|
|
320
|
+
const out = new Uint8Array((end * bitsPerChar / 8) | 0);
|
|
321
|
+
// Parse the data:
|
|
322
|
+
let bits = 0; // Number of bits currently in the buffer
|
|
323
|
+
let buffer = 0; // Bits waiting to be written out, MSB first
|
|
324
|
+
let written = 0; // Next byte to write
|
|
325
|
+
for (let i = 0; i < end; ++i) {
|
|
326
|
+
// Read one character from the string:
|
|
327
|
+
const value = codes[string[i]];
|
|
328
|
+
if (value === undefined) {
|
|
329
|
+
throw new SyntaxError(`Non-${name} character`);
|
|
330
|
+
}
|
|
331
|
+
// Append the bits to the buffer:
|
|
332
|
+
buffer = (buffer << bitsPerChar) | value;
|
|
333
|
+
bits += bitsPerChar;
|
|
334
|
+
// Write out some bits if the buffer has a byte's worth:
|
|
335
|
+
if (bits >= 8) {
|
|
336
|
+
bits -= 8;
|
|
337
|
+
out[written++] = 0xff & (buffer >> bits);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Verify that we have received just enough bits:
|
|
341
|
+
if (bits >= bitsPerChar || (0xff & (buffer << (8 - bits))) !== 0) {
|
|
342
|
+
throw new SyntaxError('Unexpected end of data');
|
|
343
|
+
}
|
|
344
|
+
return out;
|
|
345
|
+
}
|
|
346
|
+
function encode$1(data, alphabet, bitsPerChar) {
|
|
347
|
+
const pad = alphabet[alphabet.length - 1] === '=';
|
|
348
|
+
const mask = (1 << bitsPerChar) - 1;
|
|
349
|
+
let out = '';
|
|
350
|
+
let bits = 0; // Number of bits currently in the buffer
|
|
351
|
+
let buffer = 0; // Bits waiting to be written out, MSB first
|
|
352
|
+
for (let i = 0; i < data.length; ++i) {
|
|
353
|
+
// Slurp data into the buffer:
|
|
354
|
+
buffer = (buffer << 8) | data[i];
|
|
355
|
+
bits += 8;
|
|
356
|
+
// Write out as much as we can:
|
|
357
|
+
while (bits > bitsPerChar) {
|
|
358
|
+
bits -= bitsPerChar;
|
|
359
|
+
out += alphabet[mask & (buffer >> bits)];
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// Partial character:
|
|
363
|
+
if (bits !== 0) {
|
|
364
|
+
out += alphabet[mask & (buffer << (bitsPerChar - bits))];
|
|
365
|
+
}
|
|
366
|
+
// Add padding characters until we hit a byte boundary:
|
|
367
|
+
if (pad) {
|
|
368
|
+
while (((out.length * bitsPerChar) & 7) !== 0) {
|
|
369
|
+
out += '=';
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return out;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* RFC4648 Factory
|
|
376
|
+
*/
|
|
377
|
+
function rfc4648({ name, prefix, bitsPerChar, alphabet }) {
|
|
378
|
+
return from({
|
|
379
|
+
prefix,
|
|
380
|
+
name,
|
|
381
|
+
encode(input) {
|
|
382
|
+
return encode$1(input, alphabet, bitsPerChar);
|
|
383
|
+
},
|
|
384
|
+
decode(input) {
|
|
385
|
+
return decode$1(input, alphabet, bitsPerChar, name);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const base10 = baseX({
|
|
391
|
+
prefix: '9',
|
|
392
|
+
name: 'base10',
|
|
393
|
+
alphabet: '0123456789'
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
var base10$1 = /*#__PURE__*/Object.freeze({
|
|
397
|
+
__proto__: null,
|
|
398
|
+
base10: base10
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const base16 = rfc4648({
|
|
402
|
+
prefix: 'f',
|
|
403
|
+
name: 'base16',
|
|
404
|
+
alphabet: '0123456789abcdef',
|
|
405
|
+
bitsPerChar: 4
|
|
406
|
+
});
|
|
407
|
+
const base16upper = rfc4648({
|
|
408
|
+
prefix: 'F',
|
|
409
|
+
name: 'base16upper',
|
|
410
|
+
alphabet: '0123456789ABCDEF',
|
|
411
|
+
bitsPerChar: 4
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
var base16$1 = /*#__PURE__*/Object.freeze({
|
|
415
|
+
__proto__: null,
|
|
416
|
+
base16: base16,
|
|
417
|
+
base16upper: base16upper
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
const base2 = rfc4648({
|
|
421
|
+
prefix: '0',
|
|
422
|
+
name: 'base2',
|
|
423
|
+
alphabet: '01',
|
|
424
|
+
bitsPerChar: 1
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
var base2$1 = /*#__PURE__*/Object.freeze({
|
|
428
|
+
__proto__: null,
|
|
429
|
+
base2: base2
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
const alphabet = Array.from('🚀🪐☄🛰🌌🌑🌒🌓🌔🌕🌖🌗🌘🌍🌏🌎🐉☀💻🖥💾💿😂❤😍🤣😊🙏💕😭😘👍😅👏😁🔥🥰💔💖💙😢🤔😆🙄💪😉☺👌🤗💜😔😎😇🌹🤦🎉💞✌✨🤷😱😌🌸🙌😋💗💚😏💛🙂💓🤩😄😀🖤😃💯🙈👇🎶😒🤭❣😜💋👀😪😑💥🙋😞😩😡🤪👊🥳😥🤤👉💃😳✋😚😝😴🌟😬🙃🍀🌷😻😓⭐✅🥺🌈😈🤘💦✔😣🏃💐☹🎊💘😠☝😕🌺🎂🌻😐🖕💝🙊😹🗣💫💀👑🎵🤞😛🔴😤🌼😫⚽🤙☕🏆🤫👈😮🙆🍻🍃🐶💁😲🌿🧡🎁⚡🌞🎈❌✊👋😰🤨😶🤝🚶💰🍓💢🤟🙁🚨💨🤬✈🎀🍺🤓😙💟🌱😖👶🥴▶➡❓💎💸⬇😨🌚🦋😷🕺⚠🙅😟😵👎🤲🤠🤧📌🔵💅🧐🐾🍒😗🤑🌊🤯🐷☎💧😯💆👆🎤🙇🍑❄🌴💣🐸💌📍🥀🤢👅💡💩👐📸👻🤐🤮🎼🥵🚩🍎🍊👼💍📣🥂');
|
|
433
|
+
const alphabetBytesToChars = (alphabet.reduce((p, c, i) => { p[i] = c; return p; }, ([])));
|
|
434
|
+
const alphabetCharsToBytes = (alphabet.reduce((p, c, i) => {
|
|
435
|
+
const codePoint = c.codePointAt(0);
|
|
436
|
+
if (codePoint == null) {
|
|
437
|
+
throw new Error(`Invalid character: ${c}`);
|
|
438
|
+
}
|
|
439
|
+
p[codePoint] = i;
|
|
440
|
+
return p;
|
|
441
|
+
}, ([])));
|
|
442
|
+
function encode(data) {
|
|
443
|
+
return data.reduce((p, c) => {
|
|
444
|
+
p += alphabetBytesToChars[c];
|
|
445
|
+
return p;
|
|
446
|
+
}, '');
|
|
447
|
+
}
|
|
448
|
+
function decode(str) {
|
|
449
|
+
const byts = [];
|
|
450
|
+
for (const char of str) {
|
|
451
|
+
const codePoint = char.codePointAt(0);
|
|
452
|
+
if (codePoint == null) {
|
|
453
|
+
throw new Error(`Invalid character: ${char}`);
|
|
454
|
+
}
|
|
455
|
+
const byt = alphabetCharsToBytes[codePoint];
|
|
456
|
+
if (byt == null) {
|
|
457
|
+
throw new Error(`Non-base256emoji character: ${char}`);
|
|
458
|
+
}
|
|
459
|
+
byts.push(byt);
|
|
460
|
+
}
|
|
461
|
+
return new Uint8Array(byts);
|
|
462
|
+
}
|
|
463
|
+
const base256emoji = from({
|
|
464
|
+
prefix: '🚀',
|
|
465
|
+
name: 'base256emoji',
|
|
466
|
+
encode,
|
|
467
|
+
decode
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
var base256emoji$1 = /*#__PURE__*/Object.freeze({
|
|
471
|
+
__proto__: null,
|
|
472
|
+
base256emoji: base256emoji
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
const base32 = rfc4648({
|
|
476
|
+
prefix: 'b',
|
|
477
|
+
name: 'base32',
|
|
478
|
+
alphabet: 'abcdefghijklmnopqrstuvwxyz234567',
|
|
479
|
+
bitsPerChar: 5
|
|
480
|
+
});
|
|
481
|
+
const base32upper = rfc4648({
|
|
482
|
+
prefix: 'B',
|
|
483
|
+
name: 'base32upper',
|
|
484
|
+
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
|
|
485
|
+
bitsPerChar: 5
|
|
486
|
+
});
|
|
487
|
+
const base32pad = rfc4648({
|
|
488
|
+
prefix: 'c',
|
|
489
|
+
name: 'base32pad',
|
|
490
|
+
alphabet: 'abcdefghijklmnopqrstuvwxyz234567=',
|
|
491
|
+
bitsPerChar: 5
|
|
492
|
+
});
|
|
493
|
+
const base32padupper = rfc4648({
|
|
494
|
+
prefix: 'C',
|
|
495
|
+
name: 'base32padupper',
|
|
496
|
+
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=',
|
|
497
|
+
bitsPerChar: 5
|
|
498
|
+
});
|
|
499
|
+
const base32hex = rfc4648({
|
|
500
|
+
prefix: 'v',
|
|
501
|
+
name: 'base32hex',
|
|
502
|
+
alphabet: '0123456789abcdefghijklmnopqrstuv',
|
|
503
|
+
bitsPerChar: 5
|
|
504
|
+
});
|
|
505
|
+
const base32hexupper = rfc4648({
|
|
506
|
+
prefix: 'V',
|
|
507
|
+
name: 'base32hexupper',
|
|
508
|
+
alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUV',
|
|
509
|
+
bitsPerChar: 5
|
|
510
|
+
});
|
|
511
|
+
const base32hexpad = rfc4648({
|
|
512
|
+
prefix: 't',
|
|
513
|
+
name: 'base32hexpad',
|
|
514
|
+
alphabet: '0123456789abcdefghijklmnopqrstuv=',
|
|
515
|
+
bitsPerChar: 5
|
|
516
|
+
});
|
|
517
|
+
const base32hexpadupper = rfc4648({
|
|
518
|
+
prefix: 'T',
|
|
519
|
+
name: 'base32hexpadupper',
|
|
520
|
+
alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUV=',
|
|
521
|
+
bitsPerChar: 5
|
|
522
|
+
});
|
|
523
|
+
const base32z = rfc4648({
|
|
524
|
+
prefix: 'h',
|
|
525
|
+
name: 'base32z',
|
|
526
|
+
alphabet: 'ybndrfg8ejkmcpqxot1uwisza345h769',
|
|
527
|
+
bitsPerChar: 5
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
var base32$1 = /*#__PURE__*/Object.freeze({
|
|
531
|
+
__proto__: null,
|
|
532
|
+
base32: base32,
|
|
533
|
+
base32hex: base32hex,
|
|
534
|
+
base32hexpad: base32hexpad,
|
|
535
|
+
base32hexpadupper: base32hexpadupper,
|
|
536
|
+
base32hexupper: base32hexupper,
|
|
537
|
+
base32pad: base32pad,
|
|
538
|
+
base32padupper: base32padupper,
|
|
539
|
+
base32upper: base32upper,
|
|
540
|
+
base32z: base32z
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
const base36 = baseX({
|
|
544
|
+
prefix: 'k',
|
|
545
|
+
name: 'base36',
|
|
546
|
+
alphabet: '0123456789abcdefghijklmnopqrstuvwxyz'
|
|
547
|
+
});
|
|
548
|
+
const base36upper = baseX({
|
|
549
|
+
prefix: 'K',
|
|
550
|
+
name: 'base36upper',
|
|
551
|
+
alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
var base36$1 = /*#__PURE__*/Object.freeze({
|
|
555
|
+
__proto__: null,
|
|
556
|
+
base36: base36,
|
|
557
|
+
base36upper: base36upper
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
const base58btc = baseX({
|
|
561
|
+
name: 'base58btc',
|
|
562
|
+
prefix: 'z',
|
|
563
|
+
alphabet: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
|
564
|
+
});
|
|
565
|
+
const base58flickr = baseX({
|
|
566
|
+
name: 'base58flickr',
|
|
567
|
+
prefix: 'Z',
|
|
568
|
+
alphabet: '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
var base58 = /*#__PURE__*/Object.freeze({
|
|
572
|
+
__proto__: null,
|
|
573
|
+
base58btc: base58btc,
|
|
574
|
+
base58flickr: base58flickr
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
const base64 = rfc4648({
|
|
578
|
+
prefix: 'm',
|
|
579
|
+
name: 'base64',
|
|
580
|
+
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
|
|
581
|
+
bitsPerChar: 6
|
|
582
|
+
});
|
|
583
|
+
const base64pad = rfc4648({
|
|
584
|
+
prefix: 'M',
|
|
585
|
+
name: 'base64pad',
|
|
586
|
+
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
|
|
587
|
+
bitsPerChar: 6
|
|
588
|
+
});
|
|
589
|
+
const base64url = rfc4648({
|
|
590
|
+
prefix: 'u',
|
|
591
|
+
name: 'base64url',
|
|
592
|
+
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
|
|
593
|
+
bitsPerChar: 6
|
|
594
|
+
});
|
|
595
|
+
const base64urlpad = rfc4648({
|
|
596
|
+
prefix: 'U',
|
|
597
|
+
name: 'base64urlpad',
|
|
598
|
+
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=',
|
|
599
|
+
bitsPerChar: 6
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
var base64$1 = /*#__PURE__*/Object.freeze({
|
|
603
|
+
__proto__: null,
|
|
604
|
+
base64: base64,
|
|
605
|
+
base64pad: base64pad,
|
|
606
|
+
base64url: base64url,
|
|
607
|
+
base64urlpad: base64urlpad
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
const base8 = rfc4648({
|
|
611
|
+
prefix: '7',
|
|
612
|
+
name: 'base8',
|
|
613
|
+
alphabet: '01234567',
|
|
614
|
+
bitsPerChar: 3
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
var base8$1 = /*#__PURE__*/Object.freeze({
|
|
618
|
+
__proto__: null,
|
|
619
|
+
base8: base8
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
const identity = from({
|
|
623
|
+
prefix: '\x00',
|
|
624
|
+
name: 'identity',
|
|
625
|
+
encode: (buf) => toString$1(buf),
|
|
626
|
+
decode: (str) => fromString(str)
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
var identityBase = /*#__PURE__*/Object.freeze({
|
|
630
|
+
__proto__: null,
|
|
631
|
+
identity: identity
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
new TextEncoder();
|
|
635
|
+
new TextDecoder();
|
|
636
|
+
|
|
637
|
+
const bases = { ...identityBase, ...base2$1, ...base8$1, ...base10$1, ...base16$1, ...base32$1, ...base36$1, ...base58, ...base64$1, ...base256emoji$1 };
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Returns a `Uint8Array` of the requested size. Referenced memory will
|
|
641
|
+
* be initialized to 0.
|
|
642
|
+
*/
|
|
643
|
+
/**
|
|
644
|
+
* Where possible returns a Uint8Array of the requested size that references
|
|
645
|
+
* uninitialized memory. Only use if you are certain you will immediately
|
|
646
|
+
* overwrite every value in the returned `Uint8Array`.
|
|
647
|
+
*/
|
|
648
|
+
function allocUnsafe(size = 0) {
|
|
649
|
+
return new Uint8Array(size);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function createCodec(name, prefix, encode, decode) {
|
|
653
|
+
return {
|
|
654
|
+
name,
|
|
655
|
+
prefix,
|
|
656
|
+
encoder: {
|
|
657
|
+
name,
|
|
658
|
+
prefix,
|
|
659
|
+
encode
|
|
660
|
+
},
|
|
661
|
+
decoder: {
|
|
662
|
+
decode
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
const string = createCodec('utf8', 'u', (buf) => {
|
|
667
|
+
const decoder = new TextDecoder('utf8');
|
|
668
|
+
return 'u' + decoder.decode(buf);
|
|
669
|
+
}, (str) => {
|
|
670
|
+
const encoder = new TextEncoder();
|
|
671
|
+
return encoder.encode(str.substring(1));
|
|
672
|
+
});
|
|
673
|
+
const ascii = createCodec('ascii', 'a', (buf) => {
|
|
674
|
+
let string = 'a';
|
|
675
|
+
for (let i = 0; i < buf.length; i++) {
|
|
676
|
+
string += String.fromCharCode(buf[i]);
|
|
677
|
+
}
|
|
678
|
+
return string;
|
|
679
|
+
}, (str) => {
|
|
680
|
+
str = str.substring(1);
|
|
681
|
+
const buf = allocUnsafe(str.length);
|
|
682
|
+
for (let i = 0; i < str.length; i++) {
|
|
683
|
+
buf[i] = str.charCodeAt(i);
|
|
684
|
+
}
|
|
685
|
+
return buf;
|
|
686
|
+
});
|
|
687
|
+
const BASES = {
|
|
688
|
+
utf8: string,
|
|
689
|
+
'utf-8': string,
|
|
690
|
+
hex: bases.base16,
|
|
691
|
+
latin1: ascii,
|
|
692
|
+
ascii,
|
|
693
|
+
binary: ascii,
|
|
694
|
+
...bases
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Turns a `Uint8Array` into a string.
|
|
699
|
+
*
|
|
700
|
+
* Supports `utf8`, `utf-8` and any encoding supported by the multibase module.
|
|
701
|
+
*
|
|
702
|
+
* Also `ascii` which is similar to node's 'binary' encoding.
|
|
703
|
+
*/
|
|
704
|
+
function toString(array, encoding = 'utf8') {
|
|
705
|
+
const base = BASES[encoding];
|
|
706
|
+
if (base == null) {
|
|
707
|
+
throw new Error(`Unsupported encoding "${encoding}"`);
|
|
708
|
+
}
|
|
709
|
+
// strip multibase prefix
|
|
710
|
+
return base.encoder.encode(array).substring(1);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// HPACK Implementation
|
|
714
|
+
class HPACK {
|
|
715
|
+
constructor(maxDynamicTableSize = 4096) {
|
|
716
|
+
// 初始化动态表
|
|
717
|
+
this.dynamicTable = [];
|
|
718
|
+
this.dynamicTableSize = 0;
|
|
719
|
+
this.maxDynamicTableSize = maxDynamicTableSize;
|
|
720
|
+
// 初始化静态表
|
|
721
|
+
this.staticTable = [
|
|
722
|
+
['', ''], // Index 0 is not used
|
|
723
|
+
[':authority', ''],
|
|
724
|
+
[':method', 'GET'],
|
|
725
|
+
[':method', 'POST'],
|
|
726
|
+
[':path', '/'],
|
|
727
|
+
[':path', '/index.html'],
|
|
728
|
+
[':scheme', 'http'],
|
|
729
|
+
[':scheme', 'https'],
|
|
730
|
+
[':status', '200'],
|
|
731
|
+
[':status', '204'],
|
|
732
|
+
[':status', '206'],
|
|
733
|
+
[':status', '304'],
|
|
734
|
+
[':status', '400'],
|
|
735
|
+
[':status', '404'],
|
|
736
|
+
[':status', '500'],
|
|
737
|
+
['accept-charset', ''],
|
|
738
|
+
['accept-encoding', 'gzip, deflate'],
|
|
739
|
+
['accept-language', ''],
|
|
740
|
+
['accept-ranges', ''],
|
|
741
|
+
['accept', ''],
|
|
742
|
+
['access-control-allow-origin', ''],
|
|
743
|
+
['age', ''],
|
|
744
|
+
['allow', ''],
|
|
745
|
+
['authorization', ''],
|
|
746
|
+
['cache-control', ''],
|
|
747
|
+
['content-disposition', ''],
|
|
748
|
+
['content-encoding', ''],
|
|
749
|
+
['content-language', ''],
|
|
750
|
+
['content-length', ''],
|
|
751
|
+
['content-location', ''],
|
|
752
|
+
['content-range', ''],
|
|
753
|
+
['content-type', ''],
|
|
754
|
+
['cookie', ''],
|
|
755
|
+
['date', ''],
|
|
756
|
+
['etag', ''],
|
|
757
|
+
['expect', ''],
|
|
758
|
+
['expires', ''],
|
|
759
|
+
['from', ''],
|
|
760
|
+
['host', ''],
|
|
761
|
+
['if-match', ''],
|
|
762
|
+
['if-modified-since', ''],
|
|
763
|
+
['if-none-match', ''],
|
|
764
|
+
['if-range', ''],
|
|
765
|
+
['if-unmodified-since', ''],
|
|
766
|
+
['last-modified', ''],
|
|
767
|
+
['link', ''],
|
|
768
|
+
['location', ''],
|
|
769
|
+
['max-forwards', ''],
|
|
770
|
+
['proxy-authenticate', ''],
|
|
771
|
+
['proxy-authorization', ''],
|
|
772
|
+
['range', ''],
|
|
773
|
+
['referer', ''],
|
|
774
|
+
['refresh', ''],
|
|
775
|
+
['retry-after', ''],
|
|
776
|
+
['server', ''],
|
|
777
|
+
['set-cookie', ''],
|
|
778
|
+
['strict-transport-security', ''],
|
|
779
|
+
['transfer-encoding', ''],
|
|
780
|
+
['user-agent', ''],
|
|
781
|
+
['vary', ''],
|
|
782
|
+
['via', ''],
|
|
783
|
+
['www-authenticate', '']
|
|
784
|
+
];
|
|
785
|
+
// Huffman编码表
|
|
786
|
+
this.huffmanTable = this.buildHuffmanTable();
|
|
787
|
+
}
|
|
788
|
+
// 编码headers
|
|
789
|
+
encode(headers) {
|
|
790
|
+
const buffer = [];
|
|
791
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
792
|
+
// 查找静态表索引
|
|
793
|
+
const staticIndex = this.findInStaticTable(name, value);
|
|
794
|
+
if (staticIndex !== -1) {
|
|
795
|
+
// 使用索引编码
|
|
796
|
+
buffer.push(...this.encodeInteger(staticIndex, 7, 0x80));
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
// 查找动态表索引
|
|
800
|
+
const dynamicIndex = this.findInDynamicTable(name, value);
|
|
801
|
+
if (dynamicIndex !== -1) {
|
|
802
|
+
buffer.push(...this.encodeInteger(dynamicIndex + this.staticTable.length, 7, 0x80));
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
// 字面量编码
|
|
806
|
+
this.encodeLiteral(buffer, name, value);
|
|
807
|
+
}
|
|
808
|
+
return new Uint8Array(buffer);
|
|
809
|
+
}
|
|
810
|
+
// // 解码二进制数据
|
|
811
|
+
// decode(buffer: Uint8Array): { [key: string]: string } {
|
|
812
|
+
// const headers: { [key: string]: string } = {};
|
|
813
|
+
// let pos = 0;
|
|
814
|
+
// while (pos < buffer.length) {
|
|
815
|
+
// const firstByte = buffer[pos];
|
|
816
|
+
// // 检查是否是索引头部字段
|
|
817
|
+
// if ((firstByte & 0x80) === 0x80) {
|
|
818
|
+
// const { value: index, bytesRead } = this.decodeInteger(
|
|
819
|
+
// buffer.slice(pos),
|
|
820
|
+
// 7
|
|
821
|
+
// );
|
|
822
|
+
// pos += bytesRead;
|
|
823
|
+
// const [name, value] = this.getIndexedHeader(index);
|
|
824
|
+
// if (name !== undefined && value !== undefined) {
|
|
825
|
+
// headers[name] = value;
|
|
826
|
+
// }
|
|
827
|
+
// }
|
|
828
|
+
// // 字面量头部字段
|
|
829
|
+
// else {
|
|
830
|
+
// const { name, value, bytesRead } = this.decodeLiteral(
|
|
831
|
+
// buffer.slice(pos)
|
|
832
|
+
// );
|
|
833
|
+
// pos += bytesRead;
|
|
834
|
+
// headers[name] = value;
|
|
835
|
+
// }
|
|
836
|
+
// }
|
|
837
|
+
// return headers;
|
|
838
|
+
// }
|
|
839
|
+
// 编码整数
|
|
840
|
+
encodeInteger(value, prefixBits, prefix = 0) {
|
|
841
|
+
const buffer = [];
|
|
842
|
+
const mask = (1 << prefixBits) - 1;
|
|
843
|
+
if (value < mask) {
|
|
844
|
+
buffer.push(prefix | value);
|
|
845
|
+
return buffer;
|
|
846
|
+
}
|
|
847
|
+
buffer.push(prefix | mask);
|
|
848
|
+
value -= mask;
|
|
849
|
+
while (value >= 128) {
|
|
850
|
+
buffer.push((value & 127) | 128);
|
|
851
|
+
value = value >> 7;
|
|
852
|
+
}
|
|
853
|
+
buffer.push(value);
|
|
854
|
+
return buffer;
|
|
855
|
+
}
|
|
856
|
+
// // 解码整数
|
|
857
|
+
// decodeInteger(buffer: Uint8Array, prefixBits: number) {
|
|
858
|
+
// let value = buffer[0] & ((1 << prefixBits) - 1);
|
|
859
|
+
// let bytesRead = 1;
|
|
860
|
+
// if (value === (1 << prefixBits) - 1) {
|
|
861
|
+
// let shift = 0;
|
|
862
|
+
// do {
|
|
863
|
+
// value += (buffer[bytesRead] & 127) << shift;
|
|
864
|
+
// shift += 7;
|
|
865
|
+
// bytesRead++;
|
|
866
|
+
// } while (buffer[bytesRead - 1] & 128);
|
|
867
|
+
// }
|
|
868
|
+
// return { value, bytesRead };
|
|
869
|
+
// }
|
|
870
|
+
// 编码字符串
|
|
871
|
+
encodeString(str) {
|
|
872
|
+
const buffer = [];
|
|
873
|
+
const bytes = new TextEncoder().encode(str);
|
|
874
|
+
// 尝试Huffman编码
|
|
875
|
+
const huffmanEncoded = this.huffmanEncode(bytes);
|
|
876
|
+
if (huffmanEncoded.length < bytes.length) {
|
|
877
|
+
// 使用Huffman编码
|
|
878
|
+
buffer.push(...this.encodeInteger(huffmanEncoded.length, 7, 0x80));
|
|
879
|
+
buffer.push(...huffmanEncoded);
|
|
880
|
+
}
|
|
881
|
+
else {
|
|
882
|
+
// 不使用Huffman编码
|
|
883
|
+
buffer.push(...this.encodeInteger(bytes.length, 7, 0x00));
|
|
884
|
+
buffer.push(...bytes);
|
|
885
|
+
}
|
|
886
|
+
return buffer;
|
|
887
|
+
}
|
|
888
|
+
// 在静态表中查找
|
|
889
|
+
findInStaticTable(name, value) {
|
|
890
|
+
for (let i = 1; i < this.staticTable.length; i++) {
|
|
891
|
+
if (this.staticTable[i][0] === name &&
|
|
892
|
+
this.staticTable[i][1] === value) {
|
|
893
|
+
return i;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
return -1;
|
|
897
|
+
}
|
|
898
|
+
// 在动态表中查找
|
|
899
|
+
findInDynamicTable(name, value) {
|
|
900
|
+
for (let i = 0; i < this.dynamicTable.length; i++) {
|
|
901
|
+
if (this.dynamicTable[i][0] === name &&
|
|
902
|
+
this.dynamicTable[i][1] === value) {
|
|
903
|
+
return i;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return -1;
|
|
907
|
+
}
|
|
908
|
+
// 编码字面量头部字段
|
|
909
|
+
encodeLiteral(buffer, name, value) {
|
|
910
|
+
const nameIndex = this.findInStaticTable(name, '');
|
|
911
|
+
if (nameIndex !== -1) {
|
|
912
|
+
// 名称索引存在
|
|
913
|
+
buffer.push(...this.encodeInteger(nameIndex, 6, 0x40));
|
|
914
|
+
}
|
|
915
|
+
else {
|
|
916
|
+
// 名称需要字面量编码
|
|
917
|
+
buffer.push(0x40);
|
|
918
|
+
buffer.push(...this.encodeString(name));
|
|
919
|
+
}
|
|
920
|
+
// 值总是需要字面量编码
|
|
921
|
+
buffer.push(...this.encodeString(value));
|
|
922
|
+
// 添加到动态表
|
|
923
|
+
this.addToDynamicTable(name, value);
|
|
924
|
+
}
|
|
925
|
+
// 添加到动态表
|
|
926
|
+
addToDynamicTable(name, value) {
|
|
927
|
+
const size = name.length + value.length + 32; // 32 bytes overhead
|
|
928
|
+
// 确保不超过最大大小
|
|
929
|
+
while (this.dynamicTableSize + size > this.maxDynamicTableSize &&
|
|
930
|
+
this.dynamicTable.length > 0) {
|
|
931
|
+
const entry = this.dynamicTable.pop();
|
|
932
|
+
if (entry) {
|
|
933
|
+
this.dynamicTableSize -= entry[0].length + entry[1].length + 32;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
if (size <= this.maxDynamicTableSize) {
|
|
937
|
+
this.dynamicTable.unshift([name, value]);
|
|
938
|
+
this.dynamicTableSize += size;
|
|
939
|
+
}
|
|
940
|
+
this.dynamicTable.push([name, value]);
|
|
941
|
+
}
|
|
942
|
+
// 获取索引的头部
|
|
943
|
+
getIndexedHeader(index) {
|
|
944
|
+
if (index <= this.staticTable.length - 1) {
|
|
945
|
+
return this.staticTable[index];
|
|
946
|
+
}
|
|
947
|
+
return this.dynamicTable[index - this.staticTable.length];
|
|
948
|
+
}
|
|
949
|
+
buildHuffmanTable() {
|
|
950
|
+
// HTTP/2规范中定义的完整Huffman编码表
|
|
951
|
+
return {
|
|
952
|
+
codes: new Uint32Array([
|
|
953
|
+
0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, 0xfffffe7,
|
|
954
|
+
0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, 0x3ffffffd, 0xfffffeb, 0xfffffec,
|
|
955
|
+
0xfffffed, 0xfffffee, 0xfffffef, 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3,
|
|
956
|
+
0xffffff4, 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, 0xffffffb,
|
|
957
|
+
0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa,
|
|
958
|
+
0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18,
|
|
959
|
+
0x0, 0x1, 0x2, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
|
|
960
|
+
0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, 0xffb, 0x3fc,
|
|
961
|
+
0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
|
|
962
|
+
0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
|
|
963
|
+
0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
|
964
|
+
0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, 0x1ffc, 0x3ffc, 0x22,
|
|
965
|
+
0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26,
|
|
966
|
+
0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7,
|
|
967
|
+
0x2b, 0x76, 0x2c, 0x8, 0x9, 0x2d, 0x77, 0x78,
|
|
968
|
+
0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, 0x1ffd, 0xffffffc,
|
|
969
|
+
0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, 0x3fffd4, 0x3fffd5, 0x7fffd9,
|
|
970
|
+
0x3fffd6, 0x7fffda, 0x7fffdb, 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf,
|
|
971
|
+
0xffffec, 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, 0x7fffe3,
|
|
972
|
+
0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, 0x7fffe6, 0x7fffe7, 0xffffef,
|
|
973
|
+
0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde,
|
|
974
|
+
0x7fffea, 0x3fffdd, 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec,
|
|
975
|
+
0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, 0x7fffee, 0x7fffef,
|
|
976
|
+
0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, 0x3fffe5, 0x3fffe6, 0x7ffff1,
|
|
977
|
+
0x3ffffe0, 0x3ffffe1, 0xfffeb, 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec,
|
|
978
|
+
0x3ffffe2, 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, 0x1ffffed,
|
|
979
|
+
0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, 0x3ffffe7, 0x7ffffe2, 0xfffff2,
|
|
980
|
+
0x1fffe4, 0x1fffe5, 0x3ffffe8, 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5,
|
|
981
|
+
0xfffec, 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3,
|
|
982
|
+
0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, 0x3ffffea, 0x7ffff4,
|
|
983
|
+
0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea,
|
|
984
|
+
0x7ffffeb, 0xffffffe, 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee
|
|
985
|
+
]),
|
|
986
|
+
lengths: new Uint8Array([
|
|
987
|
+
13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
|
|
988
|
+
28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
|
989
|
+
6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6,
|
|
990
|
+
5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10,
|
|
991
|
+
13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
|
992
|
+
7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6,
|
|
993
|
+
15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5,
|
|
994
|
+
6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28,
|
|
995
|
+
20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23,
|
|
996
|
+
24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24,
|
|
997
|
+
22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23,
|
|
998
|
+
21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23,
|
|
999
|
+
26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25,
|
|
1000
|
+
19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27,
|
|
1001
|
+
20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23,
|
|
1002
|
+
26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26
|
|
1003
|
+
])
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* 解码Uint8Array为字符串
|
|
1008
|
+
* @param input 编码后的Uint8Array
|
|
1009
|
+
* @returns 解码后的字符串
|
|
1010
|
+
*/
|
|
1011
|
+
decode(input) {
|
|
1012
|
+
let result = '';
|
|
1013
|
+
let accumulator = 0;
|
|
1014
|
+
let bits = 0;
|
|
1015
|
+
for (let i = 0; i < input.length; i++) {
|
|
1016
|
+
const byte = input[i];
|
|
1017
|
+
accumulator = (accumulator << 8) | byte;
|
|
1018
|
+
bits += 8;
|
|
1019
|
+
while (bits >= 5) {
|
|
1020
|
+
const decoded = this.findSymbol(accumulator, bits);
|
|
1021
|
+
if (!decoded) {
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
const [symbol, length] = decoded;
|
|
1025
|
+
result += String.fromCharCode(symbol);
|
|
1026
|
+
accumulator &= (1 << (bits - length)) - 1;
|
|
1027
|
+
bits -= length;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
// 验证填充
|
|
1031
|
+
if (bits > 0) {
|
|
1032
|
+
const mask = (1 << bits) - 1;
|
|
1033
|
+
if ((accumulator & mask) !== mask) {
|
|
1034
|
+
throw new Error('Invalid Huffman padding');
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
return result;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* 在Huffman树中查找符号
|
|
1041
|
+
*/
|
|
1042
|
+
findSymbol(value, bits) {
|
|
1043
|
+
for (let i = 0; i < this.huffmanTable.codes.length; i++) {
|
|
1044
|
+
const length = this.huffmanTable.lengths[i];
|
|
1045
|
+
if (bits < length)
|
|
1046
|
+
continue;
|
|
1047
|
+
const code = (value >> (bits - length)) & ((1 << length) - 1);
|
|
1048
|
+
if (code === this.huffmanTable.codes[i]) {
|
|
1049
|
+
return [i, length];
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
1054
|
+
// Huffman编码实现
|
|
1055
|
+
huffmanEncode(bytes) {
|
|
1056
|
+
let result = [];
|
|
1057
|
+
let current = 0;
|
|
1058
|
+
let bits = 0;
|
|
1059
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1060
|
+
const b = bytes[i];
|
|
1061
|
+
const code = this.huffmanTable.codes[b];
|
|
1062
|
+
const length = this.huffmanTable.lengths[b];
|
|
1063
|
+
bits += length;
|
|
1064
|
+
current = (current << length) | code;
|
|
1065
|
+
while (bits >= 8) {
|
|
1066
|
+
bits -= 8;
|
|
1067
|
+
result.push((current >> bits) & 0xFF);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
// 处理剩余的位
|
|
1071
|
+
if (bits > 0) {
|
|
1072
|
+
current = (current << (8 - bits)) | ((1 << (8 - bits)) - 1);
|
|
1073
|
+
result.push(current & 0xFF);
|
|
1074
|
+
}
|
|
1075
|
+
return new Uint8Array(result);
|
|
1076
|
+
}
|
|
1077
|
+
decodeHeaderFields(buffer) {
|
|
1078
|
+
const headers = new Map();
|
|
1079
|
+
let index = 0;
|
|
1080
|
+
while (index < buffer.length) {
|
|
1081
|
+
const firstByte = buffer[index];
|
|
1082
|
+
// 检查首字节的类型
|
|
1083
|
+
if ((firstByte & 0x80) !== 0) { // 1xxxxxxx - Indexed Header Field
|
|
1084
|
+
const [name, value, newIndex] = this.decodeIndexedHeader(buffer, index);
|
|
1085
|
+
if (name && value)
|
|
1086
|
+
headers.set(name, value);
|
|
1087
|
+
index = newIndex;
|
|
1088
|
+
}
|
|
1089
|
+
else if ((firstByte & 0x40) !== 0) { // 01xxxxxx - Literal Header Field with Incremental Indexing
|
|
1090
|
+
const [name, value, newIndex] = this.decodeLiteralHeaderWithIndexing(buffer, index);
|
|
1091
|
+
if (name && value)
|
|
1092
|
+
headers.set(name, value);
|
|
1093
|
+
index = newIndex;
|
|
1094
|
+
}
|
|
1095
|
+
else if ((firstByte & 0x20) !== 0) { // 001xxxxx - Dynamic Table Size Update
|
|
1096
|
+
index++; // 简单跳过,实际应该更新动态表大小
|
|
1097
|
+
}
|
|
1098
|
+
else if ((firstByte & 0x10) !== 0) { // 0001xxxx - Literal Header Field Never Indexed
|
|
1099
|
+
const [name, value, newIndex] = this.decodeLiteralHeaderWithoutIndexing(buffer, index);
|
|
1100
|
+
if (name && value)
|
|
1101
|
+
headers.set(name, value);
|
|
1102
|
+
index = newIndex;
|
|
1103
|
+
}
|
|
1104
|
+
else { // 0000xxxx - Literal Header Field without Indexing
|
|
1105
|
+
const [name, value, newIndex] = this.decodeLiteralHeaderWithoutIndexing(buffer, index);
|
|
1106
|
+
if (name && value)
|
|
1107
|
+
headers.set(name, value);
|
|
1108
|
+
index = newIndex;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return headers;
|
|
1112
|
+
}
|
|
1113
|
+
decodeInteger(buffer, startIndex, prefixBits) {
|
|
1114
|
+
const prefix = (1 << prefixBits) - 1;
|
|
1115
|
+
const firstByte = buffer[startIndex];
|
|
1116
|
+
let index = startIndex;
|
|
1117
|
+
let value = firstByte & prefix;
|
|
1118
|
+
if (value < prefix) {
|
|
1119
|
+
return [value, index + 1];
|
|
1120
|
+
}
|
|
1121
|
+
index++;
|
|
1122
|
+
let shift = 0;
|
|
1123
|
+
while (index < buffer.length) {
|
|
1124
|
+
const byte = buffer[index++];
|
|
1125
|
+
value += (byte & 0x7F) << shift;
|
|
1126
|
+
shift += 7;
|
|
1127
|
+
if ((byte & 0x80) === 0) {
|
|
1128
|
+
break;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
return [value, index];
|
|
1132
|
+
}
|
|
1133
|
+
decodeLiteralString(buffer, startIndex) {
|
|
1134
|
+
if (startIndex >= buffer.length) {
|
|
1135
|
+
return ['', startIndex];
|
|
1136
|
+
}
|
|
1137
|
+
const firstByte = buffer[startIndex];
|
|
1138
|
+
const isHuffman = (firstByte & 0x80) !== 0;
|
|
1139
|
+
const [length, index] = this.decodeInteger(buffer, startIndex, 7);
|
|
1140
|
+
if (index + length > buffer.length) {
|
|
1141
|
+
return ['', index];
|
|
1142
|
+
}
|
|
1143
|
+
const bytes = buffer.slice(index, index + length);
|
|
1144
|
+
let result;
|
|
1145
|
+
if (isHuffman) {
|
|
1146
|
+
try {
|
|
1147
|
+
result = this.decode(bytes);
|
|
1148
|
+
}
|
|
1149
|
+
catch (e) {
|
|
1150
|
+
result = '';
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
else {
|
|
1154
|
+
try {
|
|
1155
|
+
// result = new TextDecoder().decode(bytes);
|
|
1156
|
+
result = toString(bytes);
|
|
1157
|
+
}
|
|
1158
|
+
catch (e) {
|
|
1159
|
+
result = '';
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
return [result, index + length];
|
|
1163
|
+
}
|
|
1164
|
+
decodeIndexedHeader(buffer, index) {
|
|
1165
|
+
const [staticIndex, newIndex] = this.decodeInteger(buffer, index, 7);
|
|
1166
|
+
if (staticIndex <= 0) {
|
|
1167
|
+
return ['', '', newIndex];
|
|
1168
|
+
}
|
|
1169
|
+
const headerField = this.staticTable[staticIndex];
|
|
1170
|
+
if (!headerField) {
|
|
1171
|
+
return ['', '', newIndex];
|
|
1172
|
+
}
|
|
1173
|
+
return [headerField[0], headerField[1], newIndex];
|
|
1174
|
+
}
|
|
1175
|
+
decodeLiteralHeaderWithIndexing(buffer, index) {
|
|
1176
|
+
const [staticIndex, nameIndex] = this.decodeInteger(buffer, index, 6);
|
|
1177
|
+
index = nameIndex;
|
|
1178
|
+
let name;
|
|
1179
|
+
if (staticIndex > 0) {
|
|
1180
|
+
const headerField = this.staticTable[staticIndex];
|
|
1181
|
+
name = headerField ? headerField[0] : '';
|
|
1182
|
+
}
|
|
1183
|
+
else {
|
|
1184
|
+
const [decodedName, newIndex] = this.decodeLiteralString(buffer, index);
|
|
1185
|
+
name = decodedName;
|
|
1186
|
+
index = newIndex;
|
|
1187
|
+
}
|
|
1188
|
+
const [value, finalIndex] = this.decodeLiteralString(buffer, index);
|
|
1189
|
+
return [name, value, finalIndex];
|
|
1190
|
+
}
|
|
1191
|
+
decodeLiteralHeaderWithoutIndexing(buffer, index) {
|
|
1192
|
+
return this.decodeLiteralHeaderWithIndexing(buffer, index);
|
|
1193
|
+
}
|
|
1194
|
+
// 直接转换为字符串的方法
|
|
1195
|
+
huffmanDecodeToString(bytes) {
|
|
1196
|
+
return this.decode(bytes);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
class FrameEncoder {
|
|
1201
|
+
// 编码SETTINGS帧
|
|
1202
|
+
static encodeSettingsFrame(frame) {
|
|
1203
|
+
// 计算payload总长度:每个设置项占6字节
|
|
1204
|
+
const payloadLength = frame.payload.length * 6;
|
|
1205
|
+
// 分配缓冲区:9字节头部 + payload长度
|
|
1206
|
+
const buffer = new Uint8Array(9 + payloadLength);
|
|
1207
|
+
// 编码帧头部(前9个字节)
|
|
1208
|
+
_encodeFrameHeader(buffer, {
|
|
1209
|
+
length: payloadLength,
|
|
1210
|
+
type: frame.type,
|
|
1211
|
+
flags: frame.flags,
|
|
1212
|
+
streamId: frame.streamId
|
|
1213
|
+
});
|
|
1214
|
+
// 编码payload
|
|
1215
|
+
_encodeSettingsPayload(buffer, frame.payload);
|
|
1216
|
+
return buffer;
|
|
1217
|
+
}
|
|
1218
|
+
// 编码SETTINGS ACK帧
|
|
1219
|
+
static encodeSettingsAckFrame() {
|
|
1220
|
+
const buffer = new Uint8Array(9); // ACK帧只有头部
|
|
1221
|
+
_encodeFrameHeader(buffer, {
|
|
1222
|
+
length: 0,
|
|
1223
|
+
type: 0x4, // SETTINGS
|
|
1224
|
+
flags: 0x1, // ACK
|
|
1225
|
+
streamId: 0
|
|
1226
|
+
});
|
|
1227
|
+
return buffer;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
// 编码帧头部(9字节)
|
|
1231
|
+
function _encodeFrameHeader(buffer, header) {
|
|
1232
|
+
// Length: 3 bytes
|
|
1233
|
+
const length = header.length ?? 0;
|
|
1234
|
+
buffer[0] = (length >> 16) & 0xFF;
|
|
1235
|
+
buffer[1] = (length >> 8) & 0xFF;
|
|
1236
|
+
buffer[2] = length & 0xFF;
|
|
1237
|
+
// Type: 1 byte
|
|
1238
|
+
buffer[3] = header.type;
|
|
1239
|
+
// Flags: 1 byte
|
|
1240
|
+
buffer[4] = header.flags;
|
|
1241
|
+
// Stream Identifier: 4 bytes
|
|
1242
|
+
_writeUInt32BE(buffer, header.streamId & 0x7FFFFFFF, 5);
|
|
1243
|
+
}
|
|
1244
|
+
// 编码SETTINGS payload
|
|
1245
|
+
function _encodeSettingsPayload(buffer, payload) {
|
|
1246
|
+
let offset = 9; // 从第9个字节开始
|
|
1247
|
+
payload.forEach(setting => {
|
|
1248
|
+
// 写入2字节的标识符
|
|
1249
|
+
_writeUInt16BE(buffer, setting.identifier, offset);
|
|
1250
|
+
offset += 2;
|
|
1251
|
+
// 写入4字节的值
|
|
1252
|
+
_writeUInt32BE(buffer, setting.value, offset);
|
|
1253
|
+
offset += 4;
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
// 写入16位无符号整数(大端序)
|
|
1257
|
+
function _writeUInt16BE(buffer, value, offset) {
|
|
1258
|
+
buffer[offset] = (value >> 8) & 0xFF;
|
|
1259
|
+
buffer[offset + 1] = value & 0xFF;
|
|
1260
|
+
}
|
|
1261
|
+
// 写入32位无符号整数(大端序)
|
|
1262
|
+
function _writeUInt32BE(buffer, value, offset) {
|
|
1263
|
+
buffer[offset] = (value >> 24) & 0xFF;
|
|
1264
|
+
buffer[offset + 1] = (value >> 16) & 0xFF;
|
|
1265
|
+
buffer[offset + 2] = (value >> 8) & 0xFF;
|
|
1266
|
+
buffer[offset + 3] = value & 0xFF;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
const SETTINGS_PARAMETERS = {
|
|
1270
|
+
HEADER_TABLE_SIZE: 0x1,
|
|
1271
|
+
ENABLE_PUSH: 0x2,
|
|
1272
|
+
MAX_CONCURRENT_STREAMS: 0x3,
|
|
1273
|
+
INITIAL_WINDOW_SIZE: 0x4,
|
|
1274
|
+
MAX_FRAME_SIZE: 0x5,
|
|
1275
|
+
MAX_HEADER_LIST_SIZE: 0x6
|
|
1276
|
+
};
|
|
1277
|
+
const defaultSettings = {
|
|
1278
|
+
[SETTINGS_PARAMETERS.HEADER_TABLE_SIZE]: 4096,
|
|
1279
|
+
[SETTINGS_PARAMETERS.ENABLE_PUSH]: 1,
|
|
1280
|
+
[SETTINGS_PARAMETERS.MAX_CONCURRENT_STREAMS]: 100,
|
|
1281
|
+
[SETTINGS_PARAMETERS.INITIAL_WINDOW_SIZE]: 4 << 20,
|
|
1282
|
+
[SETTINGS_PARAMETERS.MAX_FRAME_SIZE]: 4 << 20, // 4MB
|
|
1283
|
+
[SETTINGS_PARAMETERS.MAX_HEADER_LIST_SIZE]: 8192
|
|
1284
|
+
};
|
|
1285
|
+
const HTTP2_PREFACE = 'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n';
|
|
1286
|
+
class Http2Frame {
|
|
1287
|
+
// 创建并编码PREFACE帧
|
|
1288
|
+
static createPreface() {
|
|
1289
|
+
return new TextEncoder().encode(HTTP2_PREFACE);
|
|
1290
|
+
}
|
|
1291
|
+
static createPongFrame(payload) {
|
|
1292
|
+
return Http2Frame.createFrame(0x6, 0x1, 0, payload);
|
|
1293
|
+
}
|
|
1294
|
+
// 创建并编码SETTINGS帧
|
|
1295
|
+
static createSettingsFrame(settings = {}) {
|
|
1296
|
+
// 先创建帧对象
|
|
1297
|
+
const frame = Http2Frame.createOriginSettingsFrame(settings);
|
|
1298
|
+
// 然后编码
|
|
1299
|
+
return FrameEncoder.encodeSettingsFrame(frame);
|
|
1300
|
+
}
|
|
1301
|
+
// 创建并编码SETTINGS ACK帧
|
|
1302
|
+
static createSettingsAckFrame() {
|
|
1303
|
+
return FrameEncoder.encodeSettingsAckFrame();
|
|
1304
|
+
}
|
|
1305
|
+
static createDataFrames(streamId, data, shouldEnd = false, maxFrameSize = 1024) {
|
|
1306
|
+
const frames = [];
|
|
1307
|
+
let offset = 0;
|
|
1308
|
+
while (offset < data.length) {
|
|
1309
|
+
const chunkSize = Math.min(maxFrameSize, data.length - offset);
|
|
1310
|
+
const chunk = data.slice(offset, offset + chunkSize);
|
|
1311
|
+
let isEndStream = shouldEnd && (offset + chunkSize >= data.length); // 最后一帧设置 END_STREAM
|
|
1312
|
+
const frame = Http2Frame.createDataFrame(streamId, chunk, isEndStream);
|
|
1313
|
+
frames.push(frame);
|
|
1314
|
+
offset += chunkSize;
|
|
1315
|
+
}
|
|
1316
|
+
return frames;
|
|
1317
|
+
}
|
|
1318
|
+
static createDataFrame(streamId, data, endStream = true) {
|
|
1319
|
+
// gRPC 消息格式: 压缩标志(1字节) + 消息长度(4字节) + 消息内容
|
|
1320
|
+
const messageLen = data.length;
|
|
1321
|
+
const framedData = new Uint8Array(5 + messageLen);
|
|
1322
|
+
// Compression flag (0 = 不压缩)
|
|
1323
|
+
framedData[0] = 0;
|
|
1324
|
+
// Message length (4 bytes)
|
|
1325
|
+
framedData[1] = (messageLen >> 24) & 0xFF;
|
|
1326
|
+
framedData[2] = (messageLen >> 16) & 0xFF;
|
|
1327
|
+
framedData[3] = (messageLen >> 8) & 0xFF;
|
|
1328
|
+
framedData[4] = messageLen & 0xFF;
|
|
1329
|
+
// Message content
|
|
1330
|
+
framedData.set(data, 5);
|
|
1331
|
+
const flags = endStream ? 0x01 : 0x0; // END_STREAM flag
|
|
1332
|
+
return Http2Frame.createFrame(0x0, flags, streamId, framedData);
|
|
1333
|
+
}
|
|
1334
|
+
static createHeadersFrame(streamId, path, endHeaders = true, token) {
|
|
1335
|
+
// gRPC-Web 需要的标准 headers
|
|
1336
|
+
const headersList = {
|
|
1337
|
+
':path': path,
|
|
1338
|
+
':method': 'POST',
|
|
1339
|
+
':scheme': 'http',
|
|
1340
|
+
':authority': 'localhost',
|
|
1341
|
+
'content-type': 'application/grpc+proto',
|
|
1342
|
+
'user-agent': 'grpc-web-client/0.1',
|
|
1343
|
+
'accept': 'application/grpc+proto',
|
|
1344
|
+
'grpc-timeout': '3600S'
|
|
1345
|
+
};
|
|
1346
|
+
if (token) {
|
|
1347
|
+
headersList['authorization'] = `Bearer ${token}`;
|
|
1348
|
+
}
|
|
1349
|
+
// 将 headers 编码为 HPACK 格式
|
|
1350
|
+
const hpack = new HPACK();
|
|
1351
|
+
const encodedHeaders = hpack.encode(headersList);
|
|
1352
|
+
console.log('Encoded:', encodedHeaders);
|
|
1353
|
+
// HEADERS frame flags: END_HEADERS | END_STREAM
|
|
1354
|
+
const flags = endHeaders ? 0x04 : 0x00;
|
|
1355
|
+
return Http2Frame.createFrame(0x01, flags, streamId, encodedHeaders);
|
|
1356
|
+
}
|
|
1357
|
+
static createResponseHeadersFrame(streamId, headersList, endHeaders = true) {
|
|
1358
|
+
// 将 headers 编码为 HPACK 格式
|
|
1359
|
+
const hpack = new HPACK();
|
|
1360
|
+
const encodedHeaders = hpack.encode(headersList);
|
|
1361
|
+
console.log('Encoded:', encodedHeaders);
|
|
1362
|
+
// HEADERS frame flags: END_HEADERS | END_STREAM
|
|
1363
|
+
const flags = endHeaders ? 0x04 : 0x00;
|
|
1364
|
+
return Http2Frame.createFrame(0x01, flags, streamId, encodedHeaders);
|
|
1365
|
+
}
|
|
1366
|
+
static createTrailersFrame(streamId, trailers) {
|
|
1367
|
+
// 将 trailers 编码为 HPACK 格式
|
|
1368
|
+
const hpack = new HPACK();
|
|
1369
|
+
const encodedTrailers = hpack.encode(trailers);
|
|
1370
|
+
// HEADERS frame flags: END_HEADERS | END_STREAM
|
|
1371
|
+
const flags = 0x05; // 0x04 (END_HEADERS) | 0x01 (END_STREAM)
|
|
1372
|
+
return Http2Frame.createFrame(0x01, flags, streamId, encodedTrailers);
|
|
1373
|
+
}
|
|
1374
|
+
// 创建 SETTINGS 帧
|
|
1375
|
+
static createOriginSettingsFrame(settings = {}) {
|
|
1376
|
+
// 合并默认值和用户提供的设置
|
|
1377
|
+
const finalSettings = { ...defaultSettings, ...settings };
|
|
1378
|
+
// 验证设置值
|
|
1379
|
+
_validateSettings(finalSettings);
|
|
1380
|
+
// 创建帧
|
|
1381
|
+
const frame = {
|
|
1382
|
+
type: 0x4, // SETTINGS frame type
|
|
1383
|
+
flags: 0x0, // 无标志
|
|
1384
|
+
streamId: 0, // SETTINGS 总是在 stream 0 上发送
|
|
1385
|
+
payload: _createPayload(finalSettings)
|
|
1386
|
+
};
|
|
1387
|
+
return frame;
|
|
1388
|
+
}
|
|
1389
|
+
// 创建确认帧(SETTINGS ACK)
|
|
1390
|
+
static createOriginSettingsAckFrame() {
|
|
1391
|
+
return {
|
|
1392
|
+
type: 0x4, // SETTINGS frame type
|
|
1393
|
+
flags: 0x1, // ACK flag
|
|
1394
|
+
streamId: 0,
|
|
1395
|
+
payload: [] // ACK 帧没有payload
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
static createFrame(type, flags, streamId, payload) {
|
|
1399
|
+
const length = payload ? payload.length : 0;
|
|
1400
|
+
const frame = new Uint8Array(9 + length); // 9 bytes for header + payload
|
|
1401
|
+
// Length (24 bits)
|
|
1402
|
+
frame[0] = (length >> 16) & 0xFF;
|
|
1403
|
+
frame[1] = (length >> 8) & 0xFF;
|
|
1404
|
+
frame[2] = length & 0xFF;
|
|
1405
|
+
// Type (8 bits)
|
|
1406
|
+
frame[3] = type;
|
|
1407
|
+
// Flags (8 bits)
|
|
1408
|
+
frame[4] = flags;
|
|
1409
|
+
// Stream Identifier (32 bits)
|
|
1410
|
+
frame[5] = (streamId >> 24) & 0xFF;
|
|
1411
|
+
frame[6] = (streamId >> 16) & 0xFF;
|
|
1412
|
+
frame[7] = (streamId >> 8) & 0xFF;
|
|
1413
|
+
frame[8] = streamId & 0xFF;
|
|
1414
|
+
if (payload && length > 0) {
|
|
1415
|
+
frame.set(payload, 9);
|
|
1416
|
+
}
|
|
1417
|
+
return frame;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
// 验证设置值
|
|
1421
|
+
function _validateSettings(settings) {
|
|
1422
|
+
for (const [id, value] of Object.entries(settings)) {
|
|
1423
|
+
switch (Number(id)) {
|
|
1424
|
+
case SETTINGS_PARAMETERS.HEADER_TABLE_SIZE:
|
|
1425
|
+
if (value < 0) {
|
|
1426
|
+
throw new Error('HEADER_TABLE_SIZE must be non-negative');
|
|
1427
|
+
}
|
|
1428
|
+
break;
|
|
1429
|
+
case SETTINGS_PARAMETERS.ENABLE_PUSH:
|
|
1430
|
+
if (value !== 0 && value !== 1) {
|
|
1431
|
+
throw new Error('ENABLE_PUSH must be 0 or 1');
|
|
1432
|
+
}
|
|
1433
|
+
break;
|
|
1434
|
+
case SETTINGS_PARAMETERS.INITIAL_WINDOW_SIZE:
|
|
1435
|
+
if (value < 0 || value > 2147483647) { // 2^31 - 1
|
|
1436
|
+
throw new Error('INITIAL_WINDOW_SIZE must be between 0 and 2^31-1');
|
|
1437
|
+
}
|
|
1438
|
+
break;
|
|
1439
|
+
case SETTINGS_PARAMETERS.MAX_FRAME_SIZE:
|
|
1440
|
+
if (value < 16384 || value > 16777215) { // 2^14 to 2^24-1
|
|
1441
|
+
throw new Error('MAX_FRAME_SIZE must be between 16,384 and 16,777,215');
|
|
1442
|
+
}
|
|
1443
|
+
break;
|
|
1444
|
+
case SETTINGS_PARAMETERS.MAX_CONCURRENT_STREAMS:
|
|
1445
|
+
case SETTINGS_PARAMETERS.MAX_HEADER_LIST_SIZE:
|
|
1446
|
+
if (value < 0) {
|
|
1447
|
+
throw new Error(`Parameter ${id} must be non-negative`);
|
|
1448
|
+
}
|
|
1449
|
+
break;
|
|
1450
|
+
default:
|
|
1451
|
+
console.warn(`Unknown settings parameter: ${id}`);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
// 创建payload
|
|
1456
|
+
function _createPayload(settings) {
|
|
1457
|
+
const payload = [];
|
|
1458
|
+
for (const [id, value] of Object.entries(settings)) {
|
|
1459
|
+
payload.push({
|
|
1460
|
+
identifier: Number(id),
|
|
1461
|
+
value: value
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
return payload;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
exports.Http2Frame = Http2Frame;
|
|
1468
|
+
//# sourceMappingURL=frame.cjs.js.map
|