add http start line parsing + version
This commit is contained in:
parent
1fc257d6b6
commit
48da068f31
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,4 @@
|
|||||||
|
*.swp
|
||||||
|
|
||||||
/target
|
/target
|
||||||
/Cargo.lock
|
/Cargo.lock
|
||||||
|
|||||||
@ -6,3 +6,5 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
regex = "1"
|
||||||
|
|||||||
21
src/lib.rs
21
src/lib.rs
@ -1,14 +1,11 @@
|
|||||||
pub fn add(left: usize, right: usize) -> usize {
|
//! http parses the request according to the HTTP message specifications
|
||||||
left + right
|
//! 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 start_line;
|
||||||
mod tests {
|
mod version;
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
pub use start_line::HTTPStartLine;
|
||||||
fn it_works() {
|
pub use version::HTTPVersion;
|
||||||
let result = add(2, 2);
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
6
src/request.rs
Normal file
6
src/request.rs
Normal 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
91
src/start_line.rs
Normal 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
29
src/version.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user