diff --git a/core/http/src/handlers/handler.rs b/core/http/src/handlers/handler.rs index 3c461599f79b654ca947075b65931056dca998d7..67897c4490c7c566e11d8e5aaaa6968df50702e2 100644 --- a/core/http/src/handlers/handler.rs +++ b/core/http/src/handlers/handler.rs @@ -1,12 +1,16 @@ -use std::{path::PathBuf, io}; +use std::{io, path::PathBuf}; -use tokio::{io::{AsyncReadExt, BufReader, AsyncBufReadExt}, net::TcpStream}; +use tokio::{ + io::{AsyncBufReadExt, AsyncReadExt, BufReader}, + net::TcpStream, +}; use crate::handling::{ file_handlers::NamedFile, + methods::Method, request::Request, response::{Outcome, Response, ResponseBody, Status}, - routes::{Data, Body}, methods::Method, + routes::{Body, Data}, }; use crate::setup::MountPoint; @@ -44,9 +48,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin cookies: Request::extract_cookies_from_vec(&mut http_request), headers: http_request, mime_type: None, - method: if let Some(method) = request_status_line - .split(' ') - .next() { + method: if let Some(method) = request_status_line.split(' ').next() { if let Ok(ok) = method.parse() { ok } else { @@ -77,7 +79,10 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin if let Ok(size) = len { size } else { - eprintln!("\x1b[31m`{}` must have a `Content-Length` header\x1b[0m", request.method); + eprintln!( + "\x1b[31m`{}` must have a `Content-Length` header\x1b[0m", + request.method + ); if let Err(e) = len_not_defined(stream, Status::LengthRequired).await { error_occured_when_writing(e) }; @@ -85,7 +90,10 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin } } else { if request.mandatory_body() { - eprintln!("\x1b[31m`{}` must have a `Content-Length` header\x1b[0m", request.method); + eprintln!( + "\x1b[31m`{}` must have a `Content-Length` header\x1b[0m", + request.method + ); if let Err(e) = len_not_defined(stream, Status::LengthRequired).await { error_occured_when_writing(e) }; @@ -115,8 +123,9 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin } let mounted_request_uri = request.uri.strip_prefix(mountpoint.mountpoint).unwrap(); for route in mountpoint.routes { - if (route.method != request.method) && - ((route.method != Method::Get) && (request.method == Method::Head)) { + if (route.method != request.method) + && ((route.method != Method::Get) && (request.method == Method::Head)) + { continue; } if !route.compare_uri(mounted_request_uri) { @@ -139,8 +148,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin let response = match handled_response { Some(val) => match val { - Outcome::Success(success) => success - , + Outcome::Success(success) => success, Outcome::Failure(error) => failure_handler(error), Outcome::Forward(_) => failure_handler(Status::NotFound), }, @@ -149,7 +157,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin if let Err(e) = response.write(stream, Some(request)).await { eprintln!("\x1b[31mError {e} occured when trying to write answer to TCP-Stream for Client, aborting\x1b[0m"); - } + } } fn failure_handler<'a>(status: Status) -> Response<'a> { @@ -162,14 +170,16 @@ fn failure_handler<'a>(status: Status) -> Response<'a> { } } -async fn len_not_defined(stream: TcpStream, status: Status) -> io::Result<()>{ +async fn len_not_defined(stream: TcpStream, status: Status) -> io::Result<()> { let page_411 = NamedFile::open(PathBuf::from("411.html")).unwrap(); Response { cookies: None, headers: vec![], status: Some(status), body: Box::new(Body::new(page_411.get_data(), page_411.get_mime())), - }.write(stream, None).await?; + } + .write(stream, None) + .await?; Ok(()) } diff --git a/core/http/src/handling/request/datatypes.rs b/core/http/src/handling/request/datatypes.rs index f49013726cd8c1382b72ca9ecdec49adca8bf7a5..acde6d80ceec9184f35cbe3678904c39114533c6 100644 --- a/core/http/src/handling/request/datatypes.rs +++ b/core/http/src/handling/request/datatypes.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, error::Error, fmt::Display}; +use std::{borrow::Cow, collections::HashMap, error::Error, fmt::Display}; use crate::{ handling::{methods::Method, routes::Uri}, diff --git a/core/http/src/handling/response/cookie_management/cookie.rs b/core/http/src/handling/response/cookie_management/cookie.rs new file mode 100644 index 0000000000000000000000000000000000000000..cfe3501e4fb56f389823e14467475089b8f00c48 --- /dev/null +++ b/core/http/src/handling/response/cookie_management/cookie.rs @@ -0,0 +1,28 @@ +use std::time::Duration; + +pub struct Cookie<'a> { + /// 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, + // expires: Option<Tm>, + pub(crate) max_age: Option<Duration>, + /// The cookie's domain, if any. + pub(crate) domain: Option<&'a str>, + /// The cookie's path domain, if any. + pub(crate) path: Option<&'a str>, + /// 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 enum SameSite { + None, + Lax, + Strict, +} 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 0a800f1b19c104d1086d44ef682a32a37aaa6c40..ead2df0829600279328bd0e9741faa2ef0befe7a 100644 --- a/core/http/src/handling/response/cookie_management/cookie_builder.rs +++ b/core/http/src/handling/response/cookie_management/cookie_builder.rs @@ -1 +1,65 @@ -struct CookieBuilder {} +use std::time::Duration; + +use super::{Cookie, SameSite}; + +pub struct CookieBuilder<'a> { + inner: Cookie<'a>, +} + +impl<'a> CookieBuilder<'a> { + pub fn build(name: &'a str, value: &'a str) -> Self { + CookieBuilder { + inner: Cookie { + cookie_string: None, + name, + value, + max_age: None, + domain: None, + path: None, + secure: None, + http_only: None, + same_site: None, + expires: None, + }, + } + } + pub fn finish(self) -> Cookie<'a> { + 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); + self + } + pub fn path(mut self, path: &'a str) -> Self { + self.inner.path = Some(path); + self + } + pub fn secure(mut self, secure: bool) -> Self { + self.inner.secure = Some(secure); + self + } + pub fn http_only(mut self, http_only: bool) -> Self { + self.inner.http_only = Some(http_only); + self + } + pub fn same_site(mut self, same_site: SameSite) -> Self { + self.inner.same_site = Some(same_site); + self + } + pub fn expires(mut self, expire: &'a str) -> Self { + self.inner.expires = Some(expire); + self + } + pub fn name(mut self, name: &'a str) -> Self { + self.inner.name = name; + self + } + pub fn value(mut self, value: &'a str) -> Self { + self.inner.value = value; + self + } +} diff --git a/core/http/src/handling/response/cookie_management/mod.rs b/core/http/src/handling/response/cookie_management/mod.rs index ee6df234d2c2e86c3daa39c173c42275006168de..06d6b85578319879491961ad465272996469b194 100644 --- a/core/http/src/handling/response/cookie_management/mod.rs +++ b/core/http/src/handling/response/cookie_management/mod.rs @@ -1 +1,5 @@ +mod cookie; mod cookie_builder; + +pub use cookie::Cookie; +pub use cookie::SameSite; diff --git a/core/http/src/handling/response/datatypes.rs b/core/http/src/handling/response/datatypes.rs index 669005e5c6d190a3e660312c7c2a68eb237051fc..c174a5d3ded9a641fa02978815038e3c066dc0d8 100644 --- a/core/http/src/handling/response/datatypes.rs +++ b/core/http/src/handling/response/datatypes.rs @@ -1,6 +1,4 @@ -use std::time::Duration; - -use super::{ResponseBody, Status}; +use super::{Cookie, ResponseBody, Status}; type HeaderMap = Vec<String>; @@ -11,32 +9,6 @@ pub enum Outcome<S, E, F> { Forward(F), } -pub enum SameSite { - None, - Lax, - Strict, -} - -pub struct Cookie<'a> { - /// 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, - // expires: Option<Tm>, - pub(crate) max_age: Option<Duration>, - /// The cookie's domain, if any. - pub(crate) domain: Option<&'a str>, - /// The cookie's path domain, if any. - pub(crate) path: Option<&'a str>, - /// 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 struct Response<'a> { pub headers: HeaderMap, pub cookies: Option<Cookie<'a>>, diff --git a/core/http/src/handling/response/mod.rs b/core/http/src/handling/response/mod.rs index 1fdc8755f16a7bb2bceaa8c89ec07d18afa31af7..ede6a42639847eca70d1376891eb61aa22d391c8 100644 --- a/core/http/src/handling/response/mod.rs +++ b/core/http/src/handling/response/mod.rs @@ -4,9 +4,9 @@ mod response; mod status; mod traits; -pub use datatypes::Cookie; +pub use cookie_management::Cookie; +pub use cookie_management::SameSite; pub use datatypes::Outcome; pub use datatypes::Response; -pub use datatypes::SameSite; pub use status::Status; pub use traits::ResponseBody; diff --git a/core/http/src/handling/response/response.rs b/core/http/src/handling/response/response.rs index dfdb90989a732542a28cdc1774601812e81943f7..b986d5c2dc4f545bd8b143b7357b6951125ab40d 100644 --- a/core/http/src/handling/response/response.rs +++ b/core/http/src/handling/response/response.rs @@ -1,6 +1,6 @@ use std::io::Result; -use tokio::{net::TcpStream, io::AsyncWriteExt}; +use tokio::{io::AsyncWriteExt, net::TcpStream}; use crate::handling::{methods::Method, request::Request, response::Status}; @@ -8,8 +8,12 @@ use super::Response; impl Response<'_> { pub fn build(self, request: Option<Request>) -> Vec<u8> { - let mut compiled_headers = format!("HTTP/1.1 {}\r\nContent-Length: {}\r\nContent-Type: {}\r\n", self.status.unwrap_or(Status::Ok), self.body.get_len(), self.body.get_mime()) - + &self.headers.join("\r\n") + let compiled_headers = format!( + "HTTP/1.1 {}\r\nContent-Length: {}\r\nContent-Type: {}\r\n", + self.status.unwrap_or(Status::Ok), + self.body.get_len(), + self.body.get_mime() + ) + &self.headers.join("\r\n") + "\r\n"; let is_head = if let Some(req) = request { req.method == Method::Head @@ -21,12 +25,13 @@ impl Response<'_> { } else { self.body.get_data() }; - let compiled_out = unsafe { compiled_headers.as_mut_vec() }; + let mut compiled_out: Vec<u8> = compiled_headers.into(); compiled_out.extend_from_slice(&compiled_body); - compiled_out.to_vec() + compiled_out } 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/setup.rs b/core/http/src/setup.rs index 417cf3df34ed0592492c67b06c5f1665aec7b296..1f061c6e3c58d053aa528c486643cf4048cc95cb 100644 --- a/core/http/src/setup.rs +++ b/core/http/src/setup.rs @@ -1,6 +1,10 @@ use std::thread::available_parallelism; -use tokio::{net::TcpListener, signal::unix::{SignalKind, signal}, select}; +use tokio::{ + net::TcpListener, + select, + signal::unix::{signal, SignalKind}, +}; use crate::{ handlers::handler::handle_connection, @@ -65,7 +69,7 @@ impl<'a> Config { } /// # Launches/Starts the webserver /// Launches a Webserver Configuration - /// + /// /// Is Async /// /// Is Blocking -> Can be interrupted with ^C @@ -74,7 +78,10 @@ impl<'a> Config { /// Panics if there are no Mountpoints in the Confiuration to lauch, as well as when the there /// is no interrupt signal pub async fn launch(self) { - println!("Server launched from http://{}", self.address.local_addr().unwrap()); + println!( + "Server launched from http://{}", + self.address.local_addr().unwrap() + ); let mut sigint = signal(SignalKind::interrupt()).unwrap(); loop { select! { @@ -120,7 +127,7 @@ pub async fn build(ip: &str) -> Config { let ip = ip[0]; let workers = available_parallelism().unwrap().get(); println!( -"\x1b[34mâš™ Configuration\x1b[0m + "\x1b[34mâš™ Configuration\x1b[0m >> \x1b[34mIp\x1b[0m: {ip} >> \x1b[34mPort\x1b[0m: {port} >> \x1b[34mWorkers\x1b[0m: {workers}