#[cfg(feature = "download")]
use crate::download::{ContentDisposition, DispositionType};
use crate::{error::Error,body::HttpBody};
use headers::{Header, HeaderMapExt};
use hyper::{
body::Bytes,
http::{self, HeaderValue},
StatusCode,
};
use serde::Serialize;
use std::ops::{Deref,DerefMut};
pub type Response<T = HttpBody> = hyper::http::Response<T>;
#[derive(Debug)]
pub struct Builder {
#[doc(hidden)]
pub inner: hyper::http::response::Builder,
#[doc(hidden)]
pub body: HttpBody,
#[doc(hidden)]
status_set: bool,
}
impl Builder {
#[inline]
pub fn new() -> Self {
Builder {
inner: hyper::http::response::Builder::new(),
body: HttpBody::Empty,
status_set: false,
}
}
#[cfg(feature = "cookie")]
#[inline]
pub fn cookie(mut self, cookie: cookie::Cookie<'static>) -> Self {
self.inner.headers_mut().map(|h|h.append(
http::header::SET_COOKIE,
http::HeaderValue::from_str(cookie.to_string().as_str()).expect("Invalid Cookie"),
));
self
}
#[inline]
pub fn header_set<H: Header>(mut self, h: H) -> Self {
self.inner.headers_mut().map(|header| header.typed_insert(h));
self
}
#[inline]
pub(crate) fn status_if_not_set<T>(self, status: T) -> Self
where
StatusCode: TryFrom<T>,
<StatusCode as TryFrom<T>>::Error: Into<http::Error>,
{
if !self.status_set {
self.status(status)
} else {
self
}
}
#[inline]
pub fn status<T>(mut self, status: T) -> Self
where
StatusCode: TryFrom<T>,
<StatusCode as TryFrom<T>>::Error: Into<http::Error>,
{
self.status_set = true;
self.inner = self.inner.status(status);
self
}
#[inline]
pub fn redirect(mut self, status: http::StatusCode, url: impl AsRef<str>) -> Self {
let value = http::header::HeaderValue::try_from(url.as_ref())
.expect("url is not the correct value");
self.status_set = true;
self.inner = self.inner.status(status).header(http::header::LOCATION, value);
self
}
#[inline]
pub fn location(mut self, location: impl AsRef<str>) -> Self {
let value = http::header::HeaderValue::try_from(location.as_ref())
.expect("location is not the correct value");
self.inner = self.inner.header(http::header::CONTENT_LOCATION, value);
self
}
#[inline]
pub fn body(mut self, data: impl Into<Bytes>) -> Self {
let body: Bytes = data.into();
let body: HttpBody = body.into();
self.body = body;
self
}
#[inline]
pub fn with<T: Into<Bytes>>(mut self, content_type: &str, value: T) -> Self {
if let Some(headers) = self.inner.headers_mut() {
if let Ok(c_type) = HeaderValue::from_str(content_type) {
headers.insert("content-type", c_type);
}
};
self.body(value)
}
#[inline]
pub fn html<T: Into<Bytes>>(self, value: T) -> Self {
self.with("text/html; charset=utf-8", value)
}
#[inline]
pub fn text<T: Into<Bytes>>(self, value: T) -> Self {
self.with("text/plain; charset=utf-8", value)
}
#[inline]
pub fn json<T: Serialize>(mut self, value: &T) -> Self {
match serde_json::to_vec(value) {
Ok(v) => self.with("application/json", v),
Err(e) => {
self.inner = self.inner.status(StatusCode::INTERNAL_SERVER_ERROR);
self.text(e.to_string())
}
}
}
#[inline]
pub fn form<T: Serialize>(mut self, value: &T) -> Self {
match serde_urlencoded::to_string(value) {
Ok(v) => self.with("application/x-www-form-urlencoded", v),
Err(e) => {
self.inner = self.inner.status(StatusCode::INTERNAL_SERVER_ERROR);
self.text(e.to_string())
}
}
}
#[inline]
pub fn stream<S, O, E>(mut self, stream: S) -> Self
where
S: futures::Stream<Item = Result<O, E>> + Send + Sync + 'static,
O: Into<Bytes> + 'static,
E: Into<Error> + 'static,
{
self.body = HttpBody::stream(stream);
self
}
#[inline]
pub async fn send_file<P>(self, path: P,req: crate::request::Request) -> Self
where
P: Into<std::path::PathBuf> + Send,
{
let path = path.into();
if !path.exists() {
self.status(StatusCode::NOT_FOUND)
} else {
match crate::fs::NamedFile::builder(path).build().await {
Ok(file) => file.send(req.headers()).await,
Err(e) => self.status(StatusCode::INTERNAL_SERVER_ERROR).text(e.to_string())
}
}
}
#[cfg(feature = "download")]
#[inline]
pub fn write_file(
mut self,
path: impl AsRef<std::path::Path>,
disposition_type: DispositionType,
) -> Result<Self, Error> {
let path = path.as_ref();
let mut file = std::fs::File::open(path)?;
let mut buffer = Vec::new();
use std::io::Read;
file.read_to_end(&mut buffer)?;
if let Some(filename) = path.file_name() {
let name = filename.to_string_lossy();
let content_disposition = ContentDisposition::new(disposition_type, Some(&name));
self.inner
.headers_mut()
.map(|h| h.insert("content-disposition", content_disposition.try_into().ok()?));
}
let body: Bytes = buffer.into();
let body: HttpBody = body.into();
self.body = body;
Ok(self)
}
#[inline]
pub fn build(self) -> Result<Response,Error> {
let Builder {
inner,
body,
..
} = self;
let res = inner.body(body)?;
Ok(res)
}
}
impl Deref for Builder {
type Target = hyper::http::response::Builder;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for Builder {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}