use super::*;
use alloc::vec;
fn f(name: &[u8], value: &[u8]) -> HeaderField {
HeaderField::new(name, value)
}
#[test]
fn static_table_has_99_entries_and_boundaries() {
assert_eq!(static_table::STATIC_LEN, 99);
assert_eq!(static_table::get(0), Some((&b":authority"[..], &b""[..])));
assert_eq!(static_table::get(1), Some((&b":path"[..], &b"/"[..])));
assert_eq!(static_table::get(17), Some((&b":method"[..], &b"GET"[..])));
assert_eq!(
static_table::get(98),
Some((&b"x-frame-options"[..], &b"sameorigin"[..]))
);
assert_eq!(static_table::get(99), None);
}
#[test]
fn rfc_b1_literal_field_line_static_name_ref() {
let expected: &[u8] = &[
0x00, 0x00, 0x51, 0x0b, b'/', b'i', b'n', b'd', b'e', b'x', b'.', b'h', b't', b'm', b'l',
];
let mut enc = QpackEncoder::new();
enc.set_huffman(false); let block = enc.encode_field_section(&[f(b":path", b"/index.html")]);
assert_eq!(block, expected);
let mut dec = QpackDecoder::new();
let out = dec.decode_field_section(expected).unwrap();
assert_eq!(out, vec![f(b":path", b"/index.html")]);
assert_eq!(dec.insert_count(), 0);
assert_eq!(dec.table_size(), 0);
}
#[test]
fn rfc_b2_dynamic_table_inserts_and_post_base() {
let mut dec = QpackDecoder::new();
let mut estream: Vec<u8> = vec![0x3f, 0xbd, 0x01, 0xc0, 0x0f];
estream.extend_from_slice(b"www.example.com");
estream.extend_from_slice(&[0xc1, 0x0c]);
estream.extend_from_slice(b"/sample/path");
dec.feed_encoder_stream(&estream).unwrap();
assert_eq!(dec.table_capacity(), 220);
assert_eq!(dec.insert_count(), 2);
assert_eq!(dec.table_len(), 2);
assert_eq!(dec.table_size(), 106);
let block: &[u8] = &[0x03, 0x81, 0x10, 0x11];
let out = dec.decode_field_section(block).unwrap();
assert_eq!(
out,
vec![
f(b":authority", b"www.example.com"),
f(b":path", b"/sample/path"),
]
);
}
#[test]
fn rfc_b3_speculative_insert_literal_name() {
let mut dec = QpackDecoder::new();
let mut estream: Vec<u8> = vec![0x3f, 0xbd, 0x01, 0xc0, 0x0f];
estream.extend_from_slice(b"www.example.com");
estream.extend_from_slice(&[0xc1, 0x0c]);
estream.extend_from_slice(b"/sample/path");
dec.feed_encoder_stream(&estream).unwrap();
let mut ins: Vec<u8> = vec![0x4a];
ins.extend_from_slice(b"custom-key");
ins.push(0x0c);
ins.extend_from_slice(b"custom-value");
dec.feed_encoder_stream(&ins).unwrap();
assert_eq!(dec.insert_count(), 3);
assert_eq!(dec.table_len(), 3);
assert_eq!(dec.table_size(), 160);
}
#[test]
fn rfc_b4_duplicate_and_dynamic_field_section() {
let mut dec = QpackDecoder::new();
let mut estream: Vec<u8> = vec![0x3f, 0xbd, 0x01, 0xc0, 0x0f];
estream.extend_from_slice(b"www.example.com");
estream.extend_from_slice(&[0xc1, 0x0c]);
estream.extend_from_slice(b"/sample/path");
let mut ins: Vec<u8> = vec![0x4a];
ins.extend_from_slice(b"custom-key");
ins.push(0x0c);
ins.extend_from_slice(b"custom-value");
estream.extend_from_slice(&ins);
dec.feed_encoder_stream(&estream).unwrap();
assert_eq!(dec.insert_count(), 3);
dec.feed_encoder_stream(&[0x02]).unwrap();
assert_eq!(dec.insert_count(), 4);
assert_eq!(dec.table_len(), 4);
assert_eq!(dec.table_size(), 217);
let block: &[u8] = &[0x05, 0x00, 0x80, 0xc1, 0x81];
let out = dec.decode_field_section(block).unwrap();
assert_eq!(
out,
vec![
f(b":authority", b"www.example.com"),
f(b":path", b"/"),
f(b"custom-key", b"custom-value"),
]
);
}
#[test]
fn rfc_b5_dynamic_name_ref_insert_with_eviction() {
let mut dec = QpackDecoder::new();
let mut estream: Vec<u8> = vec![0x3f, 0xbd, 0x01, 0xc0, 0x0f];
estream.extend_from_slice(b"www.example.com");
estream.extend_from_slice(&[0xc1, 0x0c]);
estream.extend_from_slice(b"/sample/path");
let mut ins: Vec<u8> = vec![0x4a];
ins.extend_from_slice(b"custom-key");
ins.push(0x0c);
ins.extend_from_slice(b"custom-value");
estream.extend_from_slice(&ins);
estream.push(0x02); dec.feed_encoder_stream(&estream).unwrap();
assert_eq!(dec.insert_count(), 4);
assert_eq!(dec.table_size(), 217);
let mut b5: Vec<u8> = vec![0x81, 0x0d];
b5.extend_from_slice(b"custom-value2");
dec.feed_encoder_stream(&b5).unwrap();
assert_eq!(dec.insert_count(), 5);
assert_eq!(dec.table_len(), 4); assert_eq!(dec.table_size(), 215);
let block: &[u8] = &[0x06, 0x00, 0x80];
let out = dec.decode_field_section(block).unwrap();
assert_eq!(out, vec![f(b"custom-key", b"custom-value2")]);
}
#[test]
fn encode_indexed_static_full_match() {
let mut enc = QpackEncoder::new();
let block = enc.encode_field_section(&[f(b":path", b"/")]);
assert_eq!(block, &[0x00, 0x00, 0xc1]);
let mut dec = QpackDecoder::new();
assert_eq!(
dec.decode_field_section(&block).unwrap(),
vec![f(b":path", b"/")]
);
}
#[test]
fn round_trip_static_and_literal_huffman() {
let mut enc = QpackEncoder::new(); let mut dec = QpackDecoder::new();
let fields = vec![
f(b":method", b"GET"),
f(b":scheme", b"https"),
f(b":path", b"/index.html"),
f(b":authority", b"www.example.com"),
f(b"custom-key", b"custom-value"),
f(b"accept", b"*/*"),
];
let block = enc.encode_field_section(&fields);
assert_eq!(dec.decode_field_section(&block).unwrap(), fields);
}
#[test]
fn round_trip_many_fields_no_huffman() {
let mut enc = QpackEncoder::new();
enc.set_huffman(false);
let mut dec = QpackDecoder::new();
let fields: Vec<HeaderField> = (0..40)
.map(|i| {
let name = alloc::format!("x-header-{i}");
let val = alloc::format!("value-{}-{}", i, "blahblah".repeat(i % 3));
f(name.as_bytes(), val.as_bytes())
})
.collect();
let block = enc.encode_field_section(&fields);
assert_eq!(dec.decode_field_section(&block).unwrap(), fields);
}
#[test]
fn sensitive_field_sets_never_index_bit() {
let mut enc = QpackEncoder::new();
enc.set_huffman(false);
let mut dec = QpackDecoder::new();
let fields = vec![HeaderField::sensitive(b"authorization", b"secret")];
let block = enc.encode_field_section(&fields);
assert_eq!(block[2] & 0b0010_0000, 0b0010_0000); let out = dec.decode_field_section(&block).unwrap();
assert_eq!(out, fields);
assert!(out[0].sensitive);
let fields2 = vec![HeaderField::sensitive(b"x-secret-hdr", b"v")];
let block2 = enc.encode_field_section(&fields2);
assert_eq!(block2[2] & 0b0001_0000, 0b0001_0000);
let out2 = dec.decode_field_section(&block2).unwrap();
assert!(out2[0].sensitive);
}
#[test]
fn blocked_reference_rejected() {
let mut dec = QpackDecoder::new();
let block: &[u8] = &[0x03, 0x81, 0x10];
assert!(matches!(
dec.decode_field_section(block),
Err(Error::Corrupt)
));
}
#[test]
fn over_limit_capacity_rejected() {
let mut dec = QpackDecoder::with_max_table_capacity(100);
assert!(matches!(
dec.feed_encoder_stream(&[0x3f, 0xbd, 0x01]),
Err(Error::Corrupt)
));
}
#[test]
fn insert_without_capacity_rejected() {
let mut dec = QpackDecoder::new();
let mut ins: Vec<u8> = vec![0xc0, 0x0f];
ins.extend_from_slice(b"www.example.com");
assert!(matches!(dec.feed_encoder_stream(&ins), Err(Error::Corrupt)));
}
#[test]
fn bad_static_index_rejected() {
let mut dec = QpackDecoder::new();
let block: &[u8] = &[0x00, 0x00, 0xff, 0x24];
assert!(matches!(
dec.decode_field_section(block),
Err(Error::Corrupt)
));
}
#[test]
fn truncated_value_string_rejected() {
let mut dec = QpackDecoder::new();
let block: &[u8] = &[0x00, 0x00, 0x21, b'x', 0x05, b'a', b'b'];
assert!(matches!(
dec.decode_field_section(block),
Err(Error::UnexpectedEnd)
));
}
#[test]
fn duplicate_bad_index_rejected() {
let mut dec = QpackDecoder::new();
dec.feed_encoder_stream(&[0x3f, 0xbd, 0x01]).unwrap();
assert!(matches!(
dec.feed_encoder_stream(&[0x00]),
Err(Error::Corrupt)
));
}