pub mod body;
pub mod parser;
mod test;
use smallvec::SmallVec;
use crate::{config::PRE_ALLOCATED_REQUEST_HEADERS, utils::buffer::Buffer};
use super::{
headers::{
Connection, ContentType, HeaderEntry, HttpHeader, HttpHeaderValue, TransferEncoding,
},
methods::HttpMethod,
};
#[derive(Debug)]
pub struct HttpRequest {
pub method: HttpMethod,
pub uri: String,
pub version: (u8, u8),
pub headers: SmallVec<[HeaderEntry; PRE_ALLOCATED_REQUEST_HEADERS]>,
pub content_type: Option<ContentType>,
pub body_type: Option<BodyType>,
pub body: Option<Buffer>,
pub range: Option<(u16, u16)>,
pub keep_alive: bool,
}
#[derive(Debug, PartialEq)]
pub enum BodyType {
Chunked,
Fixed(u64),
}
impl HttpRequest {
pub fn new(
method: HttpMethod,
uri: String,
version: (u8, u8),
headers: SmallVec<[HeaderEntry; PRE_ALLOCATED_REQUEST_HEADERS]>,
body: Option<Buffer>,
) -> Self {
Self {
method,
uri,
version,
headers,
content_type: None,
body_type: None,
body,
keep_alive: false,
range: None,
}
}
pub fn process_headers(&mut self) {
for header_entry in &self.headers {
let (header, value) = (&header_entry.name, &header_entry.value);
match (header, value) {
(HttpHeader::ContentLength, HttpHeaderValue::ContentLength(length)) => {
self.body_type = Some(BodyType::Fixed(*length));
}
(HttpHeader::TransferEncoding, HttpHeaderValue::TransferEncoding(encoding)) => {
if encoding == &TransferEncoding::Chunked {
self.body_type = Some(BodyType::Chunked)
}
}
(HttpHeader::ContentType, HttpHeaderValue::ContentType(content_type)) => {
self.content_type = Some(*content_type);
}
(HttpHeader::Connection, HttpHeaderValue::Connection(connection)) => {
match connection {
Connection::KeepAlive => self.keep_alive = true,
Connection::Close => self.keep_alive = false,
}
}
(HttpHeader::Range, HttpHeaderValue::Range(range)) => self.range = Some(*range),
_ => {}
}
}
}
pub fn find_header(&mut self, header: HttpHeader) -> Option<&mut HeaderEntry> {
self.headers.iter_mut().find(|entry| entry.name == header)
}
}
impl ToString for HttpRequest {
fn to_string(&self) -> String {
let headers_str = self
.headers
.iter()
.map(|header_entry| format!("{}\r\n", header_entry.to_string()))
.collect::<Vec<String>>()
.concat();
let body = match &self.body {
Some(body) => body.to_string(),
None => String::new(),
};
format!(
"{} {} HTTP/{}.{}\r\n{}\r\n{}",
self.method, self.uri, self.version.0, self.version.1, headers_str, body
)
}
}