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 { 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) } } impl<'a> Into for HTTPStartLine<'a> { fn into(self) -> String { let version: String = self.version.into(); return format!("{} {} {}", self.method, self.target, version); } } #[test] fn test_parse() { 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); } } } #[test] fn test_into_string() { let sl: String = HTTPStartLine::new("POST", "/health", HTTPVersion::Http2).into(); assert_eq!("POST /health HTTP/2".to_string(), sl); }