From 3afe437143aca62e6df8d79c5971d98f3a4c1c80 Mon Sep 17 00:00:00 2001 From: landrigun Date: Fri, 9 Sep 2022 11:08:35 +0100 Subject: [PATCH] feat: #1 add HTTPStartLine struct to parse request start line + some fixes --- src/handlers/request.rs | 104 +++++++++++++++++++++++++++++----------- src/main.rs | 8 +--- 2 files changed, 78 insertions(+), 34 deletions(-) diff --git a/src/handlers/request.rs b/src/handlers/request.rs index 2e37cff..583508c 100644 --- a/src/handlers/request.rs +++ b/src/handlers/request.rs @@ -4,72 +4,118 @@ #[derive(Debug)] pub enum HTTPVersion { - HTTP1, - HTTP2, + Http1, + //http2, } impl Into for HTTPVersion { fn into(self) -> String { match self { - HTTP1 => "HTTP/1.1".to_string(), - HTTP2 => "HTTP/2.2".to_string(), - _ => "UNKNOWN".to_string(), + Self::Http1 => "HTTP/1.1".to_string(), + //Self::Http2 => "HTTP/2.2".to_string(), } } } -/// Request defined the HTTP request -// TODO: method, target, version must be set in new struct : `HTTPStartLine` #[derive(Debug)] -pub struct HTTPRequest { +pub struct HTTPStartLine { pub method: String, pub target: String, pub version: HTTPVersion, } -impl HTTPRequest { - // associated function to build a new HTTPRequest +impl HTTPStartLine { fn new(method: String, target: String, version: HTTPVersion) -> Self { - HTTPRequest { + HTTPStartLine { method, target, version, } } - fn parse(request: &str) -> Result { - // declare a new `request` var to borrow to &str `request` - let request = request.to_string(); + fn parse(start_line: &str) -> Result { + // declare a new `start_line` var to borrow to &str `start_line` + let start_line = start_line.to_string(); - let request_parts: Vec<&str> = request.split(" ").collect(); - if request_parts.len() < 3 { - return Err("unable to parse the request correctly"); + let parts: Vec<&str> = start_line.split(" ").collect(); + if parts.len() < 3 { + return Err("unable to parse the start correctly"); } - Ok(HTTPRequest::new( - request_parts[0].to_string(), - request_parts[1].to_string(), - HTTPVersion::HTTP1, + // TODO: parse correctly the different parts (using regex ?) + Ok(HTTPStartLine::new( + parts[0].to_string(), + parts[1].to_string(), + HTTPVersion::Http1, )) } fn is_valid(self) -> bool { return self.method != "" && self.target != ""; } +} - // TODO: to be tested - pub fn start_line(self) -> String { +impl Default for HTTPStartLine { + fn default() -> Self { + HTTPStartLine { + method: "".to_string(), + target: "".to_string(), + version: HTTPVersion::Http1, + } + } +} + +impl Into for HTTPStartLine { + fn into(self) -> String { let version: String = self.version.into(); return format!("{} {} {}", self.method, self.target, version); } } +/// Request defined the HTTP request +#[derive(Debug)] +pub struct HTTPRequest { + pub start_line: HTTPStartLine, +} + +impl HTTPRequest { + // associated function to build a new HTTPRequest + fn new(start_line: HTTPStartLine) -> Self { + HTTPRequest { start_line } + } + + /// parse parses the request by spliting the incoming request with the separator `\r\n` + fn parse(request: &str) -> Result { + // declare a new `request` var to borrow to &str `request` + let request = request.to_string(); + + let request_parts: Vec<&str> = request.split("\r\n").collect(); + println!("request part : {:?}", request_parts); + // TODO: increase the check to 3 to match the request (for now only the start_line) + if request_parts.len() < 1 { + return Err("unable to parse the request correctly"); + } + + let mut request = HTTPRequest::default(); + + let start_line = HTTPStartLine::parse(request_parts[0]); + match start_line { + Ok(v) => request.start_line = v, + Err(e) => eprintln!("error occured while parsing start_line err={}", e), + } + + return Ok(request); + } + + fn is_valid(self) -> bool { + return self.start_line.is_valid(); + } +} + impl Default for HTTPRequest { fn default() -> Self { HTTPRequest { - method: "".to_string(), - target: "".to_string(), - version: HTTPVersion::HTTP1, + start_line: HTTPStartLine::default(), } } } @@ -92,8 +138,12 @@ pub fn handle_request(request: &str) -> HTTPRequest { #[test] fn test_handle_request() { - let test_cases: [(&str, bool); 5] = [ + let test_cases: [(&str, bool); 6] = [ ("GET / HTTP/1.1", true), + ( + "OPTIONS /admin/2 HTTP/\r\nContent-Type: application/json", + true, + ), // intentionally add HTTP with no version number ("POST HTTP/1.1", false), ("", false), ("fjlqskjd /oks?id=65 HTTP/2", true), diff --git a/src/main.rs b/src/main.rs index 9dc5ba4..fb4b02a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,9 @@ mod handlers; -use std::fs; use std::io::prelude::*; use std::net::TcpListener; use std::net::TcpStream; -use async_std::task; -use std::time::Duration; - use handlers::handle_request; // TODO: must be set in a conf file @@ -28,14 +24,12 @@ async fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 1024]; stream.read(&mut buffer).unwrap(); - let get = b"GET / HTTP/1.1\r\n"; - // transform buffer bytes array into `String` let buffer_string = String::from_utf8_lossy(&buffer); let request = handle_request(&buffer_string); // TODO: `Response` struct must be implemented - let status_line = if request.target == "/".to_string() { + let status_line = if request.start_line.target == "/".to_string() { "HTTP/1.1 200 OK\r\n" } else { "HTTP/1.1 404 NOT FOUND\r\n"