use std::collections::VecDeque;
use crate::bytes_eq;
pub static STATIC_TABLE: [(&[u8], &[u8]); 61] = STATIC_ENTRIES;
const STATIC_ENTRIES: [(&[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""),
];
const MAX_NAME: usize = 27;
static NAMES: ([(u8, u8); 61], [u8; MAX_NAME + 2]) = names_by_len();
const fn same(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut i = 0;
while i < a.len() {
if a[i] != b[i] {
return false;
}
i += 1;
}
true
}
const fn names_by_len() -> ([(u8, u8); 61], [u8; MAX_NAME + 2]) {
let t = &STATIC_ENTRIES;
let mut names = [(0u8, 0u8); 61];
let mut starts = [0u8; MAX_NAME + 2];
let mut k = 0;
let mut len = 0;
while len <= MAX_NAME {
starts[len] = k as u8;
let mut i = 0;
while i < t.len() {
assert!(t[i].0.len() <= MAX_NAME);
if t[i].0.len() == len && (i == 0 || !same(t[i - 1].0, t[i].0)) {
let mut n = 1;
while i + n < t.len() && same(t[i + n].0, t[i].0) {
n += 1;
}
names[k] = (i as u8 + 1, n as u8);
k += 1;
}
i += 1;
}
len += 1;
}
starts[MAX_NAME + 1] = k as u8;
(names, starts)
}
pub(crate) fn static_name(name: &[u8]) -> Option<(usize, usize)> {
let n = name.len();
if n > MAX_NAME {
return None;
}
let (names, starts) = &NAMES;
for &(first, count) in &names[starts[n] as usize..starts[n + 1] as usize] {
if bytes_eq(STATIC_TABLE[first as usize - 1].0, name) {
return Some((first as usize, count as usize));
}
}
None
}
#[derive(Clone, Copy, Debug)]
struct Entry {
off: u32,
name: u32,
value: u32,
}
pub(crate) enum Name<'a> {
Bytes(&'a [u8]),
Dyn(usize),
}
#[derive(Debug, Clone)]
pub(crate) struct DynTable {
buf: Vec<u8>,
entries: VecDeque<Entry>, size: usize,
max: usize,
}
impl DynTable {
pub(crate) fn new(max: usize) -> Self {
DynTable { buf: Vec::new(), entries: VecDeque::new(), size: 0, max }
}
pub(crate) fn size(&self) -> usize {
self.size
}
pub(crate) fn max(&self) -> usize {
self.max
}
pub(crate) fn len(&self) -> usize {
self.entries.len()
}
#[inline]
pub(crate) fn get(&self, i: usize) -> Option<(&[u8], &[u8])> {
let e = self.entries.get(i)?;
let (o, n, v) = (e.off as usize, e.name as usize, e.value as usize);
Some((&self.buf[o..o + n], &self.buf[o + n..o + n + v]))
}
pub(crate) fn clear(&mut self) {
self.entries.clear();
self.size = 0;
self.buf.clear();
}
pub(crate) fn set_max(&mut self, max: usize) {
self.max = max;
self.evict(0);
if self.entries.is_empty() {
self.buf.clear();
}
}
fn evict(&mut self, incoming: usize) {
while self.size + incoming > self.max {
match self.entries.pop_back() {
Some(e) => self.size -= e.name as usize + e.value as usize + 32,
None => break,
}
}
}
pub(crate) fn insert(&mut self, name: Name<'_>, value: &[u8]) {
let nlen = match name {
Name::Bytes(b) => b.len(),
Name::Dyn(i) => self.entries[i].name as usize,
};
let s = nlen + value.len() + 32;
if s > self.max {
self.entries.clear();
self.size = 0;
self.buf.clear();
return;
}
let off = self.buf.len();
match name {
Name::Bytes(b) => self.buf.extend_from_slice(b),
Name::Dyn(i) => {
let o = self.entries[i].off as usize;
self.buf.extend_from_within(o..o + nlen);
}
}
self.buf.extend_from_slice(value);
self.evict(s);
self.entries.push_front(Entry { off: off as u32, name: nlen as u32, value: value.len() as u32 });
self.size += s;
let start = self.entries.back().map_or(0, |e| e.off as usize);
if start > 1024 && start * 2 > self.buf.len() {
self.buf.drain(..start);
for e in self.entries.iter_mut() {
e.off -= start as u32;
}
}
}
#[inline]
pub(crate) fn find(&self, name: &[u8], value: &[u8]) -> (Option<usize>, Option<usize>) {
let mut name_idx = None;
for (i, e) in self.entries.iter().enumerate() {
if e.name as usize != name.len() {
continue;
}
let o = e.off as usize;
if !bytes_eq(&self.buf[o..o + name.len()], name) {
continue;
}
if e.value as usize == value.len() && bytes_eq(&self.buf[o + name.len()..o + name.len() + value.len()], value) {
return (Some(i), Some(i));
}
name_idx.get_or_insert(i);
}
(None, name_idx)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn static_lookup_matches_table() {
for (i, (n, _)) in STATIC_TABLE.iter().enumerate() {
let (first, count) = static_name(n).unwrap();
assert!(first <= i + 1 && i + 1 < first + count, "{}", String::from_utf8_lossy(n));
assert_eq!(STATIC_TABLE[first - 1].0, *n);
}
assert!(static_name(b"x-unknown").is_none());
assert!(static_name(b":protocol").is_none());
}
#[test]
fn eviction_and_compaction() {
let mut t = DynTable::new(200);
for i in 0..1000u32 {
let v = format!("value-{i}");
if i % 3 == 0 && t.len() > 0 {
t.insert(Name::Dyn(t.len() - 1), v.as_bytes()); } else {
t.insert(Name::Bytes(b"name"), v.as_bytes());
}
assert!(t.size() <= 200);
assert_eq!(t.get(0).unwrap(), (&b"name"[..], v.as_bytes()));
assert!(t.buf.len() <= 2 * 200 + 2048);
}
let mut t = DynTable::new(60);
t.insert(Name::Bytes(b"abcdefgh"), b"1");
t.insert(Name::Dyn(0), b"2");
assert_eq!(t.len(), 1);
assert_eq!(t.get(0).unwrap(), (&b"abcdefgh"[..], &b"2"[..]));
t.insert(Name::Bytes(&[b'x'; 300]), b"");
assert_eq!(t.len(), 0);
assert_eq!(t.size(), 0);
t.insert(Name::Bytes(b"a"), b"b");
t.set_max(0);
assert_eq!(t.len(), 0);
}
}