diff --git a/src/main.rs b/src/main.rs index e227dbf..52a7f27 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,27 +1,141 @@ use std::{ + collections::HashMap, io::{BufRead, BufReader, Write}, - net::TcpListener, + net::{TcpListener, TcpStream}, }; +const VALID_METHODS: [&str; 8] = [ + "GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", +]; + +#[derive(Debug)] +struct StartLine { + method: String, + target: String, + version: String, +} + +impl StartLine { + pub fn new(input: String) -> Self { + let input_array = input.trim().split(" ").collect::>(); + + Self { + method: input_array[0].to_owned(), + target: input_array[1].to_owned(), + version: input_array[2].to_owned(), + } + } + + pub fn is_valid_method(input: &StartLine) -> bool { + if input.method.is_empty() { + return false; + } + let method: &str = input.method.as_str(); + + if VALID_METHODS.contains(&method) { + return true; + } else { + return false; + } + } +} + +fn handle_request(mut stream: TcpStream) { + let mut reader = BufReader::new(&mut stream); + + let mut start_line = String::new(); + reader.read_line(&mut start_line).unwrap(); + + // I will not support ignoring the CR + if !start_line.ends_with("\r\n") { + stream + .write_all(b"HTTP/1.1 400 Bad Request\r\n\r\nLines must end with CRLF") + .unwrap(); + return; + } + + // Request can have one or many empty lines preceding the request-line and I will ignore these + loop { + if !start_line.trim().is_empty() { + break; + } + reader.read_line(&mut start_line).unwrap(); + } + + if start_line.trim().is_empty() { + reader.read_line(&mut start_line).unwrap(); + } + if start_line.trim().is_empty() { + stream + .write_all(b"HTTP/1.1 400 Bad Request\r\n\r\nReceived too many preceding empty lines") + .unwrap(); + return; + } + + let start_line = StartLine::new(start_line); + dbg!(&start_line); + + if !StartLine::is_valid_method(&start_line) { + stream + .write_all(b"HTTP/1.1 400 Bad Request\r\n\r\nInvalid request method") + .unwrap(); + return; + } + + if start_line.method != "GET" { + stream + .write_all(b"HTTP/1.1 501 Not Implemented\r\n\r\nServer currently only supports GET") + .unwrap(); + return; + } + + let mut is_first_line = true; + let mut field_lines: HashMap = HashMap::new(); + + loop { + let mut line = String::new(); + reader.read_line(&mut line).unwrap(); + print!("{line}"); + + if line.starts_with(" ") { + if !is_first_line { + continue; + } + stream + .write_all(b"HTTP/1.1 400 Bad Request\r\n\r\nCannot have whitespace between the start-line and field-lines") + .unwrap(); + return; + } + + if !line.ends_with("\r\n") { + stream + .write_all(b"HTTP/1.1 400 Bad Request\r\n\r\nLines must end with CRLF") + .unwrap(); + return; + } + + if line.trim().is_empty() { + break; + } + + let field_line = line.split_once(":").expect("Shits fucked: {}"); + field_lines.insert(field_line.0.to_owned(), field_line.1.trim().to_owned()); + + is_first_line = false; + } + + dbg!(&field_lines); + + stream + .write_all(b"HTTP/1.1 200 OK\r\n\r\nHello, World!") + .unwrap(); +} + fn main() { let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); for stream in listener.incoming() { - let mut stream = stream.unwrap(); - let mut reader = BufReader::new(&mut stream); - - loop { - let mut line = String::new(); - reader.read_line(&mut line).unwrap(); - print!("{line}"); - - if line.trim().is_empty() { - break; - } - } - - stream - .write_all(b"HTTP/1.1 200 OK\r\n\r\nHello, World!") - .unwrap(); + let stream = stream.unwrap(); + handle_request(stream) } }