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

Complete `get_post_data()` implementation

set maximum post body size to 4196 bytes
parent 66349541
No related branches found
No related tags found
No related merge requests found
...@@ -10,6 +10,8 @@ use crate::handling::{ ...@@ -10,6 +10,8 @@ use crate::handling::{
}; };
use crate::setup::MountPoint; use crate::setup::MountPoint;
static MAX_HTTPMESSAGE_SIZE: u16 = 4196;
pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoint<'_>>) { pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoint<'_>>) {
let mut buf_reader = BufReader::new(&mut stream); let mut buf_reader = BufReader::new(&mut stream);
let mut http_request: Vec<String> = Vec::with_capacity(30); let mut http_request: Vec<String> = Vec::with_capacity(30);
...@@ -61,6 +63,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin ...@@ -61,6 +63,7 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
buffer: vec![], buffer: vec![],
}; };
if request.can_have_body() { if request.can_have_body() {
println!("{:#?}", request.headers);
let length = if let Some(len) = request let length = if let Some(len) = request
.headers .headers
.iter() .iter()
...@@ -87,9 +90,9 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin ...@@ -87,9 +90,9 @@ pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoin
0 0
}; };
if length != 0 { if length != 0 {
let mut buffer: Vec<u8> = vec![]; let mut buffer = vec![0u8; MAX_HTTPMESSAGE_SIZE.into()];
buf_reader.read_buf(&mut buffer).await.unwrap(); let read = buf_reader.read(&mut buffer).await.unwrap();
if buffer.len() != length { if read != length {
len_not_defined(stream, Status::LengthRequired).await; len_not_defined(stream, Status::LengthRequired).await;
return; return;
} }
......
...@@ -4,6 +4,8 @@ use std::{collections::HashMap, error::Error, fmt::Display}; ...@@ -4,6 +4,8 @@ use std::{collections::HashMap, error::Error, fmt::Display};
use crate::utils::mime::mime_enum::Mime; use crate::utils::mime::mime_enum::Mime;
static TWO_NEWLINES: u8 = 3;
use super::{ use super::{
methods::Method, methods::Method,
routes::{Data, Uri}, routes::{Data, Uri},
...@@ -125,9 +127,13 @@ impl Request<'_> { ...@@ -125,9 +127,13 @@ impl Request<'_> {
let content_type = val.trim().strip_prefix("Content-Type: ").unwrap(); let content_type = val.trim().strip_prefix("Content-Type: ").unwrap();
let type_vec = content_type.split(';').collect::<Vec<&str>>(); let type_vec = content_type.split(';').collect::<Vec<&str>>();
boundary = if let Some(bound) = type_vec.iter().find(|part| part.contains("boundary=")) boundary = if let Some(bound) = type_vec.iter().find(|part| part.contains(" boundary="))
{ {
bound.strip_prefix("boundary=").unwrap().trim_matches('"') bound
.strip_prefix(" boundary=")
.unwrap()
.trim_end()
.trim_matches('"')
} else { } else {
"" ""
}; };
...@@ -192,62 +198,10 @@ impl Request<'_> { ...@@ -192,62 +198,10 @@ impl Request<'_> {
Mime::MultipartFormData => { Mime::MultipartFormData => {
let mut temp_bound = "--".to_string(); let mut temp_bound = "--".to_string();
temp_bound.push_str(&format!("{boundary}")); temp_bound.push_str(&format!("{boundary}"));
let end_boundary = format!("{temp_bound}--").as_bytes().to_owned(); let end_boundary = format!("{temp_bound}--\r").as_bytes().to_owned();
temp_bound.push('\r');
let boundary = temp_bound.as_bytes(); let boundary = temp_bound.as_bytes();
let parts = data.split(|byte| byte == &b'\n').collect::<Vec<&[u8]>>(); Request::get_multipart(data, boundary, &end_boundary, &mut keymap);
let mut current_key: Option<String> = None;
let mut boundary_found = true;
for part in parts {
if part == [] {
continue;
}
if part == end_boundary {
break;
}
if !boundary_found && part == boundary {
boundary_found = true;
current_key = None;
continue;
}
if part.starts_with(b"Content-Disposition: form-data; name=") {
let headers = part
.split(|byte| byte == &b';')
.filter(|header| !header.is_empty())
.collect::<Vec<_>>();
if headers.len() < 2 {
continue;
}
let name = headers[1].split(|byte| byte == &b'=').collect::<Vec<_>>();
if name.len() != 2 {
continue;
}
let mkey = String::from_utf8_lossy(name[1])
.as_ref()
.trim_end()
.trim_matches('"')
.to_owned();
if keymap.contains_key::<str>(&mkey) {
current_key = Some(mkey.to_owned());
}
boundary_found = false;
continue;
} else if let Some(key) = &current_key {
if let Some(val) = keymap.get::<str>(&key) {
if let Err(_) = val {
keymap.insert(key.to_string(), Ok(part.to_vec()));
continue;
}
keymap
.get_mut(key)
.unwrap()
.as_mut()
.unwrap()
.extend_from_slice(part);
}
}
}
} }
_ => { _ => {
return Err(ParseFormError { return Err(ParseFormError {
...@@ -257,6 +211,77 @@ impl Request<'_> { ...@@ -257,6 +211,77 @@ impl Request<'_> {
}; };
Ok(keymap) Ok(keymap)
} }
fn get_multipart(
data: &[u8],
boundary: &[u8],
end_boundary: &[u8],
map: &mut HashMap<String, Result<Vec<u8>, ParseFormError>>,
) {
let parts = data.split(|byte| byte == &b'\n').collect::<Vec<&[u8]>>();
let mut current_part: Vec<&[u8]> = vec![];
let mut current_key: Option<String> = None;
let mut ignore_line = 0;
for part in parts {
if part == &[b'\r'] {
if let Some(_) = current_key {
if ignore_line >= TWO_NEWLINES {
current_part.push(&[b'\n']);
continue;
}
ignore_line += 1;
}
continue;
}
if part == end_boundary {
if let Some(key) = &current_key {
let mut part = current_part.join(&b'\n');
if part.ends_with(&[b'\r']) {
part.pop();
}
map.insert(key.to_string(), Ok(part));
}
break;
}
if part == boundary {
if let Some(key) = &current_key {
let mut part = current_part.join(&b'\n');
if part.ends_with(&[b'\r']) {
part.pop();
}
map.insert(key.to_string(), Ok(part));
}
current_part = vec![];
current_key = None;
ignore_line = 0;
continue;
}
if part.starts_with(b"Content-Disposition: form-data; name=") {
let headers = part
.split(|byte| byte == &b';')
.filter(|header| !header.is_empty())
.collect::<Vec<_>>();
if headers.len() < 2 {
continue;
}
let name = headers[1].split(|byte| byte == &b'=').collect::<Vec<_>>();
if name.len() != 2 {
continue;
}
let mkey = String::from_utf8_lossy(name[1])
.as_ref()
.trim_end()
.trim_matches('"')
.to_owned();
if map.contains_key::<str>(&mkey) {
current_key = Some(mkey.to_owned());
}
continue;
} else if let Some(_) = &current_key {
current_part.push(part);
}
}
}
} }
#[cfg(test)] #[cfg(test)]
...@@ -287,30 +312,31 @@ mod test { ...@@ -287,30 +312,31 @@ mod test {
); );
let req = Request { let req = Request {
uri: "", uri: "",
headers: vec!["Content-Type: multipart/form-data;boundary=\"boundary\"".to_string()], headers: vec!["Content-Type: multipart/form-data; boundary=\"boundary\"".to_string()],
method: crate::handling::methods::Method::Post, method: crate::handling::methods::Method::Post,
}; };
let data = Data { let data = Data {
buffer: b"--boundary buffer: b"--boundary\r
Content-Disposition: form-data; name=\"field1\" Content-Disposition: form-data; name=\"field1\"\r
\r
value1 value1\r
--boundary --boundary\r
Content-Disposition: form-data; name=\"field2\"; filename=\"example.txt\" Content-Disposition: form-data; name=\"field2\"; filename=\"example.txt\"\r
\r
value2\n va\nlue2\r
--boundary-- --boundary--\r
" "
.to_vec(), .to_vec(),
is_complete: true, is_complete: true,
}; };
let map = req.get_post_data(&["field1", "field2"], &data).unwrap(); let map = req.get_post_data(&["field1", "field2"], &data).unwrap();
assert_eq!( assert_eq!(
&b"value1".to_vec(), &b"value1".to_vec(),
map.get("field1").unwrap().as_ref().unwrap() map.get("field1").unwrap().as_ref().unwrap()
); );
assert_eq!( assert_eq!(
&b"value2\n".to_vec(), &b"va\nlue2".to_vec(),
map.get("field2").unwrap().as_ref().unwrap() map.get("field2").unwrap().as_ref().unwrap()
); );
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<body> <body>
<h1>404</h1> <h1>404</h1>
<p>Hi from Rust</p> <p>Hi from Rust</p>
<form action="/post/post" method="post"> <form action="/post/post" method="post" enctype="multipart/form-data">
<label for="message">Enter your message:</label> <label for="message">Enter your message:</label>
<input type="text" id="message" name="message" /> <input type="text" id="message" name="message" />
<button type="submit">Send</button> <button type="submit">Send</button>
......
...@@ -62,12 +62,11 @@ fn post_hi_handler(request: Request, data: Data) -> Outcome<Response, Status, Da ...@@ -62,12 +62,11 @@ fn post_hi_handler(request: Request, data: Data) -> Outcome<Response, Status, Da
if data.is_empty() { if data.is_empty() {
return Outcome::Forward(data); return Outcome::Forward(data);
} }
// let data = if let Ok(val) = request.get_post_text_form_key("message", &data) { let dat = if let Ok(val) = request.get_post_data(&["message"], &data) {
// val post_hi(String::from_utf8_lossy(val.get("message").unwrap().as_ref().unwrap()).to_string())
// } else { } else {
// return Outcome::Failure(Status::BadRequest); return Outcome::Failure(Status::BadRequest);
// }; };
let dat = post_hi(String::from_utf8(data.buffer).unwrap());
Outcome::Success(Response { Outcome::Success(Response {
headers: vec![ headers: vec![
format!("Content-Length: {}", dat.len()), format!("Content-Length: {}", dat.len()),
......
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