use super::decode;
use crate::{
KnownHeaderName, Method, Status,
headers::{
compression_error::CompressionError,
entry_name::{EntryName, PseudoHeaderName},
field_section::{FieldLineValue, PseudoHeaders},
hpack::{HpackDecodeError, dynamic_table::DynamicTable},
},
};
const DEFAULT_PROTOCOL_MAX: usize = 4096;
fn fresh_table() -> DynamicTable {
DynamicTable::new(DEFAULT_PROTOCOL_MAX)
}
#[test]
fn indexed_static_method_get() {
let mut table = fresh_table();
let fs = decode(&[0x82], &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
assert_eq!(fs.pseudo_headers().method(), Some(Method::Get));
}
#[test]
fn indexed_static_scheme_http() {
let mut table = fresh_table();
let fs = decode(&[0x86], &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
assert_eq!(fs.pseudo_headers().scheme(), Some("http"));
}
#[test]
fn indexed_zero_is_error() {
let mut table = fresh_table();
assert!(matches!(
decode(&[0x80], &mut table, DEFAULT_PROTOCOL_MAX),
Err(HpackDecodeError::Compression(
CompressionError::InvalidStaticIndex(0)
))
));
}
#[test]
fn indexed_beyond_dynamic_is_error() {
let mut table = fresh_table();
assert!(matches!(
decode(&[0xBE], &mut table, DEFAULT_PROTOCOL_MAX),
Err(HpackDecodeError::Compression(
CompressionError::InvalidStaticIndex(62)
))
));
}
#[test]
fn literal_without_indexing_static_name_ref() {
let mut bytes = vec![0x01, 15];
bytes.extend_from_slice(b"www.example.com");
let mut table = fresh_table();
let fs = decode(&bytes, &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
assert_eq!(fs.pseudo_headers().authority(), Some("www.example.com"));
assert_eq!(table.len(), 0);
}
#[test]
fn literal_with_indexing_literal_name() {
let mut bytes = vec![0x40, 10];
bytes.extend_from_slice(b"custom-key");
bytes.push(13);
bytes.extend_from_slice(b"custom-header");
let mut table = fresh_table();
let fs = decode(&bytes, &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
let (pseudos, headers) = fs.into_parts();
assert_eq!(pseudos, PseudoHeaders::default());
assert_eq!(
headers.get_str("custom-key").map(ToOwned::to_owned),
Some("custom-header".to_owned())
);
assert_eq!(table.len(), 1);
let entry = table.get(1).unwrap();
assert!(matches!(
entry.name,
EntryName::Unknown(_) | EntryName::Known(_)
));
assert_eq!(entry.value.as_bytes(), b"custom-header");
}
#[test]
fn insert_then_reference_dynamic_entry() {
let mut bytes = vec![0x40, 5];
bytes.extend_from_slice(b"x-foo");
bytes.push(3);
bytes.extend_from_slice(b"bar");
bytes.push(0xBE);
let mut table = fresh_table();
let fs = decode(&bytes, &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
let (_, headers) = fs.into_parts();
let values: Vec<_> = headers
.get_values("x-foo")
.unwrap()
.into_iter()
.map(|v| v.as_ref().to_vec())
.collect();
assert_eq!(values, vec![b"bar".to_vec(), b"bar".to_vec()]);
}
#[test]
fn size_update_shrinks_table() {
let mut table = fresh_table();
let mut seed = vec![0x40, 5];
seed.extend_from_slice(b"x-foo");
seed.push(3);
seed.extend_from_slice(b"bar");
decode(&seed, &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
assert_eq!(table.len(), 1);
let block = [0x20];
decode(&block, &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
assert_eq!(table.len(), 0);
}
#[test]
fn size_update_above_protocol_max_is_error() {
let mut table = fresh_table();
let block = [0x20 | 16]; let result = decode(&block, &mut table, 15);
assert!(matches!(
result,
Err(HpackDecodeError::Compression(
CompressionError::InvalidStaticIndex(16)
))
));
}
#[test]
fn size_update_after_field_is_error() {
let mut table = fresh_table();
let block = [0x82, 0x20];
assert!(matches!(
decode(&block, &mut table, DEFAULT_PROTOCOL_MAX),
Err(HpackDecodeError::Compression(
CompressionError::UnexpectedEnd
))
));
}
#[test]
fn indexed_static_status_ok() {
let mut table = fresh_table();
let fs = decode(&[0x88], &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
assert_eq!(fs.pseudo_headers().status(), Some(Status::Ok));
}
#[test]
fn literal_overrides_static_slot_value() {
let mut bytes = vec![0x1F, 0x01];
bytes.push(2);
bytes.extend_from_slice(b"br");
let mut table = fresh_table();
let fs = decode(&bytes, &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
let (_, headers) = fs.into_parts();
assert_eq!(
headers
.get(KnownHeaderName::AcceptEncoding)
.map(|v| v.as_ref().to_vec()),
Some(b"br".to_vec())
);
assert_eq!(table.len(), 0);
}
#[test]
fn duplicate_pseudo_is_malformed() {
let mut table = fresh_table();
assert!(matches!(
decode(&[0x82, 0x83], &mut table, DEFAULT_PROTOCOL_MAX),
Err(HpackDecodeError::MalformedRequest(
super::MalformedRequest::DuplicatePseudoHeader
))
));
}
#[test]
fn pseudo_after_regular_is_malformed() {
let mut table = fresh_table();
let mut bytes = vec![0x82, 0x00, 0x03];
bytes.extend_from_slice(b"foo");
bytes.push(0x03);
bytes.extend_from_slice(b"bar");
bytes.push(0x85);
assert!(matches!(
decode(&bytes, &mut table, DEFAULT_PROTOCOL_MAX),
Err(HpackDecodeError::MalformedRequest(
super::MalformedRequest::PseudoHeaderAfterRegular
))
));
}
#[test]
fn pseudo_authority_surfaces() {
let _ = PseudoHeaderName::Authority; let mut table = fresh_table();
let mut bytes = vec![0x01, 11]; bytes.extend_from_slice(b"example.com");
let fs = decode(&bytes, &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
assert_eq!(fs.pseudo_headers().authority(), Some("example.com"));
}
#[test]
fn decoded_values_are_owned() {
let mut table = fresh_table();
let mut bytes = vec![0x40, 3];
bytes.extend_from_slice(b"foo");
bytes.push(3);
bytes.extend_from_slice(b"bar");
decode(&bytes, &mut table, DEFAULT_PROTOCOL_MAX).unwrap();
let entry = table.get(1).unwrap();
assert!(matches!(entry.value, FieldLineValue::Owned(_)));
}