use super::*;
use crate::h3::qpack::encoder::Encoder;
#[inline]
fn hdr(name: &str, value: &str) -> (Bytes, Bytes) {
(
Bytes::copy_from_slice(name.as_bytes()),
Bytes::copy_from_slice(value.as_bytes()),
)
}
#[inline]
fn hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{b:02x}")).collect()
}
#[test]
fn vector_b1_static_name_ref() {
let mut dec = Decoder::new(0, 8);
let headers = dec
.decode_block(b"\x00\x00\x51\x0b/index.html", 0, 0)
.unwrap();
assert_eq!(headers.as_deref(), Some(&[hdr(":path", "/index.html")][..]));
assert!(dec.take_decoder_stream().is_empty());
assert_eq!(dec.known_received(), 0);
}
#[test]
fn chromium_style_capacity_round_trip() {
let mut enc = Encoder::new(65536, true);
let mut dec = Decoder::new(65536, 100);
let response = [
hdr(":status", "200"),
hdr("content-type", "text/html"),
hdr("x-custom-header", "hello-world"),
hdr("x-another", "value-2"),
];
let s1 = enc.encode_section(0, &response);
assert!(dec.decode_block(&s1.block, 0, 0).unwrap().is_none());
let unblocked = dec.feed_encoder_stream(&s1.encoder_stream).unwrap();
assert_eq!(unblocked.len(), 1);
assert_eq!(unblocked[0].headers.as_slice(), response.as_slice());
let s2 = enc.encode_section(
0,
&[hdr(":status", "200"), hdr("x-custom-header", "hello-world")],
);
let unblocked2 = dec.feed_encoder_stream(&s2.encoder_stream).unwrap();
assert!(unblocked2.is_empty());
let out2 = dec.decode_block(&s2.block, 1, 0).unwrap().expect("decodes");
assert_eq!(
out2,
&[hdr(":status", "200"), hdr("x-custom-header", "hello-world")][..]
);
}
#[test]
fn vector_b2_round_trip() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(
&enc.insert_with_name_ref(b":authority", b"www.example.com")
.unwrap(),
);
es.extend_from_slice(&enc.insert_with_name_ref(b":path", b"/sample/path").unwrap());
let mut dec = Decoder::new(220, 8);
assert!(dec.feed_encoder_stream(&es).unwrap().is_empty());
assert_eq!(hex(&dec.take_decoder_stream()), "02");
assert_eq!(dec.known_received(), 2);
let headers = dec.decode_block(b"\x03\x81\x10\x11", 5, 0).unwrap();
assert_eq!(
headers.as_deref(),
Some(
&[
hdr(":authority", "www.example.com"),
hdr(":path", "/sample/path")
][..]
)
);
assert_eq!(hex(&dec.take_decoder_stream()), "85");
assert_eq!(dec.known_received(), 2);
}
#[test]
fn vector_b4_round_trip() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(
&enc.insert_with_name_ref(b":authority", b"www.example.com")
.unwrap(),
);
es.extend_from_slice(&enc.insert_with_name_ref(b":path", b"/sample/path").unwrap());
es.extend_from_slice(&enc.insert_literal(b"custom-key", b"custom-value").unwrap());
es.extend_from_slice(&enc.duplicate(2).unwrap());
assert_eq!(
hex(&es[..]),
concat!(
"3fbd01c00f7777772e6578616d706c652e636f6d",
"c10c2f73616d706c652f706174684a637573746f6d2d6b65790c637573746f6d2d76616c756502"
)
);
let mut dec = Decoder::new(220, 8);
assert!(dec.feed_encoder_stream(&es).unwrap().is_empty());
let headers = dec.decode_block(b"\x05\x00\x80", 1, 0).unwrap();
assert_eq!(
headers.as_deref(),
Some(&[hdr(":authority", "www.example.com")][..])
);
assert_eq!(hex(&dec.take_decoder_stream()), "0481");
}
#[test]
fn relative_ref_round_trip() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_with_name_ref(b":path", b"/sample/path").unwrap());
let block = enc.encode_section(0, &[hdr(":path", "/sample/path")]).block;
let mut dec = Decoder::new(220, 8);
assert!(dec.feed_encoder_stream(&es).unwrap().is_empty());
let headers = dec.decode_block(&block, 2, 0).unwrap();
assert_eq!(
headers.as_deref(),
Some(&[hdr(":path", "/sample/path")][..])
);
}
#[test]
fn huffman_round_trip() {
let mut enc = Encoder::new(220, true);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_with_name_ref(b":path", b"/sample/path").unwrap());
let block = enc
.encode_section(
0,
&[hdr(":path", "www.example.com/aaaaaaaaaaaaaaaaaaaaaaaaa")],
)
.block;
let mut dec = Decoder::new(220, 8);
assert!(dec.feed_encoder_stream(&es).unwrap().is_empty());
let headers = dec.decode_block(&block, 2, 0).unwrap();
assert_eq!(
headers.as_deref(),
Some(&[hdr(":path", "www.example.com/aaaaaaaaaaaaaaaaaaaaaaaaa")][..])
);
}
#[test]
fn fresh_name_inserted_then_referenced() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let out = enc.encode_section(0, &[hdr("custom-key", "custom-value")]);
let mut dec = Decoder::new(220, 8);
assert!(dec
.feed_encoder_stream(&out.encoder_stream)
.unwrap()
.is_empty());
let headers = dec.decode_block(&out.block, 4, 0).unwrap();
assert_eq!(
headers.as_deref(),
Some(&[hdr("custom-key", "custom-value")][..])
);
assert_eq!(hex(&dec.take_decoder_stream()), "0184");
}
#[test]
fn blocked_then_unblocked() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_literal(b"custom-key", b"custom-value").unwrap());
let block = enc
.encode_section(0, &[hdr("custom-key", "custom-value")])
.block;
let mut dec = Decoder::new(220, 8);
assert!(dec.decode_block(&block, 7, 100).unwrap().is_none());
assert_eq!(dec.pending_blocked(), 1);
assert!(dec.take_decoder_stream().is_empty());
let sections = dec.feed_encoder_stream(&es).unwrap();
assert_eq!(sections.len(), 1);
assert_eq!(sections[0].stream_id, 7);
assert_eq!(sections[0].headers, vec![hdr("custom-key", "custom-value")]);
assert_eq!(dec.pending_blocked(), 0);
assert_eq!(hex(&dec.take_decoder_stream()), "87");
assert_eq!(dec.known_received(), 1);
}
#[test]
fn blocked_stream_limit() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_literal(b"custom-key", b"custom-value").unwrap());
let block = enc
.encode_section(0, &[hdr("custom-key", "custom-value")])
.block;
let mut dec = Decoder::new(220, 1);
assert!(dec.decode_block(&block, 1, 0).unwrap().is_none());
assert_eq!(
dec.decode_block(&block, 2, 0),
Err(QpackError::DecompressionFailed),
"more blocked streams than SETTINGS_QPACK_BLOCKED_STREAMS"
);
drop(es);
}
#[test]
fn same_stream_sections_stay_ordered() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(
&enc.insert_with_name_ref(b":authority", b"www.example.com")
.unwrap(),
);
es.extend_from_slice(&enc.insert_with_name_ref(b":path", b"/sample/path").unwrap());
let first = enc
.encode_section(0, &[hdr(":authority", "www.example.com")])
.block;
let second = enc.encode_section(0, &[hdr(":method", "GET")]).block;
let mut dec = Decoder::new(220, 8);
assert!(dec.decode_block(&first, 3, 0).unwrap().is_none());
assert!(dec.decode_block(&second, 3, 0).unwrap().is_none());
assert_eq!(dec.pending_blocked(), 2);
let sections = dec.feed_encoder_stream(&es).unwrap();
assert_eq!(sections.len(), 2);
assert_eq!(
sections[0].headers,
vec![hdr(":authority", "www.example.com")]
);
assert_eq!(sections[1].headers, vec![hdr(":method", "GET")]);
assert_eq!(hex(&dec.take_decoder_stream()), "8301");
}
#[test]
fn static_only_section_on_populated_table() {
let mut dec = Decoder::new(220, 8);
let headers = dec.decode_block(b"\x00\x01\xd1", 0, 0).unwrap();
assert_eq!(headers.as_deref(), Some(&[hdr(":method", "GET")][..]));
assert!(dec.take_decoder_stream().is_empty());
}
#[test]
fn literals_with_literal_name() {
let mut dec = Decoder::new(0, 8);
let headers = dec
.decode_block(b"\x00\x00\x23foo\x03bar\x33foo\x03baz", 0, 0)
.unwrap();
assert_eq!(
headers.as_deref(),
Some(&[hdr("foo", "bar"), hdr("foo", "baz")][..])
);
}
#[test]
fn post_base_literal_name_ref() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_literal(b"foo", b"bar").unwrap());
let mut dec = Decoder::new(220, 8);
assert!(dec.feed_encoder_stream(&es).unwrap().is_empty());
let headers = dec.decode_block(b"\x02\x80\x00\x03bar", 1, 0).unwrap();
assert_eq!(headers.as_deref(), Some(&[hdr("foo", "bar")][..]));
}
#[test]
fn empty_section() {
let mut dec = Decoder::new(220, 8);
assert_eq!(dec.decode_block(b"\x00\x00", 0, 0).unwrap(), Some(vec![]));
assert!(dec.take_decoder_stream().is_empty());
}
#[test]
fn field_section_size_capped() {
let mut dec = Decoder::new(220, 8);
dec.set_max_field_section_size(10);
assert_eq!(
dec.decode_block(b"\x00\x00\x21x\x0ayyyyyyyyyy", 0, 0),
Err(QpackError::DecompressionFailed)
);
let headers = dec.decode_block(b"\x00\x00\x21x\tyyyyyyyyy", 1, 0).unwrap();
assert_eq!(headers.as_deref(), Some(&[hdr("x", "yyyyyyyyy")][..]));
assert_eq!(
dec.decode_block(b"\x00\x00\xc4", 2, 0),
Err(QpackError::DecompressionFailed)
);
}
#[test]
fn field_section_size_budget_is_per_stream() {
let mut dec = Decoder::new(220, 8);
dec.set_max_field_section_size(20);
let headers = dec.decode_block(b"\x00\x01\xd1\xc0", 3, 0).unwrap();
assert_eq!(
headers.as_deref(),
Some(&[hdr(":method", "GET"), hdr(":authority", "")][..])
);
assert_eq!(
dec.decode_block(b"\x00\x00\x21a\x01b", 3, 0),
Err(QpackError::DecompressionFailed)
);
let headers = dec.decode_block(b"\x00\x00\x21a\x01b", 5, 0).unwrap();
assert_eq!(headers.as_deref(), Some(&[hdr("a", "b")][..]));
}
#[test]
fn stream_finished_releases_budget() {
let mut dec = Decoder::new(220, 8);
dec.set_max_field_section_size(10);
assert!(dec.decode_block(b"\x00\x01\xd1", 2, 0).unwrap().is_some());
assert_eq!(
dec.decode_block(b"\x00\x00\x21a\x01b", 2, 0),
Err(QpackError::DecompressionFailed)
);
dec.stream_finished(2);
assert!(dec
.decode_block(b"\x00\x00\x21a\x01b", 2, 0)
.unwrap()
.is_some());
}
#[test]
fn stream_cancelled_releases_budget() {
let mut dec = Decoder::new(220, 8);
dec.set_max_field_section_size(10);
assert!(dec.decode_block(b"\x00\x01\xd1", 4, 0).unwrap().is_some());
assert_eq!(hex(&dec.stream_cancelled(4)), "44");
assert!(dec
.decode_block(b"\x00\x00\x21a\x01b", 4, 0)
.unwrap()
.is_some());
}
#[test]
fn blocked_section_size_capped_on_unblock() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_literal(b"custom-key", b"custom-value").unwrap());
let block = enc
.encode_section(0, &[hdr("custom-key", "custom-value")])
.block;
let mut dec = Decoder::new(220, 8);
dec.set_max_field_section_size(10);
assert!(dec.decode_block(&block, 7, 0).unwrap().is_none());
assert_eq!(
dec.feed_encoder_stream(&es),
Err(QpackError::DecompressionFailed)
);
}
#[test]
fn blocked_section_charges_the_stream_budget() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_literal(b"custom-key", b"custom-value").unwrap());
let block = enc
.encode_section(0, &[hdr("custom-key", "custom-value")])
.block;
let mut dec = Decoder::new(220, 8);
dec.set_max_field_section_size(30);
assert!(dec.decode_block(b"\x00\x01\xd1", 7, 0).unwrap().is_some());
assert!(dec.decode_block(&block, 7, 0).unwrap().is_none());
assert_eq!(
dec.feed_encoder_stream(&es),
Err(QpackError::DecompressionFailed)
);
let mut dec = Decoder::new(220, 8);
dec.set_max_field_section_size(30);
assert!(dec.decode_block(&block, 9, 0).unwrap().is_none());
let sections = dec.feed_encoder_stream(&es).unwrap();
assert_eq!(sections.len(), 1);
assert_eq!(sections[0].headers, vec![hdr("custom-key", "custom-value")]);
}
#[test]
fn ric_wrap_round_trip() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es: Vec<Vec<u8>> = Vec::new();
es.push(enc.set_capacity(220).unwrap().to_vec());
for i in 0..15 {
es.push(
enc.insert_literal(format!("h{i}").as_bytes(), b"v")
.unwrap()
.to_vec(),
);
enc.feed_decoder_stream(&[1]).unwrap();
}
let block = enc.encode_section(0, &[hdr("h14", "v")]).block;
assert_eq!(&block[..2], &[4, 0], "enc_ric 4, Sign 0 Delta 0");
let mut dec = Decoder::new(220, 8);
for batch in es.chunks(6) {
let mut joined = Vec::new();
for part in batch {
joined.extend_from_slice(part);
}
assert!(dec.feed_encoder_stream(&joined).unwrap().is_empty());
}
assert_eq!(dec.inserted(), 15);
assert_eq!(dec.known_received(), 15);
let headers = dec.decode_block(&block, 9, 0).unwrap();
assert_eq!(headers.as_deref(), Some(&[hdr("h14", "v")][..]));
}
#[test]
fn increment_coalesced() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut set = Vec::new();
set.extend_from_slice(&enc.set_capacity(220).unwrap());
let a = enc.insert_literal(b"a", b"a").unwrap();
let b = enc.insert_literal(b"b", b"b").unwrap();
let c = enc.insert_literal(b"c", b"c").unwrap();
let mut dec = Decoder::new(220, 8);
assert!(dec.feed_encoder_stream(&set).unwrap().is_empty());
assert!(dec.feed_encoder_stream(&a).unwrap().is_empty());
assert_eq!(hex(&dec.take_decoder_stream()), "01");
let mut bc = b.to_vec();
bc.extend_from_slice(&c);
assert!(dec.feed_encoder_stream(&bc).unwrap().is_empty());
assert_eq!(hex(&dec.take_decoder_stream()), "02");
assert_eq!(dec.known_received(), 3);
}
#[test]
fn stream_cancelled_drops_blocked() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_literal(b"custom-key", b"custom-value").unwrap());
let block = enc
.encode_section(0, &[hdr("custom-key", "custom-value")])
.block;
let mut dec = Decoder::new(220, 8);
assert!(dec.decode_block(&block, 9, 0).unwrap().is_none());
assert_eq!(hex(&dec.stream_cancelled(9)), "49");
assert_eq!(dec.pending_blocked(), 0);
assert!(dec.feed_encoder_stream(&es).unwrap().is_empty());
}
#[test]
fn expire_blocked_emits_cancellations() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_literal(b"custom-key", b"custom-value").unwrap());
let block = enc
.encode_section(0, &[hdr("custom-key", "custom-value")])
.block;
let mut dec = Decoder::new(220, 8);
assert!(dec.decode_block(&block, 4, 1000).unwrap().is_none());
assert!(dec.decode_block(&block, 6, 1200).unwrap().is_none());
assert!(dec.expire_blocked(1500, 500).is_empty());
assert_eq!(dec.pending_blocked(), 2);
assert_eq!(hex(&dec.expire_blocked(1600, 500)), "44");
assert_eq!(dec.pending_blocked(), 1);
assert_eq!(hex(&dec.expire_blocked(1701, 500)), "46");
assert_eq!(dec.pending_blocked(), 0);
drop(es);
}
#[test]
fn eviction_into_unacknowledged_rejected() {
let mut dec = Decoder::new(88, 8);
let es = [
0x3f, 0x39, 0x41, b'a', 0x01, b'a', 0x41, b'b', 0x01, b'b', 0x41, b'c', 0x01, b'c',
];
assert_eq!(dec.feed_encoder_stream(&es), Err(QpackError::EncoderStream));
}
#[test]
fn decoder_stream_section_ack_classifies_high_stream_id() {
let mut dec = Decoder::new(0, 8);
assert!(dec.feed_decoder_stream(&[0xC0]).is_ok());
assert!(dec.feed_decoder_stream(&[0x44]).is_ok());
assert!(dec.feed_decoder_stream(&[0x01]).is_ok());
assert_eq!(
dec.feed_decoder_stream(&[0x00]),
Err(QpackError::DecoderStream)
);
}
#[test]
fn capacity_eviction_guard() {
let mut dec = Decoder::new(220, 8);
let es = [
0x3f, 0xbd, 0x01, 0x41, b'a', 0x01, b'a', 0x41, b'b', 0x01, b'b', 0x3f, 0x03, ];
assert_eq!(dec.feed_encoder_stream(&es), Err(QpackError::EncoderStream));
let mut dec = Decoder::new(220, 8);
let first = [
0x3f, 0xbd, 0x01, 0x41, b'a', 0x01, b'a', 0x41, b'b', 0x01, b'b',
];
assert!(dec.feed_encoder_stream(&first).unwrap().is_empty());
assert!(dec.feed_encoder_stream(&[0x3f, 0x03]).unwrap().is_empty());
assert_eq!(dec.dynamic.len(), 1);
assert_eq!(
dec.dynamic.get_absolute(1),
Some((b"b".as_slice(), b"b".as_slice()))
);
}
#[test]
fn capacity_above_advertised_rejected() {
let mut dec = Decoder::new(64, 8);
assert_eq!(
dec.feed_encoder_stream(&[0x3f, 0x22]),
Err(QpackError::EncoderStream),
"Set Dynamic Table Capacity 65 exceeds the advertised maximum"
);
}
#[test]
fn duplicate_out_of_range() {
let mut dec = Decoder::new(220, 8);
assert_eq!(
dec.feed_encoder_stream(&[0x00]),
Err(QpackError::EncoderStream)
);
}
#[test]
fn encoder_stream_refs_evicted_entry() {
let mut dec = Decoder::new(220, 8);
assert!(dec
.feed_encoder_stream(&[
0x3f, 0xbd, 0x01, 0x41, b'a', 0x01, b'a', 0x41, b'b', 0x01, b'b', 0x41, b'c', 0x01,
b'c',
])
.unwrap()
.is_empty());
assert!(dec.feed_encoder_stream(&[0x3f, 0x03]).unwrap().is_empty());
assert_eq!(dec.dynamic.len(), 1);
assert_eq!(
dec.dynamic.get_absolute(2),
Some((b"c".as_slice(), b"c".as_slice()))
);
let mut ref_instr = Vec::new();
integer::encode(&mut ref_instr, 1, 6, INSERT_WITH_NAME_REF);
ref_instr.push(1);
ref_instr.push(b'v');
assert_eq!(
dec.feed_encoder_stream(&ref_instr),
Err(QpackError::EncoderStream)
);
}
#[test]
fn static_index_out_of_range() {
let mut dec = Decoder::new(0, 8);
assert_eq!(
dec.decode_block(b"\x00\x00\xff\x24", 0, 0),
Err(QpackError::DecompressionFailed)
);
}
#[test]
fn relative_index_out_of_range() {
let mut dec = Decoder::new(220, 8);
assert_eq!(
dec.decode_block(b"\x00\x00\x80", 0, 0),
Err(QpackError::DecompressionFailed)
);
}
#[test]
fn post_base_beyond_table() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_literal(b"a", b"a").unwrap());
let mut dec = Decoder::new(220, 8);
assert!(dec.feed_encoder_stream(&es).unwrap().is_empty());
assert_eq!(
dec.decode_block(b"\x02\x80\x15", 0, 0),
Err(QpackError::DecompressionFailed)
);
}
#[test]
fn ric_mismatch_rejected() {
let mut enc = crate::h3::qpack::encoder::Encoder::new(220, false);
let mut es = Vec::new();
es.extend_from_slice(&enc.set_capacity(220).unwrap());
es.extend_from_slice(&enc.insert_literal(b"a", b"a").unwrap());
es.extend_from_slice(&enc.insert_literal(b"b", b"b").unwrap());
let mut dec = Decoder::new(220, 8);
assert!(dec.feed_encoder_stream(&es).unwrap().is_empty());
assert_eq!(
dec.decode_block(b"\x03\x00\x81", 0, 0),
Err(QpackError::DecompressionFailed)
);
assert_eq!(
dec.decode_block(b"\x00\x01\x80", 0, 0),
Err(QpackError::DecompressionFailed)
);
}
#[test]
fn truncated_inputs_do_not_panic() {
let mut dec = Decoder::new(220, 8);
assert!(dec.feed_encoder_stream(b"\x4aab").is_ok());
let mut dec2 = Decoder::new(220, 8);
assert_eq!(
dec2.feed_encoder_stream(b"\x80\x01a"),
Err(QpackError::EncoderStream)
);
let mut dec3 = Decoder::new(220, 8);
assert_eq!(
dec3.decode_block(b"\x00\x00\x51\x0aabc", 0, 0),
Err(QpackError::DecompressionFailed)
);
assert_eq!(
dec3.decode_block(b"\x00\x00\x80", 0, 0),
Err(QpackError::DecompressionFailed)
);
assert_eq!(
dec3.decode_block(b"\x00", 0, 0),
Err(QpackError::DecompressionFailed)
);
assert_eq!(
dec3.decode_block(b"\x02\xff\x00", 0, 0),
Err(QpackError::DecompressionFailed)
);
}
#[test]
fn multi_section_round_trip() {
let mut enc = Encoder::new(300, false);
let first = enc.encode_section(
0,
&[
hdr("custom-key", "custom-value"),
hdr("x-more", "0123456789"),
],
);
let second = enc.encode_section(0, &[hdr("custom-key", "custom-value")]);
let third = enc.encode_section(0, &[hdr(":method", "GET")]);
let mut dec = Decoder::new(300, 8);
assert!(dec
.feed_encoder_stream(&first.encoder_stream)
.unwrap()
.is_empty());
let headers = dec.decode_block(&first.block, 1, 0).unwrap();
assert_eq!(
headers.as_deref(),
Some(
&[
hdr("custom-key", "custom-value"),
hdr("x-more", "0123456789")
][..]
)
);
assert!(dec
.feed_encoder_stream(&second.encoder_stream)
.unwrap()
.is_empty());
assert_eq!(
dec.decode_block(&second.block, 1, 0).unwrap().as_deref(),
Some(&[hdr("custom-key", "custom-value")][..])
);
assert_eq!(
dec.decode_block(&third.block, 1, 0).unwrap().as_deref(),
Some(&[hdr(":method", "GET")][..])
);
assert_eq!(dec.known_received(), 2);
}
#[test]
fn split_encoder_stream_chunk_repro() {
use crate::h3::qpack::encoder::Encoder;
let mut enc = Encoder::new(4096, true);
let cap = enc.set_capacity(4096).unwrap();
let name = vec![b'x'; 40];
let instruction = enc.insert_literal(&name, b"v").unwrap();
assert!(instruction.len() >= 2, "instruction must be multi-byte");
let mut dec = Decoder::new(4096, 8);
assert!(dec.feed_encoder_stream(&cap).is_ok());
assert!(
dec.feed_encoder_stream(&instruction[..1]).is_ok(),
"partial instruction must not raise QPACK_ENCODER_STREAM_ERROR"
);
assert!(dec.feed_encoder_stream(&instruction[1..]).is_ok());
assert_eq!(dec.inserted(), 1, "split insert must be materialized");
}