pub(in crate::headers) mod decoder;
pub(in crate::headers) mod encoder;
pub(in crate::headers) mod field_section;
use crate::headers::{huffman, integer_prefix};
use futures_lite::io::{AsyncRead, AsyncReadExt};
const STRING_HUFFMAN_FLAG: u8 = 0x80;
pub(super) async fn read_first_byte(
stream: &mut (impl AsyncRead + Unpin),
) -> Result<Option<u8>, ()> {
let mut b = [0u8; 1];
match stream.read(&mut b).await {
Ok(0) => Ok(None),
Ok(_) => Ok(Some(b[0])),
Err(e) => {
log::debug!("QPACK: read_first_byte io error: {e}");
Err(())
}
}
}
async fn read_byte(stream: &mut (impl AsyncRead + Unpin)) -> Result<u8, ()> {
let mut b = [0u8; 1];
stream.read_exact(&mut b).await.map_err(|e| {
log::error!("QPACK: read_byte io error: {e:?}");
})?;
Ok(b[0])
}
pub(super) async fn read_varint(
first: u8,
prefix_size: u8,
stream: &mut (impl AsyncRead + Unpin),
) -> Result<usize, ()> {
let prefix_mask = u8::MAX >> (8 - prefix_size);
let mut value = usize::from(first & prefix_mask);
let mut shift = 0u32;
if value < usize::from(prefix_mask) {
return Ok(value);
}
loop {
let byte = read_byte(stream).await?;
let payload = usize::from(byte & 0x7F);
let increment = payload.checked_shl(shift).ok_or_else(|| {
log::error!("QPACK: varint checked_shl overflow (payload={payload}, shift={shift})");
})?;
value = value.checked_add(increment).ok_or_else(|| {
log::error!(
"QPACK: varint checked_add overflow (value={value}, increment={increment})"
);
})?;
shift += 7;
if byte & 0x80 == 0 {
return Ok(value);
}
}
}
pub(super) async fn read_exact(
len: usize,
max: usize,
stream: &mut (impl AsyncRead + Unpin),
) -> Result<Vec<u8>, ()> {
if len > max {
log::error!("QPACK: read_exact len {len} exceeds max {max}");
return Err(());
}
let mut buf = vec![0u8; len];
stream.read_exact(&mut buf).await.map_err(|e| {
log::error!("QPACK: read_exact({len}) io error: {e:?}");
})?;
Ok(buf)
}
pub(super) async fn read_string_with_huffman(
max: usize,
stream: &mut (impl AsyncRead + Unpin),
) -> Result<Vec<u8>, ()> {
let first = read_byte(stream).await?;
let is_huffman = first & STRING_HUFFMAN_FLAG != 0;
let len = read_varint(first, 7, stream).await?;
let raw = read_exact(len, max, stream).await?;
if is_huffman {
huffman::decode(&raw).map_err(|e| {
log::error!("QPACK: huffman string decode failed ({len} bytes): {e:?}");
})
} else {
Ok(raw)
}
}
pub(super) fn validate_value(value: &[u8]) -> Result<(), ()> {
if memchr::memchr3(b'\r', b'\n', 0, value).is_some() {
Err(())
} else {
Ok(())
}
}
pub(in crate::headers) fn encode_string(value: &[u8], prefix_size: u8, buf: &mut Vec<u8>) {
let start = buf.len();
if let Some(huffman_len) = huffman::encoded_length_if_shorter(value) {
integer_prefix::encode_into(huffman_len, prefix_size, buf);
buf[start] |= 1_u8 << prefix_size;
huffman::encode_into(value, buf);
} else {
integer_prefix::encode_into(value.len(), prefix_size, buf);
buf.extend_from_slice(value);
}
}