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

Add write and build function to result, add basis for cookie setting

possibility, divide the response module;
parent 9e291184
No related branches found
No related tags found
1 merge request!1Initial feature merge
use std::path::PathBuf;
use std::{path::PathBuf, io};
use tokio::{io::{AsyncReadExt, BufReader, AsyncBufReadExt, AsyncWriteExt}, net::TcpStream};
use tokio::{io::{AsyncReadExt, BufReader, AsyncBufReadExt}, net::TcpStream};
use crate::handling::{
file_handlers::NamedFile,
request::Request,
response::{Outcome, Response, ResponseBody, Status},
routes::Data, methods::Method,
routes::{Data, Body}, methods::Method,
};
use crate::setup::MountPoint;
......@@ -78,13 +78,17 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
size
} else {
eprintln!("\x1b[31m`{}` must have a `Content-Length` header\x1b[0m", request.method);
len_not_defined(stream, Status::LengthRequired).await;
if let Err(e) = len_not_defined(stream, Status::LengthRequired).await {
error_occured_when_writing(e)
};
return;
}
} else {
if request.mandatory_body() {
eprintln!("\x1b[31m`{}` must have a `Content-Length` header\x1b[0m", request.method);
len_not_defined(stream, Status::LengthRequired).await;
if let Err(e) = len_not_defined(stream, Status::LengthRequired).await {
error_occured_when_writing(e)
};
return;
}
0
......@@ -94,7 +98,9 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
let mut buffer = vec![0u8; MAX_HTTP_MESSAGE_SIZE.into()];
let read = buf_reader.read(&mut buffer).await.unwrap();
if read != length {
len_not_defined(stream, Status::LengthRequired).await;
if let Err(e) = len_not_defined(stream, Status::LengthRequired).await {
error_occured_when_writing(e)
};
return;
}
data.is_complete = true;
......@@ -133,47 +139,40 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
let response = match handled_response {
Some(val) => match val {
Outcome::Success(success) => (
format!("HTTP/1.1 {}\r\n", success.status.unwrap_or(Status::Ok))
+ &success.headers.join("\r\n")
+ "\r\n\r\n",
if request.method == Method::Head {
vec![]
} else {
success.body.get_data()
}
),
Outcome::Success(success) => success
,
Outcome::Failure(error) => failure_handler(error),
Outcome::Forward(_) => failure_handler(Status::NotFound),
},
None => failure_handler(Status::NotFound),
};
let mut resp = response.0.as_bytes().to_vec();
resp.extend_from_slice(&response.1);
if let Err(e) = stream.write_all(&resp).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");
}
}
fn failure_handler(status: Status) -> (String, Vec<u8>) {
fn failure_handler<'a>(status: Status) -> Response<'a> {
let page_404 = NamedFile::open(PathBuf::from("404.html")).unwrap();
(
format!(
"HTTP/1.1 {}\r\nContent-Length: {}\r\nContent-Type: {}\r\n\r\n",
status,
page_404.get_len(),
page_404.get_mime()
),
page_404.get_data(),
)
Response {
cookies: None,
headers: vec![],
status: Some(status),
body: Box::new(Body::new(page_404.get_data(), page_404.get_mime())),
}
}
async fn len_not_defined(mut stream: TcpStream, status: Status) {
async fn len_not_defined(stream: TcpStream, status: Status) -> io::Result<()>{
let page_411 = NamedFile::open(PathBuf::from("411.html")).unwrap();
let mut response = format!("HTTP/1.1 {}\r\nContent-Length: {}\r\nContent-Type: {}\r\n\r\n", status, page_411.get_len(), page_411.get_mime()).as_bytes().to_vec();
response.extend_from_slice(&page_411.get_data());
if let Err(e) = stream.write_all(&response).await {
eprintln!("\x1b[31mError {e} occured when trying to write answer to TCP-Stream for Client, aborting\x1b[0m");
}
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?;
Ok(())
}
fn error_occured_when_writing(e: io::Error) {
eprintln!("\x1b[31mError {e} occured when trying to write answer to TCP-Stream for Client, aborting\x1b[0m");
}
......@@ -17,6 +17,12 @@ impl Request<'_> {
///
/// # Examples
/// ```
/// use http::handling::request::Request;
/// use http::handling::request::ParseFormError;
/// use http::handling::request::ParseErrors;
/// use http::handling::methods::Method;
///
///
/// let request = Request {
/// uri: "/form?name=Name&age=Age",
/// headers: vec![],
......
......@@ -3,4 +3,4 @@ mod datatypes;
mod form_utils;
mod request_impl;
mod request_mime;
pub use datatypes::{MediaType, ParseFormError, Request};
pub use datatypes::{MediaType, ParseErrors, ParseFormError, Request};
use std::time::Duration;
use super::{ResponseBody, Status};
type HeaderMap = Vec<String>;
#[derive(Debug)]
pub enum Outcome<S, E, F> {
Success(S),
Failure(E),
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.
cookie_string: &'a str,
name: &'a str,
value: &'a str,
// expires: Option<Tm>,
max_age: Option<Duration>,
/// The cookie's domain, if any.
domain: Option<&'a str>,
/// The cookie's path domain, if any.
path: Option<&'a str>,
/// Whether this cookie was marked Secure.
secure: Option<bool>,
/// Whether this cookie was marked HttpOnly.
http_only: Option<bool>,
/// The draft `SameSite` attribute.
same_site: Option<SameSite>,
}
pub struct Response<'a> {
pub headers: HeaderMap,
pub cookies: Option<Cookie<'a>>,
pub status: Option<Status>,
pub body: Box<dyn ResponseBody>,
}
mod cookie;
mod datatypes;
mod response;
mod status;
mod traits;
pub use datatypes::Cookie;
pub use datatypes::Outcome;
pub use datatypes::Response;
pub use datatypes::SameSite;
pub use status::Status;
pub use traits::ResponseBody;
use std::io::Result;
use tokio::{net::TcpStream, io::AsyncWriteExt};
use crate::handling::{methods::Method, request::Request, response::Status};
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")
+ "\r\n";
let is_head = if let Some(req) = request {
req.method == Method::Head
} else {
false
};
let compiled_body = if is_head {
vec![]
} else {
self.body.get_data()
};
let compiled_out = unsafe { compiled_headers.as_mut_vec() };
compiled_out.extend_from_slice(&compiled_body);
compiled_out.to_vec()
}
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::fmt::Display;
use crate::utils::mime::mime_enum::Mime;
use super::routes::Body;
type HeaderMap = Vec<String>;
#[derive(Debug)]
pub enum Outcome<S, E, F> {
Success(S),
Failure(E),
Forward(F),
}
pub trait ResponseBody: Send {
fn get_data(&self) -> Vec<u8>;
fn get_mime(&self) -> Mime;
fn get_len(&self) -> usize;
}
impl ResponseBody for Body {
fn get_data(&self) -> Vec<u8> {
self.body()
}
fn get_mime(&self) -> Mime {
self.mime_type()
}
fn get_len(&self) -> usize {
self.get_data().len()
}
}
impl ResponseBody for &str {
fn get_data(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
fn get_mime(&self) -> Mime {
Mime::TextPlain
}
fn get_len(&self) -> usize {
self.len()
}
}
impl ResponseBody for String {
fn get_data(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
fn get_mime(&self) -> Mime {
Mime::TextPlain
}
fn get_len(&self) -> usize {
self.len()
}
}
pub struct Response {
pub headers: HeaderMap,
pub status: Option<Status>,
pub body: Box<dyn ResponseBody>,
}
#[derive(Debug)]
pub enum Status {
Continue,
......
use crate::{handling::routes::Body, utils::mime::mime_enum::Mime};
pub trait ResponseBody: Send {
fn get_data(&self) -> Vec<u8>;
fn get_mime(&self) -> Mime;
fn get_len(&self) -> usize;
}
impl ResponseBody for Body {
fn get_data(&self) -> Vec<u8> {
self.body()
}
fn get_mime(&self) -> Mime {
self.mime_type()
}
fn get_len(&self) -> usize {
self.get_data().len()
}
}
impl ResponseBody for &str {
fn get_data(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
fn get_mime(&self) -> Mime {
Mime::TextPlain
}
fn get_len(&self) -> usize {
self.len()
}
}
impl ResponseBody for String {
fn get_data(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
fn get_mime(&self) -> Mime {
Mime::TextPlain
}
fn get_len(&self) -> usize {
self.len()
}
}
......@@ -4,7 +4,7 @@ use http::handling::{
file_handlers::NamedFile,
methods::Method,
request::{Request, ParseFormError},
response::{Outcome, Response, ResponseBody, Status},
response::{Outcome, Response, Status},
routes::{Data, Route},
};
......@@ -28,10 +28,8 @@ fn handle_static_hi(request: Request<'_>, data: Data) -> Outcome<Response, Statu
};
let response = hashmap_to_string(&keys);
Outcome::Success(Response {
headers: vec![
format!("Content-Length: {}", response.len()),
format!("Content-Type: text/plain"),
],
headers: vec![],
cookies: None,
status: Some(Status::Ok),
body: Box::new(response),
})
......@@ -42,10 +40,8 @@ fn handler(request: Request<'_>, _data: Data) -> Outcome<Response, Status, Data>
let response = fileserver(request.uri.strip_prefix("static/").unwrap());
let response = match response {
Ok(dat) => Response {
headers: vec![
format!("Content-Length: {}", dat.get_len()),
format!("Content-Type: {}", dat.get_mime()),
],
headers: vec![],
cookies: None,
status: Some(Status::Ok),
body: Box::new(dat),
},
......@@ -69,10 +65,8 @@ fn post_hi_handler(request: Request, data: Data) -> Outcome<Response, Status, Da
return Outcome::Failure(Status::BadRequest);
};
Outcome::Success(Response {
headers: vec![
format!("Content-Length: {}", dat.len()),
format!("Content-Type: text/plain"),
],
headers: vec![],
cookies: None,
status: Some(Status::Ok),
body: Box::new(dat),
})
......
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