diff options
author | Tyler Murphy <tylerm@tylerm.dev> | 2023-07-05 21:34:20 -0400 |
---|---|---|
committer | Tyler Murphy <tylerm@tylerm.dev> | 2023-07-05 21:34:20 -0400 |
commit | 168b8937eb0fe88311fe474ab9569691a19d087f (patch) | |
tree | 3e70d7de89adc9e62987cea9341ba2cc10d6cf25 /src/header.rs | |
download | http-168b8937eb0fe88311fe474ab9569691a19d087f.tar.gz http-168b8937eb0fe88311fe474ab9569691a19d087f.tar.bz2 http-168b8937eb0fe88311fe474ab9569691a19d087f.zip |
changes
Diffstat (limited to 'src/header.rs')
-rw-r--r-- | src/header.rs | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/src/header.rs b/src/header.rs new file mode 100644 index 0000000..74786e6 --- /dev/null +++ b/src/header.rs @@ -0,0 +1,174 @@ +use std::{string::ToString, cmp::{PartialEq, Eq}, hash::{Hash, Hasher}}; +use multimap::{MultiMap, IterAll}; +use crate::{error::HTTPError, parse::{TryParse, Parse}}; + +#[derive(Debug, Clone, Eq)] +pub struct HeaderName { + inner: String +} + +impl HeaderName { + pub fn as_str(&self) -> &str { + self.inner.as_ref() + } +} + +impl TryParse for HeaderName { + fn try_parse(s: impl Into<String>) -> Result<Self, HTTPError> { + let name = s.into(); + if name.len() < 1 { + return Err(HTTPError::InvalidHeaderName(name)) + } else { + Ok(Self { inner: name }) + } + } + +} + +impl ToString for HeaderName { + fn to_string(&self) -> String { + self.inner.clone() + } +} + +impl PartialEq for HeaderName { + fn eq(&self, other: &Self) -> bool { + self.inner.eq_ignore_ascii_case(&other.inner) + } +} + +impl Hash for HeaderName { + fn hash<H: Hasher>(&self, state: &mut H) { + let mut hash: i32 = 0; + for char in self.inner.chars() { + let byte = char.to_ascii_lowercase() as u8; + hash = hash ^ ((byte as i32) << 0); + hash = hash ^ ((byte as i32) << 8); + hash = hash ^ ((byte as i32) << 16); + hash = hash ^ ((byte as i32) << 24); + hash = hash % 16777213; + } + state.write_i32(hash); + } +} + +#[derive(Debug, Clone)] +pub struct HeaderValue { + inner: String +} + +impl HeaderValue { + pub fn as_str(&self) -> &str { + self.inner.as_ref() + } +} + +impl Parse for HeaderValue { + fn parse(value: impl Into<String>) -> Self { + Self { inner: value.into() } + } + +} + +impl ToString for HeaderValue { + fn to_string(&self) -> String { + self.inner.clone() + } +} + +pub struct Header { + pub name: HeaderName, + pub value: HeaderValue +} + +impl Header { + pub fn new(name: impl Into<HeaderName>, value: impl Into<HeaderValue>) -> Self { + Self { name: name.into(), value: value.into() } + } +} + +impl TryParse for Header { + fn try_parse(s: impl Into<String>) -> Result<Self, HTTPError> { + let s = s.into(); + let Some(mid) = s.find(": ") else { + return Err(HTTPError::InvalidHeader(s.to_string())) + }; + if mid == 0 || mid >= s.len() - 2 { + return Err(HTTPError::InvalidHeader(s.to_string())) + } + let name = HeaderName::try_parse(&s[0..mid])?; + let value = HeaderValue::parse(&s[(mid+2)..]); + Ok(Header::new(name, value)) + } +} + +impl ToString for Header { + fn to_string(&self) -> String { + let mut s = String::new(); + s.push_str(self.name.as_str()); + s.push_str(": "); + s.push_str(self.value.as_str()); + s + } +} + +impl<H,V> From<(H, V)> for Header +where + H: Into<HeaderName>, + V: Into<HeaderValue> +{ + fn from(value: (H, V)) -> Self { + Self { name: value.0.into(), value: value.1.into() } + } +} + +pub struct HeaderMap { + inner: MultiMap<HeaderName, Header> +} + +impl HeaderMap { + pub fn new() -> Self { + Self::with_headers(Vec::new()) + } + + pub fn with_headers(headers: Vec<Header>) -> Self { + let mut inner = MultiMap::with_capacity(headers.len()); + for header in headers { + inner.insert(header.name.clone(), Header::new(header.name, header.value)); + } + + Self { inner } + } + + pub fn insert(&mut self, header: impl Into<Header>) { + let header = header.into(); + self.inner.insert(header.name.clone(), Header::new(header.name, header.value)) + } + + pub fn remove(&mut self, name: &HeaderName) -> Option<Vec<Header>> { + self.inner.remove(name) + } + + pub fn get(&mut self, name: &HeaderName) -> Option<&Vec<Header>> { + self.inner.get_vec(name) + } + + pub fn iter(&self) -> HeaderMapIter { + HeaderMapIter { inner: self.inner.iter_all() } + } +} + +pub struct HeaderMapIter<'i> { + inner: IterAll<'i, HeaderName, Vec<Header>> +} + +impl<'i> Iterator for HeaderMapIter<'i> { + type Item = &'i Vec<Header>; + + fn next(&mut self) -> Option<Self::Item> { + match self.inner.next() { + Some(h) => Some(h.1), + None => None, + } + } +} |