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