use std::collections::VecDeque;
use std::fmt;
use std::sync::OnceLock;
use crate::helpers::fields::{self, HeaderField, Integer, StaticIndex, StringLiteral};
use crate::helpers::huffman;
pub struct StaticTable;
impl StaticTable {
pub fn entries() -> &'static [HeaderField; 61] {
static STATIC_TABLE: OnceLock<[HeaderField; 61]> = OnceLock::new();
STATIC_TABLE.get_or_init(|| {
[
(":authority", ""), (":method", "GET"), (":method", "POST"), (":path", "/"), (":path", "/index.html"), (":scheme", "http"), (":scheme", "https"), (":status", "200"), (":status", "204"), (":status", "206"), (":status", "304"), (":status", "400"), (":status", "404"), (":status", "500"), ("accept-charset", ""), ("accept-encoding", "gzip, deflate"), ("accept-language", ""), ("accept-ranges", ""), ("accept", ""), ("access-control-allow-origin", ""), ("age", ""), ("allow", ""), ("authorization", ""), ("cache-control", ""), ("content-disposition", ""), ("content-encoding", ""), ("content-language", ""), ("content-length", ""), ("content-location", ""), ("content-range", ""), ("content-type", ""), ("cookie", ""), ("date", ""), ("etag", ""), ("expect", ""), ("expires", ""), ("from", ""), ("host", ""), ("if-match", ""), ("if-modified-since", ""), ("if-none-match", ""), ("if-range", ""), ("if-unmodified-since", ""), ("last-modified", ""), ("link", ""), ("location", ""), ("max-forwards", ""), ("proxy-authenticate", ""), ("proxy-authorization", ""), ("range", ""), ("referer", ""), ("refresh", ""), ("retry-after", ""), ("server", ""), ("set-cookie", ""), ("strict-transport-security", ""), ("transfer-encoding", ""), ("user-agent", ""), ("vary", ""), ("via", ""), ("www-authenticate", ""), ]
.map(|(name, value)| HeaderField::new(name, value))
})
}
pub fn index() -> &'static StaticIndex {
static INDEX: OnceLock<StaticIndex> = OnceLock::new();
INDEX.get_or_init(|| StaticIndex::new(StaticTable::entries(), 1))
}
pub fn find(field: &HeaderField) -> Option<(usize, bool)> {
let (named, exact) = StaticTable::index().lookup(&field.name, &field.value);
match (exact, named) {
(Some(index), _) => Some((index, true)),
(None, Some(index)) => Some((index, false)),
(None, None) => None,
}
}
}
pub struct DynamicTable {
entries: VecDeque<HeaderField>,
size: usize,
capacity: usize,
}
impl DynamicTable {
pub const DEFAULT_CAPACITY: usize = 4096;
pub fn new(capacity: usize) -> Self {
Self { entries: VecDeque::new(), size: 0, capacity }
}
pub fn insert(&mut self, field: HeaderField) {
let size = field.size();
while self.size + size > self.capacity {
match self.entries.pop_back() {
Some(evicted) => self.size -= evicted.size(),
None => return,
}
}
self.size += size;
self.entries.push_front(field);
}
pub fn get(&self, index: usize) -> Option<&HeaderField> {
self.entries.get(index)
}
pub fn set_capacity(&mut self, capacity: usize) {
self.capacity = capacity;
while self.size > self.capacity {
match self.entries.pop_back() {
Some(evicted) => self.size -= evicted.size(),
None => break,
}
}
}
pub fn size(&self) -> usize {
self.size
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn find(&self, field: &HeaderField) -> Option<(usize, bool)> {
let mut name_only = None;
for (offset, entry) in self.entries.iter().enumerate() {
if entry.name != field.name {
continue;
}
if entry.value == field.value {
return Some((offset, true));
}
name_only.get_or_insert(offset);
}
name_only.map(|offset| (offset, false))
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
IndexOutOfRange(u64),
IntegerOverflow,
InvalidCapacityUpdate,
Incomplete,
Huffman(huffman::DecodeError),
DecodedSizeExceeded,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IndexOutOfRange(index) => write!(f, "index {index} is out of range"),
Self::IntegerOverflow => write!(f, "integer representation overflowed"),
Self::InvalidCapacityUpdate => write!(f, "dynamic table capacity update exceeds the negotiated limit"),
Self::Incomplete => write!(f, "representation ends before the block does"),
Self::Huffman(err) => write!(f, "huffman error: {err}"),
Self::DecodedSizeExceeded => write!(f, "decoded header list exceeds the permitted size"),
}
}
}
impl std::error::Error for Error {}
impl From<huffman::DecodeError> for Error {
fn from(err: huffman::DecodeError) -> Self {
Self::Huffman(err)
}
}
impl From<fields::Error> for Error {
fn from(err: fields::Error) -> Self {
match err {
fields::Error::IntegerOverflow => Self::IntegerOverflow,
fields::Error::Incomplete => Self::Incomplete,
fields::Error::Huffman(err) => Self::Huffman(err),
}
}
}
pub struct Encoder {
dynamic_table: DynamicTable,
max_capacity: usize,
capacity_limit: usize,
pending_size_update: Option<usize>,
}
impl Encoder {
pub const DEFAULT_CAPACITY_LIMIT: usize = 4096;
pub fn new() -> Self {
Self {
dynamic_table: DynamicTable::new(DynamicTable::DEFAULT_CAPACITY),
max_capacity: DynamicTable::DEFAULT_CAPACITY,
capacity_limit: Self::DEFAULT_CAPACITY_LIMIT,
pending_size_update: None,
}
}
pub fn encode(&mut self, headers: &[HeaderField]) -> Vec<u8> {
let mut out = Vec::with_capacity(headers.len() * 8 + 16);
self.encode_into(&mut out, headers);
out
}
pub fn encode_into(&mut self, out: &mut Vec<u8>, headers: &[HeaderField]) {
out.reserve(headers.len() * 8 + 16);
if let Some(capacity) = self.pending_size_update.take() {
Integer::encode(out, capacity as u64, 5, 0x20);
}
for field in headers {
self.encode_field(out, field);
}
}
pub fn reference(&self, field: &HeaderField) -> Option<(usize, bool)> {
let in_static = StaticTable::find(field);
if let Some((index, true)) = in_static {
return Some((index, true));
}
let base = StaticTable::entries().len() + 1;
let in_dynamic = self.dynamic_table.find(field).map(|(offset, exact)| (base + offset, exact));
if let Some((index, true)) = in_dynamic {
return Some((index, true));
}
in_static.or(in_dynamic)
}
pub fn encode_field(&mut self, out: &mut Vec<u8>, field: &HeaderField) {
let found = self.reference(field);
if let Some((index, true)) = found {
Integer::encode(out, index as u64, 7, 0x80);
return;
}
let index = found.map_or(0, |(index, _)| index as u64);
let sensitive = field.sensitive();
if sensitive {
Integer::encode(out, index, 4, 0x10);
} else {
Integer::encode(out, index, 6, 0x40);
}
if index == 0 {
StringLiteral::encode_shorter(out, field.name.as_bytes(), 7, 0x00);
}
StringLiteral::encode_shorter(out, field.value.as_bytes(), 7, 0x00);
if !sensitive {
self.dynamic_table.insert(field.clone());
}
}
pub fn set_max_capacity(&mut self, max_capacity: usize) {
self.max_capacity = max_capacity;
let capacity = max_capacity.min(self.capacity_limit);
self.dynamic_table.set_capacity(capacity);
self.pending_size_update = Some(capacity);
}
pub fn set_capacity_limit(&mut self, capacity_limit: usize) {
self.capacity_limit = capacity_limit;
if self.dynamic_table.capacity() > capacity_limit {
self.dynamic_table.set_capacity(capacity_limit);
self.pending_size_update = Some(capacity_limit);
}
}
pub fn capacity_limit(&self) -> usize {
self.capacity_limit
}
pub fn max_capacity(&self) -> usize {
self.max_capacity
}
pub fn dynamic_table(&self) -> &DynamicTable {
&self.dynamic_table
}
}
impl Default for Encoder {
fn default() -> Self {
Self::new()
}
}
pub struct Decoder {
dynamic_table: DynamicTable,
max_capacity: usize,
max_decoded_size: usize,
scratch: Vec<u8>,
}
impl Decoder {
pub const DEFAULT_MAX_DECODED_SIZE: usize = 64 * 1024;
pub fn new() -> Self {
Self {
dynamic_table: DynamicTable::new(DynamicTable::DEFAULT_CAPACITY),
max_capacity: DynamicTable::DEFAULT_CAPACITY,
max_decoded_size: Self::DEFAULT_MAX_DECODED_SIZE,
scratch: Vec::new(),
}
}
pub fn decode(&mut self, block: &[u8]) -> Result<Vec<HeaderField>, Error> {
let mut scratch = std::mem::take(&mut self.scratch);
let decoded = self.decode_into(block, &mut scratch);
self.scratch = scratch;
decoded
}
pub fn decode_into(&mut self, block: &[u8], scratch: &mut Vec<u8>) -> Result<Vec<HeaderField>, Error> {
let mut headers = Vec::new();
let mut decoded_size = 0usize;
let mut rest = block;
while let Some(first) = rest.first() {
let (consumed, field) = match first {
_ if first & 0x80 != 0 => {
let (consumed, index) = Integer::decode(rest, 7)?;
(consumed, Some(self.resolve(index)?.clone()))
}
_ if first & 0x40 != 0 => {
let (consumed, field) = self.decode_literal(rest, 6, scratch)?;
self.dynamic_table.insert(field.clone());
(consumed, Some(field))
}
_ if first & 0x20 != 0 => {
let (consumed, capacity) = Integer::decode(rest, 5)?;
if capacity as usize > self.max_capacity {
return Err(Error::InvalidCapacityUpdate);
}
self.dynamic_table.set_capacity(capacity as usize);
(consumed, None)
}
_ => {
let (consumed, field) = self.decode_literal(rest, 4, scratch)?;
(consumed, Some(field))
}
};
if let Some(field) = field {
decoded_size += field.size();
if decoded_size > self.max_decoded_size {
return Err(Error::DecodedSizeExceeded);
}
if headers.is_empty() {
headers.reserve(block.len().min(64));
}
headers.push(field);
}
rest = &rest[consumed..];
}
Ok(headers)
}
pub fn set_max_decoded_size(&mut self, max_size: usize) {
self.max_decoded_size = max_size;
}
pub fn resolve(&self, index: u64) -> Result<&HeaderField, Error> {
if index == 0 {
return Err(Error::IndexOutOfRange(index));
}
let table = StaticTable::entries();
if index <= table.len() as u64 {
return Ok(&table[index as usize - 1]);
}
usize::try_from(index - table.len() as u64 - 1)
.ok()
.and_then(|offset| self.dynamic_table.get(offset))
.ok_or(Error::IndexOutOfRange(index))
}
pub fn decode_literal(&self, input: &[u8], prefix_bits: u8, scratch: &mut Vec<u8>) -> Result<(usize, HeaderField), Error> {
let (mut consumed, index) = Integer::decode(input, prefix_bits)?;
let name = if index == 0 {
let (taken, ascii) = StringLiteral::decode_into_ascii(&input[consumed..], 7, scratch)?;
consumed += taken;
StringLiteral::text(scratch, ascii)
} else {
self.resolve(index)?.name.clone()
};
let (taken, ascii) = StringLiteral::decode_into_ascii(&input[consumed..], 7, scratch)?;
consumed += taken;
Ok((consumed, HeaderField { name, value: StringLiteral::text(scratch, ascii) }))
}
pub fn set_max_capacity(&mut self, max_capacity: usize) {
self.max_capacity = max_capacity;
if self.dynamic_table.capacity() > max_capacity {
self.dynamic_table.set_capacity(max_capacity);
}
}
pub fn dynamic_table(&self) -> &DynamicTable {
&self.dynamic_table
}
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}