use std::fmt;
use super::header::{COOKIE, HeaderVec};
use super::request_cookie::RequestCookie;
use super::url::Url;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Request {
pub url: Url,
pub method: String,
pub headers: HeaderVec,
pub body: Vec<u8>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub enum RequestedHttpVersion {
#[default]
Default,
Http10,
Http11,
Http2,
Http3,
}
impl fmt::Display for RequestedHttpVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let value = match self {
RequestedHttpVersion::Default => "HTTP (default)",
RequestedHttpVersion::Http10 => "HTTP/1.0",
RequestedHttpVersion::Http11 => "HTTP/1.1",
RequestedHttpVersion::Http2 => "HTTP/2",
RequestedHttpVersion::Http3 => "HTTP/3",
};
write!(f, "{value}")
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub enum IpResolve {
#[default]
Default,
IpV4,
IpV6,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub enum FollowLocation {
#[default]
No,
Follow(CredentialForwarding),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub enum CredentialForwarding {
#[default]
OnlyInitialHost,
AllHosts,
}
impl Request {
pub fn new(method: &str, url: Url, headers: HeaderVec, body: Vec<u8>) -> Self {
Request {
url,
method: method.to_string(),
headers,
body,
}
}
pub fn cookies(&self) -> Vec<RequestCookie> {
self.headers
.get_all(COOKIE)
.iter()
.flat_map(|h| parse_cookies(h.value.as_str().trim()))
.collect()
}
}
fn parse_cookies(s: &str) -> Vec<RequestCookie> {
s.split(';').map(|t| parse_cookie(t.trim())).collect()
}
fn parse_cookie(s: &str) -> RequestCookie {
match s.find('=') {
Some(i) => RequestCookie {
name: s.split_at(i).0.to_string(),
value: s.split_at(i + 1).1.to_string(),
},
None => RequestCookie {
name: s.to_string(),
value: String::new(),
},
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::http::{Header, RequestCookie};
fn hello_request() -> Request {
let mut headers = HeaderVec::new();
headers.push(Header::new("Host", "localhost:8000"));
headers.push(Header::new("Accept", "*/*"));
headers.push(Header::new("User-Agent", "hurl/1.0"));
headers.push(Header::new("content-type", "application/json"));
let url = "http://localhost:8000/hello".parse().unwrap();
Request::new("GET", url, headers, vec![])
}
fn query_string_request() -> Request {
let url = "http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1%2C2%2C3".parse().unwrap();
Request::new("GET", url, HeaderVec::new(), vec![])
}
fn cookies_request() -> Request {
let mut headers = HeaderVec::new();
headers.push(Header::new("Cookie", "cookie1=value1; cookie2=value2"));
let url = "http://localhost:8000/cookies".parse().unwrap();
Request::new("GET", url, headers, vec![])
}
#[test]
fn test_content_type() {
assert_eq!(
hello_request().headers.content_type(),
Some("application/json")
);
assert_eq!(query_string_request().headers.content_type(), None);
assert_eq!(cookies_request().headers.content_type(), None);
}
#[test]
fn test_cookies() {
assert!(hello_request().cookies().is_empty());
assert_eq!(
cookies_request().cookies(),
vec![
RequestCookie {
name: "cookie1".to_string(),
value: "value1".to_string(),
},
RequestCookie {
name: "cookie2".to_string(),
value: "value2".to_string(),
},
]
);
}
#[test]
fn test_parse_cookies() {
assert_eq!(
parse_cookies("cookie1=value1; cookie2=value2"),
vec![
RequestCookie {
name: "cookie1".to_string(),
value: "value1".to_string(),
},
RequestCookie {
name: "cookie2".to_string(),
value: "value2".to_string(),
},
]
);
}
#[test]
fn test_parse_cookie() {
assert_eq!(
parse_cookie("cookie1=value1"),
RequestCookie {
name: "cookie1".to_string(),
value: "value1".to_string(),
},
);
}
}