cargo clippy and proper HTTP codes in parse_start_line

This commit is contained in:
2025-03-21 10:17:06 +01:00
parent bb2e683f20
commit 6472b0a278

View File

@ -12,9 +12,9 @@ use std::{
#[derive(PartialEq, Eq, Debug, Copy, Clone)] #[derive(PartialEq, Eq, Debug, Copy, Clone)]
enum RequestMethods { enum RequestMethods {
NULL = -1, // This is only to initialise the struct Null = -1, // This is only to initialise the struct
GET, Get,
HEAD, Head,
} }
#[derive(Debug)] #[derive(Debug)]
@ -27,7 +27,7 @@ struct StartLine {
impl StartLine { impl StartLine {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
method: RequestMethods::NULL, method: RequestMethods::Null,
target: String::new(), target: String::new(),
version: String::new(), version: String::new(),
} }
@ -73,7 +73,7 @@ impl StartLine {
return true; return true;
} }
return false; false
} }
pub fn is_valid_version(version: &str) -> bool { pub fn is_valid_version(version: &str) -> bool {
@ -102,14 +102,10 @@ impl StartLine {
} }
}; };
if major <= 9 && minor <= 9 { return major <= 9 && minor <= 9;
return true;
} else {
return false;
}
} else {
return false;
} }
false
} }
} }
@ -135,7 +131,7 @@ fn parse_start_line(input: &str) -> Result<StartLine, Vec<u8>> {
response_field_lines.insert(String::from("Content-Type"), String::from("text/plain")); response_field_lines.insert(String::from("Content-Type"), String::from("text/plain"));
return Err(response_builder( return Err(response_builder(
RequestMethods::GET, RequestMethods::Get,
"HTTP/1.1 400 ", "HTTP/1.1 400 ",
Some(response_field_lines), Some(response_field_lines),
Some(response_body), Some(response_body),
@ -146,31 +142,64 @@ fn parse_start_line(input: &str) -> Result<StartLine, Vec<u8>> {
let target = vec[1]; let target = vec[1];
let version = vec[2]; let version = vec[2];
if StartLine::is_valid_method(&method) if !StartLine::is_valid_method(method) {
&& StartLine::is_valid_target(&target)
&& StartLine::is_valid_version(&version)
{
// start_line.method will remain RequestMethods::NULL if it is not supported.
match method {
"GET" => start_line.method = RequestMethods::GET,
"HEAD" => start_line.method = RequestMethods::HEAD,
_ => start_line.method = RequestMethods::NULL,
}
start_line.target = target.to_string();
if version == "HTTP/1.1" || version == "HTTP/1.0" {
start_line.version = version.to_string();
}
} else {
return Err(response_builder( return Err(response_builder(
RequestMethods::HEAD, RequestMethods::Head,
"HTTP/1.1 501 ",
None,
None,
));
}
// start_line.method will remain RequestMethods::NULL if it is not supported.
match method {
"GET" => start_line.method = RequestMethods::Get,
"HEAD" => start_line.method = RequestMethods::Head,
_ => start_line.method = RequestMethods::Null,
}
if !StartLine::is_valid_version(version) {
return Err(response_builder(
RequestMethods::Head,
"HTTP/1.1 400 ", "HTTP/1.1 400 ",
None, None,
None, None,
)); ));
} }
if version != "HTTP/1.1" && version != "HTTP/1.0" {
"Server only supports major version 1 of HTTP"
.as_bytes()
.iter()
.for_each(|byte| response_body.push(*byte));
response_field_lines.insert(
String::from("Content-Length"),
response_body.len().to_string(),
);
response_field_lines.insert(String::from("Content-Type"), String::from("text/plain"));
return Err(response_builder(
RequestMethods::Head,
"HTTP/1.1 505 ",
Some(response_field_lines),
Some(response_body),
));
}
start_line.version = version.to_string();
if !StartLine::is_valid_target(target) {
return Err(response_builder(
RequestMethods::Head,
"HTTP/1.1 400 ",
None,
None,
));
}
start_line.target = target.to_string();
Ok(start_line) Ok(start_line)
} }
@ -199,7 +228,7 @@ fn parse_field_lines(
response_field_lines.insert(String::from("Content-Type"), String::from("text/plain")); response_field_lines.insert(String::from("Content-Type"), String::from("text/plain"));
return Err(response_builder( return Err(response_builder(
RequestMethods::GET, RequestMethods::Get,
"HTTP/1.1 400 ", "HTTP/1.1 400 ",
Some(response_field_lines), Some(response_field_lines),
Some(response_body), Some(response_body),
@ -226,7 +255,7 @@ fn parse_field_lines(
.insert(String::from("Content-Type"), String::from("text/plain")); .insert(String::from("Content-Type"), String::from("text/plain"));
return Err(response_builder( return Err(response_builder(
RequestMethods::GET, RequestMethods::Get,
"HTTP/1.1 400 ", "HTTP/1.1 400 ",
Some(response_field_lines), Some(response_field_lines),
Some(response_body), Some(response_body),
@ -250,14 +279,14 @@ fn parse_field_lines(
response_field_lines.insert(String::from("Content-Type"), String::from("text/plain")); response_field_lines.insert(String::from("Content-Type"), String::from("text/plain"));
return Err(response_builder( return Err(response_builder(
RequestMethods::GET, RequestMethods::Get,
"HTTP/1.1 400 ", "HTTP/1.1 400 ",
Some(response_field_lines), Some(response_field_lines),
Some(response_body), Some(response_body),
)); ));
} }
return Ok(field_lines); Ok(field_lines)
} }
fn response_builder( fn response_builder(
@ -275,47 +304,41 @@ fn response_builder(
response.push(b'\r'); response.push(b'\r');
response.push(b'\n'); response.push(b'\n');
match field_lines { if let Some(val) = field_lines {
Some(val) => { for field_line in val.iter() {
for field_line in val.iter() { field_line
field_line .0
.0 .as_bytes()
.as_bytes() .iter()
.iter() .for_each(|byte| response.push(*byte));
.for_each(|byte| response.push(*byte));
response.push(b':'); response.push(b':');
response.push(b' '); response.push(b' ');
field_line field_line
.1 .1
.as_bytes() .as_bytes()
.iter() .iter()
.for_each(|byte| response.push(*byte)); .for_each(|byte| response.push(*byte));
response.push(b'\r'); response.push(b'\r');
response.push(b'\n'); response.push(b'\n');
}
} }
None => (),
} }
// Mandatory empty line between header and body // Mandatory empty line between header and body
response.push(b'\r'); response.push(b'\r');
response.push(b'\n'); response.push(b'\n');
if method != RequestMethods::HEAD { if method != RequestMethods::Head {
match body { if let Some(val) = body {
Some(val) => { val.iter().for_each(|byte| response.push(*byte));
val.iter().for_each(|byte| response.push(*byte)); response.push(b'\r');
response.push(b'\r'); response.push(b'\n');
response.push(b'\n');
}
None => (),
} }
} }
return response; response
} }
fn try_get_file(start_line: &StartLine, _field_lines: &HashMap<String, String>) -> Vec<u8> { fn try_get_file(start_line: &StartLine, _field_lines: &HashMap<String, String>) -> Vec<u8> {
@ -387,7 +410,7 @@ fn handle_request(mut stream: TcpStream) -> Result<(), Box<dyn Error>> {
response_field_lines.insert(String::from("Content-Type"), String::from("text/plain")); response_field_lines.insert(String::from("Content-Type"), String::from("text/plain"));
let response = response_builder( let response = response_builder(
RequestMethods::GET, RequestMethods::Get,
"HTTP/1.1 400 ", "HTTP/1.1 400 ",
Some(response_field_lines), Some(response_field_lines),
Some(response_body), Some(response_body),
@ -419,7 +442,7 @@ fn handle_request(mut stream: TcpStream) -> Result<(), Box<dyn Error>> {
let response = match start_line.target.as_str() { let response = match start_line.target.as_str() {
// For docker healtcheck. If the server can properly respond, then it must be healthy. // For docker healtcheck. If the server can properly respond, then it must be healthy.
"/server-health" => response_builder(RequestMethods::HEAD, "HTTP/1.1 200 ", None, None), "/server-health" => response_builder(RequestMethods::Head, "HTTP/1.1 200 ", None, None),
"/server-stats" => response_builder(start_line.method, "HTTP/1.1 404 ", None, None), "/server-stats" => response_builder(start_line.method, "HTTP/1.1 404 ", None, None),
"/server-info" => response_builder(start_line.method, "HTTP/1.1 404 ", None, None), "/server-info" => response_builder(start_line.method, "HTTP/1.1 404 ", None, None),
_ => try_get_file(&start_line, &field_lines), _ => try_get_file(&start_line, &field_lines),
@ -434,7 +457,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// TODO: Gracefully shutdown server // TODO: Gracefully shutdown server
thread::spawn(move || { thread::spawn(move || {
for sig in signals.forever() { if let Some(sig) = signals.forever().next() {
println!("Received signal {:?}", sig); println!("Received signal {:?}", sig);
println!("Shutting down"); println!("Shutting down");
exit(1); exit(1);