add http start line parsing + version

This commit is contained in:
landrigun 2023-02-15 10:41:27 +00:00
parent 1fc257d6b6
commit 48da068f31
6 changed files with 139 additions and 12 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
*.swp
/target
/Cargo.lock

View File

@ -6,3 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lazy_static = "1.4.0"
regex = "1"

View File

@ -1,14 +1,11 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
//! http parses the request according to the HTTP message specifications
//! it also includes `HTTPResponse` to build an HTTPResponse
//!
//! see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages
//! NOTE: only few parts of the specification has been implemented
#[cfg(test)]
mod tests {
use super::*;
mod start_line;
mod version;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
pub use start_line::HTTPStartLine;
pub use version::HTTPVersion;

6
src/request.rs Normal file
View File

@ -0,0 +1,6 @@
use std::collections::VecDeque;
type RequestParts = (String, VecDeque<String>, String);
const HTTP_REQUEST_SEPARATOR: &'static str = "\r\n";
const NULL_CHAR: &'static str = "\0";

91
src/start_line.rs Normal file
View File

@ -0,0 +1,91 @@
use lazy_static::lazy_static;
use regex::Regex;
use crate::version::HTTPVersion;
lazy_static! {
static ref HTTP_VERSION_REGEX: Regex = Regex::new("^HTTP/(1.1|1.0|2)$").unwrap();
}
#[derive(Debug)]
pub struct HTTPStartLine<'a> {
method: &'a str,
target: &'a str,
version: HTTPVersion,
}
impl<'a> HTTPStartLine<'a> {
fn new(method: &'a str, target: &'a str, version: HTTPVersion) -> Self {
HTTPStartLine {
method: &method,
target: &target,
version: version,
}
}
fn parse(start_line: &'a str) -> Result<Self, &str> {
let parts: Vec<&str> = start_line.split(" ").collect();
if parts.len() < 3 {
return Err("unable to parse the start correctly");
}
let (method, target, version) = (parts[0], parts[1], parts[2]);
if !Self::check_version(version) {
return Err("http version validation failed, unknown version");
}
Ok(HTTPStartLine::new(
method,
target,
HTTPVersion::from(version),
))
}
fn check_version(version: &str) -> bool {
HTTP_VERSION_REGEX.is_match(version)
}
}
#[test]
fn test_start_line() {
struct Expect<'a> {
method: &'a str,
target: &'a str,
version: HTTPVersion,
}
let test_cases: [(&str, Expect, bool); 2] = [
(
"POST /get/ HTTP/1.1",
Expect {
method: "POST",
target: "/get/",
version: HTTPVersion::Http1_1,
},
true,
),
(
"POST /get/ HTTP/1.1 blablablalfjhskfh",
Expect {
method: "",
target: "",
version: HTTPVersion::Unknown,
},
false,
),
];
for (start_line, expect, is_valid) in test_cases {
let res = HTTPStartLine::parse(start_line);
if is_valid {
assert!(res.is_ok());
let st = res.unwrap();
assert_eq!(expect.method, st.method);
assert_eq!(expect.target, st.target);
assert_eq!(expect.target, st.target);
}
}
}

29
src/version.rs Normal file
View File

@ -0,0 +1,29 @@
#[derive(Debug)]
pub enum HTTPVersion {
Http1_0,
Http1_1,
Http2,
Unknown,
}
impl Into<String> for HTTPVersion {
fn into(self) -> String {
match self {
Self::Http1_0 => "HTTP/1.0".to_string(),
Self::Http1_1 => "HTTP/1.1".to_string(),
Self::Http2 => "HTTP/2".to_string(),
Self::Unknown => "UNKNOWN".to_string(),
}
}
}
impl From<&str> for HTTPVersion {
fn from(http_version: &str) -> Self {
match http_version {
"HTTP/1.0" => Self::Http1_0,
"HTTP/1.1" => Self::Http1_1,
"HTTP/2" => Self::Http2,
_ => Self::Unknown,
}
}
}