Skip to content

Commit

Permalink
Change compression body error type to BoxError (#166)
Browse files Browse the repository at this point in the history
* Change compression body error type to `BoxError`

The compression related middleware would previously use `BodyOrIoError`
as the body error type. That only implemented `std::error::Error` if the
inner error did as well. Since `Box<dyn std::error::Error + Send + Sync>`
does not implement `std::error::Error` `Compression` and
`Decompression` wouldn't be usable with hyper if the body they wrapped
had that error type.

This changes the middleware to use `BoxError` as the error type which
resolves the issue. Its also consistent with other middleware.

Reimplementation of #107 on top of latest `master`.

* changelog

* fix doc tests
  • Loading branch information
davidpdrsn authored Nov 15, 2021
1 parent 18e4122 commit 8d446f4
Show file tree
Hide file tree
Showing 8 changed files with 35 additions and 68 deletions.
8 changes: 7 additions & 1 deletion tower-http/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- `ServeDir` and `ServeFile`: Ability to serve precompressed files ([#156])
- `Trace`: Add `DefaultMakeSpan::level` to make log level of tracing spans easily configurable ([#124])
- Change the response body error type of `Compression` and `Decompression` to
`Box<dyn std::error::Error + Send + Sync>`. This makes them usable if the body
they're wrapping uses `Box<dyn std::error::Error + Send + Sync>` as its error
type which they previously weren't ([#166])
- Remove `BodyOrIoError`. Its been replaced with `Box<dyn std::error::Error +
Send + Sync>` ([#166])
- `SetRequestHeaderLayer`, `SetResponseHeaderLayer`: Remove unnecessary generic parameter ([#148])

This removes the need (and possibility) to specify a body type for these layers.

[#124]: https://github.com/tower-rs/tower-http/pull/124
[#156]: https://github.com/tower-rs/tower-http/pull/156
[#166]: https://github.com/tower-rs/tower-http/pull/166
[#148]: https://github.com/tower-rs/tower-http/pull/148

# 0.1.2 (November 13, 2021)
Expand Down
9 changes: 5 additions & 4 deletions tower-http/src/compression/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use crate::{
compression_utils::{AsyncReadBody, BodyIntoStream, DecorateAsyncRead, WrapBody},
BodyOrIoError,
BoxError,
};
#[cfg(feature = "compression-br")]
use async_compression::tokio::bufread::BrotliEncoder;
Expand Down Expand Up @@ -137,9 +137,10 @@ where
impl<B> Body for CompressionBody<B>
where
B: Body,
B::Error: Into<BoxError>,
{
type Data = Bytes;
type Error = BodyOrIoError<B::Error>;
type Error = BoxError;

fn poll_data(
self: Pin<&mut Self>,
Expand All @@ -157,7 +158,7 @@ where
let bytes = buf.copy_to_bytes(buf.remaining());
Poll::Ready(Some(Ok(bytes)))
}
Some(Err(err)) => Poll::Ready(Some(Err(BodyOrIoError::Body(err)))),
Some(Err(err)) => Poll::Ready(Some(Err(err.into()))),
None => Poll::Ready(None),
},
}
Expand All @@ -174,7 +175,7 @@ where
BodyInnerProj::Deflate(inner) => inner.poll_trailers(cx),
#[cfg(feature = "compression-br")]
BodyInnerProj::Brotli(inner) => inner.poll_trailers(cx),
BodyInnerProj::Identity(body) => body.poll_trailers(cx).map_err(BodyOrIoError::Body),
BodyInnerProj::Identity(body) => body.poll_trailers(cx).map_err(Into::into),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tower-http/src/compression/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ mod tests {
}

#[tokio::test]
async fn accept_encoding_configuration_works() -> Result<(), Box<dyn std::error::Error>> {
async fn accept_encoding_configuration_works() -> Result<(), crate::BoxError> {
let deflate_only_layer = CompressionLayer::new().no_br().no_gzip();

let mut service = ServiceBuilder::new()
Expand Down
4 changes: 2 additions & 2 deletions tower-http/src/compression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
//! use tokio::fs::{self, File};
//! use tokio_util::io::ReaderStream;
//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn};
//! use tower_http::compression::CompressionLayer;
//! use tower_http::{compression::CompressionLayer, BoxError};
//!
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # async fn main() -> Result<(), BoxError> {
//! async fn handle(req: Request<Body>) -> Result<Response<Body>, Infallible> {
//! // Open the file.
//! let file = File::open("Cargo.toml").await.expect("file missing");
Expand Down
12 changes: 6 additions & 6 deletions tower-http/src/compression_utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Types used by compression and decompression middleware.
use crate::{content_encoding::SupportedEncodings, BoxError};
use bytes::{Bytes, BytesMut};
use futures_core::Stream;
use futures_util::ready;
Expand All @@ -14,8 +15,6 @@ use std::{
use tokio::io::AsyncRead;
use tokio_util::io::{poll_read_buf, StreamReader};

use crate::{content_encoding::SupportedEncodings, BodyOrIoError};

#[derive(Debug, Clone, Copy)]
pub(crate) struct AcceptEncoding {
pub(crate) gzip: bool,
Expand Down Expand Up @@ -155,10 +154,11 @@ impl<M: DecorateAsyncRead> WrapBody<M> {
impl<B, M> Body for WrapBody<M>
where
B: Body,
B::Error: Into<BoxError>,
M: DecorateAsyncRead<Input = AsyncReadBody<B>>,
{
type Data = Bytes;
type Error = BodyOrIoError<B::Error>;
type Error = BoxError;

fn poll_data(
self: Pin<&mut Self>,
Expand All @@ -177,12 +177,12 @@ where
.take();

if let Some(body_error) = body_error {
return Poll::Ready(Some(Err(BodyOrIoError::Body(body_error))));
return Poll::Ready(Some(Err(body_error.into())));
} else if err.raw_os_error() == Some(SENTINEL_ERROR_CODE) {
// SENTINEL_ERROR_CODE only gets used when storing an underlying body error
unreachable!()
} else {
return Poll::Ready(Some(Err(BodyOrIoError::Io(err))));
return Poll::Ready(Some(Err(err.into())));
}
}
};
Expand All @@ -203,7 +203,7 @@ where
.get_pin_mut()
.get_pin_mut()
.get_pin_mut();
body.poll_trailers(cx).map_err(BodyOrIoError::Body)
body.poll_trailers(cx).map_err(Into::into)
}
}

Expand Down
13 changes: 8 additions & 5 deletions tower-http/src/decompression/body.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#![allow(unused_imports)]

use crate::compression_utils::{AsyncReadBody, BodyIntoStream, DecorateAsyncRead, WrapBody};
use crate::BodyOrIoError;
use crate::{
compression_utils::{AsyncReadBody, BodyIntoStream, DecorateAsyncRead, WrapBody},
BoxError,
};
#[cfg(feature = "decompression-br")]
use async_compression::tokio::bufread::BrotliDecoder;
#[cfg(feature = "decompression-gzip")]
Expand Down Expand Up @@ -132,9 +134,10 @@ where
impl<B> Body for DecompressionBody<B>
where
B: Body,
B::Error: Into<BoxError>,
{
type Data = Bytes;
type Error = BodyOrIoError<B::Error>;
type Error = BoxError;

fn poll_data(
self: Pin<&mut Self>,
Expand All @@ -152,7 +155,7 @@ where
let bytes = buf.copy_to_bytes(buf.remaining());
Poll::Ready(Some(Ok(bytes)))
}
Some(Err(err)) => Poll::Ready(Some(Err(BodyOrIoError::Body(err)))),
Some(Err(err)) => Poll::Ready(Some(Err(err.into()))),
None => Poll::Ready(None),
},
}
Expand All @@ -169,7 +172,7 @@ where
BodyInnerProj::Deflate(inner) => inner.poll_trailers(cx),
#[cfg(feature = "decompression-br")]
BodyInnerProj::Brotli(inner) => inner.poll_trailers(cx),
BodyInnerProj::Identity(body) => body.poll_trailers(cx).map_err(BodyOrIoError::Body),
BodyInnerProj::Identity(body) => body.poll_trailers(cx).map_err(Into::into),
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions tower-http/src/decompression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
//! use hyper::Body;
//! use std::convert::Infallible;
//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn};
//! use tower_http::{compression::Compression, decompression::DecompressionLayer};
//! use tower_http::{compression::Compression, decompression::DecompressionLayer, BoxError};
//! #
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # async fn main() -> Result<(), tower_http::BoxError> {
//! # async fn handle(req: Request<Body>) -> Result<Response<Body>, Infallible> {
//! # let body = Body::from("Hello, World!");
//! # Ok(Response::new(body))
Expand Down Expand Up @@ -45,7 +45,7 @@
//! let chunk = chunk?;
//! bytes.extend_from_slice(&chunk[..]);
//! }
//! let body = String::from_utf8(bytes.to_vec())?;
//! let body = String::from_utf8(bytes.to_vec()).map_err(Into::<BoxError>::into)?;
//!
//! assert_eq!(body, "Hello, World!");
//! #
Expand Down
49 changes: 3 additions & 46 deletions tower-http/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,52 +285,6 @@ pub mod cors;
pub mod classify;
pub mod services;

/// Error type containing either a body error or an IO error.
///
/// This type is used to combine errors produced by response bodies with compression or
/// decompression applied. The body itself can produce errors of type `E` whereas compression or
/// decompression can produce [`io::Error`]s.
///
/// [`io::Error`]: std::io::Error
#[cfg(any(feature = "compression", feature = "decompression"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "compression", feature = "decompression")))
)]
#[derive(Debug)]
pub enum BodyOrIoError<E> {
/// Errors produced by the body.
Body(E),
/// IO errors produced by compression or decompression.
Io(std::io::Error),
}

#[cfg(any(feature = "compression", feature = "decompression"))]
impl<E> std::fmt::Display for BodyOrIoError<E>
where
E: std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BodyOrIoError::Io(inner) => inner.fmt(f),
BodyOrIoError::Body(inner) => inner.fmt(f),
}
}
}

#[cfg(any(feature = "compression", feature = "decompression"))]
impl<E> std::error::Error for BodyOrIoError<E>
where
E: std::error::Error,
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
BodyOrIoError::Io(inner) => inner.source(),
BodyOrIoError::Body(inner) => inner.source(),
}
}
}

/// The latency unit used to report latencies by middleware.
#[non_exhaustive]
#[derive(Copy, Clone, Debug)]
Expand All @@ -342,3 +296,6 @@ pub enum LatencyUnit {
/// Use nanoseconds.
Nanos,
}

/// Alias for a type-erased error type.
pub type BoxError = Box<dyn std::error::Error + Send + Sync>;

0 comments on commit 8d446f4

Please sign in to comment.