#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Request {
pub method: String,
pub uri: String,
pub version: String,
pub headers: Vec<(String, String)>,
pub body: Vec<u8>,
}
impl Request {
pub fn new(method: &str, uri: &str) -> Self {
Self {
method: method.to_string(),
uri: uri.to_string(),
version: "HTTP/1.1".to_string(),
headers: Vec::new(),
body: Vec::new(),
}
}
pub fn with_version(method: &str, uri: &str, version: &str) -> Self {
Self {
method: method.to_string(),
uri: uri.to_string(),
version: version.to_string(),
headers: Vec::new(),
body: Vec::new(),
}
}
pub fn header(mut self, name: &str, value: &str) -> Self {
self.headers.push((name.to_string(), value.to_string()));
self
}
pub fn body(mut self, body: Vec<u8>) -> Self {
self.body = body;
self
}
pub fn add_header(&mut self, name: &str, value: &str) {
self.headers.push((name.to_string(), value.to_string()));
}
pub fn get_header(&self, name: &str) -> Option<&str> {
self.headers
.iter()
.find(|(n, _)| n.eq_ignore_ascii_case(name))
.map(|(_, v)| v.as_str())
}
pub fn get_headers(&self, name: &str) -> Vec<&str> {
self.headers
.iter()
.filter(|(n, _)| n.eq_ignore_ascii_case(name))
.map(|(_, v)| v.as_str())
.collect()
}
pub fn has_header(&self, name: &str) -> bool {
self.headers
.iter()
.any(|(n, _)| n.eq_ignore_ascii_case(name))
}
pub fn connection(&self) -> Option<&str> {
self.get_header("Connection")
}
pub fn is_keep_alive(&self) -> bool {
let mut has_keep_alive = false;
for (name, value) in &self.headers {
if name.eq_ignore_ascii_case("Connection") {
for token in value.split(',') {
let token = token.trim();
if token.eq_ignore_ascii_case("close") {
return false;
}
if token.eq_ignore_ascii_case("keep-alive") {
has_keep_alive = true;
}
}
}
}
if has_keep_alive {
return true;
}
self.version.ends_with("/1.1")
}
pub fn content_length(&self) -> Option<usize> {
self.get_header("Content-Length")
.and_then(|v| v.parse().ok())
}
pub fn is_chunked(&self) -> bool {
let mut last_token: Option<&str> = None;
for (name, value) in &self.headers {
if name.eq_ignore_ascii_case("Transfer-Encoding") {
for token in value.split(',') {
let token = token.trim();
if !token.is_empty() {
last_token = Some(token);
}
}
}
}
last_token.is_some_and(|t| t.eq_ignore_ascii_case("chunked"))
}
}