diff --git a/core/http/src/handlers/handler.rs b/core/http/src/handlers/handler.rs index 9aded69e0256be7f3658849a0ed31f9cecab7965..8b021882f78c3341eb4144303f9a14bf20f8d7ff 100644 --- a/core/http/src/handlers/handler.rs +++ b/core/http/src/handlers/handler.rs @@ -123,10 +123,13 @@ 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.raw().starts_with(mountpoint.mountpoint) { + if request.uri.raw_string().is_none() { + return; + } + if !request.uri.raw_string().unwrap().starts_with(mountpoint.mountpoint) { continue; } - let mounted_request_uri = request.uri.strip_prefix(mountpoint.mountpoint).unwrap(); + let mounted_request_uri = request.uri.raw_string().unwrap().strip_prefix(mountpoint.mountpoint).unwrap(); for route in mountpoint.routes { if (route.method != request.method) && ((route.method != Method::Get) && (request.method == Method::Head)) @@ -138,7 +141,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin } handled_response = Some((route.handler)( Request { - uri: mounted_request_uri, + uri: UrlEncodeData::from_raw(mounted_request_uri), ..request.clone() }, data.clone(), @@ -165,7 +168,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin } } -fn failure_handler<'a>(status: Status) -> Response<'a> { +fn failure_handler<'a>(status: Status) -> Response { let page_404 = NamedFile::open(PathBuf::from("404.html")).unwrap(); Response { cookies: None, diff --git a/core/http/src/handling/request/form_utils.rs b/core/http/src/handling/request/form_utils.rs index 1579c115b6fce38ee10a96221a6f533097a7a8d3..b1e3a761cb5749ff150a3490baacb8b6a9011aaa 100644 --- a/core/http/src/handling/request/form_utils.rs +++ b/core/http/src/handling/request/form_utils.rs @@ -68,7 +68,10 @@ 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 Some(uri) = self.uri.raw_string() else { + return Err(ParseFormError { error: ParseErrors::BadData }); + }; + let data = if let Some(val) = uri.split_once('?') { val } else { return Err(ParseFormError { @@ -133,6 +136,9 @@ impl Request { let Ok(key) = kvp.0.decode() else { return Err(ParseFormError { error: ParseErrors::BadData }); }; + let Ok(key) = String::from_utf8(key) else { + return Err(ParseFormError { error: ParseErrors::BadData }); + }; let Ok(value) = kvp.1.trim_end_matches('\0').decode() else { return Err(ParseFormError { error: ParseErrors::BadData }); }; @@ -245,14 +251,17 @@ mod test { request::{datatypes::ParseErrors, ParseFormError}, routes::Data, }, - utils::mime::Mime::{ApplicationXWwwFormUrlencoded, MultipartFormData}, + utils::{ + mime::Mime::{ApplicationXWwwFormUrlencoded, MultipartFormData}, + urlencoded::UrlEncodeData, + }, }; use super::Request; #[test] fn try_get_test() { let request = Request { - uri: "/form?name=Name&age=Age", + uri: UrlEncodeData::from_encoded("/form?name=Name&age=Age").unwrap(), headers: vec![], method: Method::Get, cookies: None, @@ -263,7 +272,7 @@ mod test { assert_eq!(&"Age", right.get("age").unwrap().as_ref().unwrap()); let wrong_request = Request { - uri: "/form", + uri: UrlEncodeData::from_encoded("/form").unwrap(), ..request.clone() }; assert_eq!( @@ -274,7 +283,7 @@ mod test { ); let bad_data = Request { - uri: "/form?age=", + uri: UrlEncodeData::from_encoded("/form?age=").unwrap(), ..request.clone() }; let wrong = bad_data.get_get_form_keys(&["name", "age"]).unwrap(); @@ -290,7 +299,7 @@ mod test { #[test] fn try_post_text() { let req = Request { - uri: "", + uri: UrlEncodeData::from_encoded("").unwrap(), headers: vec!["Content-Type: application/x-www-form-urlencoded".to_string()], method: Method::Post, cookies: None, @@ -310,7 +319,7 @@ mod test { map.get("message1").unwrap().as_ref().unwrap() ); let req = Request { - uri: "", + uri: UrlEncodeData::from_encoded("").unwrap(), headers: vec!["Content-Type: multipart/form-data; boundary=\"boundary\"".to_string()], method: Method::Post, cookies: None, diff --git a/core/http/src/handling/response/cookie_management/cookie.rs b/core/http/src/handling/response/cookie_management/cookie.rs index 4e072ebcd7e4a169ec890ca00676fa256a0e735e..e96f825961745f3333034d9d8dda795fe0ce1994 100644 --- a/core/http/src/handling/response/cookie_management/cookie.rs +++ b/core/http/src/handling/response/cookie_management/cookie.rs @@ -1,24 +1,24 @@ use std::time::Duration; -pub struct Cookie<'a> { +pub struct Cookie { /// Storage for the cookie string. Only used if this structure was derived /// from a string that was subsequently parsed. - pub(crate) cookie_string: Option<&'a str>, - pub(crate) name: &'a str, - pub(crate) value: &'a str, + pub(crate) cookie_string: Option<String>, + pub(crate) name: String, + pub(crate) value: String, // expires: Option<Tm>, pub(crate) max_age: Option<Duration>, /// The cookie's domain, if any. - pub(crate) domain: Option<&'a str>, + pub(crate) domain: Option<String>, /// The cookie's path domain, if any. - pub(crate) path: Option<&'a str>, + pub(crate) path: Option<String>, /// Whether this cookie was marked Secure. pub(crate) secure: Option<bool>, /// Whether this cookie was marked HttpOnly. pub(crate) http_only: Option<bool>, /// The draft `SameSite` attribute. pub(crate) same_site: Option<SameSite>, - pub(crate) expires: Option<&'a str>, + pub(crate) expires: Option<String>, pub(crate) partitioned: Option<bool>, } @@ -39,7 +39,7 @@ impl std::fmt::Display for SameSite { } } -impl std::fmt::Display for Cookie<'_> { +impl std::fmt::Display for Cookie { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { todo!() } diff --git a/core/http/src/handling/response/cookie_management/cookie_builder.rs b/core/http/src/handling/response/cookie_management/cookie_builder.rs index 431160ab199f61e8984b6b8f30e191f1bada19a2..e71eb449258ead8826554bee88eae9dc1c6ca8a7 100644 --- a/core/http/src/handling/response/cookie_management/cookie_builder.rs +++ b/core/http/src/handling/response/cookie_management/cookie_builder.rs @@ -2,17 +2,17 @@ use std::time::Duration; use super::{Cookie, SameSite}; -pub struct CookieBuilder<'a> { - inner: Cookie<'a>, +pub struct CookieBuilder { + inner: Cookie, } -impl<'a> CookieBuilder<'a> { - pub fn build(name: &'a str, value: &'a str) -> Self { +impl CookieBuilder { + pub fn build(name: &str, value: &str) -> Self { CookieBuilder { inner: Cookie { cookie_string: None, - name, - value, + name: name.to_owned(), + value: value.to_owned(), max_age: None, domain: None, path: None, @@ -24,19 +24,19 @@ impl<'a> CookieBuilder<'a> { }, } } - pub fn finish(self) -> Cookie<'a> { + pub fn finish(self) -> Cookie { self.inner } pub fn max_age(mut self, duration: Duration) -> Self { self.inner.max_age = Some(duration); self } - pub fn domain(mut self, domain: &'a str) -> Self { - self.inner.domain = Some(domain); + pub fn domain(mut self, domain: &str) -> Self { + self.inner.domain = Some(domain.to_owned()); self } - pub fn path(mut self, path: &'a str) -> Self { - self.inner.path = Some(path); + pub fn path(mut self, path: &str) -> Self { + self.inner.path = Some(path.to_owned()); self } pub fn secure(mut self, secure: bool) -> Self { @@ -51,20 +51,20 @@ impl<'a> CookieBuilder<'a> { self.inner.same_site = Some(same_site); self } - pub fn expires(mut self, expire: &'a str) -> Self { - self.inner.expires = Some(expire); + pub fn expires(mut self, expire: &str) -> Self { + self.inner.expires = Some(expire.to_owned()); self } pub fn partitioned(mut self, partitioned: bool) -> Self { self.inner.partitioned = Some(partitioned); self } - pub fn name(mut self, name: &'a str) -> Self { - self.inner.name = name; + pub fn name(mut self, name: &str) -> Self { + self.inner.name = name.to_owned(); self } - pub fn value(mut self, value: &'a str) -> Self { - self.inner.value = value; + pub fn value(mut self, value: &str) -> Self { + self.inner.value = value.to_owned(); self } } diff --git a/core/http/src/handling/response/datatypes.rs b/core/http/src/handling/response/datatypes.rs index c174a5d3ded9a641fa02978815038e3c066dc0d8..7800deb53bb5749475e2093d89fd4c9b2855fcba 100644 --- a/core/http/src/handling/response/datatypes.rs +++ b/core/http/src/handling/response/datatypes.rs @@ -9,9 +9,9 @@ pub enum Outcome<S, E, F> { Forward(F), } -pub struct Response<'a> { +pub struct Response { pub headers: HeaderMap, - pub cookies: Option<Cookie<'a>>, + pub cookies: Option<Cookie>, pub status: Option<Status>, pub body: Box<dyn ResponseBody>, } diff --git a/core/http/src/handling/response/response.rs b/core/http/src/handling/response/response.rs index 3284d0253630664a1b993634ed275b7a337622d4..23d2b09c68c2a2fb35e3caff84475de24124d9ef 100644 --- a/core/http/src/handling/response/response.rs +++ b/core/http/src/handling/response/response.rs @@ -6,7 +6,7 @@ use crate::handling::{methods::Method, request::Request, response::Status}; use super::Response; -impl Response<'_> { +impl Response<> { pub fn build(self, request: Option<Request>) -> Vec<u8> { let compiled_headers = format!( "HTTP/1.1 {}\r\nContent-Length: {}\r\nContent-Type: {}\r\n", diff --git a/core/http/src/utils/urlencoded/datatypes.rs b/core/http/src/utils/urlencoded/datatypes.rs index 97dc0a9ad89bb9d106b9306a83b1ea43bba8b2b2..6829e6b97a512b3fce0b066f69847cf53b4740a3 100644 --- a/core/http/src/utils/urlencoded/datatypes.rs +++ b/core/http/src/utils/urlencoded/datatypes.rs @@ -1,5 +1,3 @@ -use std::{borrow::Cow, string::FromUtf8Error}; - use crate::utils::urlencoded::endecode::EnCodable; use super::endecode::DeCodable; @@ -12,12 +10,12 @@ pub struct UrlEncodeData { } impl UrlEncodeData { - pub fn from_raw<T: AsRef<[u8]>>(raw: T) -> Result<Self, FromUtf8Error> { - Ok(Self { + pub fn from_raw<T: AsRef<[u8]>>(raw: T) -> Self { + 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) -> Result<Self, ()> { Ok(Self { diff --git a/core/http/src/utils/urlencoded/endecode.rs b/core/http/src/utils/urlencoded/endecode.rs index 83e2261f248660f2b0c15a261f144ca2b8356893..389eb28902e51d2d64d9de55a67105ab6ee6f6e0 100644 --- a/core/http/src/utils/urlencoded/endecode.rs +++ b/core/http/src/utils/urlencoded/endecode.rs @@ -40,7 +40,7 @@ impl DeCodable for &str { 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 { + let Ok(char) = u8::from_str_radix(i[0..=1].as_ref(), BASE16_HEXA_DECIMAL.into()) else { return Err(()); }; result.push(char); @@ -84,5 +84,9 @@ mod test { Err(()), "Darius%2iis%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire".decode() ); + assert_eq!( + "hi?asdf=sadf%%&jkl=s", + String::from_utf8("hi?asdf=sadf%25%25&jkl=s".decode().unwrap()).unwrap() + ) } } diff --git a/site/src/main.rs b/site/src/main.rs index f677a34b0f691f8c5c97069324d579c00ee202d2..22e790001eaafc456015137a3ff01d618e516614 100644 --- a/site/src/main.rs +++ b/site/src/main.rs @@ -20,7 +20,7 @@ fn hashmap_to_string(map: &HashMap<&str, Result<&str, ParseFormError>>) -> Strin result } -fn handle_static_hi(request: Request<'_>, data: Data) -> Outcome<Response, Status, Data> { +fn handle_static_hi(request: Request<>, data: Data) -> Outcome<Response, Status, Data> { let keys = if let Ok(keys) = request.get_get_form_keys(&["asdf", "jkl"]) { keys } else { @@ -36,8 +36,8 @@ fn handle_static_hi(request: Request<'_>, data: Data) -> Outcome<Response, Statu // Outcome::Forward(data) } -fn handler(request: Request<'_>, _data: Data) -> Outcome<Response, Status, Data> { - let response = fileserver(request.uri.strip_prefix("static/").unwrap()); +fn handler(request: Request<>, _data: Data) -> Outcome<Response, Status, Data> { + let response = fileserver(request.uri.raw_string().unwrap().strip_prefix("static/").unwrap()); let response = match response { Ok(dat) => Response { headers: vec![],