summaryrefslogtreecommitdiff
path: root/src/header.rs
diff options
context:
space:
mode:
authorTyler Murphy <tylerm@tylerm.dev>2023-07-05 21:34:20 -0400
committerTyler Murphy <tylerm@tylerm.dev>2023-07-05 21:34:20 -0400
commit168b8937eb0fe88311fe474ab9569691a19d087f (patch)
tree3e70d7de89adc9e62987cea9341ba2cc10d6cf25 /src/header.rs
downloadhttp-168b8937eb0fe88311fe474ab9569691a19d087f.tar.gz
http-168b8937eb0fe88311fe474ab9569691a19d087f.tar.bz2
http-168b8937eb0fe88311fe474ab9569691a19d087f.zip
changes
Diffstat (limited to 'src/header.rs')
-rw-r--r--src/header.rs174
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,
+ }
+ }
+}