#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeaderField {
pub name: Vec<u8>,
pub value: Vec<u8>,
pub sensitive: bool,
}
impl HeaderField {
#[must_use]
pub fn new(name: Vec<u8>, value: Vec<u8>) -> Self {
Self {
name,
value,
sensitive: false,
}
}
#[must_use]
pub fn new_sensitive(name: Vec<u8>, value: Vec<u8>, sensitive: bool) -> Self {
Self {
name,
value,
sensitive,
}
}
#[must_use]
pub fn from_str(name: &str, value: &str) -> Self {
Self {
name: name.as_bytes().to_vec(),
value: value.as_bytes().to_vec(),
sensitive: false,
}
}
#[must_use]
pub fn sensitive(name: &str, value: &str) -> Self {
Self {
name: name.as_bytes().to_vec(),
value: value.as_bytes().to_vec(),
sensitive: true,
}
}
#[must_use]
pub fn size(&self) -> usize {
self.name.len() + self.value.len() + 32
}
}
#[derive(Debug, Clone, Copy)]
pub struct StaticEntry {
pub name: &'static [u8],
pub value: &'static [u8],
}
impl StaticEntry {
#[must_use]
pub fn to_header_field(&self) -> HeaderField {
HeaderField {
name: self.name.to_vec(),
value: self.value.to_vec(),
sensitive: false,
}
}
}
pub static STATIC_TABLE: [StaticEntry; 62] = [
StaticEntry {
name: b"",
value: b"",
},
StaticEntry {
name: b":authority",
value: b"",
},
StaticEntry {
name: b":method",
value: b"GET",
},
StaticEntry {
name: b":method",
value: b"POST",
},
StaticEntry {
name: b":path",
value: b"/",
},
StaticEntry {
name: b":path",
value: b"/index.html",
},
StaticEntry {
name: b":scheme",
value: b"http",
},
StaticEntry {
name: b":scheme",
value: b"https",
},
StaticEntry {
name: b":status",
value: b"200",
},
StaticEntry {
name: b":status",
value: b"204",
},
StaticEntry {
name: b":status",
value: b"206",
},
StaticEntry {
name: b":status",
value: b"304",
},
StaticEntry {
name: b":status",
value: b"400",
},
StaticEntry {
name: b":status",
value: b"404",
},
StaticEntry {
name: b":status",
value: b"500",
},
StaticEntry {
name: b"accept-charset",
value: b"",
},
StaticEntry {
name: b"accept-encoding",
value: b"gzip, deflate",
},
StaticEntry {
name: b"accept-language",
value: b"",
},
StaticEntry {
name: b"accept-ranges",
value: b"",
},
StaticEntry {
name: b"accept",
value: b"",
},
StaticEntry {
name: b"access-control-allow-origin",
value: b"",
},
StaticEntry {
name: b"age",
value: b"",
},
StaticEntry {
name: b"allow",
value: b"",
},
StaticEntry {
name: b"authorization",
value: b"",
},
StaticEntry {
name: b"cache-control",
value: b"",
},
StaticEntry {
name: b"content-disposition",
value: b"",
},
StaticEntry {
name: b"content-encoding",
value: b"",
},
StaticEntry {
name: b"content-language",
value: b"",
},
StaticEntry {
name: b"content-length",
value: b"",
},
StaticEntry {
name: b"content-location",
value: b"",
},
StaticEntry {
name: b"content-range",
value: b"",
},
StaticEntry {
name: b"content-type",
value: b"",
},
StaticEntry {
name: b"cookie",
value: b"",
},
StaticEntry {
name: b"date",
value: b"",
},
StaticEntry {
name: b"etag",
value: b"",
},
StaticEntry {
name: b"expect",
value: b"",
},
StaticEntry {
name: b"expires",
value: b"",
},
StaticEntry {
name: b"from",
value: b"",
},
StaticEntry {
name: b"host",
value: b"",
},
StaticEntry {
name: b"if-match",
value: b"",
},
StaticEntry {
name: b"if-modified-since",
value: b"",
},
StaticEntry {
name: b"if-none-match",
value: b"",
},
StaticEntry {
name: b"if-range",
value: b"",
},
StaticEntry {
name: b"if-unmodified-since",
value: b"",
},
StaticEntry {
name: b"last-modified",
value: b"",
},
StaticEntry {
name: b"link",
value: b"",
},
StaticEntry {
name: b"location",
value: b"",
},
StaticEntry {
name: b"max-forwards",
value: b"",
},
StaticEntry {
name: b"proxy-authenticate",
value: b"",
},
StaticEntry {
name: b"proxy-authorization",
value: b"",
},
StaticEntry {
name: b"range",
value: b"",
},
StaticEntry {
name: b"referer",
value: b"",
},
StaticEntry {
name: b"refresh",
value: b"",
},
StaticEntry {
name: b"retry-after",
value: b"",
},
StaticEntry {
name: b"server",
value: b"",
},
StaticEntry {
name: b"set-cookie",
value: b"",
},
StaticEntry {
name: b"strict-transport-security",
value: b"",
},
StaticEntry {
name: b"transfer-encoding",
value: b"",
},
StaticEntry {
name: b"user-agent",
value: b"",
},
StaticEntry {
name: b"vary",
value: b"",
},
StaticEntry {
name: b"via",
value: b"",
},
StaticEntry {
name: b"www-authenticate",
value: b"",
},
];
pub const STATIC_TABLE_SIZE: usize = 61;
#[must_use]
pub fn get_static_entry(index: usize) -> Option<&'static StaticEntry> {
if (1..=STATIC_TABLE_SIZE).contains(&index) {
Some(&STATIC_TABLE[index])
} else {
None
}
}
#[must_use]
pub fn find_static_index(name: &[u8], value: &[u8]) -> Option<(usize, bool)> {
let mut name_match = None;
for (i, entry) in STATIC_TABLE.iter().enumerate().skip(1) {
if entry.name == name {
if entry.value == value {
return Some((i, true));
}
if name_match.is_none() {
name_match = Some(i);
}
}
}
name_match.map(|i| (i, false))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_static_table_size() {
assert_eq!(STATIC_TABLE.len(), 62); }
#[test]
fn test_get_static_entry() {
let entry = get_static_entry(1).unwrap();
assert_eq!(entry.name, b":authority");
assert_eq!(entry.value, b"");
let entry = get_static_entry(2).unwrap();
assert_eq!(entry.name, b":method");
assert_eq!(entry.value, b"GET");
let entry = get_static_entry(61).unwrap();
assert_eq!(entry.name, b"www-authenticate");
assert_eq!(entry.value, b"");
assert!(get_static_entry(0).is_none());
assert!(get_static_entry(62).is_none());
}
#[test]
fn test_find_static_index() {
let result = find_static_index(b":method", b"GET");
assert_eq!(result, Some((2, true)));
let result = find_static_index(b":method", b"PUT");
assert_eq!(result, Some((2, false)));
let result = find_static_index(b"x-custom-header", b"value");
assert_eq!(result, None);
}
#[test]
fn test_header_field_size() {
let field = HeaderField::from_str("content-type", "application/json");
assert_eq!(field.size(), 60);
}
}