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