From af4ab2e39df8ba1d49209303095bfcf3c969d0d9 Mon Sep 17 00:00:00 2001
From: Darius Auding <Darius.auding@gmx.de>
Date: Fri, 23 Jun 2023 14:41:15 +0200
Subject: [PATCH] refactoring the request module, add cookie functionality

---
 core/http/src/handlers/handlers.rs            |  4 +-
 core/http/src/handling/request/cookies.rs     | 54 ++++++++++++
 core/http/src/handling/request/datatypes.rs   | 73 ++++++++++++++++
 core/http/src/handling/request/mod.rs         |  5 ++
 .../{request.rs => request/request_impl.rs}   | 84 ++-----------------
 5 files changed, 141 insertions(+), 79 deletions(-)
 create mode 100644 core/http/src/handling/request/cookies.rs
 create mode 100644 core/http/src/handling/request/datatypes.rs
 create mode 100644 core/http/src/handling/request/mod.rs
 rename core/http/src/handling/{request.rs => request/request_impl.rs} (84%)

diff --git a/core/http/src/handlers/handlers.rs b/core/http/src/handlers/handlers.rs
index df6ec34..32376bc 100755
--- a/core/http/src/handlers/handlers.rs
+++ b/core/http/src/handlers/handlers.rs
@@ -4,7 +4,7 @@ use tokio::{io::{AsyncReadExt, BufReader, AsyncBufReadExt, AsyncWriteExt}, net::
 
 use crate::handling::{
     file_handlers::NamedFile,
-    request::Request,
+    request::{Request, extract_cookies_from_vec},
     response::{Outcome, Response, ResponseBody, Status},
     routes::Data, methods::Method,
 };
@@ -42,7 +42,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
             eprintln!("\x1b[31mAborting due to invalid status line\x1b[0m");
             return;
         },
-        cookies: None,
+        cookies: extract_cookies_from_vec(&mut http_request),
         headers: http_request,
         method: if let Some(method) = request_status_line
             .split(" ")
diff --git a/core/http/src/handling/request/cookies.rs b/core/http/src/handling/request/cookies.rs
new file mode 100644
index 0000000..c9d0fcd
--- /dev/null
+++ b/core/http/src/handling/request/cookies.rs
@@ -0,0 +1,54 @@
+use std::collections::HashMap;
+
+pub fn extract_cookies_from_vec(headers: &mut Vec<String>) -> Option<HashMap<String, String>> {
+    let mut cookies: HashMap<String, String> = HashMap::new();
+    let mut cookies_string = if let Some(index) = headers
+        .iter()
+        .position(|header| header.starts_with("Cookie: "))
+    {
+        headers.remove(index)
+    } else {
+        return None;
+    };
+    cookies_string = cookies_string
+        .strip_prefix("Cookie: ")
+        .unwrap()
+        .trim()
+        .to_string();
+    for cookie in cookies_string.split(';') {
+        let Some((name, cookie)) = cookie.split_once('=') else {
+            return None;
+        };
+        cookies
+            .entry(name.trim().to_string())
+            .or_insert(cookie.trim().to_string());
+    }
+    Some(cookies)
+}
+
+#[cfg(test)]
+mod test {
+    use crate::handling::request::extract_cookies_from_vec;
+
+    #[test]
+    fn test_cookies() {
+        let mut request_vec = vec![
+            "GET / HTTP/1.1".to_string(),
+            "Accept: sdf".to_string(),
+            "Cookie: io=23; f=as".to_string(),
+            "Format: gzip".to_string(),
+        ];
+        let right_finished_vec = vec![
+            "GET / HTTP/1.1".to_string(),
+            "Accept: sdf".to_string(),
+            "Format: gzip".to_string(),
+        ];
+
+        let mut wrong = vec!["GET / HTTP/1.1".to_string()];
+        assert_eq!(None, extract_cookies_from_vec(&mut wrong));
+        let cookies = extract_cookies_from_vec(&mut request_vec);
+        assert_eq!(right_finished_vec, request_vec);
+        assert_eq!("23", cookies.clone().unwrap().get("io").unwrap());
+        assert_eq!("as", cookies.unwrap().get("f").unwrap());
+    }
+}
diff --git a/core/http/src/handling/request/datatypes.rs b/core/http/src/handling/request/datatypes.rs
new file mode 100644
index 0000000..68e32d5
--- /dev/null
+++ b/core/http/src/handling/request/datatypes.rs
@@ -0,0 +1,73 @@
+use std::{collections::HashMap, error::Error, fmt::Display};
+
+use crate::handling::{methods::Method, routes::Uri};
+
+pub trait FromRequest: Send {
+    fn get_data(&self) -> &Self;
+    fn set_data(&mut self, data: &Self);
+    fn append(&mut self, data: &Self);
+}
+
+impl FromRequest for Vec<u8> {
+    fn get_data(&self) -> &Self {
+        &self
+    }
+
+    fn set_data(&mut self, data: &Self) {
+        *self = data.to_vec();
+    }
+    fn append(&mut self, data: &Self) {
+        self.extend_from_slice(data);
+    }
+}
+
+type HeaderMap = Vec<String>;
+
+#[derive(Clone)]
+pub struct Request<'a> {
+    pub uri: Uri<'a>,
+    pub headers: HeaderMap,
+    pub method: Method,
+    pub cookies: Option<HashMap<String, String>>,
+    // pub connection: ConnectionMeta,
+}
+
+// struct ConnectionMeta {
+//     remote: Option<SocketAddr>,
+//     // certificates
+// }
+
+#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum MediaType {
+    Json,
+    Plain,
+    Html,
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum ParseErrors {
+    NoData,
+    BadData,
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ParseFormError {
+    pub error: ParseErrors,
+}
+
+impl Display for ParseFormError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.error)
+    }
+}
+
+impl Display for ParseErrors {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            ParseErrors::NoData => write!(f, "No Data at key"),
+            ParseErrors::BadData => write!(f, "Bad Data at key"),
+        }
+    }
+}
+
+impl Error for ParseFormError {}
diff --git a/core/http/src/handling/request/mod.rs b/core/http/src/handling/request/mod.rs
new file mode 100644
index 0000000..23c9f1a
--- /dev/null
+++ b/core/http/src/handling/request/mod.rs
@@ -0,0 +1,5 @@
+mod cookies;
+mod datatypes;
+mod request_impl;
+pub use cookies::extract_cookies_from_vec;
+pub use datatypes::{MediaType, ParseFormError, Request};
diff --git a/core/http/src/handling/request.rs b/core/http/src/handling/request/request_impl.rs
similarity index 84%
rename from core/http/src/handling/request.rs
rename to core/http/src/handling/request/request_impl.rs
index 8841901..2c582ea 100644
--- a/core/http/src/handling/request.rs
+++ b/core/http/src/handling/request/request_impl.rs
@@ -1,85 +1,15 @@
 // use std::net::SocketAddr;
 
-use std::{collections::HashMap, error::Error, fmt::Display};
+use std::collections::HashMap;
 
-use crate::utils::mime::mime_enum::Mime;
-
-static TWO_NEWLINES: u8 = 3;
-
-use super::{
-    methods::Method,
-    routes::{Data, Uri},
+use crate::{
+    handling::{methods::Method, routes::Data},
+    utils::mime::mime_enum::Mime,
 };
 
-pub trait FromRequest: Send {
-    fn get_data(&self) -> &Self;
-    fn set_data(&mut self, data: &Self);
-    fn append(&mut self, data: &Self);
-}
-
-impl FromRequest for Vec<u8> {
-    fn get_data(&self) -> &Self {
-        &self
-    }
-
-    fn set_data(&mut self, data: &Self) {
-        *self = data.to_vec();
-    }
-    fn append(&mut self, data: &Self) {
-        self.extend_from_slice(data);
-    }
-}
-
-type HeaderMap = Vec<String>;
-
-#[derive(Clone)]
-pub struct Request<'a> {
-    pub uri: Uri<'a>,
-    pub headers: HeaderMap,
-    pub method: Method,
-    pub cookies: Option<Vec<&'a str>>,
-    // pub connection: ConnectionMeta,
-}
-
-// struct ConnectionMeta {
-//     remote: Option<SocketAddr>,
-//     // certificates
-// }
+use super::datatypes::{ParseErrors, ParseFormError, Request};
 
-#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub enum MediaType {
-    Json,
-    Plain,
-    Html,
-}
-
-#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
-pub enum ParseErrors {
-    NoData,
-    BadData,
-}
-
-#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
-pub struct ParseFormError {
-    pub error: ParseErrors,
-}
-
-impl Display for ParseFormError {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.error)
-    }
-}
-
-impl Display for ParseErrors {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            ParseErrors::NoData => write!(f, "No Data at key"),
-            ParseErrors::BadData => write!(f, "Bad Data at key"),
-        }
-    }
-}
-
-impl Error for ParseFormError {}
+static TWO_NEWLINES: u8 = 3;
 
 impl Request<'_> {
     pub fn can_have_body(&self) -> bool {
@@ -98,7 +28,7 @@ impl Request<'_> {
         &'a self,
         keys: &'a [&str],
     ) -> Result<HashMap<&str, Result<&str, ParseFormError>>, ParseFormError> {
-        let data = if let Some(val) = self.uri.split_once("?") {
+        let data = if let Some(val) = self.uri.split_once('?') {
             val
         } else {
             return Err(ParseFormError {
-- 
GitLab