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::{
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();
......
......@@ -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
......
......@@ -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]
......
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 => {
......
......@@ -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,
......
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(),
......
......@@ -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(())
......
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)
}
}
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()
);
}
}
mod datatypes;
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