use std::fmt::{self, Debug};
use std::fs::File;
use std::io::{self, Write};
use http::StatusCode;
use cookie::Cookie;
use crate::http::headers::{self, HeaderMap};
use hyper::Body;
use hyper::Method;
pub trait BodyWriter: Send {
fn write_body(&mut self, res: &mut dyn Write) -> io::Result<()>;
}
impl BodyWriter for String {
fn write_body(&mut self, res: &mut dyn Write) -> io::Result<()> {
self.as_bytes().write_body(res)
}
}
impl<'a> BodyWriter for &'a str {
fn write_body(&mut self, res: &mut dyn Write) -> io::Result<()> {
self.as_bytes().write_body(res)
}
}
impl BodyWriter for Vec<u8> {
fn write_body(&mut self, res: &mut dyn Write) -> io::Result<()> {
res.write_all(self)
}
}
impl<'a> BodyWriter for &'a [u8] {
fn write_body(&mut self, res: &mut dyn Write) -> io::Result<()> {
res.write_all(self)
}
}
impl BodyWriter for File {
fn write_body(&mut self, res: &mut dyn Write) -> io::Result<()> {
io::copy(self, res).map(|_| ())
}
}
impl BodyWriter for Box<dyn io::Read + Send> {
fn write_body(&mut self, res: &mut dyn Write) -> io::Result<()> {
io::copy(self, res).map(|_| ())
}
}
pub struct Response {
pub status: Option<StatusCode>,
pub headers: HeaderMap,
pub body_writers: Vec<Box<dyn BodyWriter>>,
}
impl Default for Response {
fn default() -> Self {
Self::new()
}
}
impl Response {
pub fn new() -> Response {
Response {
status: None, body_writers: Vec::new(), headers: HeaderMap::new(),
}
}
#[inline(always)]
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
#[doc(hidden)]
pub fn write_back(self, http_res: &mut hyper::Response<Body>, req_method: Method) {
*http_res.headers_mut() = self.headers;
*http_res.status_mut() = self.status.unwrap_or(StatusCode::NOT_FOUND);
if let Method::HEAD = req_method {
return
}else{
if self.body_writers.len() == 0 {
http_res.headers_mut().insert(
headers::CONTENT_LENGTH,
headers::HeaderValue::from_static("0"),
);
}else{
for writer in self.body_writers {
write_with_body(http_res, writer);
}
}
}
}
pub fn cookies(&self) -> Vec<Cookie<'_>> {
let mut cookies = vec![];
for header in self.headers().get("Set-Cookie") {
if let Ok(header) = header.to_str() {
if let Ok(cookie) = Cookie::parse_encoded(header) {
cookies.push(cookie);
}
}
}
cookies
}
}
fn write_with_body(res: &mut hyper::Response<Body>, mut body: Box<dyn BodyWriter>) -> io::Result<()> {
let content_type = res.headers().get(headers::CONTENT_TYPE).map_or_else(
|| headers::HeaderValue::from_static("text/plain"),
|cx| cx.clone(),
);
res.headers_mut().insert(headers::CONTENT_TYPE, content_type);
let mut body_contents: Vec<u8> = vec![];
body.write_body(&mut body_contents)?;
*res.body_mut() = Body::from(body_contents);
Ok(())
}
impl Debug for Response {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
f,
"HTTP/1.1 {}\n{:?}",
self.status.unwrap_or(StatusCode::NOT_FOUND),
self.headers
)
}
}
impl fmt::Display for Response {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Debug::fmt(self, f)
}
}