diff --git a/src/main.rs b/src/main.rs index 25a5da2..efe71b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,9 +12,9 @@ use std::{ #[derive(PartialEq, Eq, Debug, Copy, Clone)] enum RequestMethods { - NULL = -1, // This is only to initialise the struct - GET, - HEAD, + Null = -1, // This is only to initialise the struct + Get, + Head, } #[derive(Debug)] @@ -27,7 +27,7 @@ struct StartLine { impl StartLine { pub fn new() -> Self { Self { - method: RequestMethods::NULL, + method: RequestMethods::Null, target: String::new(), version: String::new(), } @@ -73,7 +73,7 @@ impl StartLine { return true; } - return false; + false } pub fn is_valid_version(version: &str) -> bool { @@ -102,14 +102,10 @@ impl StartLine { } }; - if major <= 9 && minor <= 9 { - return true; - } else { - return false; - } - } else { - return false; + return major <= 9 && minor <= 9; } + + false } } @@ -135,7 +131,7 @@ fn parse_start_line(input: &str) -> Result> { response_field_lines.insert(String::from("Content-Type"), String::from("text/plain")); return Err(response_builder( - RequestMethods::GET, + RequestMethods::Get, "HTTP/1.1 400 ", Some(response_field_lines), Some(response_body), @@ -146,31 +142,64 @@ fn parse_start_line(input: &str) -> Result> { let target = vec[1]; let version = vec[2]; - 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 { + if !StartLine::is_valid_method(method) { 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 ", 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) } @@ -199,7 +228,7 @@ fn parse_field_lines( response_field_lines.insert(String::from("Content-Type"), String::from("text/plain")); return Err(response_builder( - RequestMethods::GET, + RequestMethods::Get, "HTTP/1.1 400 ", Some(response_field_lines), Some(response_body), @@ -226,7 +255,7 @@ fn parse_field_lines( .insert(String::from("Content-Type"), String::from("text/plain")); return Err(response_builder( - RequestMethods::GET, + RequestMethods::Get, "HTTP/1.1 400 ", Some(response_field_lines), Some(response_body), @@ -250,14 +279,14 @@ fn parse_field_lines( response_field_lines.insert(String::from("Content-Type"), String::from("text/plain")); return Err(response_builder( - RequestMethods::GET, + RequestMethods::Get, "HTTP/1.1 400 ", Some(response_field_lines), Some(response_body), )); } - return Ok(field_lines); + Ok(field_lines) } fn response_builder( @@ -275,47 +304,41 @@ fn response_builder( response.push(b'\r'); response.push(b'\n'); - match field_lines { - Some(val) => { - for field_line in val.iter() { - field_line - .0 - .as_bytes() - .iter() - .for_each(|byte| response.push(*byte)); + if let Some(val) = field_lines { + for field_line in val.iter() { + field_line + .0 + .as_bytes() + .iter() + .for_each(|byte| response.push(*byte)); - response.push(b':'); - response.push(b' '); + response.push(b':'); + response.push(b' '); - field_line - .1 - .as_bytes() - .iter() - .for_each(|byte| response.push(*byte)); + field_line + .1 + .as_bytes() + .iter() + .for_each(|byte| response.push(*byte)); - response.push(b'\r'); - response.push(b'\n'); - } + response.push(b'\r'); + response.push(b'\n'); } - None => (), } // Mandatory empty line between header and body response.push(b'\r'); response.push(b'\n'); - if method != RequestMethods::HEAD { - match body { - Some(val) => { - val.iter().for_each(|byte| response.push(*byte)); - response.push(b'\r'); - response.push(b'\n'); - } - None => (), + if method != RequestMethods::Head { + if let Some(val) = body { + val.iter().for_each(|byte| response.push(*byte)); + response.push(b'\r'); + response.push(b'\n'); } } - return response; + response } fn try_get_file(start_line: &StartLine, _field_lines: &HashMap) -> Vec { @@ -387,7 +410,7 @@ fn handle_request(mut stream: TcpStream) -> Result<(), Box> { response_field_lines.insert(String::from("Content-Type"), String::from("text/plain")); let response = response_builder( - RequestMethods::GET, + RequestMethods::Get, "HTTP/1.1 400 ", Some(response_field_lines), Some(response_body), @@ -419,7 +442,7 @@ fn handle_request(mut stream: TcpStream) -> Result<(), Box> { let response = match start_line.target.as_str() { // 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-info" => response_builder(start_line.method, "HTTP/1.1 404 ", None, None), _ => try_get_file(&start_line, &field_lines), @@ -434,7 +457,7 @@ fn main() -> Result<(), Box> { // TODO: Gracefully shutdown server thread::spawn(move || { - for sig in signals.forever() { + if let Some(sig) = signals.forever().next() { println!("Received signal {:?}", sig); println!("Shutting down"); exit(1);