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.
@@ -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 out = [];
121
- var node = _huffRoot;
122
- var nbits = 0, current = 0;
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
- current = (current << 8) | buf[i];
128
- nbits += 8;
129
- while (nbits > 0) {
130
- var bit = (current >> (nbits - 1)) & 1;
131
- node = node[bit];
132
- if (!node) throw new Error('Invalid Huffman code');
133
- nbits--;
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.push(node.sym);
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 && node !== _huffRoot) {
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 (sincePadStart > 7) throw new Error('Huffman: padding longer than 7 bits');
152
- if (!padIsOnes) throw new Error('Huffman: padding is not the EOS prefix');
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
- return Uint8Array.from(out);
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
+ };
@@ -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