feat: #1 add HTTPStartLine struct to parse request start line + some fixes

This commit is contained in:
landrigun 2022-09-09 11:08:35 +01:00
parent 2e097bbedb
commit 3afe437143
2 changed files with 78 additions and 34 deletions

View File

@ -4,72 +4,118 @@
#[derive(Debug)] #[derive(Debug)]
pub enum HTTPVersion { pub enum HTTPVersion {
HTTP1, Http1,
HTTP2, //http2,
} }
impl Into<String> for HTTPVersion { impl Into<String> for HTTPVersion {
fn into(self) -> String { fn into(self) -> String {
match self { match self {
HTTP1 => "HTTP/1.1".to_string(), Self::Http1 => "HTTP/1.1".to_string(),
HTTP2 => "HTTP/2.2".to_string(), //Self::Http2 => "HTTP/2.2".to_string(),
_ => "UNKNOWN".to_string(),
} }
} }
} }
/// Request defined the HTTP request
// TODO: method, target, version must be set in new struct : `HTTPStartLine`
#[derive(Debug)] #[derive(Debug)]
pub struct HTTPRequest { pub struct HTTPStartLine {
pub method: String, pub method: String,
pub target: String, pub target: String,
pub version: HTTPVersion, pub version: HTTPVersion,
} }
impl HTTPRequest { impl HTTPStartLine {
// associated function to build a new HTTPRequest
fn new(method: String, target: String, version: HTTPVersion) -> Self { fn new(method: String, target: String, version: HTTPVersion) -> Self {
HTTPRequest { HTTPStartLine {
method, method,
target, target,
version, version,
} }
} }
fn parse(request: &str) -> Result<Self, &str> { fn parse(start_line: &str) -> Result<Self, &str> {
// declare a new `request` var to borrow to &str `request` // declare a new `start_line` var to borrow to &str `start_line`
let request = request.to_string(); let start_line = start_line.to_string();
let request_parts: Vec<&str> = request.split(" ").collect(); let parts: Vec<&str> = start_line.split(" ").collect();
if request_parts.len() < 3 { if parts.len() < 3 {
return Err("unable to parse the request correctly"); return Err("unable to parse the start correctly");
} }
Ok(HTTPRequest::new( // TODO: parse correctly the different parts (using regex ?)
request_parts[0].to_string(), Ok(HTTPStartLine::new(
request_parts[1].to_string(), parts[0].to_string(),
HTTPVersion::HTTP1, parts[1].to_string(),
HTTPVersion::Http1,
)) ))
} }
fn is_valid(self) -> bool { fn is_valid(self) -> bool {
return self.method != "" && self.target != ""; return self.method != "" && self.target != "";
} }
}
// TODO: to be tested impl Default for HTTPStartLine {
pub fn start_line(self) -> String { fn default() -> Self {
HTTPStartLine {
method: "".to_string(),
target: "".to_string(),
version: HTTPVersion::Http1,
}
}
}
impl Into<String> for HTTPStartLine {
fn into(self) -> String {
let version: String = self.version.into(); let version: String = self.version.into();
return format!("{} {} {}", self.method, self.target, version); 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<HTTPRequest, &str> {
// 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 { impl Default for HTTPRequest {
fn default() -> Self { fn default() -> Self {
HTTPRequest { HTTPRequest {
method: "".to_string(), start_line: HTTPStartLine::default(),
target: "".to_string(),
version: HTTPVersion::HTTP1,
} }
} }
} }
@ -92,8 +138,12 @@ pub fn handle_request(request: &str) -> HTTPRequest {
#[test] #[test]
fn test_handle_request() { fn test_handle_request() {
let test_cases: [(&str, bool); 5] = [ let test_cases: [(&str, bool); 6] = [
("GET / HTTP/1.1", true), ("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), ("POST HTTP/1.1", false),
("", false), ("", false),
("fjlqskjd /oks?id=65 HTTP/2", true), ("fjlqskjd /oks?id=65 HTTP/2", true),

View File

@ -1,13 +1,9 @@
mod handlers; mod handlers;
use std::fs;
use std::io::prelude::*; use std::io::prelude::*;
use std::net::TcpListener; use std::net::TcpListener;
use std::net::TcpStream; use std::net::TcpStream;
use async_std::task;
use std::time::Duration;
use handlers::handle_request; use handlers::handle_request;
// TODO: must be set in a conf file // TODO: must be set in a conf file
@ -28,14 +24,12 @@ async fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 1024]; let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap(); stream.read(&mut buffer).unwrap();
let get = b"GET / HTTP/1.1\r\n";
// transform buffer bytes array into `String` // transform buffer bytes array into `String`
let buffer_string = String::from_utf8_lossy(&buffer); let buffer_string = String::from_utf8_lossy(&buffer);
let request = handle_request(&buffer_string); let request = handle_request(&buffer_string);
// TODO: `Response` struct must be implemented // 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" "HTTP/1.1 200 OK\r\n"
} else { } else {
"HTTP/1.1 404 NOT FOUND\r\n" "HTTP/1.1 404 NOT FOUND\r\n"