use crate::http_header::HttpHeader;
use crate::http_method::HttpMethod;
use crate::http_version::HttpVersion;
#[derive(Debug, Clone)]
pub struct HttpRequest {
method: HttpMethod,
path: String,
version: HttpVersion,
headers: Vec<HttpHeader>,
body: Vec<u8>,
contentlength: Option<usize>,
destination: Option<String>,
head: Vec<u8>,
destination_base: Option<String>,
}
impl std::fmt::Display for HttpRequest {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl HttpRequest {
pub fn new(
method: HttpMethod,
path: String,
version: HttpVersion,
headers: Vec<HttpHeader>,
body: Vec<u8>,
head: Vec<u8>,
ssl: bool,
) -> Self {
let contentlength = headers.iter().find(|x| x.name() == "Content-Length");
let contentlength = match contentlength {
Some(x) => match x.value().parse::<usize>() {
Ok(x) => Some(x),
Err(_) => Some(0),
},
None => Some(0),
};
let (destination, destination_base) = match headers.iter().find(|h| h.name() == "Host") {
Some(h) => {
let v = h.value();
let destination = match v.contains(':') {
true => h.value().to_owned(),
false => match ssl {
true => {
format!("{}:443", h.value())
}
_ => {
format!("{}:80", h.value())
}
},
};
(Some(destination), Some(h.value().to_owned()))
}
None => (None, None),
};
HttpRequest {
method,
path,
version,
headers,
body,
contentlength,
destination,
head,
destination_base,
}
}
pub fn is_terminated(&self) -> bool {
if let Some(size) = self.contentlength {
size == self.body.len()
} else {
true
}
}
pub fn method(&self) -> &HttpMethod {
&self.method
}
pub fn path(&self) -> &String {
&self.path
}
pub fn set_path(&mut self, value: &String) {
self.path = value.to_string();
}
pub fn version(&self) -> &HttpVersion {
&self.version
}
pub fn headers(&self) -> &Vec<HttpHeader> {
&self.headers
}
pub fn headers_mut(&mut self) -> &mut Vec<HttpHeader> {
&mut self.headers
}
pub fn body(&self) -> &Vec<u8> {
&self.body
}
pub fn content_length(&self) -> Option<usize> {
self.contentlength
}
pub fn destination(&self) -> Option<&String> {
if let Some(d) = &self.destination {
Some(d)
} else {
None
}
}
pub fn destination_base(&self) -> Option<&String> {
if let Some(d) = &self.destination_base {
Some(d)
} else {
None
}
}
pub fn as_bytes(&self) -> Vec<u8> {
let mut full_content = vec![];
full_content.extend_from_slice(&mut format!("{} {} {}\r\n", self.method, self.path, self.version).as_bytes());
for h in &self.headers {
full_content.append(&mut h.as_bytes());
}
full_content.extend_from_slice(b"\r\n"); for b in &self.body {
full_content.push(*b);
}
full_content
}
pub fn is_header_terminated(&self) -> bool {
let tail: Vec<u8> = self.head.iter().rev().take(4).copied().collect();
matches!(tail[..], [0x0a, 0x0d, 0x0a, 0x0d])
}
pub fn body_mut(&mut self) -> &mut Vec<u8> {
&mut self.body
}
pub fn keep_alive(&self) -> bool {
let kah = match self.headers.iter().find(|h| h.name() == "Connection") {
Some(h) => h.value().to_owned(),
None => "Close".to_string(),
};
!matches!(kah.as_ref(), "Close")
}
}