extern crate alloc;
use alloc::collections::VecDeque;
use alloc::vec::Vec;
#[rustfmt::skip]
pub(crate) const STATIC_TABLE: [(&[u8], &[u8]); 61] = [
(b":authority", b""),
(b":method", b"GET"),
(b":method", b"POST"),
(b":path", b"/"),
(b":path", b"/index.html"),
(b":scheme", b"http"),
(b":scheme", b"https"),
(b":status", b"200"),
(b":status", b"204"),
(b":status", b"206"),
(b":status", b"304"),
(b":status", b"400"),
(b":status", b"404"),
(b":status", b"500"),
(b"accept-charset", b""),
(b"accept-encoding", b"gzip, deflate"),
(b"accept-language", b""),
(b"accept-ranges", b""),
(b"accept", b""),
(b"access-control-allow-origin", b""),
(b"age", b""),
(b"allow", b""),
(b"authorization", b""),
(b"cache-control", b""),
(b"content-disposition", b""),
(b"content-encoding", b""),
(b"content-language", b""),
(b"content-length", b""),
(b"content-location", b""),
(b"content-range", b""),
(b"content-type", b""),
(b"cookie", b""),
(b"date", b""),
(b"etag", b""),
(b"expect", b""),
(b"expires", b""),
(b"from", b""),
(b"host", b""),
(b"if-match", b""),
(b"if-modified-since", b""),
(b"if-none-match", b""),
(b"if-range", b""),
(b"if-unmodified-since", b""),
(b"last-modified", b""),
(b"link", b""),
(b"location", b""),
(b"max-forwards", b""),
(b"proxy-authenticate", b""),
(b"proxy-authorization", b""),
(b"range", b""),
(b"referer", b""),
(b"refresh", b""),
(b"retry-after", b""),
(b"server", b""),
(b"set-cookie", b""),
(b"strict-transport-security", b""),
(b"transfer-encoding", b""),
(b"user-agent", b""),
(b"vary", b""),
(b"via", b""),
(b"www-authenticate", b""),
];
pub(crate) const STATIC_LEN: usize = STATIC_TABLE.len();
const ENTRY_OVERHEAD: usize = 32;
pub(crate) struct Match {
pub index: usize,
pub value_matched: bool,
}
#[derive(Debug)]
pub(crate) struct DynamicTable {
entries: VecDeque<(Vec<u8>, Vec<u8>)>,
size: usize,
max_size: usize,
}
impl DynamicTable {
pub fn new(max_size: usize) -> Self {
DynamicTable {
entries: VecDeque::new(),
size: 0,
max_size,
}
}
#[cfg(test)]
pub fn max_size(&self) -> usize {
self.max_size
}
pub fn set_max_size(&mut self, new_max: usize) {
self.max_size = new_max;
self.evict_to_fit(0);
}
fn entry_size(name: &[u8], value: &[u8]) -> usize {
name.len() + value.len() + ENTRY_OVERHEAD
}
fn evict_to_fit(&mut self, incoming: usize) {
while self.size + incoming > self.max_size {
match self.entries.pop_back() {
Some((n, v)) => self.size -= Self::entry_size(&n, &v),
None => break,
}
}
}
pub fn insert(&mut self, name: &[u8], value: &[u8]) {
let need = Self::entry_size(name, value);
self.evict_to_fit(need);
if need <= self.max_size {
self.entries.push_front((name.to_vec(), value.to_vec()));
self.size += need;
}
}
pub fn get(&self, index: usize) -> Option<(&[u8], &[u8])> {
if index == 0 {
return None;
}
if index <= STATIC_LEN {
let (n, v) = STATIC_TABLE[index - 1];
return Some((n, v));
}
let dyn_pos = index - STATIC_LEN - 1;
self.entries
.get(dyn_pos)
.map(|(n, v)| (n.as_slice(), v.as_slice()))
}
pub fn find(&self, name: &[u8], value: &[u8]) -> Option<Match> {
let mut name_only: Option<usize> = None;
for (i, (n, v)) in STATIC_TABLE.iter().enumerate() {
if *n == name {
if *v == value {
return Some(Match {
index: i + 1,
value_matched: true,
});
}
name_only.get_or_insert(i + 1);
}
}
for (pos, (n, v)) in self.entries.iter().enumerate() {
if n.as_slice() == name {
let index = STATIC_LEN + 1 + pos;
if v.as_slice() == value {
return Some(Match {
index,
value_matched: true,
});
}
name_only.get_or_insert(index);
}
}
name_only.map(|index| Match {
index,
value_matched: false,
})
}
}