zincio_http/h3/qpack/encoder/mod.rs
1//! QPACK encoder (RFC 9204 Sections 4.3 and 4.5).
2//!
3//! Consumption: the HTTP/3 layer drives the encoder per connection (it feeds
4//! field sections and drains the encoder stream); until that lands, the whole
5//! module is dead in non-test builds, which is why `dead_code` is expected
6//! here. It errors again once the encoder is used, reminding us to remove the
7//! expectation.
8//!
9//! The encoder owns the shared dynamic table (Section 4.2): it alone adds
10//! entries, and it emits an unframed sequence of instructions on the encoder
11//! stream (Section 4.3) describing every mutation. Field sections are encoded
12//! as a Required Insert Count, a Base, and one or more field line
13//! representations (Section 4.5).
14//!
15//! The encoder fixes the Base at the dynamic table insertion count before
16//! encoding a section, so every reference to entries that existed before the
17//! section is relative, and entries inserted while encoding the section are
18//! referenced with post-Base indexes (Section 4.5.1.2 recommends exactly this:
19//! Base equal to the Required Insert Count, which makes the Sign bit and the
20//! Delta Base zero whenever nothing new is referenced).
21//!
22//! Inserts are speculative but bounded: an entry is only inserted when it
23//! fits and when the eviction it would cause cannot invalidate an index
24//! already referenced by the section being encoded, nor remove an entry the
25//! decoder still needs. The decoder's acknowledgments (Section 4.4
26//! instructions on its decoder stream) raise a Known Received Count and free
27//! the references of acknowledged field sections; an entry is evictable only
28//! below the lower of the Known Received Count and the smallest reference
29//! still outstanding (RFC 9204 Sections 2.1.1 and 2.1.4). The encoder never
30//! evicts above that floor, so the peer's decoder never has to reject an
31//! insert as a QPACK_ENCODER_STREAM_ERROR.
32//!
33//! Huffman encoding follows RFC 9204 Section 4.1.2: a string is Huffman
34//! encoded when that is shorter, matching HPACK practice.
35#![expect(dead_code)]
36
37use std::collections::VecDeque;
38
39use bytes::Bytes;
40
41use crate::h3::qpack::table::DynamicTable;
42use crate::h3::qpack::{static_table, QpackError};
43use crate::hpack::{huffman, integer};
44
45/// `001` + 5-bit capacity: Set Dynamic Table Capacity (RFC 9204 4.3.1).
46const SET_CAPACITY: u8 = 0b0010_0000;
47/// `1 T` + 6-bit name index: Insert with Name Reference (RFC 9204 4.3.2).
48const INSERT_WITH_NAME_REF: u8 = 0b1000_0000;
49/// `01` + H + 5-bit name length: Insert with Literal Name (RFC 9204 4.3.3).
50const INSERT_WITH_LITERAL_NAME: u8 = 0b0100_0000;
51/// `000` + 5-bit relative index: Duplicate (RFC 9204 4.3.4).
52const DUPLICATE: u8 = 0b0000_0000;
53
54/// `1` + 7-bit stream ID: Section Acknowledgment (RFC 9204 4.4.1).
55const SECTION_ACK: u8 = 0x80;
56/// `01` + 6-bit stream ID: Stream Cancellation (RFC 9204 4.4.2).
57const STREAM_CANCELLATION: u8 = 0x40;
58/// `00` + 6-bit increment: Insert Count Increment (RFC 9204 4.4.3).
59const INSERT_COUNT_INCREMENT: u8 = 0x00;
60
61/// Upper bound on `decoder_stream_pending`. A complete decoder-stream
62/// instruction is a single prefixed integer of at most 10 bytes (a 62-bit
63/// integer), so a buffered prefix far larger than this can never become a
64/// valid instruction.
65const MAX_DECODER_STREAM_PENDING: usize = 64;
66
67/// `1 T` + 6-bit index: Indexed Field Line (RFC 9204 4.5.2).
68const INDEXED: u8 = 0b1000_0000;
69/// `0001` + 4-bit post-Base index: Indexed Field Line with Post-Base Index
70/// (RFC 9204 4.5.3).
71const INDEXED_POST_BASE: u8 = 0b0001_0000;
72/// `01 N T` + 4-bit name index: Literal Field Line with Name Reference
73/// (RFC 9204 4.5.4).
74const LITERAL_NAME_REF: u8 = 0b0100_0000;
75/// `0000 N` + 3-bit post-Base name index: Literal Field Line with Post-Base
76/// Name Reference (RFC 9204 4.5.5).
77const LITERAL_POST_BASE_NAME_REF: u8 = 0b0000_0000;
78/// `001 N` + H + 3-bit name length: Literal Field Line with Literal Name
79/// (RFC 9204 4.5.6).
80const LITERAL_LITERAL_NAME: u8 = 0b0010_0000;
81
82/// Field names that must never be added to the dynamic table and must always
83/// be sent as literal field lines with the N bit set (RFC 9204 Section 7.1).
84const NEVER_INDEXED: [&[u8]; 3] = [b"authorization", b"proxy-authorization", b"cookie"];
85
86/// Result of encoding one field section: the encoded field section carried on
87/// the request/response stream, and any encoder stream instructions queued
88/// while encoding it.
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub struct EncodedSection {
91 /// Encoded field section prefix plus field lines (RFC 9204 Section 4.5.1).
92 pub block: Bytes,
93 /// Encoder stream instructions (RFC 9204 Section 4.3).
94 pub encoder_stream: Bytes,
95}
96
97/// QPACK encoder: dynamic table owner and field section encoder.
98#[derive(Debug)]
99pub struct Encoder {
100 dynamic: DynamicTable,
101 /// Upper bound on the dynamic table capacity allowed by the decoder's
102 /// SETTINGS_QPACK_MAX_TABLE_CAPACITY (RFC 9204 Section 5).
103 max_capacity: u64,
104 /// Whether string literals are Huffman encoded when that is shorter.
105 huffman: bool,
106 /// Number of dynamic table insertions and duplications acknowledged by
107 /// the decoder (RFC 9204 Section 2.1.4). Absolute indexes below it are
108 /// acknowledged and can be evicted.
109 known_received: u64,
110 /// Smallest absolute index referenced by each unacknowledged field
111 /// section that has dynamic references, in send order (RFC 9204
112 /// Sections 2.1.1 and 4.4.1). A section is freed by a Section
113 /// Acknowledgment for its stream or by a Stream Cancellation.
114 pending_refs: VecDeque<(u64, u64)>,
115 /// Cached minimum of `pending_refs` second values (`u64::MAX` when empty).
116 /// Avoids `O(N_pending)` scan per header insertion in `evictable_floor`.
117 cached_min_pending: u64,
118 /// Bytes of the peer's decoder stream received so far but not yet forming
119 /// a complete instruction. QPACK decoder-stream instructions can span the
120 /// arbitrary chunk boundaries of the underlying QUIC stream, so partial
121 /// instructions are buffered here until the rest arrives (RFC 9204
122 /// Section 4.4) instead of being treated as a stream error.
123 decoder_stream_pending: Vec<u8>,
124}
125
126impl Encoder {
127 /// Creates an encoder bound to a decoder that advertised the given
128 /// `max_capacity` in SETTINGS_QPACK_MAX_TABLE_CAPACITY.
129 #[inline]
130 pub fn new(max_capacity: u64, huffman: bool) -> Self {
131 Self {
132 dynamic: DynamicTable::new(0),
133 max_capacity,
134 huffman,
135 known_received: 0,
136 pending_refs: VecDeque::new(),
137 cached_min_pending: u64::MAX,
138 decoder_stream_pending: Vec::new(),
139 }
140 }
141
142 /// The maximum dynamic table capacity permitted by the decoder.
143 #[inline]
144 pub fn max_capacity(&self) -> u64 {
145 self.max_capacity
146 }
147
148 /// The lowest absolute index at or above which entries are NOT
149 /// evictable: the lower of the Known Received Count and the smallest
150 /// reference of any unacknowledged field section (RFC 9204
151 /// Sections 2.1.1 and 2.1.4). An insert whose eviction would reach it
152 /// is skipped.
153 #[inline]
154 fn evictable_floor(&self) -> u64 {
155 self.known_received.min(self.cached_min_pending)
156 }
157
158 #[inline]
159 fn recompute_min_pending(&mut self) {
160 self.cached_min_pending = self
161 .pending_refs
162 .iter()
163 .map(|(_, min_ref)| *min_ref)
164 .min()
165 .unwrap_or(u64::MAX);
166 }
167
168 /// Encodes `headers` for the field section on `stream_id` into an
169 /// encoded field section plus the encoder stream instructions the
170 /// decoder needs to process it.
171 ///
172 /// The dynamic table is used only when the decoder allows a capacity of
173 /// at least one entry (RFC 9204 Section 3.2.3): a maximum capacity below
174 /// 32 bytes cannot hold any entry and disables the dynamic table.
175 #[inline]
176 pub fn encode_section(&mut self, stream_id: u64, headers: &[(Bytes, Bytes)]) -> EncodedSection {
177 self.encode_section_with_base(stream_id, headers, self.dynamic.inserted())
178 }
179
180 /// Encodes `headers` using the decoder's **acknowledged** insert count
181 /// (`known_received`) as the QPACK Base.
182 ///
183 /// RFC 9204 Section 2.1.2 forbids a relative reference to a dynamic entry
184 /// whose absolute index exceeds the Largest Reference the decoder has
185 /// acknowledged: such an entry must instead be referenced with a Post-Base
186 /// index. The shared encoder's own insert count (`dynamic.inserted()`) can
187 /// run far ahead of what the peer has acknowledged — for instance when the
188 /// client is busy consuming a large response body and lags on its
189 /// decoder-stream acknowledgments. Encoding against the insert count there
190 /// would emit relative references above the peer's Largest Reference, which
191 /// a strict decoder (e.g. Neqo/Firefox) treats as a QPACK decompression
192 /// failure, tearing down the whole connection and dropping every other in-
193 /// flight request. Encoding against `known_received` keeps every relative
194 /// reference within the acknowledged range and uses Post-Base for anything
195 /// newer, so the section decodes correctly even if the peer ACKs late.
196 #[inline]
197 pub(crate) fn encode_section_with_ack_base(
198 &mut self,
199 stream_id: u64,
200 headers: &[(Bytes, Bytes)],
201 ) -> EncodedSection {
202 self.encode_section_with_base(stream_id, headers, self.known_received)
203 }
204
205 /// The insert count a decoder must have reached to process the most
206 /// recently encoded section.
207 #[inline]
208 pub(crate) fn required_insert_count(&self) -> u64 {
209 self.dynamic.inserted()
210 }
211
212 #[inline]
213 fn encode_section_with_base(
214 &mut self,
215 stream_id: u64,
216 headers: &[(Bytes, Bytes)],
217 base: u64,
218 ) -> EncodedSection {
219 let usable = self.max_capacity >= 32;
220 let mut encoder_stream = Vec::new();
221
222 let mut block = Vec::new();
223 // Smallest absolute index referenced by the section so far; an insert
224 // is skipped when its eviction would reach it.
225 let mut min_rel_ref: Option<u64> = None;
226 // Required Insert Count: one larger than the largest absolute index
227 // of all dynamic table entries referenced by the section, and 0 when
228 // none are referenced (RFC 9204 Section 2.1.2). It only grows with
229 // dynamic references, so a section that happens to reference only
230 // static entries does not inflate it.
231 let mut ric = 0u64;
232
233 for (name, value) in headers {
234 let sensitive = NEVER_INDEXED.contains(&name.as_ref());
235
236 if sensitive {
237 self.encode_literal(name, value, true, &mut block);
238 continue;
239 }
240
241 // Full match, dynamic table first (newest first), then static.
242 // Preserve the corresponding name matches so the literal path
243 // below does not repeat either table scan.
244 let dynamic_match = usable.then(|| self.dynamic.find_full_or_name(name, value));
245 if usable {
246 if let Some(abs) = dynamic_match.and_then(|(full, _)| full) {
247 self.encode_indexed(abs, base, &mut block, &mut ric, &mut min_rel_ref);
248 continue;
249 }
250 }
251 let static_match = static_table::find_full_or_name(name, value);
252 if let Some(idx) = static_match.0 {
253 // Indexed Field Line, static table (T=1).
254 integer::encode(&mut block, idx as u64, 6, INDEXED | 0x40);
255 continue;
256 }
257
258 if usable {
259 if let Some(abs) = dynamic_match.and_then(|(_, name)| name) {
260 if abs >= base {
261 // Post-Base name reference (4.5.5). Only reachable on
262 // the vector path, which fixes Base below the insert
263 // count.
264 self.encode_literal_post_base_name_ref(
265 abs,
266 base,
267 value,
268 &mut block,
269 &mut ric,
270 &mut min_rel_ref,
271 );
272 } else {
273 // Literal with Name Reference, dynamic table (T=0).
274 self.encode_literal_with_name_ref(
275 abs,
276 base,
277 value,
278 &mut block,
279 &mut ric,
280 &mut min_rel_ref,
281 );
282 }
283 continue;
284 }
285 }
286 if let Some(idx) = static_match.1 {
287 // Literal with Name Reference, static table (T=1).
288 self.encode_literal_with_static_name_ref(idx, value, &mut block);
289 continue;
290 }
291
292 // No name match anywhere: emit a literal name, and insert the
293 // entry so later sections can reference it (Section 4.4).
294 let size = DynamicTable::entry_size(name, value);
295 // An insert only evicts the oldest entries, so it is allowed
296 // when the first surviving entry sits at or below every entry
297 // the section references and everything the decoder still
298 // needs (RFC 9204 Section 2.1.1): evicting below that floor is
299 // what the decoder's mirror of this table permits.
300 let boundary = min_rel_ref.unwrap_or(u64::MAX).min(self.evictable_floor());
301 let safe = self.dynamic.inserted() - self.dynamic.len() as u64
302 + self.dynamic.would_evict(size)
303 <= boundary;
304 if usable && size <= self.max_capacity && safe {
305 // Set Dynamic Table Capacity (4.3.1), emitted lazily right
306 // before the first insert of this section.
307 if self.dynamic.capacity() != self.max_capacity {
308 integer::encode(&mut encoder_stream, self.max_capacity, 5, SET_CAPACITY);
309 self.dynamic.set_capacity(self.max_capacity);
310 }
311 let abs = self.dynamic.next_absolute();
312 // Insert with Literal Name (4.3.3): the name length uses a
313 // 5-bit prefix, so `push_string` receives 6 (it reserves one
314 // bit for the Huffman flag).
315 self.push_string(&mut encoder_stream, name, 6, INSERT_WITH_LITERAL_NAME);
316 self.push_string(&mut encoder_stream, value, 8, 0);
317 let _ = self.dynamic.insert(name.clone(), value.clone());
318 // The fresh entry is referenced with a post-Base index.
319 self.encode_indexed(abs, base, &mut block, &mut ric, &mut min_rel_ref);
320 } else {
321 self.encode_literal(name, value, false, &mut block);
322 }
323 }
324
325 // A section with dynamic references pins the referenced entries
326 // until the decoder acknowledges it (Section 4.4.1).
327 if ric > 0 {
328 let min_ref = min_rel_ref.unwrap_or(0);
329 self.pending_refs.push_back((stream_id, min_ref));
330 if min_ref < self.cached_min_pending {
331 self.cached_min_pending = min_ref;
332 }
333 }
334
335 // Encoded Field Section Prefix (RFC 9204 4.5.1).
336 let mut prefix = Vec::new();
337 self.encode_prefix(&mut prefix, ric, base);
338 prefix.reserve(block.len());
339 prefix.extend_from_slice(&block);
340
341 EncodedSection {
342 block: Bytes::from(prefix),
343 encoder_stream: Bytes::from(encoder_stream),
344 }
345 }
346
347 /// Encodes the Required Insert Count and the Base (RFC 9204 4.5.1).
348 #[inline]
349 fn encode_prefix(&self, out: &mut Vec<u8>, ric: u64, base: u64) {
350 // Required Insert Count (4.5.1.1): 0 stays 0, otherwise it is wrapped
351 // modulo 2 * MaxEntries, where MaxEntries = floor(MaxCapacity / 32).
352 let max_entries = self.max_capacity / 32;
353 let enc_ric = if ric == 0 || max_entries == 0 {
354 0
355 } else {
356 (ric % (2 * max_entries)) + 1
357 };
358 integer::encode(out, enc_ric, 8, 0);
359 // Base (4.5.1.2): Sign=0 when Base >= Ric (Delta = Base - Ric);
360 // Sign=1 when Base < Ric (Delta = Ric - Base - 1).
361 if base >= ric {
362 integer::encode(out, base - ric, 7, 0);
363 } else {
364 integer::encode(out, ric - base - 1, 7, 0x80);
365 }
366 }
367
368 /// Encodes an indexed reference to a dynamic table entry.
369 #[inline]
370 fn encode_indexed(
371 &self,
372 abs: u64,
373 base: u64,
374 block: &mut Vec<u8>,
375 ric: &mut u64,
376 min_rel_ref: &mut Option<u64>,
377 ) {
378 *ric = (*ric).max(abs + 1);
379 if abs >= base {
380 // Post-Base index (4.5.3).
381 integer::encode(block, abs - base, 4, INDEXED_POST_BASE);
382 } else {
383 // Relative index (4.5.2): Base - Absolute - 1.
384 integer::encode(block, base - abs - 1, 6, INDEXED);
385 }
386 *min_rel_ref = Some(min_rel_ref.map_or(abs, |m| m.min(abs)));
387 }
388
389 /// Encodes a literal field line with a dynamic name reference (T=0).
390 #[inline]
391 fn encode_literal_with_name_ref(
392 &self,
393 abs: u64,
394 base: u64,
395 value: &[u8],
396 block: &mut Vec<u8>,
397 ric: &mut u64,
398 min_rel_ref: &mut Option<u64>,
399 ) {
400 *ric = (*ric).max(abs + 1);
401 let rel = base - abs - 1;
402 integer::encode(block, rel, 4, LITERAL_NAME_REF);
403 *min_rel_ref = Some(min_rel_ref.map_or(abs, |m| m.min(abs)));
404 self.push_string(block, value, 8, 0);
405 }
406
407 /// Encodes a literal field line with a post-Base name reference (4.5.5).
408 #[inline]
409 fn encode_literal_post_base_name_ref(
410 &self,
411 abs: u64,
412 base: u64,
413 value: &[u8],
414 block: &mut Vec<u8>,
415 ric: &mut u64,
416 min_rel_ref: &mut Option<u64>,
417 ) {
418 *ric = (*ric).max(abs + 1);
419 integer::encode(block, abs - base, 3, LITERAL_POST_BASE_NAME_REF);
420 *min_rel_ref = Some(min_rel_ref.map_or(abs, |m| m.min(abs)));
421 self.push_string(block, value, 8, 0);
422 }
423
424 /// Encodes a literal field line with a static name reference (T=1).
425 #[inline]
426 fn encode_literal_with_static_name_ref(&self, idx: usize, value: &[u8], block: &mut Vec<u8>) {
427 integer::encode(block, idx as u64, 4, LITERAL_NAME_REF | 0x10);
428 self.push_string(block, value, 8, 0);
429 }
430
431 /// Encodes a literal field line with a literal name (4.5.6), preferring a
432 /// static name reference when one exists.
433 #[inline]
434 fn encode_literal(&self, name: &[u8], value: &[u8], sensitive: bool, block: &mut Vec<u8>) {
435 if let Some(idx) = static_table::find_name(name) {
436 integer::encode(
437 block,
438 idx as u64,
439 4,
440 LITERAL_NAME_REF | 0x10 | (u8::from(sensitive) << 4),
441 );
442 } else {
443 // `001 N` + H + 3-bit name length.
444 self.push_string(
445 block,
446 name,
447 4,
448 LITERAL_LITERAL_NAME | (u8::from(sensitive) << 4),
449 );
450 }
451 self.push_string(block, value, 8, 0);
452 }
453
454 /// Encodes an N-bit prefix string literal (RFC 9204 Section 4.1.2):
455 /// `header` carries the bits preceding the string, the Huffman flag is
456 /// set when Huffman encoding is shorter, and the length is encoded with
457 /// an (N-1)-bit prefix.
458 #[inline]
459 fn push_string(&self, out: &mut Vec<u8>, value: &[u8], prefix: u8, header: u8) {
460 let (huffman, len) = if self.huffman {
461 let huffman_bits = huffman::encoded_len(value);
462 if huffman_bits < value.len() * 8 {
463 (true, huffman_bits.div_ceil(8) as u64)
464 } else {
465 (false, value.len() as u64)
466 }
467 } else {
468 (false, value.len() as u64)
469 };
470 integer::encode(
471 out,
472 len,
473 prefix - 1,
474 header | (u8::from(huffman) << (prefix - 1)),
475 );
476 if huffman {
477 huffman::encode_with_len(value, out, len as usize);
478 } else {
479 out.extend_from_slice(value);
480 }
481 }
482
483 /// Inserts an entry with a literal name on the encoder stream (4.3.3)
484 /// and mirrors it in the local table. Returns the instruction, or `None`
485 /// when the entry does not fit in the dynamic table.
486 #[inline]
487 pub(crate) fn insert_literal(&mut self, name: &[u8], value: &[u8]) -> Option<Bytes> {
488 let size = DynamicTable::entry_size(name, value);
489 if size > self.dynamic.capacity() || self.insert_would_evict_needed(size) {
490 return None;
491 }
492 let mut out = Vec::new();
493 // Name length uses a 5-bit prefix (RFC 9204 4.3.3), so `push_string`
494 // receives 6 (it reserves one bit for the Huffman flag).
495 self.push_string(&mut out, name, 6, INSERT_WITH_LITERAL_NAME);
496 self.push_string(&mut out, value, 8, 0);
497 let res = self
498 .dynamic
499 .insert(Bytes::copy_from_slice(name), Bytes::copy_from_slice(value));
500 debug_assert!(res.is_ok(), "insert_literal: entry passed the size check");
501 res.ok()?;
502 Some(Bytes::from(out))
503 }
504
505 /// Inserts an entry with a name reference on the encoder stream (4.3.2),
506 /// preferring the dynamic table (T=0) then the static table (T=1), and
507 /// mirrors it in the local table. Returns the instruction, or `None`
508 /// when the name is not indexed anywhere or the entry does not fit.
509 #[inline]
510 pub(crate) fn insert_with_name_ref(&mut self, name: &[u8], value: &[u8]) -> Option<Bytes> {
511 let size = DynamicTable::entry_size(name, value);
512 if size > self.dynamic.capacity() || self.insert_would_evict_needed(size) {
513 return None;
514 }
515 let mut out = Vec::new();
516 let (name_idx, pattern) = self
517 .dynamic
518 .find_name(name)
519 .map(|abs| (abs, INSERT_WITH_NAME_REF))
520 .or_else(|| {
521 static_table::find_name(name).map(|idx| (idx as u64, INSERT_WITH_NAME_REF | 0x40))
522 })?;
523 if pattern == INSERT_WITH_NAME_REF {
524 integer::encode(&mut out, self.dynamic.inserted() - name_idx - 1, 6, pattern);
525 } else {
526 integer::encode(&mut out, name_idx, 6, pattern);
527 }
528 self.push_string(&mut out, value, 8, 0);
529 let res = self
530 .dynamic
531 .insert(Bytes::copy_from_slice(name), Bytes::copy_from_slice(value));
532 debug_assert!(
533 res.is_ok(),
534 "insert_with_name_ref: entry passed the size check"
535 );
536 res.ok()?;
537 Some(Bytes::from(out))
538 }
539
540 /// Duplicates the entry at the given relative index (4.3.4) and mirrors
541 /// it in the local table. Returns the instruction, or `None` when the
542 /// index is out of range or the copy does not fit.
543 #[inline]
544 pub(crate) fn duplicate(&mut self, relative: u64) -> Option<Bytes> {
545 if relative >= self.dynamic.len() as u64 {
546 return None;
547 }
548 // The relative index is the deque position: 0 is the most recently
549 // inserted entry.
550 let (name, value) = self.dynamic.entry_at(relative)?;
551 let size = DynamicTable::entry_size(name, value);
552 if size > self.dynamic.capacity() || self.insert_would_evict_needed(size) {
553 return None;
554 }
555 let mut out = Vec::new();
556 integer::encode(&mut out, relative, 5, DUPLICATE);
557 let res = self
558 .dynamic
559 .insert(Bytes::copy_from_slice(name), Bytes::copy_from_slice(value));
560 debug_assert!(res.is_ok(), "duplicate: entry passed the size check");
561 res.ok()?;
562 Some(Bytes::from(out))
563 }
564
565 /// Sets the dynamic table capacity (4.3.1), evicting as needed. Returns
566 /// the instruction, or `None` when the capacity is unchanged, exceeds
567 /// the decoder's maximum, or the reduction would evict entries the
568 /// decoder still needs (RFC 9204 Section 2.1.1).
569 #[inline]
570 pub fn set_capacity(&mut self, capacity: u64) -> Option<Bytes> {
571 if capacity > self.max_capacity || capacity == self.dynamic.capacity() {
572 return None;
573 }
574 if capacity < self.dynamic.capacity() {
575 let evicted = self.dynamic.evict_for_capacity(capacity);
576 if self.dynamic.inserted() - self.dynamic.len() as u64 + evicted
577 > self.evictable_floor()
578 {
579 return None;
580 }
581 }
582 let mut out = Vec::new();
583 integer::encode(&mut out, capacity, 5, SET_CAPACITY);
584 self.dynamic.set_capacity(capacity);
585 Some(Bytes::from(out))
586 }
587
588 /// Whether inserting an entry of `size` bytes would evict an entry the
589 /// decoder still needs (RFC 9204 Section 2.1.1): one that is not yet
590 /// acknowledged or is referenced by an unacknowledged field section.
591 #[inline]
592 fn insert_would_evict_needed(&self, size: u64) -> bool {
593 self.dynamic.inserted() - self.dynamic.len() as u64 + self.dynamic.would_evict(size)
594 > self.evictable_floor()
595 }
596
597 /// Processes the decoder stream instructions in `buf`. Section
598 /// Acknowledgments (4.4.1) free a field section's references, Stream
599 /// Cancellations (4.4.2) free every reference of a cancelled stream,
600 /// and Insert Count Increments (4.4.3) raise the Known Received Count.
601 ///
602 /// Only an Insert Count Increment (4.4.3) is a connection error: a zero
603 /// increment, or one that advances past the number of inserts sent
604 /// (RFC 9204 Section 2.1.4). A Section Acknowledgment or Stream
605 /// Cancellation that matches no outstanding field section is benign —
606 /// RFC 9204 Section 4.4 defines no error for it and a peer may send one
607 /// for a section already acknowledged or cancelled — so it is ignored
608 /// rather than closing the connection.
609 #[inline]
610 pub fn feed_decoder_stream(&mut self, buf: &[u8]) -> Result<(), QpackError> {
611 self.decoder_stream_pending.extend_from_slice(buf);
612 // A decoder-stream instruction is a single prefixed integer, so its
613 // length is known once enough bytes have arrived. Parse only complete
614 // instructions, leaving any trailing partial instruction buffered for
615 // the next call (RFC 9204 Section 4.4). The QUIC stream beneath can
616 // deliver an instruction split across several `poll_recv` chunks.
617 // A complete instruction is at most 10 bytes (a 62-bit integer), so a
618 // much larger buffered prefix can never become one and is rejected to
619 // bound memory against a peer that streams continuation bytes.
620 if self.decoder_stream_pending.len() > MAX_DECODER_STREAM_PENDING {
621 return Err(QpackError::DecoderStream);
622 }
623 let data = &self.decoder_stream_pending;
624 let mut consumed = 0;
625 while consumed < data.len() {
626 let header = data[consumed];
627 // Every decoder-stream instruction carries one prefixed integer:
628 // a Section Acknowledgment (4.4.1) uses a 7-bit prefix, the other
629 // two types use 6 bits.
630 let prefix_bits: u8 = if header & 0x80 != 0 { 7 } else { 6 };
631 let Some(len) = integer::encoded_len(&data[consumed..], prefix_bits) else {
632 // Instruction is truncated; wait for more bytes.
633 break;
634 };
635 if consumed + len > data.len() {
636 break;
637 }
638 let instr = &data[consumed..consumed + len];
639 let mut off = 0;
640 if header & 0x80 != 0 {
641 // `1` + 7-bit stream ID: Section Acknowledgment (4.4.1).
642 // A matching field section is freed; a stray
643 // acknowledgment (already freed, or never outstanding) is
644 // ignored.
645 let stream_id = integer::decode(instr, &mut off, 7, header)
646 .map_err(|_| QpackError::DecoderStream)?;
647 if let Some(pos) = self
648 .pending_refs
649 .iter()
650 .position(|(id, _)| *id == stream_id)
651 {
652 let removed_min = self.pending_refs[pos].1;
653 self.pending_refs.remove(pos);
654 if removed_min == self.cached_min_pending {
655 self.cached_min_pending = self
656 .pending_refs
657 .iter()
658 .map(|(_, min_ref)| *min_ref)
659 .min()
660 .unwrap_or(u64::MAX);
661 }
662 }
663 } else if header & 0x40 != 0 {
664 // `01` + 6-bit stream ID: Stream Cancellation (4.4.2).
665 // Every outstanding reference of the stream is dropped; a
666 // cancellation for a stream with none is a no-op.
667 let stream_id = integer::decode(instr, &mut off, 6, header)
668 .map_err(|_| QpackError::DecoderStream)?;
669 let prev_len = self.pending_refs.len();
670 self.pending_refs.retain(|(id, _)| *id != stream_id);
671 if self.pending_refs.len() != prev_len {
672 self.cached_min_pending = self
673 .pending_refs
674 .iter()
675 .map(|(_, min_ref)| *min_ref)
676 .min()
677 .unwrap_or(u64::MAX);
678 }
679 } else {
680 // `00` + 6-bit increment: Insert Count Increment (4.4.3).
681 // A zero increment is forbidden, and the total may not
682 // exceed the number of inserts sent.
683 let increment = integer::decode(instr, &mut off, 6, header)
684 .map_err(|_| QpackError::DecoderStream)?;
685 if increment == 0
686 || self.known_received.saturating_add(increment) > self.dynamic.inserted()
687 {
688 return Err(QpackError::DecoderStream);
689 }
690 self.known_received += increment;
691 }
692 consumed += len;
693 }
694 self.decoder_stream_pending.drain(..consumed);
695 Ok(())
696 }
697}
698
699#[cfg(test)]
700mod tests;