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
13 changed files with 347 additions and 83 deletions
+247 -39
View File
@@ -1,10 +1,14 @@
//! Types and traits for generating responses.
use crate::Body;
use crate::body::{box_body, BoxBody, BoxStdError};
use bytes::Bytes;
use http::{header, HeaderMap, HeaderValue, Response, StatusCode};
use http_body::{
combinators::{MapData, MapErr},
Empty, Full,
};
use std::{borrow::Cow, convert::Infallible};
use tower::util::Either;
use tower::{util::Either, BoxError};
#[doc(no_inline)]
pub use crate::Json;
@@ -12,19 +16,119 @@ pub use crate::Json;
/// Trait for generating responses.
///
/// 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 {
/// 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.
fn into_response(self) -> Response<Body>;
fn into_response(self) -> Response<Self::Body>;
}
impl IntoResponse for () {
fn into_response(self) -> Response<Body> {
Response::new(Body::empty())
type Body = Empty<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
Response::new(Empty::new())
}
}
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 {}
}
}
@@ -34,10 +138,13 @@ where
T: IntoResponse,
K: IntoResponse,
{
fn into_response(self) -> Response<Body> {
type Body = BoxBody;
type BodyError = BoxStdError;
fn into_response(self) -> Response<Self::Body> {
match self {
Either::A(inner) => inner.into_response(),
Either::B(inner) => inner.into_response(),
Either::A(inner) => inner.into_response().map(box_body),
Either::B(inner) => inner.into_response().map(box_body),
}
}
}
@@ -47,43 +154,113 @@ where
T: IntoResponse,
E: IntoResponse,
{
fn into_response(self) -> Response<Body> {
type Body = BoxBody;
type BodyError = BoxStdError;
fn into_response(self) -> Response<Self::Body> {
match self {
Ok(value) => value.into_response(),
Err(err) => err.into_response(),
Ok(value) => value.into_response().map(box_body),
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 {
self
}
}
impl IntoResponse for Body {
fn into_response(self) -> Response<Body> {
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<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)
}
}
impl IntoResponse for &'static str {
type Body = Full<Bytes>;
type BodyError = Infallible;
#[inline]
fn into_response(self) -> Response<Body> {
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<Body> {
fn into_response(self) -> Response<Self::Body> {
Cow::<'static, str>::Owned(self).into_response()
}
}
impl IntoResponse for std::borrow::Cow<'static, str> {
fn into_response(self) -> Response<Body> {
let mut res = Response::new(Body::from(self));
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
@@ -91,8 +268,11 @@ impl IntoResponse for std::borrow::Cow<'static, str> {
}
impl IntoResponse for Bytes {
fn into_response(self) -> Response<Body> {
let mut res = Response::new(Body::from(self));
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"),
@@ -102,8 +282,11 @@ impl IntoResponse for Bytes {
}
impl IntoResponse for &'static [u8] {
fn into_response(self) -> Response<Body> {
let mut res = Response::new(Body::from(self));
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"),
@@ -113,8 +296,11 @@ impl IntoResponse for &'static [u8] {
}
impl IntoResponse for Vec<u8> {
fn into_response(self) -> Response<Body> {
let mut res = Response::new(Body::from(self));
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"),
@@ -124,8 +310,11 @@ impl IntoResponse for Vec<u8> {
}
impl IntoResponse for std::borrow::Cow<'static, [u8]> {
fn into_response(self) -> Response<Body> {
let mut res = Response::new(Body::from(self));
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"),
@@ -135,11 +324,11 @@ impl IntoResponse for std::borrow::Cow<'static, [u8]> {
}
impl IntoResponse for StatusCode {
fn into_response(self) -> Response<Body> {
Response::builder()
.status(self)
.body(Body::empty())
.unwrap()
type Body = Empty<Bytes>;
type BodyError = Infallible;
fn into_response(self) -> Response<Self::Body> {
Response::builder().status(self).body(Empty::new()).unwrap()
}
}
@@ -147,7 +336,10 @@ impl<T> IntoResponse for (StatusCode, T)
where
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();
*res.status_mut() = self.0;
res
@@ -158,7 +350,10 @@ impl<T> IntoResponse for (HeaderMap, T)
where
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();
res.headers_mut().extend(self.0);
res
@@ -169,7 +364,10 @@ impl<T> IntoResponse for (StatusCode, HeaderMap, T)
where
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();
*res.status_mut() = self.0;
res.headers_mut().extend(self.1);
@@ -178,8 +376,11 @@ where
}
impl IntoResponse for HeaderMap {
fn into_response(self) -> Response<Body> {
let mut res = Response::new(Body::empty());
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
}
@@ -193,9 +394,12 @@ pub struct Html<T>(pub T);
impl<T> IntoResponse for Html<T>
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());
res.headers_mut()
.insert(header::CONTENT_TYPE, HeaderValue::from_static("text/html"));
@@ -212,6 +416,7 @@ impl<T> From<T> for Html<T> {
#[cfg(test)]
mod tests {
use super::*;
use crate::body::Body;
use http::header::{HeaderMap, HeaderName};
#[test]
@@ -219,6 +424,9 @@ mod tests {
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()