feat: #1 rework on http request parser and include HTTPBody in HTTPRequest
This commit is contained in:
		
							parent
							
								
									e0d72b08cf
								
							
						
					
					
						commit
						ce7e49af47
					
				| @ -5,6 +5,8 @@ | ||||
| 
 | ||||
| use json; | ||||
| 
 | ||||
| type RequestParts = (String, String, String); | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum HTTPVersion { | ||||
|     Http1, | ||||
| @ -76,7 +78,8 @@ impl Into<String> for HTTPStartLine { | ||||
| } | ||||
| 
 | ||||
| /// HTTPBody represents http request body
 | ||||
| /// for simplicity, only json body are accepted
 | ||||
| /// for simplicity, only json body is accepted
 | ||||
| #[derive(Debug)] | ||||
| pub struct HTTPBody { | ||||
|     data: json::JsonValue, | ||||
| } | ||||
| @ -87,10 +90,10 @@ impl HTTPBody { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<&str> for HTTPBody { | ||||
| impl TryFrom<String> for HTTPBody { | ||||
|     type Error = String; | ||||
|     fn try_from(body: &str) -> Result<HTTPBody, Self::Error> { | ||||
|         match json::parse(body) { | ||||
|     fn try_from(body: String) -> Result<HTTPBody, Self::Error> { | ||||
|         match json::parse(&body) { | ||||
|             Ok(v) => Ok(HTTPBody::new(v)), | ||||
|             Err(e) => Err(format!( | ||||
|                 "error occurred during request body parsing err={}", | ||||
| @ -104,36 +107,87 @@ impl TryFrom<&str> for HTTPBody { | ||||
| #[derive(Debug)] | ||||
| pub struct HTTPRequest { | ||||
|     pub start_line: HTTPStartLine, | ||||
|     pub body: Option<HTTPBody>, | ||||
| } | ||||
| 
 | ||||
| impl HTTPRequest { | ||||
|     // associated function to build a new HTTPRequest
 | ||||
|     fn new(start_line: HTTPStartLine) -> Self { | ||||
|         HTTPRequest { start_line } | ||||
|     fn new(start_line: HTTPStartLine, body: Option<HTTPBody>) -> Self { | ||||
|         HTTPRequest { start_line, body } | ||||
|     } | ||||
| 
 | ||||
|     /// get mandatory request informations :
 | ||||
|     /// * start_line
 | ||||
|     /// * headers
 | ||||
|     fn get_request_mandats(request_parts: Vec<&str>) -> Result<(String, String), String> { | ||||
|         let headers_sline: Vec<&str> = request_parts[0].split("\r\n").collect(); | ||||
|         match headers_sline.len() { | ||||
|             0 => return Err("request does not contain start_line or headers".to_string()), | ||||
|             1 => return Ok((headers_sline[0].to_string(), "".to_string())), | ||||
|             // TODO: check if in the spec it must be 2 or 3 !
 | ||||
|             2 | 3 => return Ok((headers_sline[0].to_string(), headers_sline[1].to_string())), | ||||
|             _ => return Err("bad start_line headers parsing".to_string()), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// split correctly the incoming request in order to get :
 | ||||
|     /// * start_line
 | ||||
|     /// * headers
 | ||||
|     /// * data (if exists)
 | ||||
|     fn get_request_parts(request: &str) -> Result<RequestParts, String> { | ||||
|         // separate the body part from the start_line and the headers
 | ||||
|         let request_parts: Vec<&str> = request.split("\r\n\r\n").collect(); | ||||
|         if request_parts.len() == 0 { | ||||
|             return Err("request has no enough informations to be correctly parsed".to_string()); | ||||
|         } | ||||
| 
 | ||||
|         match request_parts.len() { | ||||
|             0 => { | ||||
|                 return Err("request has no enough informations to be correctly parsed".to_string()) | ||||
|             } | ||||
|             1 => match HTTPRequest::get_request_mandats(request_parts) { | ||||
|                 Ok(v) => return Ok((v.0, v.1, "".to_string())), | ||||
|                 Err(e) => return Err(e), | ||||
|             }, | ||||
|             2 => { | ||||
|                 let body = request_parts[1]; | ||||
|                 match HTTPRequest::get_request_mandats(request_parts) { | ||||
|                     Ok(v) => return Ok((v.0, v.1, body.to_string())), | ||||
|                     Err(e) => return Err(e), | ||||
|                 } | ||||
|             } | ||||
|             _ => return Err("bad incoming request, impossible to parse".to_string()), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// parse parses the request by spliting the incoming request with the separator `\r\n`
 | ||||
|     fn parse(request: &str) -> Result<HTTPRequest, &str> { | ||||
|     fn parse(request: &str) -> Result<HTTPRequest, String> { | ||||
|         // 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"); | ||||
|         } | ||||
| 
 | ||||
|         match HTTPRequest::get_request_parts(&request) { | ||||
|             Ok(rp) => { | ||||
|                 let mut request = HTTPRequest::default(); | ||||
| 
 | ||||
|         let start_line = HTTPStartLine::parse(request_parts[0]); | ||||
|                 let start_line = HTTPStartLine::parse(&rp.0); | ||||
|                 match start_line { | ||||
|                     Ok(v) => request.start_line = v, | ||||
|             Err(e) => eprintln!("error occured while parsing start_line err={}", e), | ||||
|                     Err(e) => eprintln!("error occurred while parsing start_line err={}", e), | ||||
|                 } | ||||
| 
 | ||||
|                 let body = HTTPBody::try_from(rp.2); | ||||
|                 match body { | ||||
|                     Ok(v) => request.body = Some(v), | ||||
|                     Err(e) => eprintln!("error occurred during body parsing err={}", e), | ||||
|                 } | ||||
| 
 | ||||
|                 return Ok(request); | ||||
|             } | ||||
|             Err(e) => { | ||||
|                 return Err(format!("error occurred getting request parts err={}", e)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn is_valid(self) -> bool { | ||||
|         return self.start_line.is_valid(); | ||||
| @ -144,6 +198,7 @@ impl Default for HTTPRequest { | ||||
|     fn default() -> Self { | ||||
|         HTTPRequest { | ||||
|             start_line: HTTPStartLine::default(), | ||||
|             body: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -166,16 +221,17 @@ pub fn handle_request(request: &str) -> HTTPRequest { | ||||
| 
 | ||||
| #[test] | ||||
| fn test_handle_request() { | ||||
|     let test_cases: [(&str, bool); 6] = [ | ||||
|         ("GET / HTTP/1.1", true), | ||||
|     let test_cases: [(&str, bool); 7] = [ | ||||
|         ("GET / HTTP/1.1\r\n\r\n", true), | ||||
|         ( | ||||
|             "OPTIONS /admin/2 HTTP/\r\nContent-Type: application/json", | ||||
|             "OPTIONS /admin/2 HTTP/\r\nContent-Type: application/json\r\n", | ||||
|             true, | ||||
|         ), // intentionally add HTTP with no version number
 | ||||
|         ("POST HTTP/1.1", false), | ||||
|         ("", false), | ||||
|         ("fjlqskjd /oks?id=65 HTTP/2", true), | ||||
|         ("fjlqskjd /oks?id=65 HTTP/2\r\n\r\n", true), | ||||
|         ("   ", false), | ||||
|         ("lm //// skkss\r\ndkldklkdl\r\n", true), | ||||
|     ]; | ||||
| 
 | ||||
|     for (request, is_valid) in test_cases { | ||||
| @ -197,7 +253,7 @@ fn test_http_body() { | ||||
|     ]; | ||||
| 
 | ||||
|     for (body, is_valid) in test_cases { | ||||
|         match HTTPBody::try_from(body) { | ||||
|         match HTTPBody::try_from(body.to_string()) { | ||||
|             Ok(_) => assert!(is_valid), | ||||
|             Err(_) => assert!(!is_valid), | ||||
|         } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user