From 929f96ebca64977d492a63f2118da0871967a153 Mon Sep 17 00:00:00 2001
From: Darius Auding <Darius.auding@gmx.de>
Date: Tue, 4 Jul 2023 22:03:01 +0200
Subject: [PATCH] converting the request uri to a urlencoded datatype many
 errors WIP

---
 core/http/src/handlers/handler.rs             |  13 +-
 core/http/src/handling/request/cookies.rs     |   2 +-
 core/http/src/handling/request/datatypes.rs   |   6 +-
 core/http/src/handling/request/form_utils.rs  |  22 ++-
 .../http/src/handling/request/request_impl.rs |   2 +-
 .../http/src/handling/request/request_mime.rs |   8 +-
 core/http/src/handling/response/response.rs   |   2 +-
 core/http/src/utils/urlencoded/datatypes.rs   |  51 +++++--
 core/http/src/utils/urlencoded/endecode.rs    | 142 ++++++------------
 core/http/src/utils/urlencoded/mod.rs         |   3 +
 10 files changed, 116 insertions(+), 135 deletions(-)

diff --git a/core/http/src/handlers/handler.rs b/core/http/src/handlers/handler.rs
index 67897c4..9aded69 100644
--- a/core/http/src/handlers/handler.rs
+++ b/core/http/src/handlers/handler.rs
@@ -5,13 +5,13 @@ use tokio::{
     net::TcpStream,
 };
 
-use crate::handling::{
+use crate::{handling::{
     file_handlers::NamedFile,
     methods::Method,
     request::Request,
     response::{Outcome, Response, ResponseBody, Status},
     routes::{Body, Data},
-};
+}, utils::urlencoded::UrlEncodeData};
 use crate::setup::MountPoint;
 
 static MAX_HTTP_MESSAGE_SIZE: u16 = 4196;
@@ -40,7 +40,12 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
 
     let mut request = Request {
         uri: if let Some(uri) = &request_status_line.split(' ').nth(1) {
-            uri
+            if let Ok(uri) = UrlEncodeData::from_encoded(uri) {
+                uri
+            } else {
+                eprintln!("\x1b[31mAborting due to invalid uri\x1b[0m");
+                return;
+            }
         } else {
             eprintln!("\x1b[31mAborting due to invalid status line\x1b[0m");
             return;
@@ -118,7 +123,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
 
     let mut handled_response: Option<Outcome<Response, Status, Data>> = None;
     for mountpoint in mountpoints {
-        if !request.uri.starts_with(mountpoint.mountpoint) {
+        if !request.uri.raw().starts_with(mountpoint.mountpoint) {
             continue;
         }
         let mounted_request_uri = request.uri.strip_prefix(mountpoint.mountpoint).unwrap();
diff --git a/core/http/src/handling/request/cookies.rs b/core/http/src/handling/request/cookies.rs
index cd3c4ca..7f75fc1 100644
--- a/core/http/src/handling/request/cookies.rs
+++ b/core/http/src/handling/request/cookies.rs
@@ -2,7 +2,7 @@ use std::collections::HashMap;
 
 use super::Request;
 
-impl Request<'_> {
+impl Request {
     /// Extracts the cookies from a Vector and gives back an optional HashMap of Strings
     ///
     /// Returns none if there are no cookies or there is a problem with the cookies, for example
diff --git a/core/http/src/handling/request/datatypes.rs b/core/http/src/handling/request/datatypes.rs
index df96177..6d37b1f 100644
--- a/core/http/src/handling/request/datatypes.rs
+++ b/core/http/src/handling/request/datatypes.rs
@@ -2,7 +2,7 @@ use std::{collections::HashMap, error::Error, fmt::Display};
 
 use crate::{
     handling::{methods::Method, routes::Uri},
-    utils::mime::Mime,
+    utils::{mime::Mime, urlencoded::UrlEncodeData},
 };
 
 pub trait FromRequest: Send {
@@ -29,9 +29,9 @@ type HeaderMap = Vec<String>;
 /// A struct to handle Requests
 ///
 #[derive(Clone)]
-pub struct Request<'a> {
+pub struct Request {
     /// The requested Uri
-    pub uri: Uri<'a>,
+    pub uri: UrlEncodeData,
     /// All headers of the request that haven't been parsed
     pub headers: HeaderMap,
     /// The methods Request represented with the [Method]
diff --git a/core/http/src/handling/request/form_utils.rs b/core/http/src/handling/request/form_utils.rs
index 9aa300c..1579c11 100644
--- a/core/http/src/handling/request/form_utils.rs
+++ b/core/http/src/handling/request/form_utils.rs
@@ -1,11 +1,14 @@
 use std::collections::HashMap;
 
-use crate::{handling::routes::Data, utils::mime::Mime};
+use crate::{
+    handling::routes::Data,
+    utils::{mime::Mime, urlencoded::DeCodable},
+};
 
 use super::{datatypes::ParseErrors, ParseFormError, Request};
 static TWO_NEWLINES: u8 = 3;
 
-impl Request<'_> {
+impl Request {
     /// # Gets data from a get_form as a HashMap
     ///
     /// # Errors
@@ -124,16 +127,19 @@ impl Request<'_> {
                     return Err(ParseFormError { error: ParseErrors::BadData });
                 };
                 for kvp in data.split('&') {
-                    let Some(mut kvp) = kvp.split_once('=') else {
+                    let Some(kvp) = kvp.split_once('=') else {
                         return Err(ParseFormError { error: ParseErrors::BadData });
                     };
-
-                    let Some(thing) = keymap.get_mut(kvp.0) else {
+                    let Ok(key) = kvp.0.decode() else {
+                        return Err(ParseFormError { error: ParseErrors::BadData });
+                    };
+                    let Ok(value) = kvp.1.trim_end_matches('\0').decode() else {
+                        return Err(ParseFormError { error: ParseErrors::BadData });
+                    };
+                    let Some(thing) = keymap.get_mut(&key) else {
                         continue;
                     };
-                    kvp.1 = kvp.1.trim_end_matches('\0');
-
-                    *thing = Ok(kvp.1.as_bytes().to_vec());
+                    *thing = Ok(value.into());
                 }
             }
             Mime::MultipartFormData => {
diff --git a/core/http/src/handling/request/request_impl.rs b/core/http/src/handling/request/request_impl.rs
index 2d2f7cb..2eb6fd0 100644
--- a/core/http/src/handling/request/request_impl.rs
+++ b/core/http/src/handling/request/request_impl.rs
@@ -2,7 +2,7 @@ use crate::handling::methods::Method;
 
 use super::Request;
 
-impl Request<'_> {
+impl Request {
     pub fn can_have_body(&self) -> bool {
         matches!(
             self.method,
diff --git a/core/http/src/handling/request/request_mime.rs b/core/http/src/handling/request/request_mime.rs
index 56188d1..c37045e 100644
--- a/core/http/src/handling/request/request_mime.rs
+++ b/core/http/src/handling/request/request_mime.rs
@@ -1,6 +1,6 @@
 use super::datatypes::Request;
 
-impl<'a> Request<'a> {
+impl Request {
     /// Sets the `mime_type` of this [`Request`] from the headers.
     ///
     /// The mime_type can remain none if there isn't a `Content-Type: ` header
@@ -71,13 +71,13 @@ impl<'a> Request<'a> {
 mod test {
     use crate::{
         handling::{methods::Method, request::Request},
-        utils::mime::Mime,
+        utils::{mime::Mime, urlencoded::UrlEncodeData},
     };
 
     #[test]
     pub fn test_mime_parse_from_header_vec() {
         let mut request = Request {
-            uri: "thing",
+            uri: UrlEncodeData::from_raw("thing"),
             headers: vec![
                 "GET / 23".to_string(),
                 "SDF:LKJSD:F".to_string(),
@@ -90,7 +90,7 @@ mod test {
         };
 
         let mut wrong = Request {
-            uri: "thing",
+            uri: UrlEncodeData::from_raw("thing"),
             headers: vec![
                 "GET / 23".to_string(),
                 "SDF:LKJSD:F".to_string(),
diff --git a/core/http/src/handling/response/response.rs b/core/http/src/handling/response/response.rs
index b986d5c..3284d02 100644
--- a/core/http/src/handling/response/response.rs
+++ b/core/http/src/handling/response/response.rs
@@ -29,7 +29,7 @@ impl Response<'_> {
         compiled_out.extend_from_slice(&compiled_body);
         compiled_out
     }
-    pub async fn write(self, mut stream: TcpStream, request: Option<Request<'_>>) -> Result<()> {
+    pub async fn write(self, mut stream: TcpStream, request: Option<Request>) -> Result<()> {
         let resp = self.build(request);
         stream.write_all(&resp).await?;
         Ok(())
diff --git a/core/http/src/utils/urlencoded/datatypes.rs b/core/http/src/utils/urlencoded/datatypes.rs
index c370ddf..97dc0a9 100644
--- a/core/http/src/utils/urlencoded/datatypes.rs
+++ b/core/http/src/utils/urlencoded/datatypes.rs
@@ -1,26 +1,45 @@
-use std::borrow::Cow;
+use std::{borrow::Cow, string::FromUtf8Error};
 
-use super::endecode::EnDecodable;
+use crate::utils::urlencoded::endecode::EnCodable;
 
-pub struct UrlEncodeData<'a> {
-    encoded: Cow<'a, str>,
-    raw: Cow<'a, str>,
+use super::endecode::DeCodable;
+
+#[derive(Clone)]
+pub struct UrlEncodeData {
+    encoded: String,
+    raw: Vec<u8>,
+    raw_string: Option<String>,
 }
 
-impl UrlEncodeData<'_> {
-    pub fn from_raw(raw: &str) -> Self {
-        todo!()
+impl UrlEncodeData {
+    pub fn from_raw<T: AsRef<[u8]>>(raw: T) -> Result<Self, FromUtf8Error> {
+        Ok(Self {
+            raw: raw.as_ref().to_owned(),
+            encoded: raw.as_ref().encode(),
+            raw_string: String::from_utf8(raw.as_ref().into()).ok(),
+        })
     }
-    pub fn from_encoded(encoded: &str) -> Self {
-        todo!()
+    pub fn from_encoded(encoded: &str) -> Result<Self, ()> {
+        Ok(Self {
+            encoded: encoded.to_owned(),
+            raw: encoded.decode()?,
+            raw_string: String::from_utf8(encoded.decode()?).ok(),
+        })
     }
-}
 
-impl EnDecodable for UrlEncodeData<'_> {
-    fn encode(&self) -> Cow<'_, str> {
-        self.raw.encode()
+    pub fn encoded(&self) -> &str {
+        self.encoded.as_ref()
+    }
+    pub fn raw(&self) -> &[u8] {
+        self.raw.as_ref()
     }
-    fn decode(&self) -> Result<Cow<'_, str>, ()> {
-        self.encoded.decode()
+    pub fn raw_string(&self) -> Option<&str> {
+        self.raw_string.as_ref().map(|x| x.as_str())
+    }
+}
+
+impl std::fmt::Display for UrlEncodeData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.encoded)
     }
 }
diff --git a/core/http/src/utils/urlencoded/endecode.rs b/core/http/src/utils/urlencoded/endecode.rs
index 0e7fe2c..83e2261 100644
--- a/core/http/src/utils/urlencoded/endecode.rs
+++ b/core/http/src/utils/urlencoded/endecode.rs
@@ -1,140 +1,88 @@
-use std::borrow::Cow;
-
 static BASE16_HEXA_DECIMAL: u8 = 16;
-pub trait EnDecodable {
-    fn encode(&self) -> Cow<'_, str>;
-    fn decode(&self) -> Result<Cow<'_, str>, ()>;
+static BASE16_HEXA_DECIMAL_POSSIBLE_VALUE_PER_DIGIT: u8 = 15;
+static BASE16_HEXA_DECIMAL_DIGIT_BITS: u8 = 4;
+
+pub trait EnCodable {
+    fn encode(&self) -> String;
 }
 
-impl EnDecodable for Cow<'_, str> {
-    fn encode(&self) -> Cow<'_, str> {
-        self.bytes()
-            .map(|byte| {
-                if !byte.is_ascii_alphanumeric() {
-                    format!("%{:02X}", byte)
-                } else {
-                    String::from_utf8([byte].to_vec()).unwrap()
-                }
-            })
-            .collect()
-    }
-    fn decode(&self) -> Result<Cow<'_, str>, ()> {
-        let mut first = true;
+pub trait DeCodable {
+    fn decode(&self) -> Result<Vec<u8>, ()>;
+}
+
+impl EnCodable for [u8] {
+    fn encode(self: &[u8]) -> String {
         let mut result = String::with_capacity(self.len());
-        for i in self.split('%') {
-            if first {
-                first = false;
-                result += i;
-                continue;
+        let result_vec = unsafe { result.as_mut_vec() };
+        self.iter().for_each(|byte| {
+            if !matches!(byte, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'-' | b'_' | b'.' | b'~')
+            {
+                result_vec.push(b'%');
+                result_vec.push(hex_to_digit(byte >> BASE16_HEXA_DECIMAL_DIGIT_BITS));
+                result_vec.push(hex_to_digit(
+                    byte & BASE16_HEXA_DECIMAL_POSSIBLE_VALUE_PER_DIGIT,
+                ));
+            } else {
+                result_vec.push(*byte)
             }
-            let Ok(char) = u8::from_str_radix(i[0..2].as_ref(), BASE16_HEXA_DECIMAL.into()) else {
-                return Err(());
-            };
-            unsafe {
-                result.as_mut_vec().push(char);
-            }
-            result = result + &i[2..];
-        }
-        Ok(result.into())
+        });
+        result
     }
 }
-
-impl EnDecodable for &str {
-    fn encode(&self) -> Cow<'_, str> {
-        self.bytes()
-            .map(|byte| {
-                if !byte.is_ascii_alphanumeric() {
-                    format!("%{:02X}", byte)
-                } else {
-                    String::from_utf8([byte].to_vec()).unwrap()
-                }
-            })
-            .collect()
-    }
-
-    fn decode(&self) -> Result<Cow<'_, str>, ()> {
+impl DeCodable for &str {
+    fn decode(&self) -> Result<Vec<u8>, ()> {
         let mut first = true;
-        let mut result = String::with_capacity(self.len());
+        let mut result = Vec::with_capacity(self.len());
+
         for i in self.split('%') {
             if first {
                 first = false;
-                result += i;
+                result.extend_from_slice(i.as_bytes());
                 continue;
             }
             let Ok(char) = u8::from_str_radix(i[0..2].as_ref(), BASE16_HEXA_DECIMAL.into()) else {
-                return Err(());
+                    return Err(());
             };
-            unsafe {
-                result.as_mut_vec().push(char);
-            }
-            result = result + &i[2..];
+            result.push(char);
+            result.extend_from_slice(i[2..].as_bytes());
         }
-        Ok(result.into())
+        Ok(result)
     }
 }
 
-impl EnDecodable for String {
-    fn encode(&self) -> Cow<'_, str> {
-        self.bytes()
-            .map(|byte| {
-                if !byte.is_ascii_alphanumeric() {
-                    format!("%{:02X}", byte)
-                } else {
-                    String::from_utf8([byte].to_vec()).unwrap()
-                }
-            })
-            .collect()
-    }
-    fn decode(&self) -> Result<Cow<'_, str>, ()> {
-        let mut first = true;
-        let mut result = String::with_capacity(self.len());
-        for i in self.split('%') {
-            if first {
-                first = false;
-                result += i;
-                continue;
-            }
-            let Ok(char) = u8::from_str_radix(i[0..2].as_ref(), BASE16_HEXA_DECIMAL.into()) else {
-                return Err(());
-            };
-            unsafe {
-                result.as_mut_vec().push(char);
-            }
-            result = result + &i[2..];
-        }
-        Ok(result.into())
+fn hex_to_digit(digit: u8) -> u8 {
+    match digit {
+        0..=9 => b'0' + digit,
+        10..=255 => b'A' + digit - 10,
     }
 }
 
 #[cfg(test)]
 mod test {
-    use std::borrow::Cow;
-
-    use crate::utils::urlencoded::endecode::EnDecodable;
+    use crate::utils::urlencoded::endecode::DeCodable;
+    use crate::utils::urlencoded::endecode::EnCodable;
 
     #[test]
     fn urlencoded_test() {
         assert_eq!(
             "Darius%20is%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire",
-            Cow::Borrowed("Darius is the biggest genius/Genie/Human extraordinäire").encode()
-        )
+            b"Darius is the biggest genius/Genie/Human extraordin\xC3\xA4ire".encode()
+        );
     }
     #[test]
     fn urldecoded_test() {
         assert_eq!(
             "Darius is the biggest genius/Genie/Human extraordinäire",
-            Cow::Borrowed(
+            String::from_utf8(
                 "Darius%20is%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire"
+                    .decode()
+                    .unwrap()
             )
-            .decode()
             .unwrap()
         );
         assert_eq!(
             Err(()),
-            Cow::Borrowed(
-                "Darius%2iis%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire"
-            )
-            .decode()
+            "Darius%2iis%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire".decode()
         );
     }
 }
diff --git a/core/http/src/utils/urlencoded/mod.rs b/core/http/src/utils/urlencoded/mod.rs
index 43ea58f..6511cfc 100644
--- a/core/http/src/utils/urlencoded/mod.rs
+++ b/core/http/src/utils/urlencoded/mod.rs
@@ -1,2 +1,5 @@
 mod datatypes;
 mod endecode;
+pub use datatypes::UrlEncodeData;
+pub use endecode::DeCodable;
+pub use endecode::EnCodable;
-- 
GitLab