#![doc = include_str!("../docs/response.md")]
use crate::{
body::{box_body, BoxBody},
BoxError, Error,
};
use bytes::Bytes;
use http::{header, HeaderMap, HeaderValue, Response, StatusCode};
use http_body::{
combinators::{MapData, MapErr},
Empty, Full,
};
use std::{borrow::Cow, convert::Infallible};
mod headers;
mod redirect;
pub mod sse;
#[doc(no_inline)]
#[cfg(feature = "json")]
pub use crate::Json;
#[doc(inline)]
pub use self::{headers::Headers, redirect::Redirect, sse::Sse};
pub trait IntoResponse {
type Body: http_body::Body<Data = Bytes, Error = Self::BodyError> + Send + 'static;
type BodyError: Into<BoxError>;
fn into_response(self) -> Response<Self::Body>;
}
impl IntoResponse for () {
type Body = Empty<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
Response::new(Empty::new())
}
}
impl IntoResponse for Infallible {
type Body = Empty<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
match self {}
}
}
impl<T, E> IntoResponse for Result<T, E>
where
T: IntoResponse,
E: IntoResponse,
{
type Body = BoxBody;
type BodyError = Error;
fn into_response(self) -> Response<Self::Body> {
match self {
Ok(value) => value.into_response().map(box_body),
Err(err) => err.into_response().map(box_body),
}
}
}
impl<B> IntoResponse for Response<B>
where
B: http_body::Body<Data = Bytes> + Send + 'static,
B::Error: Into<BoxError>,
{
type Body = B;
type BodyError = <B as http_body::Body>::Error;
fn into_response(self) -> Self {
self
}
}
macro_rules! impl_into_response_for_body {
($body:ty) => {
impl IntoResponse for $body {
type Body = $body;
type BodyError = <$body as http_body::Body>::Error;
fn into_response(self) -> Response<Self> {
Response::new(self)
}
}
};
}
impl_into_response_for_body!(hyper::Body);
impl_into_response_for_body!(Full<Bytes>);
impl_into_response_for_body!(Empty<Bytes>);
impl<E> IntoResponse for http_body::combinators::BoxBody<Bytes, E>
where
E: Into<BoxError> + 'static,
{
type Body = Self;
type BodyError = E;
fn into_response(self) -> Response<Self> {
Response::new(self)
}
}
impl<E> IntoResponse for http_body::combinators::UnsyncBoxBody<Bytes, E>
where
E: Into<BoxError> + 'static,
{
type Body = Self;
type BodyError = E;
fn into_response(self) -> Response<Self> {
Response::new(self)
}
}
impl<B, F> IntoResponse for MapData<B, F>
where
B: http_body::Body + Send + 'static,
F: FnMut(B::Data) -> Bytes + Send + 'static,
B::Error: Into<BoxError>,
{
type Body = Self;
type BodyError = <B as http_body::Body>::Error;
fn into_response(self) -> Response<Self::Body> {
Response::new(self)
}
}
impl<B, F, E> IntoResponse for MapErr<B, F>
where
B: http_body::Body<Data = Bytes> + Send + 'static,
F: FnMut(B::Error) -> E + Send + 'static,
E: Into<BoxError>,
{
type Body = Self;
type BodyError = E;
fn into_response(self) -> Response<Self::Body> {
Response::new(self)
}
}
impl IntoResponse for &'static str {
type Body = Full<Bytes>;
type BodyError = Infallible;
#[inline]
fn into_response(self) -> Response<Self::Body> {
Cow::Borrowed(self).into_response()
}
}
impl IntoResponse for String {
type Body = Full<Bytes>;
type BodyError = Infallible;
#[inline]
fn into_response(self) -> Response<Self::Body> {
Cow::<'static, str>::Owned(self).into_response()
}
}
impl IntoResponse for std::borrow::Cow<'static, str> {
type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Full::from(self));
res.headers_mut()
.insert(header::CONTENT_TYPE, HeaderValue::from_static("text/plain"));
res
}
}
impl IntoResponse for Bytes {
type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Full::from(self));
res.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("application/octet-stream"),
);
res
}
}
impl IntoResponse for &'static [u8] {
type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Full::from(self));
res.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("application/octet-stream"),
);
res
}
}
impl IntoResponse for Vec<u8> {
type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Full::from(self));
res.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("application/octet-stream"),
);
res
}
}
impl IntoResponse for std::borrow::Cow<'static, [u8]> {
type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Full::from(self));
res.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("application/octet-stream"),
);
res
}
}
impl IntoResponse for StatusCode {
type Body = Empty<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
Response::builder().status(self).body(Empty::new()).unwrap()
}
}
impl<T> IntoResponse for (StatusCode, T)
where
T: IntoResponse,
{
type Body = T::Body;
type BodyError = T::BodyError;
fn into_response(self) -> Response<T::Body> {
let mut res = self.1.into_response();
*res.status_mut() = self.0;
res
}
}
impl<T> IntoResponse for (HeaderMap, T)
where
T: IntoResponse,
{
type Body = T::Body;
type BodyError = T::BodyError;
fn into_response(self) -> Response<T::Body> {
let mut res = self.1.into_response();
res.headers_mut().extend(self.0);
res
}
}
impl<T> IntoResponse for (StatusCode, HeaderMap, T)
where
T: IntoResponse,
{
type Body = T::Body;
type BodyError = T::BodyError;
fn into_response(self) -> Response<T::Body> {
let mut res = self.2.into_response();
*res.status_mut() = self.0;
res.headers_mut().extend(self.1);
res
}
}
impl IntoResponse for HeaderMap {
type Body = Empty<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Empty::new());
*res.headers_mut() = self;
res
}
}
#[derive(Clone, Copy, Debug)]
pub struct Html<T>(pub T);
impl<T> IntoResponse for Html<T>
where
T: Into<Full<Bytes>>,
{
type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(self.0.into());
res.headers_mut()
.insert(header::CONTENT_TYPE, HeaderValue::from_static("text/html"));
res
}
}
impl<T> From<T> for Html<T> {
fn from(inner: T) -> Self {
Self(inner)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::body::Body;
use http::header::{HeaderMap, HeaderName};
#[test]
fn test_merge_headers() {
struct MyResponse;
impl IntoResponse for MyResponse {
type Body = Body;
type BodyError = <Self::Body as http_body::Body>::Error;
fn into_response(self) -> Response<Body> {
let mut resp = Response::new(String::new().into());
resp.headers_mut()
.insert(HeaderName::from_static("a"), HeaderValue::from_static("1"));
resp
}
}
fn check(resp: impl IntoResponse) {
let resp = resp.into_response();
assert_eq!(
resp.headers().get(HeaderName::from_static("a")).unwrap(),
&HeaderValue::from_static("1")
);
assert_eq!(
resp.headers().get(HeaderName::from_static("b")).unwrap(),
&HeaderValue::from_static("2")
);
}
let headers: HeaderMap =
std::iter::once((HeaderName::from_static("b"), HeaderValue::from_static("2")))
.collect();
check((headers.clone(), MyResponse));
check((StatusCode::OK, headers, MyResponse));
}
}