#[cfg(feature = "download")]
use crate::download::{ContentDisposition, DispositionType};
use crate::{body::HttpBody, error::Error, responder::Responder};
#[cfg(feature = "cookie")]
use cookie::CookieJar;
use headers::{Header, HeaderMapExt};
use hyper::{
body::Bytes,
http::{self, HeaderMap, HeaderName, HeaderValue},
StatusCode,
};
use serde::Serialize;
use std::ops::{Deref, DerefMut};
#[derive(Default)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Response {
#[doc(hidden)]
inner: hyper::Response<HttpBody>,
#[doc(hidden)]
status_set: bool,
#[doc(hidden)]
#[cfg(feature = "cookie")]
cookies: CookieJar,
}
impl Response {
#[inline]
pub fn new(
inner: hyper::Response<HttpBody>,
status_set: bool,
#[cfg(feature = "cookie")] cookies: CookieJar,
) -> Self {
Response {
inner,
status_set,
#[cfg(feature = "cookie")]
cookies,
}
}
#[cfg(feature = "cookie")]
#[inline]
pub fn cookie(&mut self, cookie: cookie::Cookie<'static>) -> &mut Self {
self.cookies.add(cookie);
self
}
#[cfg(feature = "cookie")]
#[inline]
pub fn cookies(&self) -> &CookieJar {
&self.cookies
}
#[cfg(feature = "cookie")]
#[inline]
pub fn cookies_mut(&mut self) -> &mut CookieJar {
&mut self.cookies
}
#[cfg(feature = "cookie")]
#[inline]
pub fn cookie_remove(&mut self, name: &str) -> &mut Self {
if let Some(cookie) = self.cookies.get(name).cloned() {
self.cookies.remove(cookie);
}
self
}
#[inline]
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
{
let name = <HeaderName as TryFrom<K>>::try_from(key)
.map_err(Into::into)
.expect("Invalid key");
let value = <HeaderValue as TryFrom<V>>::try_from(value)
.map_err(Into::into)
.expect("Invalid value");
self.inner.headers_mut().append(name, value);
self
}
#[inline]
pub fn header_set<H: Header>(&mut self, h: H) -> &mut Self {
self.inner.headers_mut().typed_insert(h);
self
}
#[inline]
pub(crate) fn status_if_not_set<T>(&mut self, status: T) -> &mut 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) -> &mut Self
where
StatusCode: TryFrom<T>,
<StatusCode as TryFrom<T>>::Error: Into<http::Error>,
{
self.status_set = true;
*self.inner.status_mut() = TryFrom::try_from(status)
.map_err(Into::into)
.expect("error");
self
}
#[inline]
pub fn redirect<T>(&mut self, status: T, url: &str) -> &mut Self
where
StatusCode: TryFrom<T>,
<StatusCode as TryFrom<T>>::Error: Into<http::Error>,
{
let value = http::header::HeaderValue::try_from(url).expect("url is not the correct value");
self.status_set = true;
*self.inner.status_mut() = TryFrom::try_from(status)
.map_err(Into::into)
.expect("error");
self.inner
.headers_mut()
.append(http::header::LOCATION, value);
self
}
#[inline]
pub fn location(&mut self, location: &str) -> &mut Self {
let value = http::header::HeaderValue::try_from(location)
.expect("location is not the correct value");
self.inner
.headers_mut()
.append(http::header::CONTENT_LOCATION, value);
self
}
#[inline]
pub fn body(&mut self, data: impl Into<Bytes>) -> &mut Self {
let body: Bytes = data.into();
let body: HttpBody = body.into();
*self.inner.body_mut() = body;
self
}
#[inline]
pub fn content_type(&mut self, content_type: &str) -> &mut Self {
if let Ok(c_type) = HeaderValue::from_str(content_type) {
self.inner.headers_mut().insert("content-type", c_type);
}
self
}
#[inline]
pub fn html<T: Into<Bytes>>(&mut self, value: T) -> &mut Self {
self.content_type("text/html; charset=utf-8").body(value)
}
#[inline]
pub fn text<T: Into<Bytes>>(&mut self, value: T) -> &mut Self {
self.content_type("text/plain; charset=utf-8").body(value)
}
#[inline]
pub fn json<T: Serialize>(&mut self, value: &T) -> &mut Self {
match serde_json::to_vec(value) {
Ok(v) => self.content_type("application/json").body(v),
Err(e) => self
.status_if_not_set(StatusCode::INTERNAL_SERVER_ERROR)
.text(e.to_string()),
}
}
#[inline]
pub fn json_pretty<T: Serialize>(&mut self, value: &T) -> &mut Self {
match serde_json::to_vec_pretty(value) {
Ok(v) => self.content_type("application/json").body(v),
Err(e) => self
.status_if_not_set(StatusCode::INTERNAL_SERVER_ERROR)
.text(e.to_string()),
}
}
#[inline]
pub fn form<T: Serialize>(&mut self, value: &T) -> &mut Self {
match serde_urlencoded::to_string(value) {
Ok(v) => self
.content_type("application/x-www-form-urlencoded")
.body(v),
Err(e) => self
.status_if_not_set(StatusCode::INTERNAL_SERVER_ERROR)
.text(e.to_string()),
}
}
#[inline]
pub fn render<R>(&mut self, responder: R)
where
R: Responder,
{
responder.response(self)
}
#[inline]
pub fn stream<S, O, E>(&mut self, stream: S) -> &mut Self
where
S: futures::Stream<Item = Result<O, E>> + Send + Sync + 'static,
O: Into<Bytes> + 'static,
E: Into<Error> + 'static,
{
*self.inner.body_mut() = HttpBody::stream(stream);
self
}
#[inline]
pub async fn send_file<P>(&mut self, path: P, req_headers: &HeaderMap) -> &mut 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, self).await;
self
}
Err(_) => self.status(StatusCode::INTERNAL_SERVER_ERROR),
}
}
}
#[cfg(feature = "download")]
#[inline]
pub fn write_file(
&mut self,
path: impl AsRef<std::path::Path>,
disposition_type: DispositionType,
) -> Result<&mut 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)).try_into()?;
self.inner
.headers_mut()
.insert("content-disposition", content_disposition);
}
let body: Bytes = buffer.into();
let body: HttpBody = body.into();
*self.inner.body_mut() = body;
Ok(self)
}
#[allow(unused_mut)]
#[inline]
pub fn into_raw(self) -> Result<hyper::Response<HttpBody>, Error> {
let Response {
mut inner,
#[cfg(feature = "cookie")]
cookies,
..
} = self;
#[cfg(feature = "cookie")]
for c in cookies.iter() {
inner.headers_mut().append(
http::header::SET_COOKIE,
http::HeaderValue::from_str(c.to_string().as_str())?,
);
}
Ok(inner)
}
#[inline]
pub fn map<F>(self, f: F) -> Response
where
F: FnOnce(HttpBody) -> HttpBody,
{
let inner = self.inner.map(f);
Response {
inner,
status_set: self.status_set,
#[cfg(feature = "cookie")]
cookies: self.cookies,
}
}
}
impl Deref for Response {
type Target = hyper::Response<HttpBody>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for Response {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}