use std::collections::{hash_map::Entry, HashMap};
use std::io::Write;
#[derive(Debug, Clone)]
struct HeaderValue {
name: String,
value: String,
}
#[derive(Debug)]
pub struct EmptyResponse {
status: u16,
headers: HashMap<String, HeaderValue>,
}
impl EmptyResponse {
pub fn new(status: u16) -> EmptyResponse {
EmptyResponse {
status,
headers: HashMap::new(),
}
}
pub fn add_header<N, V>(&mut self, name: N, value: V)
where
N: Into<String>,
V: Into<String>,
{
let name = name.into();
let value = value.into();
let name_key = (&name).to_lowercase();
match self.headers.entry(name_key) {
Entry::Occupied(mut oe) => {
let old = oe.get_mut();
(*old).value.push_str(", ");
(*old).value.push_str(&value);
}
Entry::Vacant(ve) => {
let header = HeaderValue { name, value };
ve.insert(header);
}
}
}
pub fn with_header<N, V>(self, name: N, value: V) -> EmptyResponse
where
N: Into<String>,
V: Into<String>,
{
let mut new = self;
new.add_header(name, value);
new
}
pub fn with_content_type<T>(self, content_type: T) -> FullResponse
where
T: Into<String>,
{
FullResponse {
status: self.status,
headers: self.headers,
content_type: content_type.into(),
body: Vec::new(),
}
}
pub fn get_status(&self) -> u16 {
self.status
}
pub fn set_status(&mut self, new_status: u16) {
self.status = new_status;
}
pub fn get_header<T: AsRef<str>>(&self, name: T) -> Option<&str> {
let name = name.as_ref().to_lowercase();
self.headers.get(&name).map(|s| s.value.as_str())
}
pub fn respond(mut self) -> std::io::Result<()> {
let status_str = format!("{}", &self.status);
let status_header = HeaderValue {
name: "Status".to_owned(),
value: status_str,
};
_ = self.headers.insert("status".to_owned(), status_header);
let stdout = std::io::stdout();
let mut out = stdout.lock();
for (_, header) in self.headers.iter() {
write!(&mut out, "{}: {}\r\n", &header.name, &header.value)?;
}
write!(&mut out, "\r\n")
}
}
#[derive(Debug)]
pub struct FullResponse {
status: u16,
headers: HashMap<String, HeaderValue>,
body: Vec<u8>,
content_type: String,
}
impl FullResponse {
pub fn add_header<N, V>(&mut self, name: N, value: V)
where
N: Into<String>,
V: Into<String>,
{
let name = name.into();
let value = value.into();
let name_key = (&name).to_lowercase();
match self.headers.entry(name_key) {
Entry::Occupied(mut oe) => {
let old = oe.get_mut();
(*old).value.push_str(", ");
(*old).value.push_str(&value);
}
Entry::Vacant(ve) => {
let header = HeaderValue { name, value };
ve.insert(header);
}
}
}
pub fn with_header<N, V>(self, name: N, value: V) -> FullResponse
where
N: Into<String>,
V: Into<String>,
{
let mut new = self;
new.add_header(name, value);
new
}
pub fn with_body<T: Into<Vec<u8>>>(self, new_body: T) -> FullResponse {
let mut new = self;
new.body = new_body.into();
new
}
pub fn get_status(&self) -> u16 {
self.status
}
pub fn set_status(&mut self, new_status: u16) {
self.status = new_status;
}
pub fn get_header<T: AsRef<str>>(&self, name: T) -> Option<&str> {
let name = name.as_ref().to_lowercase();
self.headers.get(&name).map(|s| s.value.as_str())
}
pub fn get_body(&self) -> &[u8] {
&self.body
}
pub fn respond(mut self) -> std::io::Result<()> {
let status_str = format!("{}", &self.status);
self.add_header("Status".to_owned(), status_str);
if !self.body.is_empty() {
self.add_header("Content-type".to_owned(), self.content_type.clone());
self.add_header("Content-length".to_owned(), format!("{}", self.body.len()));
}
let stdout = std::io::stdout();
let mut out = stdout.lock();
for (_, header) in self.headers.iter() {
write!(&mut out, "{}: {}\r\n", &header.name, &header.value)?;
}
write!(&mut out, "\r\n")?;
if !self.body.is_empty() {
out.write_all(&self.body)?;
}
Ok(())
}
}
impl Write for FullResponse {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.body.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}