use super::Error;
use super::Result;
use crate::h3::Header;
use super::INDEXED;
use super::INDEXED_WITH_POST_BASE;
use super::LITERAL;
use super::LITERAL_WITH_NAME_REF;
#[derive(Clone, Copy, Debug, PartialEq)]
enum Representation {
Indexed,
IndexedWithPostBase,
Literal,
LiteralWithNameRef,
LiteralWithPostBase,
}
impl Representation {
pub fn from_byte(b: u8) -> Representation {
if b & INDEXED == INDEXED {
return Representation::Indexed;
}
if b & LITERAL_WITH_NAME_REF == LITERAL_WITH_NAME_REF {
return Representation::LiteralWithNameRef;
}
if b & LITERAL == LITERAL {
return Representation::Literal;
}
if b & INDEXED_WITH_POST_BASE == INDEXED_WITH_POST_BASE {
return Representation::IndexedWithPostBase;
}
Representation::LiteralWithPostBase
}
}
struct FieldListSizeTracker {
remaining: u64,
}
impl FieldListSizeTracker {
fn new(max_size: u64) -> Self {
Self {
remaining: max_size,
}
}
fn on_field_start(&mut self) -> Result<()> {
self.remaining = self
.remaining
.checked_sub(32)
.ok_or(Error::HeaderListTooLarge)?;
Ok(())
}
fn on_field_part_decoded(&mut self, len: u64) -> Result<()> {
self.remaining = self
.remaining
.checked_sub(len)
.ok_or(Error::HeaderListTooLarge)?;
Ok(())
}
fn left(&self) -> u64 {
self.remaining
}
}
#[derive(Default)]
pub struct Decoder {}
impl Decoder {
pub fn new() -> Decoder {
Decoder::default()
}
pub fn control(&mut self, _buf: &mut [u8]) -> Result<()> {
Ok(())
}
pub fn decode(&mut self, buf: &[u8], max_size: u64) -> Result<Vec<Header>> {
let mut b = octets::Octets::with_slice(buf);
let mut out = Vec::new();
let mut size_tracker = FieldListSizeTracker::new(max_size);
let req_insert_count = decode_int(&mut b, 8)?;
let base = decode_int(&mut b, 7)?;
trace!("Header count={req_insert_count} base={base}");
while b.cap() > 0 {
let first = b.peek_u8()?;
size_tracker.on_field_start()?;
match Representation::from_byte(first) {
Representation::Indexed => {
const STATIC: u8 = 0x40;
let s = first & STATIC == STATIC;
let index = decode_int(&mut b, 6)?;
trace!("Indexed index={index} static={s}");
if !s {
return Err(Error::InvalidHeaderValue);
}
let (name, value) = lookup_static(index)?;
size_tracker.on_field_part_decoded(
(name.len() + value.len()) as u64,
)?;
let hdr = Header::new(name, value);
out.push(hdr);
},
Representation::IndexedWithPostBase => {
let index = decode_int(&mut b, 4)?;
trace!("Indexed With Post Base index={index}");
return Err(Error::InvalidHeaderValue);
},
Representation::Literal => {
let name_huff = b.as_ref()[0] & 0x08 == 0x08;
let name_len = decode_int(&mut b, 3)? as usize;
let mut name = b.get_bytes(name_len)?;
let name = if name_huff {
name.get_huffman_decoded_with_max_length(
size_tracker.left() as usize,
)
.map_err(|_| Error::HeaderListTooLarge)?
} else {
if name_len > size_tracker.left() as usize {
return Err(Error::HeaderListTooLarge);
}
name.to_vec()
};
size_tracker.on_field_part_decoded(name.len() as u64)?;
let value = decode_str(&mut b, size_tracker.left() as usize)?;
trace!(
"Literal Without Name Reference name={name:?} value={value:?}",
);
size_tracker.on_field_part_decoded(value.len() as u64)?;
let hdr = Header(name, value);
out.push(hdr);
},
Representation::LiteralWithNameRef => {
const STATIC: u8 = 0x10;
let s = first & STATIC == STATIC;
if !s {
return Err(Error::InvalidHeaderValue);
}
let name_idx = decode_int(&mut b, 4)?;
let (name, _) = lookup_static(name_idx)?;
size_tracker.on_field_part_decoded(name.len() as u64)?;
let value = decode_str(&mut b, size_tracker.left() as usize)?;
trace!(
"Literal name_idx={name_idx} static={s} value={value:?}"
);
size_tracker.on_field_part_decoded(value.len() as u64)?;
let hdr = Header(name.to_vec(), value);
out.push(hdr);
},
Representation::LiteralWithPostBase => {
trace!("Literal With Post Base");
return Err(Error::InvalidHeaderValue);
},
}
}
Ok(out)
}
}
fn lookup_static(idx: u64) -> Result<(&'static [u8], &'static [u8])> {
if idx >= super::static_table::STATIC_DECODE_TABLE.len() as u64 {
return Err(Error::InvalidStaticTableIndex);
}
Ok(super::static_table::STATIC_DECODE_TABLE[idx as usize])
}
fn decode_int(b: &mut octets::Octets, prefix: usize) -> Result<u64> {
let mask = 2u64.pow(prefix as u32) - 1;
let mut val = u64::from(b.get_u8()?);
val &= mask;
if val < mask {
return Ok(val);
}
let mut shift = 0;
while b.cap() > 0 {
let byte = b.get_u8()?;
let inc = u64::from(byte & 0x7f)
.checked_shl(shift)
.ok_or(Error::BufferTooShort)?;
val = val.checked_add(inc).ok_or(Error::BufferTooShort)?;
shift += 7;
if byte & 0x80 == 0 {
return Ok(val);
}
}
Err(Error::BufferTooShort)
}
fn decode_str(b: &mut octets::Octets, max_len: usize) -> Result<Vec<u8>> {
let first = b.peek_u8()?;
let huff = first & 0x80 == 0x80;
let len = decode_int(b, 7)? as usize;
let mut val = b.get_bytes(len)?;
let val = if huff {
val.get_huffman_decoded_with_max_length(max_len)
.map_err(|_| Error::HeaderListTooLarge)?
} else {
if len > max_len {
return Err(Error::HeaderListTooLarge);
}
val.to_vec()
};
Ok(val)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_int1() {
let encoded = [0b01010, 0x02];
let mut b = octets::Octets::with_slice(&encoded);
assert_eq!(decode_int(&mut b, 5), Ok(10));
}
#[test]
fn decode_int2() {
let encoded = [0b11111, 0b10011010, 0b00001010];
let mut b = octets::Octets::with_slice(&encoded);
assert_eq!(decode_int(&mut b, 5), Ok(1337));
}
#[test]
fn decode_int3() {
let encoded = [0b101010];
let mut b = octets::Octets::with_slice(&encoded);
assert_eq!(decode_int(&mut b, 8), Ok(42));
}
#[test]
fn literal_with_name_ref_dynamic_table_rejected() {
let mut dec = Decoder::new();
let encoded = [0x00, 0x00, 0x40, 0x01, 0x61];
assert_eq!(
dec.decode(&encoded, u64::MAX),
Err(super::super::Error::InvalidHeaderValue),
);
}
#[test]
fn non_huffman_value_exceeding_budget_rejected() {
let encoded = [0x00, 0x00, 0x50, 0x05, 0x61, 0x62, 0x63, 0x64, 0x65];
assert!(Decoder::new().decode(&encoded, 47).is_ok());
assert_eq!(
Decoder::new().decode(&encoded, 46),
Err(super::super::Error::HeaderListTooLarge),
);
}
#[test]
fn non_huffman_name_exceeding_budget_rejected() {
let encoded = [
0x00, 0x00, 0x27, 0x03, 0x78, 0x2d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x39,
0x39, 0x01, 0x61, ];
assert!(Decoder::new().decode(&encoded, 43).is_ok());
assert_eq!(
Decoder::new().decode(&encoded, 41),
Err(super::super::Error::HeaderListTooLarge),
);
}
#[test]
fn literal_with_name_ref_value_budget_ordering() {
use crate::h3::qpack;
let value = b"aaaaaaaaaaaaaaaa";
let headers_nameref = vec![crate::h3::Header::new(b":authority", value)];
let mut buf = [0u8; 64];
let mut enc = qpack::Encoder::new();
let nameref_len = enc.encode(&headers_nameref, &mut buf).unwrap();
let encoded_nameref = buf[..nameref_len].to_vec();
let headers_literal = vec![crate::h3::Header::new(b"x-custom99", value)];
let mut buf = [0u8; 64];
let mut enc = qpack::Encoder::new();
let literal_len = enc.encode(&headers_literal, &mut buf).unwrap();
let encoded_literal = buf[..literal_len].to_vec();
assert_eq!(
Decoder::new().decode(&encoded_nameref, 58),
Ok(headers_nameref.clone()),
);
assert_eq!(
Decoder::new().decode(&encoded_literal, 58),
Ok(headers_literal.clone()),
);
assert_eq!(
Decoder::new().decode(&encoded_nameref, 57),
Err(super::super::Error::HeaderListTooLarge),
);
assert_eq!(
Decoder::new().decode(&encoded_literal, 57),
Err(super::super::Error::HeaderListTooLarge),
);
}
}