feat: #1 rework on http request parser and include HTTPBody in HTTPRequest

This commit is contained in:
landrigun 2022-09-09 16:58:10 +01:00
parent e0d72b08cf
commit ce7e49af47

View File

@ -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,35 +107,86 @@ 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(&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 {
@ -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),
}