Support returning any http_body::Body from IntoResponse (#86)

Adds associated `Body` and `BodyError` types to `IntoResponse`. This is required for returning responses with bodies other than `hyper::Body` from handlers. That wasn't previously possible.

This is a breaking change so should be shipped in 0.2.
This commit is contained in:
David Pedersen 2021-08-07 18:03:21 +02:00 committed by GitHub
parent 4194cf70da
commit ab927033b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 347 additions and 83 deletions

View file

@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Breaking changes ## Breaking changes
- Add associated `Body` and `BodyError` types to `IntoResponse`. This is
required for returning responses with bodies other than `hyper::Body` from
handlers. See the docs for advice on how to implement `IntoResponse` ([#86](https://github.com/tokio-rs/axum/pull/86))
- Change WebSocket API to use an extractor ([#121](https://github.com/tokio-rs/axum/pull/121)) - Change WebSocket API to use an extractor ([#121](https://github.com/tokio-rs/axum/pull/121))
- Add `RoutingDsl::or` for combining routes. ([#108](https://github.com/tokio-rs/axum/pull/108)) - Add `RoutingDsl::or` for combining routes. ([#108](https://github.com/tokio-rs/axum/pull/108))
- Ensure a `HandleError` service created from `axum::ServiceExt::handle_error` - Ensure a `HandleError` service created from `axum::ServiceExt::handle_error`

View file

@ -16,10 +16,12 @@ use axum::{
response::IntoResponse, response::IntoResponse,
AddExtensionLayer, AddExtensionLayer,
}; };
use http::StatusCode; use bytes::Bytes;
use http::{Response, StatusCode};
use http_body::Full;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
use std::{net::SocketAddr, sync::Arc}; use std::{convert::Infallible, net::SocketAddr, sync::Arc};
use uuid::Uuid; use uuid::Uuid;
#[tokio::main] #[tokio::main]
@ -89,7 +91,10 @@ impl From<UserRepoError> for AppError {
} }
impl IntoResponse for AppError { impl IntoResponse for AppError {
fn into_response(self) -> http::Response<Body> { type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let (status, error_json) = match self { let (status, error_json) = match self {
AppError::UserRepo(UserRepoError::NotFound) => { AppError::UserRepo(UserRepoError::NotFound) => {
(StatusCode::NOT_FOUND, json!("User not found")) (StatusCode::NOT_FOUND, json!("User not found"))

View file

@ -6,8 +6,10 @@
use askama::Template; use askama::Template;
use axum::{prelude::*, response::IntoResponse}; use axum::{prelude::*, response::IntoResponse};
use bytes::Bytes;
use http::{Response, StatusCode}; use http::{Response, StatusCode};
use std::net::SocketAddr; use http_body::Full;
use std::{convert::Infallible, net::SocketAddr};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@ -46,12 +48,15 @@ impl<T> IntoResponse for HtmlTemplate<T>
where where
T: Template, T: Template,
{ {
fn into_response(self) -> http::Response<Body> { type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
match self.0.render() { match self.0.render() {
Ok(html) => response::Html(html).into_response(), Ok(html) => response::Html(html).into_response(),
Err(err) => Response::builder() Err(err) => Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR) .status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from(format!( .body(Full::from(format!(
"Failed to render template. Error: {}", "Failed to render template. Error: {}",
err err
))) )))

View file

@ -10,8 +10,10 @@ use axum::{
extract::{FromRequest, RequestParts}, extract::{FromRequest, RequestParts},
prelude::*, prelude::*,
}; };
use bytes::Bytes;
use http::Response; use http::Response;
use http::StatusCode; use http::StatusCode;
use http_body::Full;
use std::collections::HashMap; use std::collections::HashMap;
use std::net::SocketAddr; use std::net::SocketAddr;
@ -51,7 +53,7 @@ impl<B> FromRequest<B> for Version
where where
B: Send, B: Send,
{ {
type Rejection = Response<Body>; type Rejection = Response<Full<Bytes>>;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> { async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let params = extract::Path::<HashMap<String, String>>::from_request(req) let params = extract::Path::<HashMap<String, String>>::from_request(req)

View file

@ -1,3 +1,5 @@
use crate::response::IntoResponse;
use super::{rejection::*, FromRequest, RequestParts}; use super::{rejection::*, FromRequest, RequestParts};
use async_trait::async_trait; use async_trait::async_trait;
use std::ops::Deref; use std::ops::Deref;
@ -27,6 +29,7 @@ pub struct ContentLengthLimit<T, const N: u64>(pub T);
impl<T, B, const N: u64> FromRequest<B> for ContentLengthLimit<T, N> impl<T, B, const N: u64> FromRequest<B> for ContentLengthLimit<T, N>
where where
T: FromRequest<B>, T: FromRequest<B>,
T::Rejection: IntoResponse,
B: Send, B: Send,
{ {
type Rejection = ContentLengthLimitRejection<T::Rejection>; type Rejection = ContentLengthLimitRejection<T::Rejection>;

View file

@ -1,7 +1,10 @@
//! Rejection response types. //! Rejection response types.
use super::IntoResponse; use super::IntoResponse;
use crate::body::Body; use crate::body::{box_body, BoxBody, BoxStdError};
use bytes::Bytes;
use http_body::Full;
use std::convert::Infallible;
use tower::BoxError; use tower::BoxError;
define_rejection! { define_rejection! {
@ -141,8 +144,11 @@ impl InvalidUrlParam {
} }
impl IntoResponse for InvalidUrlParam { impl IntoResponse for InvalidUrlParam {
fn into_response(self) -> http::Response<Body> { type Body = Full<Bytes>;
let mut res = http::Response::new(Body::from(format!( type BodyError = Infallible;
fn into_response(self) -> http::Response<Self::Body> {
let mut res = http::Response::new(Full::from(format!(
"Invalid URL param. Expected something of type `{}`", "Invalid URL param. Expected something of type `{}`",
self.type_name self.type_name
))); )));
@ -163,8 +169,11 @@ impl InvalidPathParam {
} }
impl IntoResponse for InvalidPathParam { impl IntoResponse for InvalidPathParam {
fn into_response(self) -> http::Response<Body> { type Body = Full<Bytes>;
let mut res = http::Response::new(Body::from(format!("Invalid URL param. {}", self.0))); type BodyError = Infallible;
fn into_response(self) -> http::Response<Self::Body> {
let mut res = http::Response::new(Full::from(format!("Invalid URL param. {}", self.0)));
*res.status_mut() = http::StatusCode::BAD_REQUEST; *res.status_mut() = http::StatusCode::BAD_REQUEST;
res res
} }
@ -191,8 +200,11 @@ impl FailedToDeserializeQueryString {
} }
impl IntoResponse for FailedToDeserializeQueryString { impl IntoResponse for FailedToDeserializeQueryString {
fn into_response(self) -> http::Response<Body> { type Body = Full<Bytes>;
let mut res = http::Response::new(Body::from(format!( type BodyError = Infallible;
fn into_response(self) -> http::Response<Self::Body> {
let mut res = http::Response::new(Full::from(format!(
"Failed to deserialize query string. Expected something of type `{}`. Error: {}", "Failed to deserialize query string. Expected something of type `{}`. Error: {}",
self.type_name, self.error, self.type_name, self.error,
))); )));
@ -317,12 +329,15 @@ impl<T> IntoResponse for ContentLengthLimitRejection<T>
where where
T: IntoResponse, T: IntoResponse,
{ {
fn into_response(self) -> http::Response<Body> { type Body = BoxBody;
type BodyError = BoxStdError;
fn into_response(self) -> http::Response<Self::Body> {
match self { match self {
Self::PayloadTooLarge(inner) => inner.into_response(), Self::PayloadTooLarge(inner) => inner.into_response().map(box_body),
Self::LengthRequired(inner) => inner.into_response(), Self::LengthRequired(inner) => inner.into_response().map(box_body),
Self::HeadersAlreadyExtracted(inner) => inner.into_response(), Self::HeadersAlreadyExtracted(inner) => inner.into_response().map(box_body),
Self::Inner(inner) => inner.into_response(), Self::Inner(inner) => inner.into_response().map(box_body),
} }
} }
} }
@ -339,7 +354,10 @@ pub struct TypedHeaderRejection {
#[cfg(feature = "headers")] #[cfg(feature = "headers")]
#[cfg_attr(docsrs, doc(cfg(feature = "headers")))] #[cfg_attr(docsrs, doc(cfg(feature = "headers")))]
impl IntoResponse for TypedHeaderRejection { impl IntoResponse for TypedHeaderRejection {
fn into_response(self) -> http::Response<crate::Body> { type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> http::Response<Self::Body> {
let mut res = format!("{} ({})", self.err, self.name).into_response(); let mut res = format!("{} ({})", self.err, self.name).into_response();
*res.status_mut() = http::StatusCode::BAD_REQUEST; *res.status_mut() = http::StatusCode::BAD_REQUEST;
res res

View file

@ -1,5 +1,8 @@
use super::{FromRequest, RequestParts}; use super::{FromRequest, RequestParts};
use crate::response::IntoResponse; use crate::{
body::{box_body, BoxBody},
response::IntoResponse,
};
use async_trait::async_trait; use async_trait::async_trait;
use http::Response; use http::Response;
use std::convert::Infallible; use std::convert::Infallible;
@ -29,11 +32,11 @@ macro_rules! impl_from_request {
$( $tail: FromRequest<B> + Send, )* $( $tail: FromRequest<B> + Send, )*
B: Send, B: Send,
{ {
type Rejection = Response<crate::body::Body>; type Rejection = Response<BoxBody>;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> { async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let $head = $head::from_request(req).await.map_err(IntoResponse::into_response)?; let $head = $head::from_request(req).await.map_err(|err| err.into_response().map(box_body))?;
$( let $tail = $tail::from_request(req).await.map_err(IntoResponse::into_response)?; )* $( let $tail = $tail::from_request(req).await.map_err(|err| err.into_response().map(box_body))?; )*
Ok(($head, $($tail,)*)) Ok(($head, $($tail,)*))
} }
} }

View file

@ -48,10 +48,8 @@ use http::{
header::{self, HeaderName, HeaderValue}, header::{self, HeaderName, HeaderValue},
Method, Response, StatusCode, Method, Response, StatusCode,
}; };
use hyper::{ use http_body::Full;
upgrade::{OnUpgrade, Upgraded}, use hyper::upgrade::{OnUpgrade, Upgraded};
Body,
};
use sha1::{Digest, Sha1}; use sha1::{Digest, Sha1};
use std::{ use std::{
borrow::Cow, borrow::Cow,
@ -256,7 +254,10 @@ where
F: FnOnce(WebSocket) -> Fut + Send + 'static, F: FnOnce(WebSocket) -> Fut + Send + 'static,
Fut: Future + Send + 'static, Fut: Future + Send + 'static,
{ {
fn into_response(self) -> Response<Body> { type Body = Full<Bytes>;
type BodyError = <Self::Body as http_body::Body>::Error;
fn into_response(self) -> Response<Self::Body> {
// check requested protocols // check requested protocols
let protocol = self let protocol = self
.extractor .extractor
@ -315,7 +316,7 @@ where
builder = builder.header(header::SEC_WEBSOCKET_PROTOCOL, protocol); builder = builder.header(header::SEC_WEBSOCKET_PROTOCOL, protocol);
} }
builder.body(Body::empty()).unwrap() builder.body(Full::default()).unwrap()
} }
} }

View file

@ -268,6 +268,7 @@ macro_rules! impl_handler {
Fut: Future<Output = Res> + Send, Fut: Future<Output = Res> + Send,
B: Send + 'static, B: Send + 'static,
Res: IntoResponse, Res: IntoResponse,
B: Send + 'static,
$head: FromRequest<B> + Send, $head: FromRequest<B> + Send,
$( $tail: FromRequest<B> + Send,)* $( $tail: FromRequest<B> + Send,)*
{ {
@ -278,13 +279,13 @@ macro_rules! impl_handler {
let $head = match $head::from_request(&mut req).await { let $head = match $head::from_request(&mut req).await {
Ok(value) => value, Ok(value) => value,
Err(rejection) => return rejection.into_response().map(crate::body::box_body), Err(rejection) => return rejection.into_response().map(box_body),
}; };
$( $(
let $tail = match $tail::from_request(&mut req).await { let $tail = match $tail::from_request(&mut req).await {
Ok(value) => value, Ok(value) => value,
Err(rejection) => return rejection.into_response().map(crate::body::box_body), Err(rejection) => return rejection.into_response().map(box_body),
}; };
)* )*

View file

@ -1,16 +1,20 @@
use crate::{ use crate::{
extract::{has_content_type, rejection::*, take_body, FromRequest, RequestParts}, extract::{has_content_type, rejection::*, take_body, FromRequest, RequestParts},
prelude::response::IntoResponse, prelude::response::IntoResponse,
Body,
}; };
use async_trait::async_trait; use async_trait::async_trait;
use bytes::Bytes;
use http::{ use http::{
header::{self, HeaderValue}, header::{self, HeaderValue},
StatusCode, StatusCode,
}; };
use http_body::Full;
use hyper::Response; use hyper::Response;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use std::ops::{Deref, DerefMut}; use std::{
convert::Infallible,
ops::{Deref, DerefMut},
};
/// JSON Extractor/Response /// JSON Extractor/Response
/// ///
@ -132,19 +136,22 @@ impl<T> IntoResponse for Json<T>
where where
T: Serialize, T: Serialize,
{ {
fn into_response(self) -> Response<Body> { type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let bytes = match serde_json::to_vec(&self.0) { let bytes = match serde_json::to_vec(&self.0) {
Ok(res) => res, Ok(res) => res,
Err(err) => { Err(err) => {
return Response::builder() return Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR) .status(StatusCode::INTERNAL_SERVER_ERROR)
.header(header::CONTENT_TYPE, "text/plain") .header(header::CONTENT_TYPE, "text/plain")
.body(Body::from(err.to_string())) .body(Full::from(err.to_string()))
.unwrap(); .unwrap();
} }
}; };
let mut res = Response::new(Body::from(bytes)); let mut res = Response::new(Full::from(bytes));
res.headers_mut().insert( res.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_static("application/json"), HeaderValue::from_static("application/json"),

View file

@ -707,7 +707,6 @@
#![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(test, allow(clippy::float_cmp))] #![cfg_attr(test, allow(clippy::float_cmp))]
use self::body::Body;
use http::Request; use http::Request;
use routing::{EmptyRouter, Route}; use routing::{EmptyRouter, Route};
use tower::Service; use tower::Service;

View file

@ -49,8 +49,11 @@ macro_rules! define_rejection {
#[allow(deprecated)] #[allow(deprecated)]
impl $crate::response::IntoResponse for $name { impl $crate::response::IntoResponse for $name {
fn into_response(self) -> http::Response<$crate::body::Body> { type Body = http_body::Full<bytes::Bytes>;
let mut res = http::Response::new($crate::body::Body::from($body)); type BodyError = std::convert::Infallible;
fn into_response(self) -> http::Response<Self::Body> {
let mut res = http::Response::new(http_body::Full::from($body));
*res.status_mut() = http::StatusCode::$status; *res.status_mut() = http::StatusCode::$status;
res res
} }
@ -77,9 +80,12 @@ macro_rules! define_rejection {
} }
impl IntoResponse for $name { impl IntoResponse for $name {
fn into_response(self) -> http::Response<Body> { type Body = http_body::Full<bytes::Bytes>;
type BodyError = std::convert::Infallible;
fn into_response(self) -> http::Response<Self::Body> {
let mut res = let mut res =
http::Response::new(Body::from(format!(concat!($body, ": {}"), self.0))); http::Response::new(http_body::Full::from(format!(concat!($body, ": {}"), self.0)));
*res.status_mut() = http::StatusCode::$status; *res.status_mut() = http::StatusCode::$status;
res res
} }
@ -106,7 +112,10 @@ macro_rules! composite_rejection {
} }
impl $crate::response::IntoResponse for $name { impl $crate::response::IntoResponse for $name {
fn into_response(self) -> http::Response<$crate::body::Body> { type Body = http_body::Full<bytes::Bytes>;
type BodyError = std::convert::Infallible;
fn into_response(self) -> http::Response<Self::Body> {
match self { match self {
$( $(
Self::$variant(inner) => inner.into_response(), Self::$variant(inner) => inner.into_response(),

View file

@ -1,10 +1,14 @@
//! Types and traits for generating responses. //! Types and traits for generating responses.
use crate::Body; use crate::body::{box_body, BoxBody, BoxStdError};
use bytes::Bytes; use bytes::Bytes;
use http::{header, HeaderMap, HeaderValue, Response, StatusCode}; use http::{header, HeaderMap, HeaderValue, Response, StatusCode};
use http_body::{
combinators::{MapData, MapErr},
Empty, Full,
};
use std::{borrow::Cow, convert::Infallible}; use std::{borrow::Cow, convert::Infallible};
use tower::util::Either; use tower::{util::Either, BoxError};
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::Json; pub use crate::Json;
@ -12,19 +16,119 @@ pub use crate::Json;
/// Trait for generating responses. /// Trait for generating responses.
/// ///
/// Types that implement `IntoResponse` can be returned from handlers. /// Types that implement `IntoResponse` can be returned from handlers.
///
/// # Implementing `IntoResponse`
///
/// You generally shouldn't have to implement `IntoResponse` manually, as axum
/// provides implementations for many common types.
///
/// A manual implementation should only be necessary if you have a custom
/// response body type:
///
/// ```rust
/// use axum::{prelude::*, response::IntoResponse};
/// use http_body::Body;
/// use http::{Response, HeaderMap};
/// use bytes::Bytes;
/// use std::{
/// convert::Infallible,
/// task::{Poll, Context},
/// pin::Pin,
/// };
///
/// struct MyBody;
///
/// // First implement `Body` for `MyBody`. This could for example use
/// // some custom streaming protocol.
/// impl Body for MyBody {
/// type Data = Bytes;
/// type Error = Infallible;
///
/// fn poll_data(
/// self: Pin<&mut Self>,
/// cx: &mut Context<'_>
/// ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
/// # unimplemented!()
/// // ...
/// }
///
/// fn poll_trailers(
/// self: Pin<&mut Self>,
/// cx: &mut Context<'_>
/// ) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
/// # unimplemented!()
/// // ...
/// }
/// }
///
/// // Now we can implement `IntoResponse` directly for `MyBody`
/// impl IntoResponse for MyBody {
/// type Body = Self;
/// type BodyError = <Self as Body>::Error;
///
/// fn into_response(self) -> Response<Self::Body> {
/// Response::new(self)
/// }
/// }
///
/// // We don't need to implement `IntoResponse for Response<MyBody>` as that is
/// // covered by a blanket implementation in axum.
///
/// // `MyBody` can now be returned from handlers.
/// let app = route("/", get(|| async { MyBody }));
/// # async {
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub trait IntoResponse { pub trait IntoResponse {
/// The body type of the response.
///
/// Unless you're implementing this trait for a custom body type, these are
/// some common types you can use:
///
/// - [`hyper::Body`]: A good default that supports most use cases.
/// - [`http_body::Empty<Bytes>`]: When you know your response is always
/// empty.
/// - [`http_body::Full<Bytes>`]: When you know your response always
/// contains exactly one chunk.
/// - [`BoxBody`]: If you need to unify multiple body types into one, or
/// return a body type that cannot be named. Can be created with
/// [`box_body`].
///
/// [`http_body::Empty<Bytes>`]: http_body::Empty
/// [`http_body::Full<Bytes>`]: http_body::Full
type Body: http_body::Body<Data = Bytes, Error = Self::BodyError> + Send + Sync + 'static;
/// The error type `Self::Body` might generate.
///
/// Generally it should be possible to set this to:
///
/// ```rust,ignore
/// type BodyError = <Self::Body as http_body::Body>::Error;
/// ```
///
/// This associated type exists mainly to make returning `impl IntoResponse`
/// possible and to simplify trait bounds internally in axum.
type BodyError: Into<BoxError>;
/// Create a response. /// Create a response.
fn into_response(self) -> Response<Body>; fn into_response(self) -> Response<Self::Body>;
} }
impl IntoResponse for () { impl IntoResponse for () {
fn into_response(self) -> Response<Body> { type Body = Empty<Bytes>;
Response::new(Body::empty()) type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
Response::new(Empty::new())
} }
} }
impl IntoResponse for Infallible { impl IntoResponse for Infallible {
fn into_response(self) -> Response<Body> { type Body = Empty<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
match self {} match self {}
} }
} }
@ -34,10 +138,13 @@ where
T: IntoResponse, T: IntoResponse,
K: IntoResponse, K: IntoResponse,
{ {
fn into_response(self) -> Response<Body> { type Body = BoxBody;
type BodyError = BoxStdError;
fn into_response(self) -> Response<Self::Body> {
match self { match self {
Either::A(inner) => inner.into_response(), Either::A(inner) => inner.into_response().map(box_body),
Either::B(inner) => inner.into_response(), Either::B(inner) => inner.into_response().map(box_body),
} }
} }
} }
@ -47,43 +154,113 @@ where
T: IntoResponse, T: IntoResponse,
E: IntoResponse, E: IntoResponse,
{ {
fn into_response(self) -> Response<Body> { type Body = BoxBody;
type BodyError = BoxStdError;
fn into_response(self) -> Response<Self::Body> {
match self { match self {
Ok(value) => value.into_response(), Ok(value) => value.into_response().map(box_body),
Err(err) => err.into_response(), Err(err) => err.into_response().map(box_body),
} }
} }
} }
impl IntoResponse for Response<Body> { impl<B> IntoResponse for Response<B>
where
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
B::Error: Into<BoxError>,
{
type Body = B;
type BodyError = <B as http_body::Body>::Error;
fn into_response(self) -> Self { fn into_response(self) -> Self {
self self
} }
} }
impl IntoResponse for Body { macro_rules! impl_into_response_for_body {
fn into_response(self) -> Response<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<B, F> IntoResponse for MapData<B, F>
where
B: http_body::Body + Send + Sync + 'static,
F: FnMut(B::Data) -> Bytes + Send + Sync + '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 + Sync + 'static,
F: FnMut(B::Error) -> E + Send + Sync + 'static,
E: Into<BoxError>,
{
type Body = Self;
type BodyError = E;
fn into_response(self) -> Response<Self::Body> {
Response::new(self) Response::new(self)
} }
} }
impl IntoResponse for &'static str { impl IntoResponse for &'static str {
type Body = Full<Bytes>;
type BodyError = Infallible;
#[inline] #[inline]
fn into_response(self) -> Response<Body> { fn into_response(self) -> Response<Self::Body> {
Cow::Borrowed(self).into_response() Cow::Borrowed(self).into_response()
} }
} }
impl IntoResponse for String { impl IntoResponse for String {
type Body = Full<Bytes>;
type BodyError = Infallible;
#[inline] #[inline]
fn into_response(self) -> Response<Body> { fn into_response(self) -> Response<Self::Body> {
Cow::<'static, str>::Owned(self).into_response() Cow::<'static, str>::Owned(self).into_response()
} }
} }
impl IntoResponse for std::borrow::Cow<'static, str> { impl IntoResponse for std::borrow::Cow<'static, str> {
fn into_response(self) -> Response<Body> { type Body = Full<Bytes>;
let mut res = Response::new(Body::from(self)); type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Full::from(self));
res.headers_mut() res.headers_mut()
.insert(header::CONTENT_TYPE, HeaderValue::from_static("text/plain")); .insert(header::CONTENT_TYPE, HeaderValue::from_static("text/plain"));
res res
@ -91,8 +268,11 @@ impl IntoResponse for std::borrow::Cow<'static, str> {
} }
impl IntoResponse for Bytes { impl IntoResponse for Bytes {
fn into_response(self) -> Response<Body> { type Body = Full<Bytes>;
let mut res = Response::new(Body::from(self)); type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Full::from(self));
res.headers_mut().insert( res.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_static("application/octet-stream"), HeaderValue::from_static("application/octet-stream"),
@ -102,8 +282,11 @@ impl IntoResponse for Bytes {
} }
impl IntoResponse for &'static [u8] { impl IntoResponse for &'static [u8] {
fn into_response(self) -> Response<Body> { type Body = Full<Bytes>;
let mut res = Response::new(Body::from(self)); type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Full::from(self));
res.headers_mut().insert( res.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_static("application/octet-stream"), HeaderValue::from_static("application/octet-stream"),
@ -113,8 +296,11 @@ impl IntoResponse for &'static [u8] {
} }
impl IntoResponse for Vec<u8> { impl IntoResponse for Vec<u8> {
fn into_response(self) -> Response<Body> { type Body = Full<Bytes>;
let mut res = Response::new(Body::from(self)); type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Full::from(self));
res.headers_mut().insert( res.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_static("application/octet-stream"), HeaderValue::from_static("application/octet-stream"),
@ -124,8 +310,11 @@ impl IntoResponse for Vec<u8> {
} }
impl IntoResponse for std::borrow::Cow<'static, [u8]> { impl IntoResponse for std::borrow::Cow<'static, [u8]> {
fn into_response(self) -> Response<Body> { type Body = Full<Bytes>;
let mut res = Response::new(Body::from(self)); type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Full::from(self));
res.headers_mut().insert( res.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_static("application/octet-stream"), HeaderValue::from_static("application/octet-stream"),
@ -135,11 +324,11 @@ impl IntoResponse for std::borrow::Cow<'static, [u8]> {
} }
impl IntoResponse for StatusCode { impl IntoResponse for StatusCode {
fn into_response(self) -> Response<Body> { type Body = Empty<Bytes>;
Response::builder() type BodyError = Infallible;
.status(self)
.body(Body::empty()) fn into_response(self) -> Response<Self::Body> {
.unwrap() Response::builder().status(self).body(Empty::new()).unwrap()
} }
} }
@ -147,7 +336,10 @@ impl<T> IntoResponse for (StatusCode, T)
where where
T: IntoResponse, T: IntoResponse,
{ {
fn into_response(self) -> Response<Body> { type Body = T::Body;
type BodyError = T::BodyError;
fn into_response(self) -> Response<T::Body> {
let mut res = self.1.into_response(); let mut res = self.1.into_response();
*res.status_mut() = self.0; *res.status_mut() = self.0;
res res
@ -158,7 +350,10 @@ impl<T> IntoResponse for (HeaderMap, T)
where where
T: IntoResponse, T: IntoResponse,
{ {
fn into_response(self) -> Response<Body> { type Body = T::Body;
type BodyError = T::BodyError;
fn into_response(self) -> Response<T::Body> {
let mut res = self.1.into_response(); let mut res = self.1.into_response();
res.headers_mut().extend(self.0); res.headers_mut().extend(self.0);
res res
@ -169,7 +364,10 @@ impl<T> IntoResponse for (StatusCode, HeaderMap, T)
where where
T: IntoResponse, T: IntoResponse,
{ {
fn into_response(self) -> Response<Body> { type Body = T::Body;
type BodyError = T::BodyError;
fn into_response(self) -> Response<T::Body> {
let mut res = self.2.into_response(); let mut res = self.2.into_response();
*res.status_mut() = self.0; *res.status_mut() = self.0;
res.headers_mut().extend(self.1); res.headers_mut().extend(self.1);
@ -178,8 +376,11 @@ where
} }
impl IntoResponse for HeaderMap { impl IntoResponse for HeaderMap {
fn into_response(self) -> Response<Body> { type Body = Empty<Bytes>;
let mut res = Response::new(Body::empty()); type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(Empty::new());
*res.headers_mut() = self; *res.headers_mut() = self;
res res
} }
@ -193,9 +394,12 @@ pub struct Html<T>(pub T);
impl<T> IntoResponse for Html<T> impl<T> IntoResponse for Html<T>
where where
T: Into<Body>, T: Into<Full<Bytes>>,
{ {
fn into_response(self) -> Response<Body> { type Body = Full<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
let mut res = Response::new(self.0.into()); let mut res = Response::new(self.0.into());
res.headers_mut() res.headers_mut()
.insert(header::CONTENT_TYPE, HeaderValue::from_static("text/html")); .insert(header::CONTENT_TYPE, HeaderValue::from_static("text/html"));
@ -212,6 +416,7 @@ impl<T> From<T> for Html<T> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::body::Body;
use http::header::{HeaderMap, HeaderName}; use http::header::{HeaderMap, HeaderName};
#[test] #[test]
@ -219,6 +424,9 @@ mod tests {
struct MyResponse; struct MyResponse;
impl IntoResponse for MyResponse { impl IntoResponse for MyResponse {
type Body = Body;
type BodyError = <Self::Body as http_body::Body>::Error;
fn into_response(self) -> Response<Body> { fn into_response(self) -> Response<Body> {
let mut resp = Response::new(String::new().into()); let mut resp = Response::new(String::new().into());
resp.headers_mut() resp.headers_mut()