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

introducing cookie builder, performance adjustment with requets.rs ->

build
parent 40488db2
No related branches found
No related tags found
1 merge request!1Initial feature merge
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::{ use crate::handling::{
file_handlers::NamedFile, file_handlers::NamedFile,
methods::Method,
request::Request, request::Request,
response::{Outcome, Response, ResponseBody, Status}, response::{Outcome, Response, ResponseBody, Status},
routes::{Data, Body}, methods::Method, routes::{Body, Data},
}; };
use crate::setup::MountPoint; use crate::setup::MountPoint;
...@@ -44,9 +48,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin ...@@ -44,9 +48,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
cookies: Request::extract_cookies_from_vec(&mut http_request), cookies: Request::extract_cookies_from_vec(&mut http_request),
headers: http_request, headers: http_request,
mime_type: None, mime_type: None,
method: if let Some(method) = request_status_line method: if let Some(method) = request_status_line.split(' ').next() {
.split(' ')
.next() {
if let Ok(ok) = method.parse() { if let Ok(ok) = method.parse() {
ok ok
} else { } else {
...@@ -77,7 +79,10 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin ...@@ -77,7 +79,10 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
if let Ok(size) = len { if let Ok(size) = len {
size size
} else { } 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 { if let Err(e) = len_not_defined(stream, Status::LengthRequired).await {
error_occured_when_writing(e) error_occured_when_writing(e)
}; };
...@@ -85,7 +90,10 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin ...@@ -85,7 +90,10 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
} }
} else { } else {
if request.mandatory_body() { 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 { if let Err(e) = len_not_defined(stream, Status::LengthRequired).await {
error_occured_when_writing(e) error_occured_when_writing(e)
}; };
...@@ -115,8 +123,9 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin ...@@ -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(); let mounted_request_uri = request.uri.strip_prefix(mountpoint.mountpoint).unwrap();
for route in mountpoint.routes { for route in mountpoint.routes {
if (route.method != request.method) && if (route.method != request.method)
((route.method != Method::Get) && (request.method == Method::Head)) { && ((route.method != Method::Get) && (request.method == Method::Head))
{
continue; continue;
} }
if !route.compare_uri(mounted_request_uri) { if !route.compare_uri(mounted_request_uri) {
...@@ -139,8 +148,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin ...@@ -139,8 +148,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
let response = match handled_response { let response = match handled_response {
Some(val) => match val { Some(val) => match val {
Outcome::Success(success) => success Outcome::Success(success) => success,
,
Outcome::Failure(error) => failure_handler(error), Outcome::Failure(error) => failure_handler(error),
Outcome::Forward(_) => failure_handler(Status::NotFound), Outcome::Forward(_) => failure_handler(Status::NotFound),
}, },
...@@ -149,7 +157,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin ...@@ -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 { 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"); 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> { fn failure_handler<'a>(status: Status) -> Response<'a> {
...@@ -162,14 +170,16 @@ 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(); let page_411 = NamedFile::open(PathBuf::from("411.html")).unwrap();
Response { Response {
cookies: None, cookies: None,
headers: vec![], headers: vec![],
status: Some(status), status: Some(status),
body: Box::new(Body::new(page_411.get_data(), page_411.get_mime())), body: Box::new(Body::new(page_411.get_data(), page_411.get_mime())),
}.write(stream, None).await?; }
.write(stream, None)
.await?;
Ok(()) Ok(())
} }
......
use std::{collections::HashMap, error::Error, fmt::Display}; use std::{borrow::Cow, collections::HashMap, error::Error, fmt::Display};
use crate::{ use crate::{
handling::{methods::Method, routes::Uri}, handling::{methods::Method, routes::Uri},
......
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,
}
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
}
}
mod cookie;
mod cookie_builder; mod cookie_builder;
pub use cookie::Cookie;
pub use cookie::SameSite;
use std::time::Duration; use super::{Cookie, ResponseBody, Status};
use super::{ResponseBody, Status};
type HeaderMap = Vec<String>; type HeaderMap = Vec<String>;
...@@ -11,32 +9,6 @@ pub enum Outcome<S, E, F> { ...@@ -11,32 +9,6 @@ pub enum Outcome<S, E, F> {
Forward(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 struct Response<'a> {
pub headers: HeaderMap, pub headers: HeaderMap,
pub cookies: Option<Cookie<'a>>, pub cookies: Option<Cookie<'a>>,
......
...@@ -4,9 +4,9 @@ mod response; ...@@ -4,9 +4,9 @@ mod response;
mod status; mod status;
mod traits; mod traits;
pub use datatypes::Cookie; pub use cookie_management::Cookie;
pub use cookie_management::SameSite;
pub use datatypes::Outcome; pub use datatypes::Outcome;
pub use datatypes::Response; pub use datatypes::Response;
pub use datatypes::SameSite;
pub use status::Status; pub use status::Status;
pub use traits::ResponseBody; pub use traits::ResponseBody;
use std::io::Result; 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}; use crate::handling::{methods::Method, request::Request, response::Status};
...@@ -8,8 +8,12 @@ use super::Response; ...@@ -8,8 +8,12 @@ use super::Response;
impl Response<'_> { impl Response<'_> {
pub fn build(self, request: Option<Request>) -> Vec<u8> { 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()) let compiled_headers = format!(
+ &self.headers.join("\r\n") "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"; + "\r\n";
let is_head = if let Some(req) = request { let is_head = if let Some(req) = request {
req.method == Method::Head req.method == Method::Head
...@@ -21,12 +25,13 @@ impl Response<'_> { ...@@ -21,12 +25,13 @@ impl Response<'_> {
} else { } else {
self.body.get_data() 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.extend_from_slice(&compiled_body);
compiled_out.to_vec() 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::thread::available_parallelism; 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::{ use crate::{
handlers::handler::handle_connection, handlers::handler::handle_connection,
...@@ -65,7 +69,7 @@ impl<'a> Config { ...@@ -65,7 +69,7 @@ impl<'a> Config {
} }
/// # Launches/Starts the webserver /// # Launches/Starts the webserver
/// Launches a Webserver Configuration /// Launches a Webserver Configuration
/// ///
/// Is Async /// Is Async
/// ///
/// Is Blocking -> Can be interrupted with ^C /// Is Blocking -> Can be interrupted with ^C
...@@ -74,7 +78,10 @@ impl<'a> Config { ...@@ -74,7 +78,10 @@ impl<'a> Config {
/// Panics if there are no Mountpoints in the Confiuration to lauch, as well as when the there /// Panics if there are no Mountpoints in the Confiuration to lauch, as well as when the there
/// is no interrupt signal /// is no interrupt signal
pub async fn launch(self) { 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(); let mut sigint = signal(SignalKind::interrupt()).unwrap();
loop { loop {
select! { select! {
...@@ -120,7 +127,7 @@ pub async fn build(ip: &str) -> Config { ...@@ -120,7 +127,7 @@ pub async fn build(ip: &str) -> Config {
let ip = ip[0]; let ip = ip[0];
let workers = available_parallelism().unwrap().get(); let workers = available_parallelism().unwrap().get();
println!( println!(
"\x1b[34m⚙ Configuration\x1b[0m "\x1b[34m⚙ Configuration\x1b[0m
>> \x1b[34mIp\x1b[0m: {ip} >> \x1b[34mIp\x1b[0m: {ip}
>> \x1b[34mPort\x1b[0m: {port} >> \x1b[34mPort\x1b[0m: {port}
>> \x1b[34mWorkers\x1b[0m: {workers} >> \x1b[34mWorkers\x1b[0m: {workers}
......
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