quico 0.7.0 → 0.9.0
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/README.md +247 -33
- package/index.d.ts +1034 -77
- package/index.js +7 -3
- package/package.json +9 -5
- package/src/client.js +1 -0
- package/src/connect_udp.js +5 -2
- package/src/connect_udp_client.js +10 -0
- package/src/crypto.js +6 -0
- package/src/h1_server.js +51 -9
- package/src/h2_server.js +60 -11
- package/src/h3.js +115 -70
- package/src/h3_client.js +25 -5
- package/src/h3_server.js +104 -6
- package/src/hpack_primitives.js +64 -25
- package/src/http_common.js +7 -0
- package/src/qpack.js +11 -2
- package/src/quic_connection.js +379 -63
- package/src/quic_server.js +213 -72
- package/src/quic_socket.js +5 -5
- package/src/server.js +277 -24
- package/src/streams.js +183 -10
- package/src/tcp_listener.js +11 -0
- package/src/tls_bridge.js +62 -38
- package/src/transport.js +6 -2
- package/src/utils.js +36 -0
- package/src/webtransport.js +432 -57
- package/src/ws.js +120 -0
- package/src/ws_client.js +68 -88
- package/src/ws_server.js +35 -53
- package/src/ws_stream.js +250 -0
- package/src/wt_stream.js +533 -76
package/src/hpack_primitives.js
CHANGED
|
@@ -84,6 +84,7 @@ var HUFF_BITS = new Uint8Array([
|
|
|
84
84
|
]);
|
|
85
85
|
|
|
86
86
|
var HUFF_EOS = 256; // index of the EOS symbol; decoding it is an error
|
|
87
|
+
var _HD_HAS_SYM = 1 << 17, _HD_INVALID = 1 << 18; // huffDecode table flags
|
|
87
88
|
|
|
88
89
|
var _huffRoot = (function () {
|
|
89
90
|
var root = {};
|
|
@@ -100,6 +101,51 @@ var _huffRoot = (function () {
|
|
|
100
101
|
})();
|
|
101
102
|
|
|
102
103
|
|
|
104
|
+
// Table-driven decoding, four bits at a time.
|
|
105
|
+
//
|
|
106
|
+
// The decoder used to walk the code tree one bit per step: eight object
|
|
107
|
+
// lookups per input byte. Here every internal node of that same tree is a
|
|
108
|
+
// state, and for each state and each 4-bit nibble a table holds what four
|
|
109
|
+
// steps of the walk would have done — the node it ends on, the symbol it
|
|
110
|
+
// completed (at most one: the shortest code is 5 bits), and whether it fell
|
|
111
|
+
// off the tree. Two lookups per byte, same walk, same results.
|
|
112
|
+
//
|
|
113
|
+
// The padding checks at the end need only the state the input stopped in:
|
|
114
|
+
// the bits since the last complete symbol are that node's depth, and they
|
|
115
|
+
// are all ones exactly when its path from the root is.
|
|
116
|
+
var _huffDec = (function () {
|
|
117
|
+
var nodes = [_huffRoot], depth = [0], ones = [1];
|
|
118
|
+
var idOf = new Map([[_huffRoot, 0]]);
|
|
119
|
+
for (var q = 0; q < nodes.length; q++) {
|
|
120
|
+
for (var b = 0; b <= 1; b++) {
|
|
121
|
+
var child = nodes[q][b];
|
|
122
|
+
if (child && child.sym === undefined && !idOf.has(child)) {
|
|
123
|
+
idOf.set(child, nodes.length);
|
|
124
|
+
nodes.push(child);
|
|
125
|
+
depth.push(depth[q] + 1);
|
|
126
|
+
ones.push(ones[q] && b === 1 ? 1 : 0);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// entry = next | sym << 8 | HAS_SYM | INVALID ; sym is 0..256
|
|
131
|
+
var table = new Int32Array(nodes.length * 16);
|
|
132
|
+
for (var st = 0; st < nodes.length; st++) {
|
|
133
|
+
for (var nib = 0; nib < 16; nib++) {
|
|
134
|
+
var node = nodes[st], entry = 0;
|
|
135
|
+
for (var k = 3; k >= 0; k--) {
|
|
136
|
+
node = node[(nib >> k) & 1];
|
|
137
|
+
if (!node) { entry |= _HD_INVALID; node = _huffRoot; break; }
|
|
138
|
+
if (node.sym !== undefined) {
|
|
139
|
+
entry |= _HD_HAS_SYM | (node.sym << 8);
|
|
140
|
+
node = _huffRoot;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
table[st * 16 + nib] = entry | idOf.get(node);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { table: table, depth: Uint8Array.from(depth), ones: Uint8Array.from(ones) };
|
|
147
|
+
})();
|
|
148
|
+
|
|
103
149
|
/**
|
|
104
150
|
* Decode a Huffman-coded byte string (RFC 7541 §5.2). Returns the raw
|
|
105
151
|
* Uint8Array, NOT a string.
|
|
@@ -117,42 +163,35 @@ var _huffRoot = (function () {
|
|
|
117
163
|
* decoders that must answer a malformed peer with COMPRESSION_ERROR pass true.
|
|
118
164
|
*/
|
|
119
165
|
function huffDecode(buf, strict) {
|
|
120
|
-
var
|
|
121
|
-
var
|
|
122
|
-
var
|
|
123
|
-
var sincePadStart = 0; // bits consumed since the last complete symbol
|
|
124
|
-
var padIsOnes = true; // were all of those bits 1?
|
|
166
|
+
var table = _huffDec.table;
|
|
167
|
+
var out = new Uint8Array(((buf.length * 8) / 5) | 0);
|
|
168
|
+
var n = 0, state = 0;
|
|
125
169
|
|
|
126
170
|
for (var i = 0; i < buf.length; i++) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
sincePadStart++;
|
|
135
|
-
if (bit === 0) padIsOnes = false;
|
|
136
|
-
if (node.sym !== undefined) {
|
|
137
|
-
if (strict && node.sym === HUFF_EOS) {
|
|
171
|
+
var byte = buf[i];
|
|
172
|
+
for (var half = 0; half < 2; half++) {
|
|
173
|
+
var e = table[(state << 4) | (half === 0 ? byte >> 4 : byte & 15)];
|
|
174
|
+
if (e & _HD_INVALID) throw new Error('Invalid Huffman code');
|
|
175
|
+
if (e & _HD_HAS_SYM) {
|
|
176
|
+
var sym = (e >> 8) & 0x1FF;
|
|
177
|
+
if (strict && sym === HUFF_EOS) {
|
|
138
178
|
throw new Error('Huffman: EOS symbol in input');
|
|
139
179
|
}
|
|
140
|
-
out
|
|
141
|
-
node = _huffRoot;
|
|
142
|
-
sincePadStart = 0;
|
|
143
|
-
padIsOnes = true;
|
|
180
|
+
out[n++] = sym; // EOS (256) stores as 0, as Uint8Array.from did
|
|
144
181
|
}
|
|
182
|
+
state = e & 0xFF;
|
|
145
183
|
}
|
|
146
184
|
}
|
|
147
185
|
|
|
148
|
-
if (strict &&
|
|
186
|
+
if (strict && state !== 0) {
|
|
149
187
|
// Trailing bits that did not complete a symbol are padding. They must be
|
|
150
188
|
// at most 7 bits and must be the most-significant bits of the EOS code.
|
|
151
|
-
if (
|
|
152
|
-
if (!
|
|
189
|
+
if (_huffDec.depth[state] > 7) throw new Error('Huffman: padding longer than 7 bits');
|
|
190
|
+
if (!_huffDec.ones[state]) throw new Error('Huffman: padding is not the EOS prefix');
|
|
153
191
|
}
|
|
154
192
|
|
|
155
|
-
|
|
193
|
+
// A view, not a copy: every caller turns the bytes into a string at once.
|
|
194
|
+
return out.subarray(0, n);
|
|
156
195
|
}
|
|
157
196
|
|
|
158
197
|
|
|
@@ -517,4 +556,4 @@ export {
|
|
|
517
556
|
|
|
518
557
|
// Text
|
|
519
558
|
bytesToLatin1
|
|
520
|
-
};
|
|
559
|
+
};
|
package/src/http_common.js
CHANGED
|
@@ -458,6 +458,10 @@ function validateFieldSection(list, opts) {
|
|
|
458
458
|
// m,p,a,s, Safari m,s,p,a. See h2_fingerprint.js. Collected here because
|
|
459
459
|
// this loop is the only place that sees the list in its original sequence.
|
|
460
460
|
var pseudoOrder = [];
|
|
461
|
+
// The fields as they came, one name and value per field line, pseudo-headers
|
|
462
|
+
// first: for rawHeaders, which node:http2 keeps that way (the map combines
|
|
463
|
+
// repeated ones).
|
|
464
|
+
var raw = [];
|
|
461
465
|
var bad = function (m) { var e = new Error(m); e.malformed = true; return e; };
|
|
462
466
|
|
|
463
467
|
for (var i = 0; i < list.length; i++) {
|
|
@@ -477,6 +481,7 @@ function validateFieldSection(list, opts) {
|
|
|
477
481
|
if (!pseudo[name]) throw bad('unknown or misdirected pseudo-header ' + name);
|
|
478
482
|
if (hasOwn.call(headers, name)) throw bad('duplicate pseudo-header ' + name);
|
|
479
483
|
headers[name] = value;
|
|
484
|
+
raw.push(name, value); // node:http2 keeps them in rawHeaders too
|
|
480
485
|
continue;
|
|
481
486
|
}
|
|
482
487
|
seenRegular = true;
|
|
@@ -491,6 +496,7 @@ function validateFieldSection(list, opts) {
|
|
|
491
496
|
// Repeat-combining is RFC 9110 §5.3: set-cookie never joins, cookie joins
|
|
492
497
|
// with "; " (which h2 and h3 rely on: a sender may split it to compress).
|
|
493
498
|
addHeader(headers, name, value);
|
|
499
|
+
raw.push(name, value);
|
|
494
500
|
}
|
|
495
501
|
|
|
496
502
|
var protocol = null;
|
|
@@ -543,6 +549,7 @@ function validateFieldSection(list, opts) {
|
|
|
543
549
|
}
|
|
544
550
|
if (!isServer && !trailers && !headers[':status']) throw bad('response is missing :status');
|
|
545
551
|
|
|
552
|
+
Object.defineProperty(headers, '_rawHeaders', { value: raw, enumerable: false });
|
|
546
553
|
return { headers: headers, pseudoOrder: pseudoOrder, protocol: protocol };
|
|
547
554
|
}
|
|
548
555
|
|
package/src/qpack.js
CHANGED
|
@@ -270,6 +270,9 @@ var DECODER_INSERT_COUNT = 0x00; // 00xxxxxx
|
|
|
270
270
|
function parseDecoderInstructions(buf, offset) {
|
|
271
271
|
var increments = 0;
|
|
272
272
|
var i = offset || 0;
|
|
273
|
+
// Each instruction in order, for the encoder to check (RFC 9204 §4.4):
|
|
274
|
+
// { type: 'section_ack' | 'stream_cancel' | 'increment', value }.
|
|
275
|
+
var instructions = [];
|
|
273
276
|
|
|
274
277
|
// readPrefixedInt(buf, prefixBits, pos) — prefix width SECOND, position
|
|
275
278
|
// THIRD, and it returns { value, next } rather than a byte length. Getting
|
|
@@ -284,19 +287,22 @@ function parseDecoderInstructions(buf, offset) {
|
|
|
284
287
|
r = readPrefixedInt(buf, 7, i);
|
|
285
288
|
if (!r) break;
|
|
286
289
|
i = r.next;
|
|
290
|
+
instructions.push({ type: 'section_ack', value: r.value });
|
|
287
291
|
} else if (b & DECODER_STREAM_CANCEL) {
|
|
288
292
|
r = readPrefixedInt(buf, 6, i);
|
|
289
293
|
if (!r) break;
|
|
290
294
|
i = r.next;
|
|
295
|
+
instructions.push({ type: 'stream_cancel', value: r.value });
|
|
291
296
|
} else {
|
|
292
297
|
r = readPrefixedInt(buf, 6, i);
|
|
293
298
|
if (!r) break;
|
|
294
299
|
increments += r.value;
|
|
295
300
|
i = r.next;
|
|
301
|
+
instructions.push({ type: 'increment', value: r.value });
|
|
296
302
|
}
|
|
297
303
|
}
|
|
298
304
|
|
|
299
|
-
return { increments: increments, consumed: i - (offset || 0) };
|
|
305
|
+
return { increments: increments, consumed: i - (offset || 0), instructions: instructions };
|
|
300
306
|
}
|
|
301
307
|
|
|
302
308
|
|
|
@@ -439,7 +445,10 @@ function encodeHeaderBlock(headerList, table, options) {
|
|
|
439
445
|
|
|
440
446
|
return {
|
|
441
447
|
block: Buffer.from(prefix.concat(out)),
|
|
442
|
-
inserts: inserts
|
|
448
|
+
inserts: inserts,
|
|
449
|
+
// The section's Required Insert Count: non-zero, the peer acknowledges
|
|
450
|
+
// the section (Section Acknowledgment), which the encoder holds it to.
|
|
451
|
+
requiredInsertCount: maxRef
|
|
443
452
|
};
|
|
444
453
|
}
|
|
445
454
|
|