Skip to content
Snippets Groups Projects
Commit 929f96eb authored by codecraft's avatar codecraft :crocodile:
Browse files

converting the request uri to a urlencoded datatype

many errors WIP
parent 6c90900d
No related branches found
No related tags found
1 merge request!1Initial feature merge
...@@ -5,13 +5,13 @@ use tokio::{ ...@@ -5,13 +5,13 @@ use tokio::{
net::TcpStream, net::TcpStream,
}; };
use crate::handling::{ use crate::{handling::{
file_handlers::NamedFile, file_handlers::NamedFile,
methods::Method, methods::Method,
request::Request, request::Request,
response::{Outcome, Response, ResponseBody, Status}, response::{Outcome, Response, ResponseBody, Status},
routes::{Body, Data}, routes::{Body, Data},
}; }, utils::urlencoded::UrlEncodeData};
use crate::setup::MountPoint; use crate::setup::MountPoint;
static MAX_HTTP_MESSAGE_SIZE: u16 = 4196; static MAX_HTTP_MESSAGE_SIZE: u16 = 4196;
...@@ -40,7 +40,12 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin ...@@ -40,7 +40,12 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
let mut request = Request { let mut request = Request {
uri: if let Some(uri) = &request_status_line.split(' ').nth(1) { 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 { } else {
eprintln!("\x1b[31mAborting due to invalid status line\x1b[0m"); eprintln!("\x1b[31mAborting due to invalid status line\x1b[0m");
return; return;
...@@ -118,7 +123,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin ...@@ -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; let mut handled_response: Option<Outcome<Response, Status, Data>> = None;
for mountpoint in mountpoints { for mountpoint in mountpoints {
if !request.uri.starts_with(mountpoint.mountpoint) { if !request.uri.raw().starts_with(mountpoint.mountpoint) {
continue; continue;
} }
let mounted_request_uri = request.uri.strip_prefix(mountpoint.mountpoint).unwrap(); let mounted_request_uri = request.uri.strip_prefix(mountpoint.mountpoint).unwrap();
......
...@@ -2,7 +2,7 @@ use std::collections::HashMap; ...@@ -2,7 +2,7 @@ use std::collections::HashMap;
use super::Request; use super::Request;
impl Request<'_> { impl Request {
/// Extracts the cookies from a Vector and gives back an optional HashMap of Strings /// 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 /// Returns none if there are no cookies or there is a problem with the cookies, for example
......
...@@ -2,7 +2,7 @@ use std::{collections::HashMap, error::Error, fmt::Display}; ...@@ -2,7 +2,7 @@ use std::{collections::HashMap, error::Error, fmt::Display};
use crate::{ use crate::{
handling::{methods::Method, routes::Uri}, handling::{methods::Method, routes::Uri},
utils::mime::Mime, utils::{mime::Mime, urlencoded::UrlEncodeData},
}; };
pub trait FromRequest: Send { pub trait FromRequest: Send {
...@@ -29,9 +29,9 @@ type HeaderMap = Vec<String>; ...@@ -29,9 +29,9 @@ type HeaderMap = Vec<String>;
/// A struct to handle Requests /// A struct to handle Requests
/// ///
#[derive(Clone)] #[derive(Clone)]
pub struct Request<'a> { pub struct Request {
/// The requested Uri /// The requested Uri
pub uri: Uri<'a>, pub uri: UrlEncodeData,
/// All headers of the request that haven't been parsed /// All headers of the request that haven't been parsed
pub headers: HeaderMap, pub headers: HeaderMap,
/// The methods Request represented with the [Method] /// The methods Request represented with the [Method]
......
use std::collections::HashMap; 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}; use super::{datatypes::ParseErrors, ParseFormError, Request};
static TWO_NEWLINES: u8 = 3; static TWO_NEWLINES: u8 = 3;
impl Request<'_> { impl Request {
/// # Gets data from a get_form as a HashMap /// # Gets data from a get_form as a HashMap
/// ///
/// # Errors /// # Errors
...@@ -124,16 +127,19 @@ impl Request<'_> { ...@@ -124,16 +127,19 @@ impl Request<'_> {
return Err(ParseFormError { error: ParseErrors::BadData }); return Err(ParseFormError { error: ParseErrors::BadData });
}; };
for kvp in data.split('&') { 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 }); return Err(ParseFormError { error: ParseErrors::BadData });
}; };
let Ok(key) = kvp.0.decode() else {
let Some(thing) = keymap.get_mut(kvp.0) 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; continue;
}; };
kvp.1 = kvp.1.trim_end_matches('\0'); *thing = Ok(value.into());
*thing = Ok(kvp.1.as_bytes().to_vec());
} }
} }
Mime::MultipartFormData => { Mime::MultipartFormData => {
......
...@@ -2,7 +2,7 @@ use crate::handling::methods::Method; ...@@ -2,7 +2,7 @@ use crate::handling::methods::Method;
use super::Request; use super::Request;
impl Request<'_> { impl Request {
pub fn can_have_body(&self) -> bool { pub fn can_have_body(&self) -> bool {
matches!( matches!(
self.method, self.method,
......
use super::datatypes::Request; use super::datatypes::Request;
impl<'a> Request<'a> { impl Request {
/// Sets the `mime_type` of this [`Request`] from the headers. /// Sets the `mime_type` of this [`Request`] from the headers.
/// ///
/// The mime_type can remain none if there isn't a `Content-Type: ` header /// The mime_type can remain none if there isn't a `Content-Type: ` header
...@@ -71,13 +71,13 @@ impl<'a> Request<'a> { ...@@ -71,13 +71,13 @@ impl<'a> Request<'a> {
mod test { mod test {
use crate::{ use crate::{
handling::{methods::Method, request::Request}, handling::{methods::Method, request::Request},
utils::mime::Mime, utils::{mime::Mime, urlencoded::UrlEncodeData},
}; };
#[test] #[test]
pub fn test_mime_parse_from_header_vec() { pub fn test_mime_parse_from_header_vec() {
let mut request = Request { let mut request = Request {
uri: "thing", uri: UrlEncodeData::from_raw("thing"),
headers: vec![ headers: vec![
"GET / 23".to_string(), "GET / 23".to_string(),
"SDF:LKJSD:F".to_string(), "SDF:LKJSD:F".to_string(),
...@@ -90,7 +90,7 @@ mod test { ...@@ -90,7 +90,7 @@ mod test {
}; };
let mut wrong = Request { let mut wrong = Request {
uri: "thing", uri: UrlEncodeData::from_raw("thing"),
headers: vec![ headers: vec![
"GET / 23".to_string(), "GET / 23".to_string(),
"SDF:LKJSD:F".to_string(), "SDF:LKJSD:F".to_string(),
......
...@@ -29,7 +29,7 @@ impl Response<'_> { ...@@ -29,7 +29,7 @@ impl Response<'_> {
compiled_out.extend_from_slice(&compiled_body); compiled_out.extend_from_slice(&compiled_body);
compiled_out 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); let resp = self.build(request);
stream.write_all(&resp).await?; stream.write_all(&resp).await?;
Ok(()) Ok(())
......
use std::borrow::Cow; use std::{borrow::Cow, string::FromUtf8Error};
use super::endecode::EnDecodable; use crate::utils::urlencoded::endecode::EnCodable;
pub struct UrlEncodeData<'a> { use super::endecode::DeCodable;
encoded: Cow<'a, str>,
raw: Cow<'a, str>, #[derive(Clone)]
pub struct UrlEncodeData {
encoded: String,
raw: Vec<u8>,
raw_string: Option<String>,
} }
impl UrlEncodeData<'_> { impl UrlEncodeData {
pub fn from_raw(raw: &str) -> Self { pub fn from_raw<T: AsRef<[u8]>>(raw: T) -> Result<Self, FromUtf8Error> {
todo!() 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 { pub fn from_encoded(encoded: &str) -> Result<Self, ()> {
todo!() Ok(Self {
encoded: encoded.to_owned(),
raw: encoded.decode()?,
raw_string: String::from_utf8(encoded.decode()?).ok(),
})
} }
}
impl EnDecodable for UrlEncodeData<'_> { pub fn encoded(&self) -> &str {
fn encode(&self) -> Cow<'_, str> { self.encoded.as_ref()
self.raw.encode() }
pub fn raw(&self) -> &[u8] {
self.raw.as_ref()
} }
fn decode(&self) -> Result<Cow<'_, str>, ()> { pub fn raw_string(&self) -> Option<&str> {
self.encoded.decode() 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)
} }
} }
use std::borrow::Cow;
static BASE16_HEXA_DECIMAL: u8 = 16; static BASE16_HEXA_DECIMAL: u8 = 16;
pub trait EnDecodable { static BASE16_HEXA_DECIMAL_POSSIBLE_VALUE_PER_DIGIT: u8 = 15;
fn encode(&self) -> Cow<'_, str>; static BASE16_HEXA_DECIMAL_DIGIT_BITS: u8 = 4;
fn decode(&self) -> Result<Cow<'_, str>, ()>;
pub trait EnCodable {
fn encode(&self) -> String;
} }
impl EnDecodable for Cow<'_, str> { pub trait DeCodable {
fn encode(&self) -> Cow<'_, str> { fn decode(&self) -> Result<Vec<u8>, ()>;
self.bytes() }
.map(|byte| {
if !byte.is_ascii_alphanumeric() { impl EnCodable for [u8] {
format!("%{:02X}", byte) fn encode(self: &[u8]) -> String {
} 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()); let mut result = String::with_capacity(self.len());
for i in self.split('%') { let result_vec = unsafe { result.as_mut_vec() };
if first { self.iter().for_each(|byte| {
first = false; if !matches!(byte, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'-' | b'_' | b'.' | b'~')
result += i; {
continue; 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(()); result
};
unsafe {
result.as_mut_vec().push(char);
}
result = result + &i[2..];
}
Ok(result.into())
} }
} }
impl DeCodable for &str {
impl EnDecodable for &str { fn decode(&self) -> Result<Vec<u8>, ()> {
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 first = true;
let mut result = String::with_capacity(self.len()); let mut result = Vec::with_capacity(self.len());
for i in self.split('%') { for i in self.split('%') {
if first { if first {
first = false; first = false;
result += i; result.extend_from_slice(i.as_bytes());
continue; 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..2].as_ref(), BASE16_HEXA_DECIMAL.into()) else {
return Err(()); return Err(());
}; };
unsafe { result.push(char);
result.as_mut_vec().push(char); result.extend_from_slice(i[2..].as_bytes());
}
result = result + &i[2..];
} }
Ok(result.into()) Ok(result)
} }
} }
impl EnDecodable for String { fn hex_to_digit(digit: u8) -> u8 {
fn encode(&self) -> Cow<'_, str> { match digit {
self.bytes() 0..=9 => b'0' + digit,
.map(|byte| { 10..=255 => b'A' + digit - 10,
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())
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::borrow::Cow; use crate::utils::urlencoded::endecode::DeCodable;
use crate::utils::urlencoded::endecode::EnCodable;
use crate::utils::urlencoded::endecode::EnDecodable;
#[test] #[test]
fn urlencoded_test() { fn urlencoded_test() {
assert_eq!( assert_eq!(
"Darius%20is%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire", "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] #[test]
fn urldecoded_test() { fn urldecoded_test() {
assert_eq!( assert_eq!(
"Darius is the biggest genius/Genie/Human extraordinäire", "Darius is the biggest genius/Genie/Human extraordinäire",
Cow::Borrowed( String::from_utf8(
"Darius%20is%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire" "Darius%20is%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire"
.decode()
.unwrap()
) )
.decode()
.unwrap() .unwrap()
); );
assert_eq!( assert_eq!(
Err(()), 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()
); );
} }
} }
mod datatypes; mod datatypes;
mod endecode; mod endecode;
pub use datatypes::UrlEncodeData;
pub use endecode::DeCodable;
pub use endecode::EnCodable;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment