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; | use json; | ||||||
| 
 | 
 | ||||||
|  | type RequestParts = (String, String, String); | ||||||
|  | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub enum HTTPVersion { | pub enum HTTPVersion { | ||||||
|     Http1, |     Http1, | ||||||
| @ -76,7 +78,8 @@ impl Into<String> for HTTPStartLine { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// HTTPBody represents http request body
 | /// HTTPBody represents http request body
 | ||||||
| /// for simplicity, only json body are accepted
 | /// for simplicity, only json body is accepted
 | ||||||
|  | #[derive(Debug)] | ||||||
| pub struct HTTPBody { | pub struct HTTPBody { | ||||||
|     data: json::JsonValue, |     data: json::JsonValue, | ||||||
| } | } | ||||||
| @ -87,10 +90,10 @@ impl HTTPBody { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl TryFrom<&str> for HTTPBody { | impl TryFrom<String> for HTTPBody { | ||||||
|     type Error = String; |     type Error = String; | ||||||
|     fn try_from(body: &str) -> Result<HTTPBody, Self::Error> { |     fn try_from(body: String) -> Result<HTTPBody, Self::Error> { | ||||||
|         match json::parse(body) { |         match json::parse(&body) { | ||||||
|             Ok(v) => Ok(HTTPBody::new(v)), |             Ok(v) => Ok(HTTPBody::new(v)), | ||||||
|             Err(e) => Err(format!( |             Err(e) => Err(format!( | ||||||
|                 "error occurred during request body parsing err={}", |                 "error occurred during request body parsing err={}", | ||||||
| @ -104,35 +107,86 @@ impl TryFrom<&str> for HTTPBody { | |||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct HTTPRequest { | pub struct HTTPRequest { | ||||||
|     pub start_line: HTTPStartLine, |     pub start_line: HTTPStartLine, | ||||||
|  |     pub body: Option<HTTPBody>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl HTTPRequest { | impl HTTPRequest { | ||||||
|     // associated function to build a new HTTPRequest
 |     // associated function to build a new HTTPRequest
 | ||||||
|     fn new(start_line: HTTPStartLine) -> Self { |     fn new(start_line: HTTPStartLine, body: Option<HTTPBody>) -> Self { | ||||||
|         HTTPRequest { start_line } |         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`
 |     /// 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`
 |         // declare a new `request` var to borrow to &str `request`
 | ||||||
|         let request = request.to_string(); |         let request = request.to_string(); | ||||||
| 
 | 
 | ||||||
|         let request_parts: Vec<&str> = request.split("\r\n").collect(); |         match HTTPRequest::get_request_parts(&request) { | ||||||
|         println!("request part : {:?}", request_parts); |             Ok(rp) => { | ||||||
|         // TODO: increase the check to 3 to match the request (for now only the start_line)
 |                 let mut request = HTTPRequest::default(); | ||||||
|         if request_parts.len() < 1 { | 
 | ||||||
|             return Err("unable to parse the request correctly"); |                 let start_line = HTTPStartLine::parse(&rp.0); | ||||||
|  |                 match start_line { | ||||||
|  |                     Ok(v) => request.start_line = v, | ||||||
|  |                     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)); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         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 { |     fn is_valid(self) -> bool { | ||||||
| @ -144,6 +198,7 @@ impl Default for HTTPRequest { | |||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         HTTPRequest { |         HTTPRequest { | ||||||
|             start_line: HTTPStartLine::default(), |             start_line: HTTPStartLine::default(), | ||||||
|  |             body: None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -166,16 +221,17 @@ pub fn handle_request(request: &str) -> HTTPRequest { | |||||||
| 
 | 
 | ||||||
| #[test] | #[test] | ||||||
| fn test_handle_request() { | fn test_handle_request() { | ||||||
|     let test_cases: [(&str, bool); 6] = [ |     let test_cases: [(&str, bool); 7] = [ | ||||||
|         ("GET / HTTP/1.1", true), |         ("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, |             true, | ||||||
|         ), // intentionally add HTTP with no version number
 |         ), // intentionally add HTTP with no version number
 | ||||||
|         ("POST HTTP/1.1", false), |         ("POST HTTP/1.1", false), | ||||||
|         ("", false), |         ("", false), | ||||||
|         ("fjlqskjd /oks?id=65 HTTP/2", true), |         ("fjlqskjd /oks?id=65 HTTP/2\r\n\r\n", true), | ||||||
|         ("   ", false), |         ("   ", false), | ||||||
|  |         ("lm //// skkss\r\ndkldklkdl\r\n", true), | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     for (request, is_valid) in test_cases { |     for (request, is_valid) in test_cases { | ||||||
| @ -197,7 +253,7 @@ fn test_http_body() { | |||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     for (body, is_valid) in test_cases { |     for (body, is_valid) in test_cases { | ||||||
|         match HTTPBody::try_from(body) { |         match HTTPBody::try_from(body.to_string()) { | ||||||
|             Ok(_) => assert!(is_valid), |             Ok(_) => assert!(is_valid), | ||||||
|             Err(_) => assert!(!is_valid), |             Err(_) => assert!(!is_valid), | ||||||
|         } |         } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user