use std::str::FromStr;
use crate::http_header::HttpHeader;
use crate::http_response_status::HttpResponseStatus;
use crate::http_transfer_encoding::HttpTransferEncoding;
#[derive(Debug, Default, Clone)]
pub struct HttpResponse {
status: HttpResponseStatus,
headers: Vec<HttpHeader>,
body: Vec<u8>,
contentlength: Option<usize>,
transferencoding: HttpTransferEncoding,
head: Vec<u8>,
}
impl HttpResponse {
pub fn new(
status: HttpResponseStatus,
headers: Vec<HttpHeader>,
body: Vec<u8>,
head: Vec<u8>,
) -> Self {
let contentlength = headers.iter().find(|x| x.name().to_uppercase() == "CONTENT-LENGTH");
let contentlength = match contentlength {
Some(x) => {
if let Ok(x) = x.value().parse::<usize>() {
Some(x)
} else {
None
}
}
None => None,
};
let transferencoding = headers
.iter()
.find(|x| x.name() == "Transfer-Encoding" || x.name() == "transfer-encoding");
let transferencoding = match transferencoding {
Some(x) => HttpTransferEncoding::from_str(x.value()).unwrap(),
None => HttpTransferEncoding::NoEncoding,
};
HttpResponse {
status,
headers,
body,
contentlength,
transferencoding,
head,
}
}
pub fn is_terminated(&self) -> bool {
if let Some(size) = self.contentlength {
size == self.body.len()
} else {
true
}
}
pub fn headers(&self) -> &Vec<HttpHeader> {
&self.headers
}
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 content_length(&self) -> Option<usize> {
self.contentlength
}
pub fn as_bytes(&self) -> Vec<u8> {
let mut full_content = vec![];
for b in &self.head {
full_content.push(*b);
}
for b in &self.body {
full_content.push(*b);
}
full_content
}
pub fn transfer_encoding(&self) -> &HttpTransferEncoding {
&self.transferencoding
}
pub fn body_mut(&mut self) -> &mut Vec<u8> {
&mut self.body
}
pub fn is_transfer_complete(&self) -> bool {
let tail: Vec<u8> = self.body.iter().rev().take(5).copied().collect();
matches!(tail[..], [0x0a, 0x0d, 0x0a, 0x0d, 0x30])
}
pub fn status(&self) -> &HttpResponseStatus {
&self.status
}
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")
}
}