From 6c761662aa5fa2e056a3c7ccfc4eeae57865b419 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 09:46:36 +0100 Subject: [PATCH 01/56] punt on examples for now --- examples/axum-key-value-store/src/main.rs | 6 ++++++ examples/tonic-key-value-store/src/main.rs | 6 ++++++ examples/warp-key-value-store/src/main.rs | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/examples/axum-key-value-store/src/main.rs b/examples/axum-key-value-store/src/main.rs index 12841189..0379e89d 100644 --- a/examples/axum-key-value-store/src/main.rs +++ b/examples/axum-key-value-store/src/main.rs @@ -1,3 +1,8 @@ +fn main() { + eprint!("this example has not yet been updated to hyper 1.0"); +} + +/* use axum::{ body::Bytes, extract::{Path, State}, @@ -108,3 +113,4 @@ async fn set_key(Path(path): Path, state: State, value: Bytes) // See https://github.com/tokio-rs/axum/blob/main/examples/testing/src/main.rs for an example of // how to test axum apps +*/ diff --git a/examples/tonic-key-value-store/src/main.rs b/examples/tonic-key-value-store/src/main.rs index bb7ea04a..e615e0ed 100644 --- a/examples/tonic-key-value-store/src/main.rs +++ b/examples/tonic-key-value-store/src/main.rs @@ -1,3 +1,8 @@ +fn main() { + eprint!("this example has not yet been updated to hyper 1.0"); +} + +/* use bytes::Bytes; use clap::Parser; use futures::StreamExt; @@ -370,3 +375,4 @@ mod tests { addr } } +*/ diff --git a/examples/warp-key-value-store/src/main.rs b/examples/warp-key-value-store/src/main.rs index feb179f8..065f3808 100644 --- a/examples/warp-key-value-store/src/main.rs +++ b/examples/warp-key-value-store/src/main.rs @@ -1,3 +1,8 @@ +fn main() { + eprint!("this example has not yet been updated to hyper 1.0"); +} + +/* use bytes::Bytes; use clap::Parser; use hyper::{ @@ -222,3 +227,4 @@ mod tests { addr } } +*/ From 35c9aed73bbfd56fdfac71f4eb53bbb1c3e10f3a Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 10:10:35 +0100 Subject: [PATCH 02/56] remove dependency on hyper --- tower-http/Cargo.toml | 2 +- tower-http/src/add_extension.rs | 2 +- tower-http/src/auth/add_authorization.rs | 9 +- .../src/auth/async_require_authorization.rs | 2 +- tower-http/src/auth/require_authorization.rs | 2 +- tower-http/src/catch_panic.rs | 7 +- tower-http/src/classify/mod.rs | 2 +- tower-http/src/compression/layer.rs | 4 +- tower-http/src/compression/mod.rs | 26 +-- tower-http/src/decompression/mod.rs | 17 +- tower-http/src/decompression/request/mod.rs | 27 +-- tower-http/src/follow_redirect/mod.rs | 3 +- tower-http/src/lib.rs | 3 + tower-http/src/metrics/in_flight_requests.rs | 4 +- tower-http/src/request_id.rs | 3 +- tower-http/src/services/fs/serve_dir/tests.rs | 6 +- tower-http/src/services/fs/serve_file.rs | 2 +- tower-http/src/set_header/response.rs | 2 +- tower-http/src/test_helpers.rs | 164 ++++++++++++++++++ tower-http/src/trace/mod.rs | 12 +- tower-http/src/validate_request.rs | 2 +- 21 files changed, 222 insertions(+), 79 deletions(-) create mode 100644 tower-http/src/test_helpers.rs diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index a8626c93..e505fa1e 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -43,13 +43,13 @@ bytes = "1" flate2 = "1.0" brotli = "3" futures = "0.3" -hyper = { version = "0.14", features = ["full"] } once_cell = "1" tokio = { version = "1", features = ["full"] } tower = { version = "0.4.10", features = ["buffer", "util", "retry", "make", "timeout"] } tracing-subscriber = "0.3" uuid = { version = "1.0", features = ["v4"] } serde_json = "1.0" +sync_wrapper = "0.1.1" zstd = "0.11" [features] diff --git a/tower-http/src/add_extension.rs b/tower-http/src/add_extension.rs index 4949a736..eb028d99 100644 --- a/tower-http/src/add_extension.rs +++ b/tower-http/src/add_extension.rs @@ -137,8 +137,8 @@ where mod tests { #[allow(unused_imports)] use super::*; + use crate::test_helpers::Body; use http::Response; - use hyper::Body; use std::{convert::Infallible, sync::Arc}; use tower::{service_fn, ServiceBuilder, ServiceExt}; diff --git a/tower-http/src/auth/add_authorization.rs b/tower-http/src/auth/add_authorization.rs index 8139a123..f83f313a 100644 --- a/tower-http/src/auth/add_authorization.rs +++ b/tower-http/src/auth/add_authorization.rs @@ -184,12 +184,11 @@ where #[cfg(test)] mod tests { - use crate::validate_request::ValidateRequestHeaderLayer; - - #[allow(unused_imports)] use super::*; + use crate::test_helpers::Body; + use crate::validate_request::ValidateRequestHeaderLayer; use http::{Response, StatusCode}; - use hyper::Body; + use std::convert::Infallible; use tower::{BoxError, Service, ServiceBuilder, ServiceExt}; #[tokio::test] @@ -242,7 +241,7 @@ mod tests { let auth = request.headers().get(http::header::AUTHORIZATION).unwrap(); assert!(auth.is_sensitive()); - Ok::<_, hyper::Error>(Response::new(Body::empty())) + Ok::<_, Infallible>(Response::new(Body::empty())) }); let mut client = AddAuthorization::bearer(svc, "foo").as_sensitive(true); diff --git a/tower-http/src/auth/async_require_authorization.rs b/tower-http/src/auth/async_require_authorization.rs index 5963e65c..735aaa1c 100644 --- a/tower-http/src/auth/async_require_authorization.rs +++ b/tower-http/src/auth/async_require_authorization.rs @@ -307,9 +307,9 @@ where mod tests { #[allow(unused_imports)] use super::*; + use crate::test_helpers::Body; use futures_util::future::BoxFuture; use http::{header, StatusCode}; - use hyper::Body; use tower::{BoxError, ServiceBuilder, ServiceExt}; #[derive(Clone, Copy)] diff --git a/tower-http/src/auth/require_authorization.rs b/tower-http/src/auth/require_authorization.rs index cf4ade46..a1f241f1 100644 --- a/tower-http/src/auth/require_authorization.rs +++ b/tower-http/src/auth/require_authorization.rs @@ -247,8 +247,8 @@ mod tests { #[allow(unused_imports)] use super::*; + use crate::test_helpers::Body; use http::header; - use hyper::Body; use tower::{BoxError, ServiceBuilder, ServiceExt}; use tower_service::Service; diff --git a/tower-http/src/catch_panic.rs b/tower-http/src/catch_panic.rs index b8e3213e..b547b32d 100644 --- a/tower-http/src/catch_panic.rs +++ b/tower-http/src/catch_panic.rs @@ -359,7 +359,8 @@ mod tests { #![allow(unreachable_code)] use super::*; - use hyper::{Body, Response}; + use crate::test_helpers::Body; + use http::Response; use std::convert::Infallible; use tower::{ServiceBuilder, ServiceExt}; @@ -377,7 +378,7 @@ mod tests { let res = svc.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); - let body = hyper::body::to_bytes(res).await.unwrap(); + let body = crate::test_helpers::to_bytes(res).await.unwrap(); assert_eq!(&body[..], b"Service panicked"); } @@ -395,7 +396,7 @@ mod tests { let res = svc.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); - let body = hyper::body::to_bytes(res).await.unwrap(); + let body = crate::test_helpers::to_bytes(res).await.unwrap(); assert_eq!(&body[..], b"Service panicked"); } } diff --git a/tower-http/src/classify/mod.rs b/tower-http/src/classify/mod.rs index 2c1ecc56..417430c1 100644 --- a/tower-http/src/classify/mod.rs +++ b/tower-http/src/classify/mod.rs @@ -375,7 +375,7 @@ impl fmt::Display for ServerErrorsFailureClass { mod usable_for_retries { #[allow(unused_imports)] use super::*; - use hyper::{Request, Response}; + use http::{Request, Response}; use tower::retry::Policy; trait IsRetryable { diff --git a/tower-http/src/compression/layer.rs b/tower-http/src/compression/layer.rs index 1ac67670..359385e7 100644 --- a/tower-http/src/compression/layer.rs +++ b/tower-http/src/compression/layer.rs @@ -123,9 +123,9 @@ impl CompressionLayer { #[cfg(test)] mod tests { use super::*; + use crate::test_helpers::Body; use http::{header::ACCEPT_ENCODING, Request, Response}; use http_body::Body as _; - use hyper::Body; use tokio::fs::File; // for Body::data use bytes::{Bytes, BytesMut}; @@ -139,7 +139,7 @@ mod tests { // Convert the file into a `Stream`. let stream = ReaderStream::new(file); // Convert the `Stream` into a `Body`. - let body = Body::wrap_stream(stream); + let body = Body::from_stream(stream); // Create response. Ok(Response::new(body)) } diff --git a/tower-http/src/compression/mod.rs b/tower-http/src/compression/mod.rs index dbfd2364..d9d4434b 100644 --- a/tower-http/src/compression/mod.rs +++ b/tower-http/src/compression/mod.rs @@ -87,17 +87,19 @@ mod tests { use crate::compression::predicate::SizeAbove; use super::*; + use crate::test_helpers::Body; use async_compression::tokio::write::{BrotliDecoder, BrotliEncoder}; use bytes::BytesMut; use flate2::read::GzDecoder; use http::header::{ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_TYPE}; + use http::{Request, Response}; use http_body::Body as _; - use hyper::{Body, Error, Request, Response, Server}; + use std::convert::Infallible; + use std::io::Read; use std::sync::{Arc, RwLock}; - use std::{io::Read, net::SocketAddr}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio_util::io::StreamReader; - use tower::{make::Shared, service_fn, Service, ServiceExt}; + use tower::{service_fn, Service, ServiceExt}; // Compression filter allows every other request to be compressed #[derive(Clone)] @@ -171,18 +173,6 @@ mod tests { assert_eq!(decompressed, "Hello, World!"); } - #[allow(dead_code)] - async fn is_compatible_with_hyper() { - let svc = service_fn(handle); - let svc = Compression::new(svc); - - let make_service = Shared::new(svc); - - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - let server = Server::bind(&addr).serve(make_service); - server.await.unwrap(); - } - #[tokio::test] async fn no_recompress() { const DATA: &str = "Hello, World! I'm already compressed with br!"; @@ -247,7 +237,7 @@ mod tests { assert_eq!(data, DATA.as_bytes()); } - async fn handle(_req: Request) -> Result, Error> { + async fn handle(_req: Request) -> Result, Infallible> { Ok(Response::new(Body::from("Hello, World!"))) } @@ -317,7 +307,7 @@ mod tests { #[tokio::test] async fn doesnt_compress_images() { - async fn handle(_req: Request) -> Result, Error> { + async fn handle(_req: Request) -> Result, Infallible> { let mut res = Response::new(Body::from( "a".repeat((SizeAbove::DEFAULT_MIN_SIZE * 2) as usize), )); @@ -342,7 +332,7 @@ mod tests { #[tokio::test] async fn does_compress_svg() { - async fn handle(_req: Request) -> Result, Error> { + async fn handle(_req: Request) -> Result, Infallible> { let mut res = Response::new(Body::from( "a".repeat((SizeAbove::DEFAULT_MIN_SIZE * 2) as usize), )); diff --git a/tower-http/src/decompression/mod.rs b/tower-http/src/decompression/mod.rs index 567031f1..61aab330 100644 --- a/tower-http/src/decompression/mod.rs +++ b/tower-http/src/decompression/mod.rs @@ -115,12 +115,15 @@ pub use self::request::service::RequestDecompression; #[cfg(test)] mod tests { + use std::convert::Infallible; + use super::*; use crate::compression::Compression; + use crate::test_helpers::Body; use bytes::BytesMut; + use http::Request; use http::Response; use http_body::Body as _; - use hyper::{Body, Client, Error, Request}; use tower::{service_fn, Service, ServiceExt}; #[tokio::test] @@ -145,17 +148,7 @@ mod tests { assert_eq!(decompressed_data, "Hello, World!"); } - async fn handle(_req: Request) -> Result, Error> { + async fn handle(_req: Request) -> Result, Infallible> { Ok(Response::new(Body::from("Hello, World!"))) } - - #[allow(dead_code)] - async fn is_compatible_with_hyper() { - let mut client = Decompression::new(Client::new()); - - let req = Request::new(Body::empty()); - - let _: Response> = - client.ready().await.unwrap().call(req).await.unwrap(); - } } diff --git a/tower-http/src/decompression/request/mod.rs b/tower-http/src/decompression/request/mod.rs index 42621a17..6dbbf85f 100644 --- a/tower-http/src/decompression/request/mod.rs +++ b/tower-http/src/decompression/request/mod.rs @@ -6,14 +6,13 @@ pub(super) mod service; mod tests { use super::service::RequestDecompression; use crate::decompression::DecompressionBody; + use crate::test_helpers::Body; use bytes::BytesMut; use flate2::{write::GzEncoder, Compression}; - use http::{header, Response, StatusCode}; + use http::{header, Request, Response, StatusCode}; use http_body::Body as _; - use hyper::{Body, Error, Request, Server}; - use std::io::Write; - use std::net::SocketAddr; - use tower::{make::Shared, service_fn, Service, ServiceExt}; + use std::{convert::Infallible, io::Write}; + use tower::{service_fn, Service, ServiceExt}; #[tokio::test] async fn decompress_accepted_encoding() { @@ -48,7 +47,7 @@ mod tests { async fn assert_request_is_decompressed( req: Request>, - ) -> Result, Error> { + ) -> Result, Infallible> { let (parts, mut body) = req.into_parts(); let body = read_body(&mut body).await; @@ -60,7 +59,7 @@ mod tests { async fn assert_request_is_passed_through( req: Request>, - ) -> Result, Error> { + ) -> Result, Infallible> { let (parts, mut body) = req.into_parts(); let body = read_body(&mut body).await; @@ -72,7 +71,7 @@ mod tests { async fn should_not_be_called( _: Request>, - ) -> Result, Error> { + ) -> Result, Infallible> { panic!("Inner service should not be called"); } @@ -94,16 +93,4 @@ mod tests { } data.freeze().to_vec() } - - #[allow(dead_code)] - async fn is_compatible_with_hyper() { - let svc = service_fn(assert_request_is_decompressed); - let svc = RequestDecompression::new(svc); - - let make_service = Shared::new(svc); - - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - let server = Server::bind(&addr).serve(make_service); - server.await.unwrap(); - } } diff --git a/tower-http/src/follow_redirect/mod.rs b/tower-http/src/follow_redirect/mod.rs index 19f825db..af8f12aa 100644 --- a/tower-http/src/follow_redirect/mod.rs +++ b/tower-http/src/follow_redirect/mod.rs @@ -386,7 +386,8 @@ fn resolve_uri(relative: &str, base: &Uri) -> Option { #[cfg(test)] mod tests { use super::{policy::*, *}; - use hyper::{header::LOCATION, Body}; + use crate::test_helpers::Body; + use http::header::LOCATION; use std::convert::Infallible; use tower::{ServiceBuilder, ServiceExt}; diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index 6719ddbd..a18e9062 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -225,6 +225,9 @@ #[macro_use] pub(crate) mod macros; +#[cfg(test)] +mod test_helpers; + #[cfg(feature = "auth")] pub mod auth; diff --git a/tower-http/src/metrics/in_flight_requests.rs b/tower-http/src/metrics/in_flight_requests.rs index 85fb5720..c5ee157b 100644 --- a/tower-http/src/metrics/in_flight_requests.rs +++ b/tower-http/src/metrics/in_flight_requests.rs @@ -297,8 +297,8 @@ where mod tests { #[allow(unused_imports)] use super::*; + use crate::test_helpers::Body; use http::Request; - use hyper::Body; use tower::{BoxError, ServiceBuilder}; #[tokio::test] @@ -325,7 +325,7 @@ mod tests { assert_eq!(counter.get(), 1); let body = response.into_body(); - hyper::body::to_bytes(body).await.unwrap(); + crate::test_helpers::to_bytes(body).await.unwrap(); assert_eq!(counter.get(), 0); } diff --git a/tower-http/src/request_id.rs b/tower-http/src/request_id.rs index 58a8324e..49d435a0 100644 --- a/tower-http/src/request_id.rs +++ b/tower-http/src/request_id.rs @@ -481,8 +481,9 @@ impl MakeRequestId for MakeRequestUuid { #[cfg(test)] mod tests { + use crate::test_helpers::Body; use crate::ServiceBuilderExt as _; - use hyper::{Body, Response}; + use http::Response; use std::{ convert::Infallible, sync::{ diff --git a/tower-http/src/services/fs/serve_dir/tests.rs b/tower-http/src/services/fs/serve_dir/tests.rs index 9a25c6dd..1cbb0a02 100644 --- a/tower-http/src/services/fs/serve_dir/tests.rs +++ b/tower-http/src/services/fs/serve_dir/tests.rs @@ -1,4 +1,5 @@ use crate::services::{ServeDir, ServeFile}; +use crate::test_helpers::{to_bytes, Body}; use brotli::BrotliDecompress; use bytes::Bytes; use flate2::bufread::{DeflateDecoder, GzDecoder}; @@ -6,7 +7,6 @@ use http::header::ALLOW; use http::{header, Method, Response}; use http::{Request, StatusCode}; use http_body::Body as HttpBody; -use hyper::Body; use std::convert::Infallible; use std::io::{self, Read}; use tower::{service_fn, ServiceExt}; @@ -404,7 +404,7 @@ where B: HttpBody + Unpin, B::Error: std::fmt::Debug, { - let bytes = hyper::body::to_bytes(body).await.unwrap(); + let bytes = to_bytes(body).await.unwrap(); String::from_utf8(bytes.to_vec()).unwrap() } @@ -474,7 +474,7 @@ async fn read_partial_in_bounds() { ))); assert_eq!(res.headers()["content-type"], "text/markdown"); - let body = hyper::body::to_bytes(res.into_body()).await.ok().unwrap(); + let body = to_bytes(res.into_body()).await.ok().unwrap(); let source = Bytes::from(file_contents[bytes_start_incl..=bytes_end_incl].to_vec()); assert_eq!(body, source); } diff --git a/tower-http/src/services/fs/serve_file.rs b/tower-http/src/services/fs/serve_file.rs index ede79936..2eb277bf 100644 --- a/tower-http/src/services/fs/serve_file.rs +++ b/tower-http/src/services/fs/serve_file.rs @@ -128,6 +128,7 @@ where #[cfg(test)] mod tests { use crate::services::ServeFile; + use crate::test_helpers::Body; use brotli::BrotliDecompress; use flate2::bufread::DeflateDecoder; use flate2::bufread::GzDecoder; @@ -135,7 +136,6 @@ mod tests { use http::Method; use http::{Request, StatusCode}; use http_body::Body as _; - use hyper::Body; use mime::Mime; use std::io::Read; use std::str::FromStr; diff --git a/tower-http/src/set_header/response.rs b/tower-http/src/set_header/response.rs index 1440c21b..3ab971bf 100644 --- a/tower-http/src/set_header/response.rs +++ b/tower-http/src/set_header/response.rs @@ -301,8 +301,8 @@ where #[cfg(test)] mod tests { use super::*; + use crate::test_helpers::Body; use http::{header, HeaderValue}; - use hyper::Body; use std::convert::Infallible; use tower::{service_fn, ServiceExt}; diff --git a/tower-http/src/test_helpers.rs b/tower-http/src/test_helpers.rs new file mode 100644 index 00000000..4604b1c5 --- /dev/null +++ b/tower-http/src/test_helpers.rs @@ -0,0 +1,164 @@ +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use bytes::{Buf, BufMut, Bytes}; +use futures::TryStream; +use http::HeaderMap; +use http_body::Body as _; +use pin_project_lite::pin_project; +use sync_wrapper::SyncWrapper; +use tower::BoxError; + +type BoxBody = http_body::combinators::UnsyncBoxBody; + +#[derive(Debug)] +pub(crate) struct Body(BoxBody); + +impl Body { + pub(crate) fn new(body: B) -> Self + where + B: http_body::Body + Send + 'static, + B::Error: Into, + { + Self(body.map_err(Into::into).boxed_unsync()) + } + + pub(crate) fn empty() -> Self { + Self::new(http_body::Empty::new()) + } + + pub(crate) fn from_stream(stream: S) -> Self + where + S: TryStream + Send + 'static, + S::Ok: Into, + S::Error: Into, + { + Self::new(StreamBody { + stream: SyncWrapper::new(stream), + }) + } +} + +impl Default for Body { + fn default() -> Self { + Self::empty() + } +} + +macro_rules! body_from_impl { + ($ty:ty) => { + impl From<$ty> for Body { + fn from(buf: $ty) -> Self { + Self::new(http_body::Full::from(buf)) + } + } + }; +} + +body_from_impl!(&'static [u8]); +body_from_impl!(std::borrow::Cow<'static, [u8]>); +body_from_impl!(Vec); + +body_from_impl!(&'static str); +body_from_impl!(std::borrow::Cow<'static, str>); +body_from_impl!(String); + +body_from_impl!(Bytes); + +impl http_body::Body for Body { + type Data = Bytes; + type Error = BoxError; + + fn poll_data( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> std::task::Poll>> { + Pin::new(&mut self.0).poll_data(cx) + } + + fn poll_trailers( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> std::task::Poll, Self::Error>> { + Pin::new(&mut self.0).poll_trailers(cx) + } + + fn size_hint(&self) -> http_body::SizeHint { + self.0.size_hint() + } + + fn is_end_stream(&self) -> bool { + self.0.is_end_stream() + } +} + +pin_project! { + struct StreamBody { + #[pin] + stream: SyncWrapper, + } +} + +impl http_body::Body for StreamBody +where + S: TryStream, + S::Ok: Into, + S::Error: Into, +{ + type Data = Bytes; + type Error = BoxError; + + fn poll_data( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { + let stream = self.project().stream.get_pin_mut(); + match futures_util::ready!(stream.try_poll_next(cx)) { + Some(Ok(chunk)) => Poll::Ready(Some(Ok(chunk.into()))), + Some(Err(err)) => Poll::Ready(Some(Err(err.into()))), + None => Poll::Ready(None), + } + } + + fn poll_trailers( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll, Self::Error>> { + Poll::Ready(Ok(None)) + } +} + +// copied from hyper +pub(crate) async fn to_bytes(body: T) -> Result +where + T: http_body::Body, +{ + futures_util::pin_mut!(body); + + // If there's only 1 chunk, we can just return Buf::to_bytes() + let mut first = if let Some(buf) = body.data().await { + buf? + } else { + return Ok(Bytes::new()); + }; + + let second = if let Some(buf) = body.data().await { + buf? + } else { + return Ok(first.copy_to_bytes(first.remaining())); + }; + + // With more than 1 buf, we gotta flatten into a Vec first. + let cap = first.remaining() + second.remaining() + body.size_hint().lower() as usize; + let mut vec = Vec::with_capacity(cap); + vec.put(first); + vec.put(second); + + while let Some(buf) = body.data().await { + vec.put(buf?); + } + + Ok(vec.into()) +} diff --git a/tower-http/src/trace/mod.rs b/tower-http/src/trace/mod.rs index cc635ee1..da8f8549 100644 --- a/tower-http/src/trace/mod.rs +++ b/tower-http/src/trace/mod.rs @@ -416,9 +416,9 @@ const DEFAULT_ERROR_LEVEL: Level = Level::ERROR; mod tests { use super::*; use crate::classify::ServerErrorsFailureClass; + use crate::test_helpers::Body; use bytes::Bytes; use http::{HeaderMap, Request, Response}; - use hyper::Body; use once_cell::sync::Lazy; use std::{ sync::atomic::{AtomicU32, Ordering}, @@ -476,7 +476,9 @@ mod tests { assert_eq!(0, ON_EOS.load(Ordering::SeqCst), "eos"); assert_eq!(0, ON_FAILURE.load(Ordering::SeqCst), "failure"); - hyper::body::to_bytes(res.into_body()).await.unwrap(); + crate::test_helpers::to_bytes(res.into_body()) + .await + .unwrap(); assert_eq!(1, ON_BODY_CHUNK_COUNT.load(Ordering::SeqCst), "body chunk"); assert_eq!(0, ON_EOS.load(Ordering::SeqCst), "eos"); assert_eq!(0, ON_FAILURE.load(Ordering::SeqCst), "failure"); @@ -529,7 +531,9 @@ mod tests { assert_eq!(0, ON_EOS.load(Ordering::SeqCst), "eos"); assert_eq!(0, ON_FAILURE.load(Ordering::SeqCst), "failure"); - hyper::body::to_bytes(res.into_body()).await.unwrap(); + crate::test_helpers::to_bytes(res.into_body()) + .await + .unwrap(); assert_eq!(3, ON_BODY_CHUNK_COUNT.load(Ordering::SeqCst), "body chunk"); assert_eq!(0, ON_EOS.load(Ordering::SeqCst), "eos"); assert_eq!(0, ON_FAILURE.load(Ordering::SeqCst), "failure"); @@ -548,7 +552,7 @@ mod tests { Ok::<_, BoxError>(Bytes::from("three")), ]); - let body = Body::wrap_stream(stream); + let body = Body::from_stream(stream); Ok(Response::new(body)) } diff --git a/tower-http/src/validate_request.rs b/tower-http/src/validate_request.rs index c61c1bed..dff0772f 100644 --- a/tower-http/src/validate_request.rs +++ b/tower-http/src/validate_request.rs @@ -413,8 +413,8 @@ where mod tests { #[allow(unused_imports)] use super::*; + use crate::test_helpers::Body; use http::header; - use hyper::Body; use tower::{BoxError, ServiceBuilder, ServiceExt}; #[tokio::test] From 375bfa145d214084a23957ae74cb667d0f6a1011 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 12:17:19 +0100 Subject: [PATCH 03/56] port to http-body 1.0 --- Cargo.toml | 4 + tower-http/Cargo.toml | 4 +- tower-http/src/catch_panic.rs | 3 +- tower-http/src/compression/body.rs | 37 ++----- tower-http/src/compression/layer.rs | 2 +- tower-http/src/compression/mod.rs | 2 +- tower-http/src/compression_utils.rs | 103 +++++++++++------- tower-http/src/decompression/body.rs | 46 ++------ tower-http/src/decompression/mod.rs | 1 + .../src/decompression/request/future.rs | 5 +- tower-http/src/decompression/request/mod.rs | 2 +- .../src/decompression/request/service.rs | 3 +- tower-http/src/limit/body.rs | 25 ++--- tower-http/src/limit/service.rs | 3 +- tower-http/src/macros.rs | 18 +-- tower-http/src/metrics/in_flight_requests.rs | 14 +-- tower-http/src/services/fs/mod.rs | 20 ++-- .../src/services/fs/serve_dir/future.rs | 2 +- tower-http/src/services/fs/serve_dir/mod.rs | 3 +- .../src/services/fs/serve_dir/open_file.rs | 2 +- tower-http/src/services/fs/serve_dir/tests.rs | 2 +- tower-http/src/services/fs/serve_file.rs | 1 + tower-http/src/test_helpers.rs | 90 ++++++++------- tower-http/src/timeout/body.rs | 74 ++++--------- tower-http/src/trace/body.rs | 81 ++++++-------- 25 files changed, 230 insertions(+), 317 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1667aa59..162f5a93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,3 +3,7 @@ members = [ "tower-http", "examples/*", ] + +[patch.crates-io] +# for `Frame::map_data` +http-body = { git = "https://github.com/hyperium/http-body", rev = "7bf321acbb422214c89933c103417bcfe3892aed" } diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index e505fa1e..af297354 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -18,7 +18,8 @@ bytes = "1" futures-core = "0.3" futures-util = { version = "0.3.14", default_features = false, features = [] } http = "0.2.2" -http-body = "0.4.5" +http-body = "1.0.0-rc.2" +http-body-util = "0.1.0-rc.2" pin-project-lite = "0.2.7" tower-layer = "0.3" tower-service = "0.3" @@ -39,6 +40,7 @@ httpdate = { version = "1.0", optional = true } uuid = { version = "1.0", features = ["v4"], optional = true } [dev-dependencies] +async-trait = "0.1" bytes = "1" flate2 = "1.0" brotli = "3" diff --git a/tower-http/src/catch_panic.rs b/tower-http/src/catch_panic.rs index b547b32d..caa3a496 100644 --- a/tower-http/src/catch_panic.rs +++ b/tower-http/src/catch_panic.rs @@ -86,7 +86,8 @@ use bytes::Bytes; use futures_core::ready; use futures_util::future::{CatchUnwind, FutureExt}; use http::{HeaderValue, Request, Response, StatusCode}; -use http_body::{combinators::UnsyncBoxBody, Body, Full}; +use http_body::Body; +use http_body_util::{combinators::UnsyncBoxBody, BodyExt, Full}; use pin_project_lite::pin_project; use std::{ any::Any, diff --git a/tower-http/src/compression/body.rs b/tower-http/src/compression/body.rs index eeb798ba..486a14bb 100644 --- a/tower-http/src/compression/body.rs +++ b/tower-http/src/compression/body.rs @@ -247,46 +247,29 @@ where type Data = Bytes; type Error = BoxError; - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { + ) -> Poll, Self::Error>>> { match self.project().inner.project() { #[cfg(feature = "compression-gzip")] - BodyInnerProj::Gzip { inner } => inner.poll_data(cx), + BodyInnerProj::Gzip { inner } => inner.poll_frame(cx), #[cfg(feature = "compression-deflate")] - BodyInnerProj::Deflate { inner } => inner.poll_data(cx), + BodyInnerProj::Deflate { inner } => inner.poll_frame(cx), #[cfg(feature = "compression-br")] - BodyInnerProj::Brotli { inner } => inner.poll_data(cx), + BodyInnerProj::Brotli { inner } => inner.poll_frame(cx), #[cfg(feature = "compression-zstd")] - BodyInnerProj::Zstd { inner } => inner.poll_data(cx), - BodyInnerProj::Identity { inner } => match ready!(inner.poll_data(cx)) { - Some(Ok(mut buf)) => { - let bytes = buf.copy_to_bytes(buf.remaining()); - Poll::Ready(Some(Ok(bytes))) + BodyInnerProj::Zstd { inner } => inner.poll_frame(cx), + BodyInnerProj::Identity { inner } => match ready!(inner.poll_frame(cx)) { + Some(Ok(frame)) => { + let frame = frame.map_data(|mut buf| buf.copy_to_bytes(buf.remaining())); + Poll::Ready(Some(Ok(frame))) } Some(Err(err)) => Poll::Ready(Some(Err(err.into()))), None => Poll::Ready(None), }, } } - - fn poll_trailers( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - match self.project().inner.project() { - #[cfg(feature = "compression-gzip")] - BodyInnerProj::Gzip { inner } => inner.poll_trailers(cx), - #[cfg(feature = "compression-deflate")] - BodyInnerProj::Deflate { inner } => inner.poll_trailers(cx), - #[cfg(feature = "compression-br")] - BodyInnerProj::Brotli { inner } => inner.poll_trailers(cx), - #[cfg(feature = "compression-zstd")] - BodyInnerProj::Zstd { inner } => inner.poll_trailers(cx), - BodyInnerProj::Identity { inner } => inner.poll_trailers(cx).map_err(Into::into), - } - } } #[cfg(feature = "compression-gzip")] diff --git a/tower-http/src/compression/layer.rs b/tower-http/src/compression/layer.rs index 359385e7..56854da2 100644 --- a/tower-http/src/compression/layer.rs +++ b/tower-http/src/compression/layer.rs @@ -123,7 +123,7 @@ impl CompressionLayer { #[cfg(test)] mod tests { use super::*; - use crate::test_helpers::Body; + use crate::test_helpers::{Body, TowerHttpBodyExt}; use http::{header::ACCEPT_ENCODING, Request, Response}; use http_body::Body as _; use tokio::fs::File; diff --git a/tower-http/src/compression/mod.rs b/tower-http/src/compression/mod.rs index d9d4434b..1fdf7aa2 100644 --- a/tower-http/src/compression/mod.rs +++ b/tower-http/src/compression/mod.rs @@ -87,7 +87,7 @@ mod tests { use crate::compression::predicate::SizeAbove; use super::*; - use crate::test_helpers::Body; + use crate::test_helpers::{Body, TowerHttpBodyExt}; use async_compression::tokio::write::{BrotliDecoder, BrotliEncoder}; use bytes::BytesMut; use flate2::read::GzDecoder; diff --git a/tower-http/src/compression_utils.rs b/tower-http/src/compression_utils.rs index 7b289371..626543dd 100644 --- a/tower-http/src/compression_utils.rs +++ b/tower-http/src/compression_utils.rs @@ -188,51 +188,61 @@ where type Data = Bytes; type Error = BoxError; - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { - let mut this = self.project(); - let mut buf = BytesMut::new(); - - let read = match ready!(poll_read_buf(this.read.as_mut(), cx, &mut buf)) { - Ok(read) => read, - Err(err) => { - let body_error: Option = M::get_pin_mut(this.read) - .get_pin_mut() - .project() - .error - .take(); - - if let Some(body_error) = 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(err.into()))); - } - } - }; + ) -> Poll, Self::Error>>> { + // I'm not sure our previous body wrapping setup works. It assumes we can poll data and + // trailers separately, but we can't anymore - if read == 0 { - Poll::Ready(None) - } else { - Poll::Ready(Some(Ok(buf.freeze()))) - } + todo!() } - fn poll_trailers( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - let this = self.project(); - let body = M::get_pin_mut(this.read) - .get_pin_mut() - .get_pin_mut() - .get_pin_mut(); - body.poll_trailers(cx).map_err(Into::into) - } + // fn poll_data( + // self: Pin<&mut Self>, + // cx: &mut Context<'_>, + // ) -> Poll>> { + // let mut this = self.project(); + // let mut buf = BytesMut::new(); + + // let read = match ready!(poll_read_buf(this.read.as_mut(), cx, &mut buf)) { + // Ok(read) => read, + // Err(err) => { + // let body_error: Option = M::get_pin_mut(this.read) + // .get_pin_mut() + // .project() + // .error + // .take(); + + // if let Some(body_error) = 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(err.into()))); + // } + // } + // }; + + // if read == 0 { + // Poll::Ready(None) + // } else { + // Poll::Ready(Some(Ok(buf.freeze()))) + // } + // } + + // fn poll_trailers( + // self: Pin<&mut Self>, + // cx: &mut Context<'_>, + // ) -> Poll, Self::Error>> { + // let this = self.project(); + // let body = M::get_pin_mut(this.read) + // .get_pin_mut() + // .get_pin_mut() + // .get_pin_mut(); + // body.poll_trailers(cx).map_err(Into::into) + // } } pin_project! { @@ -276,8 +286,17 @@ where { type Item = Result; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.project().body.poll_data(cx) + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match futures_util::ready!(self.as_mut().project().body.poll_frame(cx)) { + Some(Ok(frame)) => match frame.into_data() { + Ok(data) => return Poll::Ready(Some(Ok(data))), + Err(_frame) => {} + }, + Some(Err(err)) => return Poll::Ready(Some(Err(err))), + None => return Poll::Ready(None), + } + } } } diff --git a/tower-http/src/decompression/body.rs b/tower-http/src/decompression/body.rs index 279dc283..e70033d2 100644 --- a/tower-http/src/decompression/body.rs +++ b/tower-http/src/decompression/body.rs @@ -277,23 +277,23 @@ where type Data = Bytes; type Error = BoxError; - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { + ) -> Poll, Self::Error>>> { match self.project().inner.project() { #[cfg(feature = "decompression-gzip")] - BodyInnerProj::Gzip { inner } => inner.poll_data(cx), + BodyInnerProj::Gzip { inner } => inner.poll_frame(cx), #[cfg(feature = "decompression-deflate")] - BodyInnerProj::Deflate { inner } => inner.poll_data(cx), + BodyInnerProj::Deflate { inner } => inner.poll_frame(cx), #[cfg(feature = "decompression-br")] - BodyInnerProj::Brotli { inner } => inner.poll_data(cx), + BodyInnerProj::Brotli { inner } => inner.poll_frame(cx), #[cfg(feature = "decompression-zstd")] - BodyInnerProj::Zstd { inner } => inner.poll_data(cx), - BodyInnerProj::Identity { inner } => match ready!(inner.poll_data(cx)) { - Some(Ok(mut buf)) => { - let bytes = buf.copy_to_bytes(buf.remaining()); - Poll::Ready(Some(Ok(bytes))) + BodyInnerProj::Zstd { inner } => inner.poll_frame(cx), + BodyInnerProj::Identity { inner } => match ready!(inner.poll_frame(cx)) { + Some(Ok(frame)) => { + let frame = frame.map_data(|mut buf| buf.copy_to_bytes(buf.remaining())); + Poll::Ready(Some(Ok(frame))) } Some(Err(err)) => Poll::Ready(Some(Err(err.into()))), None => Poll::Ready(None), @@ -309,32 +309,6 @@ where BodyInnerProj::Zstd { inner } => match inner.0 {}, } } - - fn poll_trailers( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - match self.project().inner.project() { - #[cfg(feature = "decompression-gzip")] - BodyInnerProj::Gzip { inner } => inner.poll_trailers(cx), - #[cfg(feature = "decompression-deflate")] - BodyInnerProj::Deflate { inner } => inner.poll_trailers(cx), - #[cfg(feature = "decompression-br")] - BodyInnerProj::Brotli { inner } => inner.poll_trailers(cx), - #[cfg(feature = "decompression-zstd")] - BodyInnerProj::Zstd { inner } => inner.poll_trailers(cx), - BodyInnerProj::Identity { inner } => inner.poll_trailers(cx).map_err(Into::into), - - #[cfg(not(feature = "decompression-gzip"))] - BodyInnerProj::Gzip { inner } => match inner.0 {}, - #[cfg(not(feature = "decompression-deflate"))] - BodyInnerProj::Deflate { inner } => match inner.0 {}, - #[cfg(not(feature = "decompression-br"))] - BodyInnerProj::Brotli { inner } => match inner.0 {}, - #[cfg(not(feature = "decompression-zstd"))] - BodyInnerProj::Zstd { inner } => match inner.0 {}, - } - } } #[cfg(feature = "decompression-gzip")] diff --git a/tower-http/src/decompression/mod.rs b/tower-http/src/decompression/mod.rs index 61aab330..7bad2ca3 100644 --- a/tower-http/src/decompression/mod.rs +++ b/tower-http/src/decompression/mod.rs @@ -120,6 +120,7 @@ mod tests { use super::*; use crate::compression::Compression; use crate::test_helpers::Body; + use crate::test_helpers::TowerHttpBodyExt; use bytes::BytesMut; use http::Request; use http::Response; diff --git a/tower-http/src/decompression/request/future.rs b/tower-http/src/decompression/request/future.rs index ce3b04ad..82ad412b 100644 --- a/tower-http/src/decompression/request/future.rs +++ b/tower-http/src/decompression/request/future.rs @@ -2,7 +2,10 @@ use crate::compression_utils::AcceptEncoding; use crate::BoxError; use bytes::Buf; use http::{header, HeaderValue, Response, StatusCode}; -use http_body::{combinators::UnsyncBoxBody, Body, Empty}; +use http_body::Body; +use http_body_util::combinators::UnsyncBoxBody; +use http_body_util::BodyExt; +use http_body_util::Empty; use pin_project_lite::pin_project; use std::future::Future; use std::pin::Pin; diff --git a/tower-http/src/decompression/request/mod.rs b/tower-http/src/decompression/request/mod.rs index 6dbbf85f..5f9c2438 100644 --- a/tower-http/src/decompression/request/mod.rs +++ b/tower-http/src/decompression/request/mod.rs @@ -5,8 +5,8 @@ pub(super) mod service; #[cfg(test)] mod tests { use super::service::RequestDecompression; - use crate::decompression::DecompressionBody; use crate::test_helpers::Body; + use crate::{decompression::DecompressionBody, test_helpers::TowerHttpBodyExt}; use bytes::BytesMut; use flate2::{write::GzEncoder, Compression}; use http::{header, Request, Response, StatusCode}; diff --git a/tower-http/src/decompression/request/service.rs b/tower-http/src/decompression/request/service.rs index 6d507366..dd383b6d 100644 --- a/tower-http/src/decompression/request/service.rs +++ b/tower-http/src/decompression/request/service.rs @@ -7,7 +7,8 @@ use crate::{ }; use bytes::Buf; use http::{header, Request, Response}; -use http_body::{combinators::UnsyncBoxBody, Body}; +use http_body::Body; +use http_body_util::combinators::UnsyncBoxBody; use std::task::{Context, Poll}; use tower_service::Service; diff --git a/tower-http/src/limit/body.rs b/tower-http/src/limit/body.rs index 4e746a5d..4e540f8b 100644 --- a/tower-http/src/limit/body.rs +++ b/tower-http/src/limit/body.rs @@ -1,6 +1,7 @@ use bytes::Bytes; -use http::{HeaderMap, HeaderValue, Response, StatusCode}; -use http_body::{Body, Full, SizeHint}; +use http::{HeaderValue, Response, StatusCode}; +use http_body::{Body, SizeHint}; +use http_body_util::Full; use pin_project_lite::pin_project; use std::pin::Pin; use std::task::{Context, Poll}; @@ -52,25 +53,13 @@ where type Data = Bytes; type Error = B::Error; - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { + ) -> Poll, Self::Error>>> { match self.project().inner.project() { - BodyProj::PayloadTooLarge { body } => body.poll_data(cx).map_err(|err| match err {}), - BodyProj::Body { body } => body.poll_data(cx), - } - } - - fn poll_trailers( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - match self.project().inner.project() { - BodyProj::PayloadTooLarge { body } => { - body.poll_trailers(cx).map_err(|err| match err {}) - } - BodyProj::Body { body } => body.poll_trailers(cx), + BodyProj::PayloadTooLarge { body } => body.poll_frame(cx).map_err(|err| match err {}), + BodyProj::Body { body } => body.poll_frame(cx), } } diff --git a/tower-http/src/limit/service.rs b/tower-http/src/limit/service.rs index 66ae41fe..e057379c 100644 --- a/tower-http/src/limit/service.rs +++ b/tower-http/src/limit/service.rs @@ -1,6 +1,7 @@ use super::{RequestBodyLimitLayer, ResponseBody, ResponseFuture}; use http::{Request, Response}; -use http_body::{Body, Limited}; +use http_body::Body; +use http_body_util::Limited; use std::task::{Context, Poll}; use tower_service::Service; diff --git a/tower-http/src/macros.rs b/tower-http/src/macros.rs index 6641199b..556ce815 100644 --- a/tower-http/src/macros.rs +++ b/tower-http/src/macros.rs @@ -46,19 +46,11 @@ macro_rules! opaque_body { type Error = <$actual as http_body::Body>::Error; #[inline] - fn poll_data( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll>> { - self.project().inner.poll_data(cx) - } - - #[inline] - fn poll_trailers( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll, Self::Error>> { - self.project().inner.poll_trailers(cx) + fn poll_frame( + mut self: std::pin::Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + self.project().inner.poll_frame(cx) } #[inline] diff --git a/tower-http/src/metrics/in_flight_requests.rs b/tower-http/src/metrics/in_flight_requests.rs index c5ee157b..c6fc55ae 100644 --- a/tower-http/src/metrics/in_flight_requests.rs +++ b/tower-http/src/metrics/in_flight_requests.rs @@ -267,19 +267,11 @@ where type Error = B::Error; #[inline] - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { - self.project().inner.poll_data(cx) - } - - #[inline] - fn poll_trailers( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - self.project().inner.poll_trailers(cx) + ) -> Poll, Self::Error>>> { + self.project().inner.poll_frame(cx) } #[inline] diff --git a/tower-http/src/services/fs/mod.rs b/tower-http/src/services/fs/mod.rs index ce6ef463..937e1f2a 100644 --- a/tower-http/src/services/fs/mod.rs +++ b/tower-http/src/services/fs/mod.rs @@ -2,8 +2,7 @@ use bytes::Bytes; use futures_util::Stream; -use http::HeaderMap; -use http_body::Body; +use http_body::{Body, Frame}; use pin_project_lite::pin_project; use std::{ io, @@ -67,17 +66,14 @@ where type Data = Bytes; type Error = io::Error; - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { - self.project().reader.poll_next(cx) - } - - fn poll_trailers( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - Poll::Ready(Ok(None)) + ) -> Poll, Self::Error>>> { + match futures_util::ready!(self.project().reader.poll_next(cx)) { + Some(Ok(chunk)) => Poll::Ready(Some(Ok(Frame::data(chunk)))), + Some(Err(err)) => Poll::Ready(Some(Err(err))), + None => Poll::Ready(None), + } } } diff --git a/tower-http/src/services/fs/serve_dir/future.rs b/tower-http/src/services/fs/serve_dir/future.rs index 1d7eed9e..dc2ae2e4 100644 --- a/tower-http/src/services/fs/serve_dir/future.rs +++ b/tower-http/src/services/fs/serve_dir/future.rs @@ -12,7 +12,7 @@ use http::{ header::{self, ALLOW}, HeaderValue, Request, Response, StatusCode, }; -use http_body::{Body, Empty, Full}; +use http_body_util::{BodyExt, Empty, Full}; use pin_project_lite::pin_project; use std::{ convert::Infallible, diff --git a/tower-http/src/services/fs/serve_dir/mod.rs b/tower-http/src/services/fs/serve_dir/mod.rs index 9d1e5638..19060412 100644 --- a/tower-http/src/services/fs/serve_dir/mod.rs +++ b/tower-http/src/services/fs/serve_dir/mod.rs @@ -6,7 +6,8 @@ use crate::{ use bytes::Bytes; use futures_util::FutureExt; use http::{header, HeaderValue, Method, Request, Response, StatusCode}; -use http_body::{combinators::UnsyncBoxBody, Body, Empty}; +use http_body::Body; +use http_body_util::{combinators::UnsyncBoxBody, BodyExt, Empty}; use percent_encoding::percent_decode; use std::{ convert::Infallible, diff --git a/tower-http/src/services/fs/serve_dir/open_file.rs b/tower-http/src/services/fs/serve_dir/open_file.rs index a24aa088..401d34de 100644 --- a/tower-http/src/services/fs/serve_dir/open_file.rs +++ b/tower-http/src/services/fs/serve_dir/open_file.rs @@ -5,7 +5,7 @@ use super::{ use crate::content_encoding::{Encoding, QValue}; use bytes::Bytes; use http::{header, HeaderValue, Method, Request, Uri}; -use http_body::Empty; +use http_body_util::Empty; use http_range_header::RangeUnsatisfiableError; use std::{ ffi::OsStr, diff --git a/tower-http/src/services/fs/serve_dir/tests.rs b/tower-http/src/services/fs/serve_dir/tests.rs index 1cbb0a02..e78b3064 100644 --- a/tower-http/src/services/fs/serve_dir/tests.rs +++ b/tower-http/src/services/fs/serve_dir/tests.rs @@ -1,5 +1,5 @@ use crate::services::{ServeDir, ServeFile}; -use crate::test_helpers::{to_bytes, Body}; +use crate::test_helpers::{to_bytes, Body, TowerHttpBodyExt}; use brotli::BrotliDecompress; use bytes::Bytes; use flate2::bufread::{DeflateDecoder, GzDecoder}; diff --git a/tower-http/src/services/fs/serve_file.rs b/tower-http/src/services/fs/serve_file.rs index 2eb277bf..7d519fb6 100644 --- a/tower-http/src/services/fs/serve_file.rs +++ b/tower-http/src/services/fs/serve_file.rs @@ -129,6 +129,7 @@ where mod tests { use crate::services::ServeFile; use crate::test_helpers::Body; + use crate::test_helpers::TowerHttpBodyExt; use brotli::BrotliDecompress; use flate2::bufread::DeflateDecoder; use flate2::bufread::GzDecoder; diff --git a/tower-http/src/test_helpers.rs b/tower-http/src/test_helpers.rs index 4604b1c5..ac9d862c 100644 --- a/tower-http/src/test_helpers.rs +++ b/tower-http/src/test_helpers.rs @@ -1,17 +1,19 @@ use std::{ + future::Future, pin::Pin, task::{Context, Poll}, }; -use bytes::{Buf, BufMut, Bytes}; +use async_trait::async_trait; +use bytes::Bytes; use futures::TryStream; -use http::HeaderMap; -use http_body::Body as _; +use http_body::{Body as _, Frame}; +use http_body_util::BodyExt; use pin_project_lite::pin_project; use sync_wrapper::SyncWrapper; use tower::BoxError; -type BoxBody = http_body::combinators::UnsyncBoxBody; +type BoxBody = http_body_util::combinators::UnsyncBoxBody; #[derive(Debug)] pub(crate) struct Body(BoxBody); @@ -26,7 +28,7 @@ impl Body { } pub(crate) fn empty() -> Self { - Self::new(http_body::Empty::new()) + Self::new(http_body_util::Empty::new()) } pub(crate) fn from_stream(stream: S) -> Self @@ -51,7 +53,7 @@ macro_rules! body_from_impl { ($ty:ty) => { impl From<$ty> for Body { fn from(buf: $ty) -> Self { - Self::new(http_body::Full::from(buf)) + Self::new(http_body_util::Full::from(buf)) } } }; @@ -71,18 +73,11 @@ impl http_body::Body for Body { type Data = Bytes; type Error = BoxError; - fn poll_data( + fn poll_frame( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> std::task::Poll>> { - Pin::new(&mut self.0).poll_data(cx) - } - - fn poll_trailers( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> std::task::Poll, Self::Error>> { - Pin::new(&mut self.0).poll_trailers(cx) + ) -> Poll, Self::Error>>> { + Pin::new(&mut self.0).poll_frame(cx) } fn size_hint(&self) -> http_body::SizeHint { @@ -110,24 +105,17 @@ where type Data = Bytes; type Error = BoxError; - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { + ) -> Poll, Self::Error>>> { let stream = self.project().stream.get_pin_mut(); match futures_util::ready!(stream.try_poll_next(cx)) { - Some(Ok(chunk)) => Poll::Ready(Some(Ok(chunk.into()))), + Some(Ok(chunk)) => Poll::Ready(Some(Ok(Frame::data(chunk.into())))), Some(Err(err)) => Poll::Ready(Some(Err(err.into()))), None => Poll::Ready(None), } } - - fn poll_trailers( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - Poll::Ready(Ok(None)) - } } // copied from hyper @@ -136,29 +124,39 @@ where T: http_body::Body, { futures_util::pin_mut!(body); + Ok(body.collect().await?.to_bytes()) +} - // If there's only 1 chunk, we can just return Buf::to_bytes() - let mut first = if let Some(buf) = body.data().await { - buf? - } else { - return Ok(Bytes::new()); - }; +pub(crate) trait TowerHttpBodyExt: http_body::Body + Unpin { + /// Returns future that resolves to next data chunk, if any. + fn data(&mut self) -> Data<'_, Self> + where + Self: Unpin + Sized, + { + Data(self) + } +} - let second = if let Some(buf) = body.data().await { - buf? - } else { - return Ok(first.copy_to_bytes(first.remaining())); - }; +impl TowerHttpBodyExt for B where B: http_body::Body + Unpin {} - // With more than 1 buf, we gotta flatten into a Vec first. - let cap = first.remaining() + second.remaining() + body.size_hint().lower() as usize; - let mut vec = Vec::with_capacity(cap); - vec.put(first); - vec.put(second); +pub(crate) struct Data<'a, T>(pub(crate) &'a mut T); - while let Some(buf) = body.data().await { - vec.put(buf?); +impl<'a, T> Future for Data<'a, T> +where + T: http_body::Body + Unpin, +{ + type Output = Option>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + loop { + match futures_util::ready!(Pin::new(&mut self.0).poll_frame(cx)) { + Some(Ok(frame)) => match frame.into_data() { + Ok(data) => return Poll::Ready(Some(Ok(data))), + Err(_frame) => {} + }, + Some(Err(err)) => return Poll::Ready(Some(Err(err))), + None => return Poll::Ready(None), + } + } } - - Ok(vec.into()) } diff --git a/tower-http/src/timeout/body.rs b/tower-http/src/timeout/body.rs index 79712efd..8a74ae21 100644 --- a/tower-http/src/timeout/body.rs +++ b/tower-http/src/timeout/body.rs @@ -50,13 +50,8 @@ pin_project! { /// Wrapper around a [`http_body::Body`] to time out if data is not ready within the specified duration. pub struct TimeoutBody { timeout: Duration, - // In http-body 1.0, `poll_*` will be merged into `poll_frame`. - // Merge the two `sleep_data` and `sleep_trailers` into one `sleep`. - // See: https://github.com/tower-rs/tower-http/pull/303#discussion_r1004834958 #[pin] - sleep_data: Option, - #[pin] - sleep_trailers: Option, + sleep: Option, #[pin] body: B, } @@ -67,8 +62,7 @@ impl TimeoutBody { pub fn new(timeout: Duration, body: B) -> Self { TimeoutBody { timeout, - sleep_data: None, - sleep_trailers: None, + sleep: None, body, } } @@ -82,18 +76,18 @@ where type Data = B::Data; type Error = Box; - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { + ) -> Poll, Self::Error>>> { let mut this = self.project(); // Start the `Sleep` if not active. - let sleep_pinned = if let Some(some) = this.sleep_data.as_mut().as_pin_mut() { + let sleep_pinned = if let Some(some) = this.sleep.as_mut().as_pin_mut() { some } else { - this.sleep_data.set(Some(sleep(*this.timeout))); - this.sleep_data.as_mut().as_pin_mut().unwrap() + this.sleep.set(Some(sleep(*this.timeout))); + this.sleep.as_mut().as_pin_mut().unwrap() }; // Error if the timeout has expired. @@ -102,36 +96,11 @@ where } // Check for body data. - let data = ready!(this.body.poll_data(cx)); - // Some data is ready. Reset the `Sleep`... - this.sleep_data.set(None); + let frame = ready!(this.body.poll_frame(cx)); + // A frame is ready. Reset the `Sleep`... + this.sleep.set(None); - Poll::Ready(data.transpose().map_err(Into::into).transpose()) - } - - fn poll_trailers( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - let mut this = self.project(); - - // In http-body 1.0, `poll_*` will be merged into `poll_frame`. - // Merge the two `sleep_data` and `sleep_trailers` into one `sleep`. - // See: https://github.com/tower-rs/tower-http/pull/303#discussion_r1004834958 - - let sleep_pinned = if let Some(some) = this.sleep_trailers.as_mut().as_pin_mut() { - some - } else { - this.sleep_trailers.set(Some(sleep(*this.timeout))); - this.sleep_trailers.as_mut().as_pin_mut().unwrap() - }; - - // Error if the timeout has expired. - if let Poll::Ready(()) = sleep_pinned.poll(cx) { - return Poll::Ready(Err(Box::new(TimeoutError(())))); - } - - this.body.poll_trailers(cx).map_err(Into::into) + Poll::Ready(frame.transpose().map_err(Into::into).transpose()) } } @@ -148,9 +117,13 @@ impl std::fmt::Display for TimeoutError { } #[cfg(test)] mod tests { + use crate::test_helpers::TowerHttpBodyExt; + use super::*; use bytes::Bytes; + use http_body::Frame; + use http_body_util::BodyExt; use pin_project_lite::pin_project; use std::{error::Error, fmt::Display}; @@ -175,19 +148,14 @@ mod tests { type Data = Bytes; type Error = MockError; - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { + ) -> Poll, Self::Error>>> { let this = self.project(); - this.sleep.poll(cx).map(|_| Some(Ok(vec![].into()))) - } - - fn poll_trailers( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - todo!() + this.sleep + .poll(cx) + .map(|_| Some(Ok(Frame::data(vec![].into())))) } } @@ -201,7 +169,7 @@ mod tests { }; let timeout_body = TimeoutBody::new(timeout_sleep, mock_body); - assert!(timeout_body.boxed().data().await.unwrap().is_ok()); + assert!(timeout_body.boxed().data().await.expect("no data").is_ok()); } #[tokio::test] diff --git a/tower-http/src/trace/body.rs b/tower-http/src/trace/body.rs index d38770d5..39579575 100644 --- a/tower-http/src/trace/body.rs +++ b/tower-http/src/trace/body.rs @@ -2,7 +2,7 @@ use super::{OnBodyChunk, OnEos, OnFailure}; use crate::classify::ClassifyEos; use futures_core::ready; use http::HeaderMap; -use http_body::Body; +use http_body::{Body, Frame}; use pin_project_lite::pin_project; use std::{ fmt, @@ -41,70 +41,57 @@ where type Data = B::Data; type Error = B::Error; - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { + ) -> Poll, Self::Error>>> { let this = self.project(); let _guard = this.span.enter(); - - let result = if let Some(result) = ready!(this.inner.poll_data(cx)) { - result - } else { - return Poll::Ready(None); - }; + let result = ready!(this.inner.poll_frame(cx)); let latency = this.start.elapsed(); *this.start = Instant::now(); - match &result { - Ok(chunk) => { - this.on_body_chunk.on_body_chunk(chunk, latency, this.span); + match result { + Some(Ok(frame)) => { + let frame = match frame.into_data() { + Ok(chunk) => { + this.on_body_chunk.on_body_chunk(&chunk, latency, this.span); + Frame::data(chunk) + } + Err(frame) => frame, + }; + + let frame = match frame.into_trailers() { + Ok(trailers) => { + if let Some((on_eos, stream_start)) = this.on_eos.take() { + on_eos.on_eos(Some(&trailers), stream_start.elapsed(), this.span); + } + Frame::trailers(trailers) + } + Err(frame) => frame, + }; + + Poll::Ready(Some(Ok(frame))) } - Err(err) => { + Some(Err(err)) => { if let Some((classify_eos, mut on_failure)) = this.classify_eos.take().zip(this.on_failure.take()) { - let failure_class = classify_eos.classify_error(err); + let failure_class = classify_eos.classify_error(&err); on_failure.on_failure(failure_class, latency, this.span); } - } - } - - Poll::Ready(Some(result)) - } - - fn poll_trailers( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - let this = self.project(); - let _guard = this.span.enter(); - let result = ready!(this.inner.poll_trailers(cx)); - - let latency = this.start.elapsed(); - - if let Some((classify_eos, mut on_failure)) = - this.classify_eos.take().zip(this.on_failure.take()) - { - match &result { - Ok(trailers) => { - if let Err(failure_class) = classify_eos.classify_eos(trailers.as_ref()) { - on_failure.on_failure(failure_class, latency, this.span); - } - if let Some((on_eos, stream_start)) = this.on_eos.take() { - on_eos.on_eos(trailers.as_ref(), stream_start.elapsed(), this.span); - } - } - Err(err) => { - let failure_class = classify_eos.classify_error(err); - on_failure.on_failure(failure_class, latency, this.span); + Poll::Ready(Some(Err(err))) + } + None => { + if let Some((on_eos, stream_start)) = this.on_eos.take() { + on_eos.on_eos(None, stream_start.elapsed(), this.span); } + + Poll::Ready(None) } } - - Poll::Ready(result) } fn is_end_stream(&self) -> bool { From 9fad52957d8c43bb484710639941400f14036150 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 12:59:26 +0100 Subject: [PATCH 04/56] note which types from http_body_util is in our public api --- tower-http/Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index af297354..e461b5ee 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -128,6 +128,11 @@ allowed = [ "bytes", "http", "http_body", + # because of + # - UnsyncBoxBody + # - Full + # - Limited + "http_body_util", "mime", "tokio", "tower", From 8734fc443882838c32f95df63e7594898b4e4cf3 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 14:58:37 +0100 Subject: [PATCH 05/56] wrap body types --- tower-http/Cargo.toml | 5 - tower-http/src/body.rs | 113 ++++++++++++++++++ tower-http/src/catch_panic.rs | 17 ++- .../src/decompression/request/future.rs | 10 +- .../src/decompression/request/service.rs | 2 +- tower-http/src/lib.rs | 2 + tower-http/src/limit/service.rs | 4 +- tower-http/src/macros.rs | 4 +- .../src/services/fs/serve_dir/future.rs | 28 +++-- tower-http/src/services/fs/serve_dir/mod.rs | 9 +- 10 files changed, 159 insertions(+), 35 deletions(-) create mode 100644 tower-http/src/body.rs diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index e461b5ee..af297354 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -128,11 +128,6 @@ allowed = [ "bytes", "http", "http_body", - # because of - # - UnsyncBoxBody - # - Full - # - Limited - "http_body_util", "mime", "tokio", "tower", diff --git a/tower-http/src/body.rs b/tower-http/src/body.rs new file mode 100644 index 00000000..ee5527a5 --- /dev/null +++ b/tower-http/src/body.rs @@ -0,0 +1,113 @@ +//! Body types. +//! +//! All these are wrappers around other body types. You shouldn't have to use them in your code. +//! Use `http-body-util` instead. + +use std::convert::Infallible; + +use bytes::{Buf, Bytes}; +use http_body::Body; +use pin_project_lite::pin_project; + +use crate::BoxError; + +macro_rules! body_methods { + () => { + #[inline] + fn poll_frame( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll, Self::Error>>> { + self.project().inner.poll_frame(cx) + } + + #[inline] + fn is_end_stream(&self) -> bool { + Body::is_end_stream(&self.inner) + } + + #[inline] + fn size_hint(&self) -> http_body::SizeHint { + Body::size_hint(&self.inner) + } + }; +} + +pin_project! { + #[derive(Default)] + pub struct Full { + #[pin] + pub(crate) inner: http_body_util::Full + } +} + +impl Full { + pub(crate) fn new(inner: http_body_util::Full) -> Self { + Self { inner } + } +} + +impl Body for Full { + type Data = Bytes; + type Error = Infallible; + + body_methods!(); +} + +pin_project! { + pub struct Limited { + #[pin] + pub(crate) inner: http_body_util::Limited + } +} + +impl Limited { + pub(crate) fn new(inner: http_body_util::Limited) -> Self { + Self { inner } + } +} + +impl Body for Limited +where + B: Body, + B::Error: Into, +{ + type Data = B::Data; + type Error = BoxError; + + body_methods!(); +} + +pin_project! { + pub struct UnsyncBoxBody { + #[pin] + pub(crate) inner: http_body_util::combinators::UnsyncBoxBody + } +} + +impl Default for UnsyncBoxBody +where + D: Buf + 'static, +{ + fn default() -> Self { + Self { + inner: Default::default(), + } + } +} + +impl UnsyncBoxBody { + pub(crate) fn new(inner: http_body_util::combinators::UnsyncBoxBody) -> Self { + Self { inner } + } +} + +impl Body for UnsyncBoxBody +where + D: Buf, +{ + type Data = D; + type Error = E; + + body_methods!(); +} diff --git a/tower-http/src/catch_panic.rs b/tower-http/src/catch_panic.rs index caa3a496..7eac4f1e 100644 --- a/tower-http/src/catch_panic.rs +++ b/tower-http/src/catch_panic.rs @@ -87,7 +87,7 @@ use futures_core::ready; use futures_util::future::{CatchUnwind, FutureExt}; use http::{HeaderValue, Request, Response, StatusCode}; use http_body::Body; -use http_body_util::{combinators::UnsyncBoxBody, BodyExt, Full}; +use http_body_util::BodyExt; use pin_project_lite::pin_project; use std::{ any::Any, @@ -99,7 +99,10 @@ use std::{ use tower_layer::Layer; use tower_service::Service; -use crate::BoxError; +use crate::{ + body::{Full, UnsyncBoxBody}, + BoxError, +}; /// Layer that applies the [`CatchPanic`] middleware that catches panics and converts them into /// `500 Internal Server` responses. @@ -264,7 +267,9 @@ where panic_handler, } => match ready!(future.poll(cx)) { Ok(Ok(res)) => { - Poll::Ready(Ok(res.map(|body| body.map_err(Into::into).boxed_unsync()))) + Poll::Ready(Ok(res.map(|body| { + UnsyncBoxBody::new(body.map_err(Into::into).boxed_unsync()) + }))) } Ok(Err(svc_err)) => Poll::Ready(Err(svc_err)), Err(panic_err) => Poll::Ready(Ok(response_for_panic( @@ -289,7 +294,7 @@ where { panic_handler .response_for_panic(err) - .map(|body| body.map_err(Into::into).boxed_unsync()) + .map(|body| UnsyncBoxBody::new(body.map_err(Into::into).boxed_unsync())) } /// Trait for creating responses from panics. @@ -327,7 +332,7 @@ where pub struct DefaultResponseForPanic; impl ResponseForPanic for DefaultResponseForPanic { - type ResponseBody = Full; + type ResponseBody = Full; fn response_for_panic( &mut self, @@ -343,7 +348,7 @@ impl ResponseForPanic for DefaultResponseForPanic { ); }; - let mut res = Response::new(Full::from("Service panicked")); + let mut res = Response::new(Full::new(http_body_util::Full::from("Service panicked"))); *res.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; #[allow(clippy::declare_interior_mutable_const)] diff --git a/tower-http/src/decompression/request/future.rs b/tower-http/src/decompression/request/future.rs index 82ad412b..36b65593 100644 --- a/tower-http/src/decompression/request/future.rs +++ b/tower-http/src/decompression/request/future.rs @@ -1,9 +1,9 @@ +use crate::body::UnsyncBoxBody; use crate::compression_utils::AcceptEncoding; use crate::BoxError; use bytes::Buf; use http::{header, HeaderValue, Response, StatusCode}; use http_body::Body; -use http_body_util::combinators::UnsyncBoxBody; use http_body_util::BodyExt; use http_body_util::Empty; use pin_project_lite::pin_project; @@ -78,7 +78,9 @@ where match self.project().kind.project() { StateProj::Inner { fut } => fut .poll(cx) - .map_ok(|res| res.map(|body| body.map_err(Into::into).boxed_unsync())) + .map_ok(|res| { + res.map(|body| UnsyncBoxBody::new(body.map_err(Into::into).boxed_unsync())) + }) .map_err(Into::into), StateProj::Unsupported { accept } => { let res = Response::builder() @@ -89,7 +91,9 @@ where .unwrap_or(HeaderValue::from_static("identity")), ) .status(StatusCode::UNSUPPORTED_MEDIA_TYPE) - .body(Empty::new().map_err(Into::into).boxed_unsync()) + .body(UnsyncBoxBody::new( + Empty::new().map_err(Into::into).boxed_unsync(), + )) .unwrap(); Poll::Ready(Ok(res)) } diff --git a/tower-http/src/decompression/request/service.rs b/tower-http/src/decompression/request/service.rs index dd383b6d..c44bbfaf 100644 --- a/tower-http/src/decompression/request/service.rs +++ b/tower-http/src/decompression/request/service.rs @@ -1,5 +1,6 @@ use super::future::RequestDecompressionFuture as ResponseFuture; use super::layer::RequestDecompressionLayer; +use crate::body::UnsyncBoxBody; use crate::compression_utils::CompressionLevel; use crate::{ compression_utils::AcceptEncoding, decompression::body::BodyInner, @@ -8,7 +9,6 @@ use crate::{ use bytes::Buf; use http::{header, Request, Response}; use http_body::Body; -use http_body_util::combinators::UnsyncBoxBody; use std::task::{Context, Poll}; use tower_service::Service; diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index a18e9062..9cd097c2 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -345,6 +345,8 @@ pub use self::builder::ServiceBuilderExt; #[cfg(feature = "validate-request")] pub mod validate_request; +pub mod body; + /// The latency unit used to report latencies by middleware. #[non_exhaustive] #[derive(Copy, Clone, Debug)] diff --git a/tower-http/src/limit/service.rs b/tower-http/src/limit/service.rs index e057379c..fdf65d25 100644 --- a/tower-http/src/limit/service.rs +++ b/tower-http/src/limit/service.rs @@ -1,7 +1,7 @@ use super::{RequestBodyLimitLayer, ResponseBody, ResponseFuture}; +use crate::body::Limited; use http::{Request, Response}; use http_body::Body; -use http_body_util::Limited; use std::task::{Context, Poll}; use tower_service::Service; @@ -57,7 +57,7 @@ where None => self.limit, }; - let req = req.map(|body| Limited::new(body, body_limit)); + let req = req.map(|body| Limited::new(http_body_util::Limited::new(body, body_limit))); ResponseFuture::new(self.inner.call(req)) } diff --git a/tower-http/src/macros.rs b/tower-http/src/macros.rs index 556ce815..0de32b5e 100644 --- a/tower-http/src/macros.rs +++ b/tower-http/src/macros.rs @@ -48,8 +48,8 @@ macro_rules! opaque_body { #[inline] fn poll_frame( mut self: std::pin::Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>>> { + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll, Self::Error>>> { self.project().inner.poll_frame(cx) } diff --git a/tower-http/src/services/fs/serve_dir/future.rs b/tower-http/src/services/fs/serve_dir/future.rs index dc2ae2e4..549eca2b 100644 --- a/tower-http/src/services/fs/serve_dir/future.rs +++ b/tower-http/src/services/fs/serve_dir/future.rs @@ -2,7 +2,9 @@ use super::{ open_file::{FileOpened, FileRequestExtent, OpenFileOutput}, DefaultServeDirFallback, ResponseBody, }; -use crate::{content_encoding::Encoding, services::fs::AsyncReadBody, BoxError}; +use crate::{ + body::UnsyncBoxBody, content_encoding::Encoding, services::fs::AsyncReadBody, BoxError, +}; use bytes::Bytes; use futures_util::{ future::{BoxFuture, FutureExt, TryFutureExt}, @@ -202,11 +204,13 @@ where .map_ok(|response| { response .map(|body| { - body.map_err(|err| match err.into().downcast::() { - Ok(err) => *err, - Err(err) => io::Error::new(io::ErrorKind::Other, err), - }) - .boxed_unsync() + UnsyncBoxBody::new( + body.map_err(|err| match err.into().downcast::() { + Ok(err) => *err, + Err(err) => io::Error::new(io::ErrorKind::Other, err), + }) + .boxed_unsync(), + ) }) .map(ResponseBody::new) }) @@ -250,14 +254,14 @@ fn build_response(output: FileOpened) -> Response { } else { let body = if let Some(file) = maybe_file { let range_size = range.end() - range.start() + 1; - ResponseBody::new( + ResponseBody::new(UnsyncBoxBody::new( AsyncReadBody::with_capacity_limited( file, output.chunk_size, range_size, ) .boxed_unsync(), - ) + )) } else { empty_body() }; @@ -292,9 +296,9 @@ fn build_response(output: FileOpened) -> Response { // Not a range request None => { let body = if let Some(file) = maybe_file { - ResponseBody::new( + ResponseBody::new(UnsyncBoxBody::new( AsyncReadBody::with_capacity(file, output.chunk_size).boxed_unsync(), - ) + )) } else { empty_body() }; @@ -309,10 +313,10 @@ fn build_response(output: FileOpened) -> Response { fn body_from_bytes(bytes: Bytes) -> ResponseBody { let body = Full::from(bytes).map_err(|err| match err {}).boxed_unsync(); - ResponseBody::new(body) + ResponseBody::new(UnsyncBoxBody::new(body)) } fn empty_body() -> ResponseBody { let body = Empty::new().map_err(|err| match err {}).boxed_unsync(); - ResponseBody::new(body) + ResponseBody::new(UnsyncBoxBody::new(body)) } diff --git a/tower-http/src/services/fs/serve_dir/mod.rs b/tower-http/src/services/fs/serve_dir/mod.rs index 19060412..e72bda72 100644 --- a/tower-http/src/services/fs/serve_dir/mod.rs +++ b/tower-http/src/services/fs/serve_dir/mod.rs @@ -1,13 +1,13 @@ use self::future::ResponseFuture; use crate::{ + body::UnsyncBoxBody, content_encoding::{encodings, SupportedEncodings}, set_status::SetStatus, }; use bytes::Bytes; use futures_util::FutureExt; use http::{header, HeaderValue, Method, Request, Response, StatusCode}; -use http_body::Body; -use http_body_util::{combinators::UnsyncBoxBody, BodyExt, Empty}; +use http_body_util::{BodyExt, Empty}; use percent_encoding::percent_decode; use std::{ convert::Infallible, @@ -448,8 +448,9 @@ where let response = result.unwrap_or_else(|err| { tracing::error!(error = %err, "Failed to read file"); - let body = - ResponseBody::new(Empty::new().map_err(|err| match err {}).boxed_unsync()); + let body = ResponseBody::new(UnsyncBoxBody::new( + Empty::new().map_err(|err| match err {}).boxed_unsync(), + )); Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .body(body) From c12999781b81e4b0325cc8e4cc007fcbb24755e2 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 09:35:13 +0100 Subject: [PATCH 06/56] fix some warnings --- tower-http/src/body.rs | 2 + .../src/classify/grpc_errors_as_failures.rs | 1 - tower-http/src/compression/future.rs | 2 +- tower-http/src/compression/layer.rs | 1 - tower-http/src/compression/mod.rs | 2 +- tower-http/src/compression_utils.rs | 4 +- tower-http/src/content_encoding.rs | 64 +++++++++---------- tower-http/src/decompression/mod.rs | 1 - tower-http/src/decompression/request/mod.rs | 1 - tower-http/src/lib.rs | 1 - tower-http/src/macros.rs | 2 +- tower-http/src/services/fs/serve_dir/tests.rs | 2 +- tower-http/src/services/fs/serve_file.rs | 1 - tower-http/src/test_helpers.rs | 1 - tower-http/src/trace/body.rs | 1 - tower-http/src/trace/mod.rs | 2 +- tower-http/src/validate_request.rs | 2 +- 17 files changed, 42 insertions(+), 48 deletions(-) diff --git a/tower-http/src/body.rs b/tower-http/src/body.rs index ee5527a5..d56a78c6 100644 --- a/tower-http/src/body.rs +++ b/tower-http/src/body.rs @@ -3,6 +3,8 @@ //! All these are wrappers around other body types. You shouldn't have to use them in your code. //! Use `http-body-util` instead. +#![allow(missing_docs)] + use std::convert::Infallible; use bytes::{Buf, Bytes}; diff --git a/tower-http/src/classify/grpc_errors_as_failures.rs b/tower-http/src/classify/grpc_errors_as_failures.rs index 056cec93..103c0459 100644 --- a/tower-http/src/classify/grpc_errors_as_failures.rs +++ b/tower-http/src/classify/grpc_errors_as_failures.rs @@ -262,7 +262,6 @@ impl fmt::Display for GrpcFailureClass { } } -#[allow(clippy::if_let_some_result)] pub(crate) fn classify_grpc_metadata( headers: &HeaderMap, success_codes: GrpcCodeBitmask, diff --git a/tower-http/src/compression/future.rs b/tower-http/src/compression/future.rs index 426bb161..67367c29 100644 --- a/tower-http/src/compression/future.rs +++ b/tower-http/src/compression/future.rs @@ -37,7 +37,7 @@ where { type Output = Result>, E>; - #[allow(unreachable_code, unused_mut, unused_variables)] + #[allow(unreachable_code, unused_mut, unused_variables, unreachable_patterns)] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let res = ready!(self.as_mut().project().inner.poll(cx)?); diff --git a/tower-http/src/compression/layer.rs b/tower-http/src/compression/layer.rs index 56854da2..60c3012e 100644 --- a/tower-http/src/compression/layer.rs +++ b/tower-http/src/compression/layer.rs @@ -125,7 +125,6 @@ mod tests { use super::*; use crate::test_helpers::{Body, TowerHttpBodyExt}; use http::{header::ACCEPT_ENCODING, Request, Response}; - use http_body::Body as _; use tokio::fs::File; // for Body::data use bytes::{Bytes, BytesMut}; diff --git a/tower-http/src/compression/mod.rs b/tower-http/src/compression/mod.rs index 1fdf7aa2..756bd71d 100644 --- a/tower-http/src/compression/mod.rs +++ b/tower-http/src/compression/mod.rs @@ -93,7 +93,6 @@ mod tests { use flate2::read::GzDecoder; use http::header::{ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_TYPE}; use http::{Request, Response}; - use http_body::Body as _; use std::convert::Infallible; use std::io::Read; use std::sync::{Arc, RwLock}; @@ -259,6 +258,7 @@ mod tests { #[derive(Default, Clone)] struct EveryOtherResponse(Arc>); + #[allow(clippy::dbg_macro)] impl Predicate for EveryOtherResponse { fn should_compress(&self, _: &http::Response) -> bool where diff --git a/tower-http/src/compression_utils.rs b/tower-http/src/compression_utils.rs index 626543dd..9f339836 100644 --- a/tower-http/src/compression_utils.rs +++ b/tower-http/src/compression_utils.rs @@ -1,7 +1,7 @@ //! Types used by compression and decompression middleware. use crate::{content_encoding::SupportedEncodings, BoxError}; -use bytes::{Bytes, BytesMut}; +use bytes::Bytes; use futures_core::Stream; use futures_util::ready; use http::HeaderValue; @@ -13,7 +13,7 @@ use std::{ task::{Context, Poll}, }; use tokio::io::AsyncRead; -use tokio_util::io::{poll_read_buf, StreamReader}; +use tokio_util::io::StreamReader; #[derive(Debug, Clone, Copy)] pub(crate) struct AcceptEncoding { diff --git a/tower-http/src/content_encoding.rs b/tower-http/src/content_encoding.rs index c962d0ee..a52a2ab0 100644 --- a/tower-http/src/content_encoding.rs +++ b/tower-http/src/content_encoding.rs @@ -148,7 +148,7 @@ impl QValue { let mut c = s.chars(); // Parse "q=" (case-insensitively). match c.next() { - Some('q') | Some('Q') => (), + Some('q' | 'Q') => (), _ => return None, }; match c.next() { @@ -272,7 +272,7 @@ mod tests { #[test] fn no_accept_encoding_header() { let encoding = - Encoding::from_headers(&http::HeaderMap::new(), SupportedEncodingsAll::default()); + Encoding::from_headers(&http::HeaderMap::new(), SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); } @@ -283,7 +283,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Gzip, encoding); } @@ -294,7 +294,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip,br"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -305,7 +305,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip,deflate,br"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -316,7 +316,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.5,br"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -327,7 +327,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.5,deflate,br"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -342,7 +342,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("br"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -357,7 +357,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("br"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -376,7 +376,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("br"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -387,7 +387,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.5,br;q=0.8"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); let mut headers = http::HeaderMap::new(); @@ -395,7 +395,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.8,br;q=0.5"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Gzip, encoding); let mut headers = http::HeaderMap::new(); @@ -403,7 +403,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.995,br;q=0.999"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -414,7 +414,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.5,deflate;q=0.6,br;q=0.8"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); let mut headers = http::HeaderMap::new(); @@ -422,7 +422,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.8,deflate;q=0.6,br;q=0.5"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Gzip, encoding); let mut headers = http::HeaderMap::new(); @@ -430,7 +430,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.6,deflate;q=0.8,br;q=0.5"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Deflate, encoding); let mut headers = http::HeaderMap::new(); @@ -438,7 +438,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.995,deflate;q=0.997,br;q=0.999"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -449,7 +449,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("invalid,gzip"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Gzip, encoding); } @@ -460,7 +460,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); let mut headers = http::HeaderMap::new(); @@ -468,7 +468,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0."), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); let mut headers = http::HeaderMap::new(); @@ -476,7 +476,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0,br;q=0.5"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -487,7 +487,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gZiP"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Gzip, encoding); let mut headers = http::HeaderMap::new(); @@ -495,7 +495,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.5,br;Q=0.8"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -506,7 +506,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static(" gzip\t; q=0.5 ,\tbr ;\tq=0.8\t"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Brotli, encoding); } @@ -517,7 +517,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q =0.5"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); let mut headers = http::HeaderMap::new(); @@ -525,7 +525,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q= 0.5"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); } @@ -536,7 +536,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=-0.1"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); let mut headers = http::HeaderMap::new(); @@ -544,7 +544,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=00.5"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); let mut headers = http::HeaderMap::new(); @@ -552,7 +552,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=0.5000"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); let mut headers = http::HeaderMap::new(); @@ -560,7 +560,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=.5"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); let mut headers = http::HeaderMap::new(); @@ -568,7 +568,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=1.01"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); let mut headers = http::HeaderMap::new(); @@ -576,7 +576,7 @@ mod tests { http::header::ACCEPT_ENCODING, http::HeaderValue::from_static("gzip;q=1.001"), ); - let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll::default()); + let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); } } diff --git a/tower-http/src/decompression/mod.rs b/tower-http/src/decompression/mod.rs index 7bad2ca3..2c1492b7 100644 --- a/tower-http/src/decompression/mod.rs +++ b/tower-http/src/decompression/mod.rs @@ -124,7 +124,6 @@ mod tests { use bytes::BytesMut; use http::Request; use http::Response; - use http_body::Body as _; use tower::{service_fn, Service, ServiceExt}; #[tokio::test] diff --git a/tower-http/src/decompression/request/mod.rs b/tower-http/src/decompression/request/mod.rs index 5f9c2438..88bfa632 100644 --- a/tower-http/src/decompression/request/mod.rs +++ b/tower-http/src/decompression/request/mod.rs @@ -10,7 +10,6 @@ mod tests { use bytes::BytesMut; use flate2::{write::GzEncoder, Compression}; use http::{header, Request, Response, StatusCode}; - use http_body::Body as _; use std::{convert::Infallible, io::Write}; use tower::{service_fn, Service, ServiceExt}; diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index 9cd097c2..40fb9223 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -183,7 +183,6 @@ clippy::todo, clippy::empty_enum, clippy::enum_glob_use, - clippy::pub_enum_variant_names, clippy::mem_forget, clippy::unused_self, clippy::filter_map_next, diff --git a/tower-http/src/macros.rs b/tower-http/src/macros.rs index 0de32b5e..f58d34a6 100644 --- a/tower-http/src/macros.rs +++ b/tower-http/src/macros.rs @@ -47,7 +47,7 @@ macro_rules! opaque_body { #[inline] fn poll_frame( - mut self: std::pin::Pin<&mut Self>, + self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll, Self::Error>>> { self.project().inner.poll_frame(cx) diff --git a/tower-http/src/services/fs/serve_dir/tests.rs b/tower-http/src/services/fs/serve_dir/tests.rs index e78b3064..cd6c9f2c 100644 --- a/tower-http/src/services/fs/serve_dir/tests.rs +++ b/tower-http/src/services/fs/serve_dir/tests.rs @@ -8,7 +8,7 @@ use http::{header, Method, Response}; use http::{Request, StatusCode}; use http_body::Body as HttpBody; use std::convert::Infallible; -use std::io::{self, Read}; +use std::io::Read; use tower::{service_fn, ServiceExt}; #[tokio::test] diff --git a/tower-http/src/services/fs/serve_file.rs b/tower-http/src/services/fs/serve_file.rs index 7d519fb6..42a330dc 100644 --- a/tower-http/src/services/fs/serve_file.rs +++ b/tower-http/src/services/fs/serve_file.rs @@ -136,7 +136,6 @@ mod tests { use http::header; use http::Method; use http::{Request, StatusCode}; - use http_body::Body as _; use mime::Mime; use std::io::Read; use std::str::FromStr; diff --git a/tower-http/src/test_helpers.rs b/tower-http/src/test_helpers.rs index ac9d862c..4ed0504b 100644 --- a/tower-http/src/test_helpers.rs +++ b/tower-http/src/test_helpers.rs @@ -4,7 +4,6 @@ use std::{ task::{Context, Poll}, }; -use async_trait::async_trait; use bytes::Bytes; use futures::TryStream; use http_body::{Body as _, Frame}; diff --git a/tower-http/src/trace/body.rs b/tower-http/src/trace/body.rs index 39579575..70a9500b 100644 --- a/tower-http/src/trace/body.rs +++ b/tower-http/src/trace/body.rs @@ -1,7 +1,6 @@ use super::{OnBodyChunk, OnEos, OnFailure}; use crate::classify::ClassifyEos; use futures_core::ready; -use http::HeaderMap; use http_body::{Body, Frame}; use pin_project_lite::pin_project; use std::{ diff --git a/tower-http/src/trace/mod.rs b/tower-http/src/trace/mod.rs index da8f8549..40b388c8 100644 --- a/tower-http/src/trace/mod.rs +++ b/tower-http/src/trace/mod.rs @@ -440,7 +440,7 @@ mod tests { tracing::info_span!("test-span", foo = tracing::field::Empty) }) .on_request(|_req: &Request, span: &Span| { - span.record("foo", &42); + span.record("foo", 42); ON_REQUEST_COUNT.fetch_add(1, Ordering::SeqCst); }) .on_response(|_res: &Response, _latency: Duration, _span: &Span| { diff --git a/tower-http/src/validate_request.rs b/tower-http/src/validate_request.rs index dff0772f..298f24bc 100644 --- a/tower-http/src/validate_request.rs +++ b/tower-http/src/validate_request.rs @@ -384,7 +384,7 @@ where .to_str() .ok() .into_iter() - .flat_map(|s| s.split(",").map(|typ| typ.trim())) + .flat_map(|s| s.split(',').map(|typ| typ.trim())) }) .any(|h| { h.parse::() From a34e5a26a3086a1c4d009e21c8cb3f5f5985d257 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 09:39:38 +0100 Subject: [PATCH 07/56] comment out compression stuff for now --- tower-http/Cargo.toml | 24 +++++----- tower-http/src/builder.rs | 88 +++++++++++++++++----------------- tower-http/src/lib.rs | 76 +++++++++++++++-------------- tower-http/src/timeout/body.rs | 5 +- 4 files changed, 98 insertions(+), 95 deletions(-) diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index af297354..3b346a9c 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -60,9 +60,9 @@ full = [ "add-extension", "auth", "catch-panic", - "compression-full", + # "compression-full", "cors", - "decompression-full", + # "decompression-full", "follow-redirect", "fs", "limit", @@ -104,17 +104,17 @@ trace = ["tracing"] util = ["tower"] validate-request = ["mime"] -compression-br = ["async-compression/brotli", "tokio-util", "tokio"] -compression-deflate = ["async-compression/zlib", "tokio-util", "tokio"] -compression-full = ["compression-br", "compression-deflate", "compression-gzip", "compression-zstd"] -compression-gzip = ["async-compression/gzip", "tokio-util", "tokio"] -compression-zstd = ["async-compression/zstd", "tokio-util", "tokio"] +# compression-br = ["async-compression/brotli", "tokio-util", "tokio"] +# compression-deflate = ["async-compression/zlib", "tokio-util", "tokio"] +# compression-full = ["compression-br", "compression-deflate", "compression-gzip", "compression-zstd"] +# compression-gzip = ["async-compression/gzip", "tokio-util", "tokio"] +# compression-zstd = ["async-compression/zstd", "tokio-util", "tokio"] -decompression-br = ["async-compression/brotli", "tokio-util", "tokio"] -decompression-deflate = ["async-compression/zlib", "tokio-util", "tokio"] -decompression-full = ["decompression-br", "decompression-deflate", "decompression-gzip", "decompression-zstd"] -decompression-gzip = ["async-compression/gzip", "tokio-util", "tokio"] -decompression-zstd = ["async-compression/zstd", "tokio-util", "tokio"] +# decompression-br = ["async-compression/brotli", "tokio-util", "tokio"] +# decompression-deflate = ["async-compression/zlib", "tokio-util", "tokio"] +# decompression-full = ["decompression-br", "decompression-deflate", "decompression-gzip", "decompression-zstd"] +# decompression-gzip = ["async-compression/gzip", "tokio-util", "tokio"] +# decompression-zstd = ["async-compression/zstd", "tokio-util", "tokio"] [package.metadata.docs.rs] all-features = true diff --git a/tower-http/src/builder.rs b/tower-http/src/builder.rs index 2cb4f94a..0bb97e79 100644 --- a/tower-http/src/builder.rs +++ b/tower-http/src/builder.rs @@ -88,31 +88,31 @@ pub trait ServiceBuilderExt: crate::sealed::Sealed + Sized { f: F, ) -> ServiceBuilder, L>>; - /// Compresses response bodies. - /// - /// See [`tower_http::compression`] for more details. - /// - /// [`tower_http::compression`]: crate::compression - #[cfg(any( - feature = "compression-br", - feature = "compression-deflate", - feature = "compression-gzip", - feature = "compression-zstd", - ))] - fn compression(self) -> ServiceBuilder>; - - /// Decompress response bodies. - /// - /// See [`tower_http::decompression`] for more details. - /// - /// [`tower_http::decompression`]: crate::decompression - #[cfg(any( - feature = "decompression-br", - feature = "decompression-deflate", - feature = "decompression-gzip", - feature = "decompression-zstd", - ))] - fn decompression(self) -> ServiceBuilder>; + ///// Compresses response bodies. + ///// + ///// See [`tower_http::compression`] for more details. + ///// + ///// [`tower_http::compression`]: crate::compression + //#[cfg(any( + // feature = "compression-br", + // feature = "compression-deflate", + // feature = "compression-gzip", + // feature = "compression-zstd", + //))] + //fn compression(self) -> ServiceBuilder>; + + ///// Decompress response bodies. + ///// + ///// See [`tower_http::decompression`] for more details. + ///// + ///// [`tower_http::decompression`]: crate::decompression + //#[cfg(any( + // feature = "decompression-br", + // feature = "decompression-deflate", + // feature = "decompression-gzip", + // feature = "decompression-zstd", + //))] + //fn decompression(self) -> ServiceBuilder>; /// High level tracing that classifies responses using HTTP status codes. /// @@ -404,25 +404,25 @@ impl ServiceBuilderExt for ServiceBuilder { self.layer(crate::map_response_body::MapResponseBodyLayer::new(f)) } - #[cfg(any( - feature = "compression-br", - feature = "compression-deflate", - feature = "compression-gzip", - feature = "compression-zstd", - ))] - fn compression(self) -> ServiceBuilder> { - self.layer(crate::compression::CompressionLayer::new()) - } - - #[cfg(any( - feature = "decompression-br", - feature = "decompression-deflate", - feature = "decompression-gzip", - feature = "decompression-zstd", - ))] - fn decompression(self) -> ServiceBuilder> { - self.layer(crate::decompression::DecompressionLayer::new()) - } + // #[cfg(any( + // feature = "compression-br", + // feature = "compression-deflate", + // feature = "compression-gzip", + // feature = "compression-zstd", + // ))] + // fn compression(self) -> ServiceBuilder> { + // self.layer(crate::compression::CompressionLayer::new()) + // } + + // #[cfg(any( + // feature = "decompression-br", + // feature = "decompression-deflate", + // feature = "decompression-gzip", + // feature = "decompression-zstd", + // ))] + // fn decompression(self) -> ServiceBuilder> { + // self.layer(crate::decompression::DecompressionLayer::new()) + // } #[cfg(feature = "trace")] fn trace_for_http( diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index 40fb9223..3ca06448 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -236,13 +236,14 @@ pub mod set_header; #[cfg(feature = "propagate-header")] pub mod propagate_header; -#[cfg(any( - feature = "compression-br", - feature = "compression-deflate", - feature = "compression-gzip", - feature = "compression-zstd", -))] -pub mod compression; +// TODO(david): bring this back +// #[cfg(any( +// feature = "compression-br", +// feature = "compression-deflate", +// feature = "compression-gzip", +// feature = "compression-zstd", +// ))] +// pub mod compression; #[cfg(feature = "add-extension")] pub mod add_extension; @@ -250,13 +251,14 @@ pub mod add_extension; #[cfg(feature = "sensitive-headers")] pub mod sensitive_headers; -#[cfg(any( - feature = "decompression-br", - feature = "decompression-deflate", - feature = "decompression-gzip", - feature = "decompression-zstd", -))] -pub mod decompression; +// TODO(david): bring this back +// #[cfg(any( +// feature = "decompression-br", +// feature = "decompression-deflate", +// feature = "decompression-gzip", +// feature = "decompression-zstd", +// ))] +// pub mod decompression; #[cfg(any( feature = "compression-br", @@ -271,29 +273,29 @@ pub mod decompression; ))] mod content_encoding; -#[cfg(any( - feature = "compression-br", - feature = "compression-deflate", - feature = "compression-gzip", - feature = "compression-zstd", - feature = "decompression-br", - feature = "decompression-deflate", - feature = "decompression-gzip", - feature = "decompression-zstd", -))] -mod compression_utils; - -#[cfg(any( - feature = "compression-br", - feature = "compression-deflate", - feature = "compression-gzip", - feature = "compression-zstd", - feature = "decompression-br", - feature = "decompression-deflate", - feature = "decompression-gzip", - feature = "decompression-zstd", -))] -pub use compression_utils::CompressionLevel; +// #[cfg(any( +// feature = "compression-br", +// feature = "compression-deflate", +// feature = "compression-gzip", +// feature = "compression-zstd", +// feature = "decompression-br", +// feature = "decompression-deflate", +// feature = "decompression-gzip", +// feature = "decompression-zstd", +// ))] +// mod compression_utils; + +// #[cfg(any( +// feature = "compression-br", +// feature = "compression-deflate", +// feature = "compression-gzip", +// feature = "compression-zstd", +// feature = "decompression-br", +// feature = "decompression-deflate", +// feature = "decompression-gzip", +// feature = "decompression-zstd", +// ))] +// pub use compression_utils::CompressionLevel; #[cfg(feature = "map-response-body")] pub mod map_response_body; diff --git a/tower-http/src/timeout/body.rs b/tower-http/src/timeout/body.rs index 8a74ae21..07a2a5bc 100644 --- a/tower-http/src/timeout/body.rs +++ b/tower-http/src/timeout/body.rs @@ -131,9 +131,10 @@ mod tests { struct MockError; impl Error for MockError {} + impl Display for MockError { - fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - todo!() + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "mock error") } } From bfe751f07ea0bc095609d52eba59395496ace7b8 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 09:54:57 +0100 Subject: [PATCH 08/56] validate-request docs --- tower-http/Cargo.toml | 2 +- tower-http/src/validate_request.rs | 52 ++++++++++++++++-------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index 3b346a9c..e8ca5325 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -19,7 +19,7 @@ futures-core = "0.3" futures-util = { version = "0.3.14", default_features = false, features = [] } http = "0.2.2" http-body = "1.0.0-rc.2" -http-body-util = "0.1.0-rc.2" +http-body-util = "0.1.0-rc.3" pin-project-lite = "0.2.7" tower-layer = "0.3" tower-service = "0.3" diff --git a/tower-http/src/validate_request.rs b/tower-http/src/validate_request.rs index 298f24bc..f222aef3 100644 --- a/tower-http/src/validate_request.rs +++ b/tower-http/src/validate_request.rs @@ -4,16 +4,17 @@ //! //! ``` //! use tower_http::validate_request::ValidateRequestHeaderLayer; -//! use hyper::{Request, Response, Body, Error}; -//! use http::{StatusCode, header::ACCEPT}; -//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; +//! use http::{Request, Response, StatusCode, header::ACCEPT}; +//! use http_body_util::Full; +//! use bytes::Bytes; +//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError}; //! -//! async fn handle(request: Request) -> Result, Error> { -//! Ok(Response::new(Body::empty())) +//! async fn handle(request: Request>) -> Result>, BoxError> { +//! Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! let mut service = ServiceBuilder::new() //! // Require the `Accept` header to be `application/json`, `*/*` or `application/*` //! .layer(ValidateRequestHeaderLayer::accept("application/json")) @@ -22,7 +23,7 @@ //! // Requests with the correct value are allowed through //! let request = Request::builder() //! .header(ACCEPT, "application/json") -//! .body(Body::empty()) +//! .body(Full::default()) //! .unwrap(); //! //! let response = service @@ -36,7 +37,7 @@ //! // Requests with an invalid value get a `406 Not Acceptable` response //! let request = Request::builder() //! .header(ACCEPT, "text/strings") -//! .body(Body::empty()) +//! .body(Full::default()) //! .unwrap(); //! //! let response = service @@ -54,15 +55,16 @@ //! //! ``` //! use tower_http::validate_request::{ValidateRequestHeaderLayer, ValidateRequest}; -//! use hyper::{Request, Response, Body, Error}; -//! use http::{StatusCode, header::ACCEPT}; -//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; +//! use http::{Request, Response, StatusCode, header::ACCEPT}; +//! use http_body_util::Full; +//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError}; +//! use bytes::Bytes; //! //! #[derive(Clone, Copy)] //! pub struct MyHeader { /* ... */ } //! //! impl ValidateRequest for MyHeader { -//! type ResponseBody = Body; +//! type ResponseBody = Full; //! //! fn validate( //! &mut self, @@ -73,13 +75,13 @@ //! } //! } //! -//! async fn handle(request: Request) -> Result, Error> { -//! Ok(Response::new(Body::empty())) +//! async fn handle(request: Request>) -> Result>, BoxError> { +//! Ok(Response::new(Full::default())) //! } //! //! //! # #[tokio::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! let service = ServiceBuilder::new() //! // Validate requests using `MyHeader` //! .layer(ValidateRequestHeaderLayer::custom(MyHeader { /* ... */ })) @@ -92,21 +94,22 @@ //! //! ``` //! use tower_http::validate_request::{ValidateRequestHeaderLayer, ValidateRequest}; -//! use hyper::{Request, Response, Body, Error}; -//! use http::{StatusCode, header::ACCEPT}; -//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; +//! use http::{Request, Response, StatusCode, header::ACCEPT}; +//! use bytes::Bytes; +//! use http_body_util::Full; +//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError}; //! -//! async fn handle(request: Request) -> Result, Error> { +//! async fn handle(request: Request>) -> Result>, BoxError> { //! # todo!(); //! // ... //! } //! //! # #[tokio::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! let service = ServiceBuilder::new() -//! .layer(ValidateRequestHeaderLayer::custom(|request: &mut Request| { +//! .layer(ValidateRequestHeaderLayer::custom(|request: &mut Request>| { //! // Validate the request -//! # Ok::<_, Response>(()) +//! # Ok::<_, Response>>(()) //! })) //! .service_fn(handle); //! # Ok(()) @@ -150,10 +153,11 @@ impl ValidateRequestHeaderLayer> { /// # Example /// /// ``` - /// use hyper::Body; + /// use http_body_util::Full; + /// use bytes::Bytes; /// use tower_http::validate_request::{AcceptHeader, ValidateRequestHeaderLayer}; /// - /// let layer = ValidateRequestHeaderLayer::>::accept("application/json"); + /// let layer = ValidateRequestHeaderLayer::>>::accept("application/json"); /// ``` /// /// [`Accept`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept From f951e1a0c780d4808b8f77fb52405732f7388fc5 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:02:45 +0100 Subject: [PATCH 09/56] trace --- tower-http/src/trace/mod.rs | 67 +++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/tower-http/src/trace/mod.rs b/tower-http/src/trace/mod.rs index 40b388c8..ffed3ab3 100644 --- a/tower-http/src/trace/mod.rs +++ b/tower-http/src/trace/mod.rs @@ -6,13 +6,14 @@ //! //! ```rust //! use http::{Request, Response}; -//! use hyper::Body; //! use tower::{ServiceBuilder, ServiceExt, Service}; //! use tower_http::trace::TraceLayer; //! use std::convert::Infallible; +//! use http_body_util::Full; +//! use bytes::Bytes; //! -//! async fn handle(request: Request) -> Result, Infallible> { -//! Ok(Response::new(Body::from("foo"))) +//! async fn handle(request: Request>) -> Result>, Infallible> { +//! Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] @@ -24,7 +25,7 @@ //! .layer(TraceLayer::new_for_http()) //! .service_fn(handle); //! -//! let request = Request::new(Body::from("foo")); +//! let request = Request::new(Full::from("foo")); //! //! let response = service //! .ready() @@ -50,7 +51,7 @@ //! //! ```rust //! use http::{Request, Response, HeaderMap, StatusCode}; -//! use hyper::Body; +//! use http_body_util::Full; //! use bytes::Bytes; //! use tower::ServiceBuilder; //! use tracing::Level; @@ -62,8 +63,8 @@ //! # use tower::{ServiceExt, Service}; //! # use std::convert::Infallible; //! -//! # async fn handle(request: Request) -> Result, Infallible> { -//! # Ok(Response::new(Body::from("foo"))) +//! # async fn handle(request: Request>) -> Result>, Infallible> { +//! # Ok(Response::new(Full::from("foo"))) //! # } //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { @@ -90,7 +91,7 @@ //! # let response = service //! # .ready() //! # .await? -//! # .call(Request::new(Body::from("foo"))) +//! # .call(Request::new(Full::from("foo"))) //! # .await?; //! # Ok(()) //! # } @@ -100,7 +101,7 @@ //! //! ```rust //! use http::{Request, Response, HeaderMap, StatusCode}; -//! use hyper::Body; +//! use http_body_util::Full; //! use bytes::Bytes; //! use tower::ServiceBuilder; //! use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer}; @@ -109,8 +110,8 @@ //! # use tower::{ServiceExt, Service}; //! # use std::convert::Infallible; //! -//! # async fn handle(request: Request) -> Result, Infallible> { -//! # Ok(Response::new(Body::from("foo"))) +//! # async fn handle(request: Request>) -> Result>, Infallible> { +//! # Ok(Response::new(Full::from("foo"))) //! # } //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { @@ -119,13 +120,13 @@ //! let service = ServiceBuilder::new() //! .layer( //! TraceLayer::new_for_http() -//! .make_span_with(|request: &Request| { +//! .make_span_with(|request: &Request>| { //! tracing::debug_span!("http-request") //! }) -//! .on_request(|request: &Request, _span: &Span| { +//! .on_request(|request: &Request>, _span: &Span| { //! tracing::debug!("started {} {}", request.method(), request.uri().path()) //! }) -//! .on_response(|response: &Response, latency: Duration, _span: &Span| { +//! .on_response(|response: &Response>, latency: Duration, _span: &Span| { //! tracing::debug!("response generated in {:?}", latency) //! }) //! .on_body_chunk(|chunk: &Bytes, latency: Duration, _span: &Span| { @@ -143,7 +144,7 @@ //! # let response = service //! # .ready() //! # .await? -//! # .call(Request::new(Body::from("foo"))) +//! # .call(Request::new(Full::from("foo"))) //! # .await?; //! # Ok(()) //! # } @@ -160,12 +161,13 @@ //! use std::time::Duration; //! use tracing::Span; //! # use tower::{ServiceExt, Service}; -//! # use hyper::Body; +//! # use http_body_util::Full; +//! # use bytes::Bytes; //! # use http::{Response, Request}; //! # use std::convert::Infallible; //! -//! # async fn handle(request: Request) -> Result, Infallible> { -//! # Ok(Response::new(Body::from("foo"))) +//! # async fn handle(request: Request>) -> Result>, Infallible> { +//! # Ok(Response::new(Full::from("foo"))) //! # } //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { @@ -188,7 +190,7 @@ //! # let response = service //! # .ready() //! # .await? -//! # .call(Request::new(Body::from("foo"))) +//! # .call(Request::new(Full::from("foo"))) //! # .await?; //! # Ok(()) //! # } @@ -216,14 +218,14 @@ //! ### `on_body_chunk` //! //! The `on_body_chunk` callback is called when the response body produces a new -//! chunk, that is when [`Body::poll_data`] returns `Poll::Ready(Some(Ok(chunk)))`. +//! chunk, that is when [`Body::poll_frame`] returns a data frame. //! //! `on_body_chunk` is called even if the chunk is empty. //! //! ### `on_eos` //! //! The `on_eos` callback is called when a streaming response body ends, that is -//! when [`Body::poll_trailers`] returns `Poll::Ready(Ok(trailers))`. +//! when [`Body::poll_frame`] returns a trailers frame. //! //! `on_eos` is called even if the trailers produced are `None`. //! @@ -233,8 +235,7 @@ //! //! - The inner [`Service`]'s response future resolves to an error. //! - A response is classified as a failure. -//! - [`Body::poll_data`] returns an error. -//! - [`Body::poll_trailers`] returns an error. +//! - [`Body::poll_frame`] returns an error. //! - An end-of-stream is classified as a failure. //! //! # Recording fields on the span @@ -245,7 +246,7 @@ //! //! ```rust //! use http::{Request, Response, HeaderMap, StatusCode}; -//! use hyper::Body; +//! use http_body_util::Full; //! use bytes::Bytes; //! use tower::ServiceBuilder; //! use tower_http::trace::TraceLayer; @@ -253,8 +254,8 @@ //! use std::time::Duration; //! # use std::convert::Infallible; //! -//! # async fn handle(request: Request) -> Result, Infallible> { -//! # Ok(Response::new(Body::from("foo"))) +//! # async fn handle(request: Request>) -> Result>, Infallible> { +//! # Ok(Response::new(Full::from("foo"))) //! # } //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { @@ -263,13 +264,13 @@ //! let service = ServiceBuilder::new() //! .layer( //! TraceLayer::new_for_http() -//! .make_span_with(|request: &Request| { +//! .make_span_with(|request: &Request>| { //! tracing::debug_span!( //! "http-request", //! status_code = tracing::field::Empty, //! ) //! }) -//! .on_response(|response: &Response, _latency: Duration, span: &Span| { +//! .on_response(|response: &Response>, _latency: Duration, span: &Span| { //! span.record("status_code", &tracing::field::display(response.status())); //! //! tracing::debug!("response generated") @@ -290,7 +291,8 @@ //! //! ```rust //! use http::{Request, Response}; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! use tower::ServiceBuilder; //! use tower_http::{ //! trace::TraceLayer, @@ -301,8 +303,8 @@ //! }; //! use std::convert::Infallible; //! -//! # async fn handle(request: Request) -> Result, Infallible> { -//! # Ok(Response::new(Body::from("foo"))) +//! # async fn handle(request: Request>) -> Result>, Infallible> { +//! # Ok(Response::new(Full::from("foo"))) //! # } //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { @@ -380,8 +382,7 @@ //! [`TraceLayer::make_span_with`]: crate::trace::TraceLayer::make_span_with //! [`Span`]: tracing::Span //! [`ServerErrorsAsFailures`]: crate::classify::ServerErrorsAsFailures -//! [`Body::poll_trailers`]: http_body::Body::poll_trailers -//! [`Body::poll_data`]: http_body::Body::poll_data +//! [`Body::poll_frame`]: http_body::Body::poll_frame use tracing::Level; From 46e3465e82eb6246a5549ddd9f2126e42eab1b4e Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:04:30 +0100 Subject: [PATCH 10/56] timeout --- tower-http/src/timeout/body.rs | 5 +++-- tower-http/src/timeout/service.rs | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tower-http/src/timeout/body.rs b/tower-http/src/timeout/body.rs index 07a2a5bc..67b758a4 100644 --- a/tower-http/src/timeout/body.rs +++ b/tower-http/src/timeout/body.rs @@ -15,12 +15,13 @@ //! //! ``` //! use http::{Request, Response}; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! use std::time::Duration; //! use tower::ServiceBuilder; //! use tower_http::timeout::RequestBodyTimeoutLayer; //! -//! async fn handle(_: Request) -> Result, std::convert::Infallible> { +//! async fn handle(_: Request>) -> Result>, std::convert::Infallible> { //! // ... //! # todo!() //! } diff --git a/tower-http/src/timeout/service.rs b/tower-http/src/timeout/service.rs index 13347a34..97ef55fb 100644 --- a/tower-http/src/timeout/service.rs +++ b/tower-http/src/timeout/service.rs @@ -17,14 +17,15 @@ //! //! ``` //! use http::{Request, Response}; -//! use hyper::Body; +//! use bytes::Bytes; +//! use http_body_util::Full; //! use std::{convert::Infallible, time::Duration}; //! use tower::ServiceBuilder; //! use tower_http::timeout::TimeoutLayer; //! -//! async fn handle(_: Request) -> Result, Infallible> { +//! async fn handle(_: Request>) -> Result>, Infallible> { //! // ... -//! # Ok(Response::new(Body::empty())) +//! # Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] From 3419c36819fffbbeea5bb33ceb2b91a7068e25cc Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:05:14 +0100 Subject: [PATCH 11/56] set_status --- tower-http/src/set_status.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tower-http/src/set_status.rs b/tower-http/src/set_status.rs index 3fa87c1f..d7d50a59 100644 --- a/tower-http/src/set_status.rs +++ b/tower-http/src/set_status.rs @@ -5,13 +5,14 @@ //! ``` //! use tower_http::set_status::SetStatusLayer; //! use http::{Request, Response, StatusCode}; -//! use hyper::Body; +//! use bytes::Bytes; +//! use http_body_util::Full; //! use std::{iter::once, convert::Infallible}; //! use tower::{ServiceBuilder, Service, ServiceExt}; //! -//! async fn handle(req: Request) -> Result, Infallible> { +//! async fn handle(req: Request>) -> Result>, Infallible> { //! // ... -//! # Ok(Response::new(Body::empty())) +//! # Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] @@ -22,7 +23,7 @@ //! .service_fn(handle); //! //! // Call the service. -//! let request = Request::builder().body(Body::empty())?; +//! let request = Request::builder().body(Full::default())?; //! //! let response = service.ready().await?.call(request).await?; //! From 92d3eb2b5f9632ee123696779cc1ce9f9ee6ab25 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:10:16 +0100 Subject: [PATCH 12/56] set_header --- tower-http/src/set_header/request.rs | 20 +++++++++++--------- tower-http/src/set_header/response.rs | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/tower-http/src/set_header/request.rs b/tower-http/src/set_header/request.rs index 1edec90c..4032e23a 100644 --- a/tower-http/src/set_header/request.rs +++ b/tower-http/src/set_header/request.rs @@ -12,12 +12,13 @@ //! use http::{Request, Response, header::{self, HeaderValue}}; //! use tower::{Service, ServiceExt, ServiceBuilder}; //! use tower_http::set_header::SetRequestHeaderLayer; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! # let http_client = tower::service_fn(|_: Request| async move { -//! # Ok::<_, std::convert::Infallible>(Response::new(Body::empty())) +//! # let http_client = tower::service_fn(|_: Request>| async move { +//! # Ok::<_, std::convert::Infallible>(Response::new(Full::::default())) //! # }); //! # //! let mut svc = ServiceBuilder::new() @@ -33,7 +34,7 @@ //! ) //! .service(http_client); //! -//! let request = Request::new(Body::empty()); +//! let request = Request::new(Full::default()); //! //! let response = svc.ready().await?.call(request).await?; //! # @@ -47,12 +48,13 @@ //! use http::{Request, Response, header::{self, HeaderValue}}; //! use tower::{Service, ServiceExt, ServiceBuilder}; //! use tower_http::set_header::SetRequestHeaderLayer; -//! use hyper::Body; +//! use bytes::Bytes; +//! use http_body_util::Full; //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! # let http_client = tower::service_fn(|_: Request| async move { -//! # Ok::<_, std::convert::Infallible>(Response::new(Body::empty())) +//! # let http_client = tower::service_fn(|_: Request>| async move { +//! # Ok::<_, std::convert::Infallible>(Response::new(Full::::default())) //! # }); //! fn date_header_value() -> HeaderValue { //! // ... @@ -67,14 +69,14 @@ //! // may have. //! SetRequestHeaderLayer::overriding( //! header::DATE, -//! |request: &Request| { +//! |request: &Request>| { //! Some(date_header_value()) //! } //! ) //! ) //! .service(http_client); //! -//! let request = Request::new(Body::empty()); +//! let request = Request::new(Full::default()); //! //! let response = svc.ready().await?.call(request).await?; //! # diff --git a/tower-http/src/set_header/response.rs b/tower-http/src/set_header/response.rs index 3ab971bf..5f8632b4 100644 --- a/tower-http/src/set_header/response.rs +++ b/tower-http/src/set_header/response.rs @@ -12,11 +12,12 @@ //! use http::{Request, Response, header::{self, HeaderValue}}; //! use tower::{Service, ServiceExt, ServiceBuilder}; //! use tower_http::set_header::SetResponseHeaderLayer; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! # let render_html = tower::service_fn(|request: Request| async move { +//! # let render_html = tower::service_fn(|request: Request>| async move { //! # Ok::<_, std::convert::Infallible>(Response::new(request.into_body())) //! # }); //! # @@ -33,7 +34,7 @@ //! ) //! .service(render_html); //! -//! let request = Request::new(Body::empty()); +//! let request = Request::new(Full::default()); //! //! let response = svc.ready().await?.call(request).await?; //! @@ -49,13 +50,14 @@ //! use http::{Request, Response, header::{self, HeaderValue}}; //! use tower::{Service, ServiceExt, ServiceBuilder}; //! use tower_http::set_header::SetResponseHeaderLayer; -//! use hyper::Body; +//! use bytes::Bytes; +//! use http_body_util::Full; //! use http_body::Body as _; // for `Body::size_hint` //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! # let render_html = tower::service_fn(|request: Request| async move { -//! # Ok::<_, std::convert::Infallible>(Response::new(Body::from("1234567890"))) +//! # let render_html = tower::service_fn(|request: Request>| async move { +//! # Ok::<_, std::convert::Infallible>(Response::new(Full::from("1234567890"))) //! # }); //! # //! let mut svc = ServiceBuilder::new() @@ -67,7 +69,7 @@ //! // may have. //! SetResponseHeaderLayer::overriding( //! header::CONTENT_LENGTH, -//! |response: &Response| { +//! |response: &Response>| { //! if let Some(size) = response.body().size_hint().exact() { //! // If the response body has a known size, returning `Some` will //! // set the `Content-Length` header to that value. @@ -82,7 +84,7 @@ //! ) //! .service(render_html); //! -//! let request = Request::new(Body::empty()); +//! let request = Request::new(Full::default()); //! //! let response = svc.ready().await?.call(request).await?; //! From fbee8cc0bc3d49ec8be6a857b70fa75c8541154e Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:16:25 +0100 Subject: [PATCH 13/56] serve_dir --- tower-http/src/services/fs/serve_dir/mod.rs | 46 +++------------------ 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/tower-http/src/services/fs/serve_dir/mod.rs b/tower-http/src/services/fs/serve_dir/mod.rs index e72bda72..0fc551f8 100644 --- a/tower-http/src/services/fs/serve_dir/mod.rs +++ b/tower-http/src/services/fs/serve_dir/mod.rs @@ -48,15 +48,6 @@ const DEFAULT_CAPACITY: usize = 65536; /// // This will serve files in the "assets" directory and /// // its subdirectories /// let service = ServeDir::new("assets"); -/// -/// # async { -/// // Run our service using `hyper` -/// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000)); -/// hyper::Server::bind(&addr) -/// .serve(tower::make::Shared::new(service)) -/// .await -/// .expect("server error"); -/// # }; /// ``` #[derive(Clone, Debug)] pub struct ServeDir { @@ -217,15 +208,6 @@ impl ServeDir { /// let service = ServeDir::new("assets") /// // respond with `not_found.html` for missing files /// .fallback(ServeFile::new("assets/not_found.html")); - /// - /// # async { - /// // Run our service using `hyper` - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000)); - /// hyper::Server::bind(&addr) - /// .serve(tower::make::Shared::new(service)) - /// .await - /// .expect("server error"); - /// # }; /// ``` pub fn fallback(self, new_fallback: F2) -> ServeDir { ServeDir { @@ -252,15 +234,6 @@ impl ServeDir { /// let service = ServeDir::new("assets") /// // respond with `404 Not Found` and the contents of `not_found.html` for missing files /// .not_found_service(ServeFile::new("assets/not_found.html")); - /// - /// # async { - /// // Run our service using `hyper` - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000)); - /// hyper::Server::bind(&addr) - /// .serve(tower::make::Shared::new(service)) - /// .await - /// .expect("server error"); - /// # }; /// ``` /// /// Setups like this are often found in single page applications. @@ -293,13 +266,13 @@ impl ServeDir { /// use tower_http::services::ServeDir; /// use std::{io, convert::Infallible}; /// use http::{Request, Response, StatusCode}; - /// use http_body::{combinators::UnsyncBoxBody, Body as _}; - /// use hyper::Body; + /// use http_body::Body as _; + /// use http_body_util::{Full, BodyExt, combinators::UnsyncBoxBody}; /// use bytes::Bytes; /// use tower::{service_fn, ServiceExt, BoxError}; /// /// async fn serve_dir( - /// request: Request + /// request: Request> /// ) -> Result>, Infallible> { /// let mut service = ServeDir::new("assets"); /// @@ -308,7 +281,7 @@ impl ServeDir { /// // /// // Its shown here for demonstration but you can do `service.try_call(request)` /// // otherwise - /// let ready_service = match ServiceExt::>::ready(&mut service).await { + /// let ready_service = match ServiceExt::>>::ready(&mut service).await { /// Ok(ready_service) => ready_service, /// Err(infallible) => match infallible {}, /// }; @@ -318,7 +291,7 @@ impl ServeDir { /// Ok(response.map(|body| body.map_err(Into::into).boxed_unsync())) /// } /// Err(err) => { - /// let body = Body::from("Something went wrong...") + /// let body = Full::from("Something went wrong...") /// .map_err(Into::into) /// .boxed_unsync(); /// let response = Response::builder() @@ -329,15 +302,6 @@ impl ServeDir { /// } /// } /// } - /// - /// # async { - /// // Run our service using `hyper` - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000)); - /// hyper::Server::bind(&addr) - /// .serve(tower::make::Shared::new(service_fn(serve_dir))) - /// .await - /// .expect("server error"); - /// # }; /// ``` pub fn try_call( &mut self, From 54b167088075c5afd9dec9e3de871f1c2ab1d444 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:18:08 +0100 Subject: [PATCH 14/56] redirect --- tower-http/src/services/redirect.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tower-http/src/services/redirect.rs b/tower-http/src/services/redirect.rs index f5e0552d..020927c9 100644 --- a/tower-http/src/services/redirect.rs +++ b/tower-http/src/services/redirect.rs @@ -7,18 +7,19 @@ //! //! ```rust //! use http::{Request, Uri, StatusCode}; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! use tower::{Service, ServiceExt}; //! use tower_http::services::Redirect; //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { //! let uri: Uri = "https://example.com/".parse().unwrap(); -//! let mut service: Redirect = Redirect::permanent(uri); +//! let mut service: Redirect> = Redirect::permanent(uri); //! //! let request = Request::builder() //! .uri("http://example.com") -//! .body(Body::empty()) +//! .body(Full::::default()) //! .unwrap(); //! //! let response = service.oneshot(request).await?; From fdaa24f8847645a38ad855a2dec0982ab3a32275 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:21:07 +0100 Subject: [PATCH 15/56] follow_redirect --- tower-http/src/follow_redirect/mod.rs | 16 +++++++++------- tower-http/src/follow_redirect/policy/mod.rs | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/tower-http/src/follow_redirect/mod.rs b/tower-http/src/follow_redirect/mod.rs index af8f12aa..6a4bebdb 100644 --- a/tower-http/src/follow_redirect/mod.rs +++ b/tower-http/src/follow_redirect/mod.rs @@ -18,7 +18,8 @@ //! //! ``` //! use http::{Request, Response}; -//! use hyper::Body; +//! use bytes::Bytes; +//! use http_body_util::Full; //! use tower::{Service, ServiceBuilder, ServiceExt}; //! use tower_http::follow_redirect::{FollowRedirectLayer, RequestUri}; //! @@ -32,7 +33,7 @@ //! # .status(http::StatusCode::MOVED_PERMANENTLY) //! # .header(http::header::LOCATION, dest); //! # } -//! # Ok::<_, std::convert::Infallible>(res.body(Body::empty()).unwrap()) +//! # Ok::<_, std::convert::Infallible>(res.body(Full::::default()).unwrap()) //! # }); //! let mut client = ServiceBuilder::new() //! .layer(FollowRedirectLayer::new()) @@ -40,7 +41,7 @@ //! //! let request = Request::builder() //! .uri("https://rust-lang.org/") -//! .body(Body::empty()) +//! .body(Full::::default()) //! .unwrap(); //! //! let response = client.ready().await?.call(request).await?; @@ -56,7 +57,8 @@ //! //! ``` //! use http::{Request, Response}; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! use tower::{Service, ServiceBuilder, ServiceExt}; //! use tower_http::follow_redirect::{ //! policy::{self, PolicyExt}, @@ -65,14 +67,14 @@ //! //! #[derive(Debug)] //! enum MyError { -//! Hyper(hyper::Error), //! TooManyRedirects, +//! Other(tower::BoxError), //! } //! //! # #[tokio::main] //! # async fn main() -> Result<(), MyError> { //! # let http_client = -//! # tower::service_fn(|_: Request| async { Ok(Response::new(Body::empty())) }); +//! # tower::service_fn(|_: Request>| async { Ok(Response::new(Full::::default())) }); //! let policy = policy::Limited::new(10) // Set the maximum number of redirections to 10. //! // Return an error when the limit was reached. //! .or::<_, (), _>(policy::redirect_fn(|_| Err(MyError::TooManyRedirects))) @@ -81,7 +83,7 @@ //! //! let mut client = ServiceBuilder::new() //! .layer(FollowRedirectLayer::with_policy(policy)) -//! .map_err(MyError::Hyper) +//! .map_err(MyError::Other) //! .service(http_client); //! //! // ... diff --git a/tower-http/src/follow_redirect/policy/mod.rs b/tower-http/src/follow_redirect/policy/mod.rs index adbd4d27..8e5d39ce 100644 --- a/tower-http/src/follow_redirect/policy/mod.rs +++ b/tower-http/src/follow_redirect/policy/mod.rs @@ -122,12 +122,12 @@ pub trait PolicyExt { /// /// ``` /// use bytes::Bytes; - /// use hyper::Body; + /// use http_body_util::Full; /// use tower_http::follow_redirect::policy::{self, clone_body_fn, Limited, PolicyExt}; /// /// enum MyBody { /// Bytes(Bytes), - /// Hyper(Body), + /// Full(Full), /// } /// /// let policy = Limited::default().and::<_, _, ()>(clone_body_fn(|body| { From 4b412bb165d6ad252290091bb4c08c83fe2f1f4c Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:22:53 +0100 Subject: [PATCH 16/56] sensitive_headers --- tower-http/src/sensitive_headers.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tower-http/src/sensitive_headers.rs b/tower-http/src/sensitive_headers.rs index 3fcf18d3..59a12dff 100644 --- a/tower-http/src/sensitive_headers.rs +++ b/tower-http/src/sensitive_headers.rs @@ -8,12 +8,13 @@ //! use tower_http::sensitive_headers::SetSensitiveHeadersLayer; //! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; //! use http::{Request, Response, header::AUTHORIZATION}; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! use std::{iter::once, convert::Infallible}; //! -//! async fn handle(req: Request) -> Result, Infallible> { +//! async fn handle(req: Request>) -> Result>, Infallible> { //! // ... -//! # Ok(Response::new(Body::empty())) +//! # Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] @@ -33,7 +34,7 @@ //! let response = service //! .ready() //! .await? -//! .call(Request::new(Body::empty())) +//! .call(Request::new(Full::default())) //! .await?; //! # Ok(()) //! # } @@ -56,10 +57,11 @@ //! use http::header; //! use std::sync::Arc; //! # use http::{Request, Response}; -//! # use hyper::Body; +//! # use bytes::Bytes; +//! # use http_body_util::Full; //! # use std::convert::Infallible; -//! # async fn handle(req: Request) -> Result, Infallible> { -//! # Ok(Response::new(Body::empty())) +//! # async fn handle(req: Request>) -> Result>, Infallible> { +//! # Ok(Response::new(Full::default())) //! # } //! //! # #[tokio::main] From f14f012dd33dd7ffee93bb58a66cf41cd1c23f6b Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:24:02 +0100 Subject: [PATCH 17/56] request_id --- tower-http/src/request_id.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tower-http/src/request_id.rs b/tower-http/src/request_id.rs index 49d435a0..8c404779 100644 --- a/tower-http/src/request_id.rs +++ b/tower-http/src/request_id.rs @@ -8,12 +8,13 @@ //! use tower_http::request_id::{ //! SetRequestIdLayer, PropagateRequestIdLayer, MakeRequestId, RequestId, //! }; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! use std::sync::{Arc, atomic::{AtomicU64, Ordering}}; //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! # let handler = tower::service_fn(|request: Request| async move { +//! # let handler = tower::service_fn(|request: Request>| async move { //! # Ok::<_, std::convert::Infallible>(Response::new(request.into_body())) //! # }); //! # @@ -47,7 +48,7 @@ //! .layer(PropagateRequestIdLayer::new(x_request_id)) //! .service(handler); //! -//! let request = Request::new(Body::empty()); +//! let request = Request::new(Full::default()); //! let response = svc.ready().await?.call(request).await?; //! //! assert_eq!(response.headers()["x-request-id"], "0"); @@ -65,11 +66,12 @@ //! # use tower_http::request_id::{ //! # SetRequestIdLayer, PropagateRequestIdLayer, MakeRequestId, RequestId, //! # }; -//! # use hyper::Body; +//! # use bytes::Bytes; +//! # use http_body_util::Full; //! # use std::sync::{Arc, atomic::{AtomicU64, Ordering}}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! # let handler = tower::service_fn(|request: Request| async move { +//! # let handler = tower::service_fn(|request: Request>| async move { //! # Ok::<_, std::convert::Infallible>(Response::new(request.into_body())) //! # }); //! # #[derive(Clone, Default)] @@ -92,7 +94,7 @@ //! .propagate_x_request_id() //! .service(handler); //! -//! let request = Request::new(Body::empty()); +//! let request = Request::new(Full::default()); //! let response = svc.ready().await?.call(request).await?; //! //! assert_eq!(response.headers()["x-request-id"], "0"); @@ -118,11 +120,12 @@ //! # use tower_http::request_id::{ //! # SetRequestIdLayer, PropagateRequestIdLayer, MakeRequestId, RequestId, //! # }; -//! # use hyper::Body; +//! # use http_body_util::Full; +//! # use bytes::Bytes; //! # use std::sync::{Arc, atomic::{AtomicU64, Ordering}}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! # let handler = tower::service_fn(|request: Request| async move { +//! # let handler = tower::service_fn(|request: Request>| async move { //! # Ok::<_, std::convert::Infallible>(Response::new(request.into_body())) //! # }); //! # #[derive(Clone, Default)] From 6efd5df32e6fcaff6842aa50feac688490d81a58 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:24:53 +0100 Subject: [PATCH 18/56] propagate_header --- tower-http/src/propagate_header.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tower-http/src/propagate_header.rs b/tower-http/src/propagate_header.rs index 2ac1756a..906fdf9e 100644 --- a/tower-http/src/propagate_header.rs +++ b/tower-http/src/propagate_header.rs @@ -7,13 +7,14 @@ //! use std::convert::Infallible; //! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; //! use tower_http::propagate_header::PropagateHeaderLayer; -//! use hyper::Body; +//! use bytes::Bytes; +//! use http_body_util::Full; //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! async fn handle(req: Request) -> Result, Infallible> { +//! async fn handle(req: Request>) -> Result>, Infallible> { //! // ... -//! # Ok(Response::new(Body::empty())) +//! # Ok(Response::new(Full::default())) //! } //! //! let mut svc = ServiceBuilder::new() @@ -24,7 +25,7 @@ //! // Call the service. //! let request = Request::builder() //! .header("x-request-id", "1337") -//! .body(Body::empty())?; +//! .body(Full::default())?; //! //! let response = svc.ready().await?.call(request).await?; //! From b06f942dfbc230e276386f76375f441bfdbf444c Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:25:28 +0100 Subject: [PATCH 19/56] normalize_path --- tower-http/src/normalize_path.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tower-http/src/normalize_path.rs b/tower-http/src/normalize_path.rs index 8b647539..753c2844 100644 --- a/tower-http/src/normalize_path.rs +++ b/tower-http/src/normalize_path.rs @@ -8,15 +8,16 @@ //! ``` //! use tower_http::normalize_path::NormalizePathLayer; //! use http::{Request, Response, StatusCode}; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! use std::{iter::once, convert::Infallible}; //! use tower::{ServiceBuilder, Service, ServiceExt}; //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! async fn handle(req: Request) -> Result, Infallible> { +//! async fn handle(req: Request>) -> Result>, Infallible> { //! // `req.uri().path()` will not have trailing slashes -//! # Ok(Response::new(Body::empty())) +//! # Ok(Response::new(Full::default())) //! } //! //! let mut service = ServiceBuilder::new() @@ -28,7 +29,7 @@ //! let request = Request::builder() //! // `handle` will see `/foo` //! .uri("/foo/") -//! .body(Body::empty())?; +//! .body(Full::default())?; //! //! service.ready().await?.call(request).await?; //! # From 9c98dc0ce36ecf392312e7327809e77cc88e5751 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:26:31 +0100 Subject: [PATCH 20/56] in_flight_requests --- tower-http/src/metrics/in_flight_requests.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tower-http/src/metrics/in_flight_requests.rs b/tower-http/src/metrics/in_flight_requests.rs index c6fc55ae..f68ce782 100644 --- a/tower-http/src/metrics/in_flight_requests.rs +++ b/tower-http/src/metrics/in_flight_requests.rs @@ -10,12 +10,13 @@ //! use tower::{Service, ServiceExt, ServiceBuilder}; //! use tower_http::metrics::InFlightRequestsLayer; //! use http::{Request, Response}; -//! use hyper::Body; +//! use bytes::Bytes; +//! use http_body_util::Full; //! use std::{time::Duration, convert::Infallible}; //! -//! async fn handle(req: Request) -> Result, Infallible> { +//! async fn handle(req: Request>) -> Result>, Infallible> { //! // ... -//! # Ok(Response::new(Body::empty())) +//! # Ok(Response::new(Full::default())) //! } //! //! async fn update_in_flight_requests_metric(count: usize) { @@ -44,7 +45,7 @@ //! let response = service //! .ready() //! .await? -//! .call(Request::new(Body::empty())) +//! .call(Request::new(Full::default())) //! .await?; //! # Ok(()) //! # } From 2e481e498095496a140e31d639495993bfb2f488 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:30:23 +0100 Subject: [PATCH 21/56] map_response_body --- tower-http/src/map_response_body.rs | 62 ++++++++++------------------- 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/tower-http/src/map_response_body.rs b/tower-http/src/map_response_body.rs index bbad79db..97f87332 100644 --- a/tower-http/src/map_response_body.rs +++ b/tower-http/src/map_response_body.rs @@ -5,70 +5,50 @@ //! ``` //! use bytes::Bytes; //! use http::{Request, Response}; -//! use hyper::Body; +//! use http_body_util::Full; //! use std::convert::Infallible; //! use std::{pin::Pin, task::{Context, Poll}}; //! use tower::{ServiceBuilder, service_fn, ServiceExt, Service}; //! use tower_http::map_response_body::MapResponseBodyLayer; //! use futures::ready; //! -//! // A wrapper for a `hyper::Body` that prints the size of data chunks -//! struct PrintChunkSizesBody { -//! inner: Body, +//! // A wrapper for a `Full` +//! struct BodyWrapper { +//! inner: Full, //! } //! -//! impl PrintChunkSizesBody { -//! fn new(inner: Body) -> Self { +//! impl BodyWrapper { +//! fn new(inner: Full) -> Self { //! Self { inner } //! } //! } //! -//! impl http_body::Body for PrintChunkSizesBody { -//! type Data = Bytes; -//! type Error = hyper::Error; -//! -//! fn poll_data( -//! mut self: Pin<&mut Self>, -//! cx: &mut Context<'_>, -//! ) -> Poll>> { -//! if let Some(chunk) = ready!(Pin::new(&mut self.inner).poll_data(cx)?) { -//! println!("chunk size = {}", chunk.len()); -//! Poll::Ready(Some(Ok(chunk))) -//! } else { -//! Poll::Ready(None) -//! } -//! } -//! -//! fn poll_trailers( -//! mut self: Pin<&mut Self>, -//! cx: &mut Context<'_>, -//! ) -> Poll, Self::Error>> { -//! Pin::new(&mut self.inner).poll_trailers(cx) -//! } -//! -//! fn is_end_stream(&self) -> bool { -//! self.inner.is_end_stream() -//! } -//! -//! fn size_hint(&self) -> http_body::SizeHint { -//! self.inner.size_hint() -//! } +//! impl http_body::Body for BodyWrapper { +//! // ... +//! # type Data = Bytes; +//! # type Error = tower::BoxError; +//! # fn poll_frame( +//! # self: Pin<&mut Self>, +//! # cx: &mut Context<'_> +//! # ) -> Poll, Self::Error>>> { unimplemented!() } +//! # fn is_end_stream(&self) -> bool { unimplemented!() } +//! # fn size_hint(&self) -> http_body::SizeHint { unimplemented!() } //! } //! -//! async fn handle(_: Request) -> Result, Infallible> { +//! async fn handle(_: Request) -> Result>, Infallible> { //! // ... -//! # Ok(Response::new(Body::empty())) +//! # Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { //! let mut svc = ServiceBuilder::new() -//! // Wrap response bodies in `PrintChunkSizesBody` -//! .layer(MapResponseBodyLayer::new(PrintChunkSizesBody::new)) +//! // Wrap response bodies in `BodyWrapper` +//! .layer(MapResponseBodyLayer::new(BodyWrapper::new)) //! .service_fn(handle); //! //! // Call the service -//! let request = Request::new(Body::from("foobar")); +//! let request = Request::new(Full::::from("foobar")); //! //! svc.ready().await?.call(request).await?; //! # Ok(()) From 615ef819faa16dab98ab3d6f624b4a1587d81245 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:31:40 +0100 Subject: [PATCH 22/56] map_request_body --- tower-http/src/map_request_body.rs | 62 ++++++++++-------------------- 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/tower-http/src/map_request_body.rs b/tower-http/src/map_request_body.rs index 44d5caf9..f3949d64 100644 --- a/tower-http/src/map_request_body.rs +++ b/tower-http/src/map_request_body.rs @@ -3,72 +3,52 @@ //! # Example //! //! ``` +//! use http_body_util::Full; //! use bytes::Bytes; //! use http::{Request, Response}; -//! use hyper::Body; //! use std::convert::Infallible; //! use std::{pin::Pin, task::{Context, Poll}}; //! use tower::{ServiceBuilder, service_fn, ServiceExt, Service}; //! use tower_http::map_request_body::MapRequestBodyLayer; //! use futures::ready; //! -//! // A wrapper for a `hyper::Body` that prints the size of data chunks -//! struct PrintChunkSizesBody { -//! inner: Body, +//! // A wrapper for a `Full` +//! struct BodyWrapper { +//! inner: Full, //! } //! -//! impl PrintChunkSizesBody { -//! fn new(inner: Body) -> Self { +//! impl BodyWrapper { +//! fn new(inner: Full) -> Self { //! Self { inner } //! } //! } //! -//! impl http_body::Body for PrintChunkSizesBody { -//! type Data = Bytes; -//! type Error = hyper::Error; -//! -//! fn poll_data( -//! mut self: Pin<&mut Self>, -//! cx: &mut Context<'_>, -//! ) -> Poll>> { -//! if let Some(chunk) = ready!(Pin::new(&mut self.inner).poll_data(cx)?) { -//! println!("chunk size = {}", chunk.len()); -//! Poll::Ready(Some(Ok(chunk))) -//! } else { -//! Poll::Ready(None) -//! } -//! } -//! -//! fn poll_trailers( -//! mut self: Pin<&mut Self>, -//! cx: &mut Context<'_>, -//! ) -> Poll, Self::Error>> { -//! Pin::new(&mut self.inner).poll_trailers(cx) -//! } -//! -//! fn is_end_stream(&self) -> bool { -//! self.inner.is_end_stream() -//! } -//! -//! fn size_hint(&self) -> http_body::SizeHint { -//! self.inner.size_hint() -//! } +//! impl http_body::Body for BodyWrapper { +//! // ... +//! # type Data = Bytes; +//! # type Error = tower::BoxError; +//! # fn poll_frame( +//! # self: Pin<&mut Self>, +//! # cx: &mut Context<'_> +//! # ) -> Poll, Self::Error>>> { unimplemented!() } +//! # fn is_end_stream(&self) -> bool { unimplemented!() } +//! # fn size_hint(&self) -> http_body::SizeHint { unimplemented!() } //! } //! -//! async fn handle(_: Request) -> Result, Infallible> { +//! async fn handle(_: Request) -> Result>, Infallible> { //! // ... -//! # Ok(Response::new(Body::empty())) +//! # Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { //! let mut svc = ServiceBuilder::new() -//! // Wrap response bodies in `PrintChunkSizesBody` -//! .layer(MapRequestBodyLayer::new(PrintChunkSizesBody::new)) +//! // Wrap response bodies in `BodyWrapper` +//! .layer(MapRequestBodyLayer::new(BodyWrapper::new)) //! .service_fn(handle); //! //! // Call the service -//! let request = Request::new(Body::empty()); +//! let request = Request::new(Full::default()); //! //! svc.ready().await?.call(request).await?; //! # Ok(()) From 8f14848c9b38313cf756496b5bb5e06ec7b93040 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:47:24 +0100 Subject: [PATCH 23/56] limit --- tower-http/src/body.rs | 2 ++ tower-http/src/limit/mod.rs | 36 +++++++++++++++++++----------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/tower-http/src/body.rs b/tower-http/src/body.rs index d56a78c6..b40992a9 100644 --- a/tower-http/src/body.rs +++ b/tower-http/src/body.rs @@ -2,6 +2,8 @@ //! //! All these are wrappers around other body types. You shouldn't have to use them in your code. //! Use `http-body-util` instead. +//! +//! They exist because we cannot expose types from `http-body-util` in `tower-http`s public API. #![allow(missing_docs)] diff --git a/tower-http/src/limit/mod.rs b/tower-http/src/limit/mod.rs index a71ddbe7..9f611a83 100644 --- a/tower-http/src/limit/mod.rs +++ b/tower-http/src/limit/mod.rs @@ -24,15 +24,15 @@ //! use bytes::Bytes; //! use std::convert::Infallible; //! use http::{Request, Response, StatusCode, HeaderValue, header::CONTENT_LENGTH}; -//! use http_body::{Limited, LengthLimitError}; +//! use http_body_util::{LengthLimitError}; //! use tower::{Service, ServiceExt, ServiceBuilder}; -//! use tower_http::limit::RequestBodyLimitLayer; -//! use hyper::Body; +//! use tower_http::{body::Limited, limit::RequestBodyLimitLayer}; +//! use http_body_util::Full; //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! async fn handle(req: Request>) -> Result, Infallible> { -//! panic!("This will not be hit") +//! async fn handle(req: Request>>) -> Result>, Infallible> { +//! panic!("This should not be hit") //! } //! //! let mut svc = ServiceBuilder::new() @@ -43,10 +43,11 @@ //! // Call the service with a header that indicates the body is too large. //! let mut request = Request::builder() //! .header(CONTENT_LENGTH, HeaderValue::from_static("5000")) -//! .body(Body::empty()) +//! .body(Full::::default()) //! .unwrap(); //! -//! let response = svc.ready().await?.call(request).await?; +//! // let response = svc.ready().await?.call(request).await?; +//! let response = svc.call(request).await?; //! //! assert_eq!(response.status(), StatusCode::PAYLOAD_TOO_LARGE); //! # @@ -71,19 +72,20 @@ //! # use bytes::Bytes; //! # use std::convert::Infallible; //! # use http::{Request, Response, StatusCode}; -//! # use http_body::{Limited, LengthLimitError}; +//! # use http_body_util::LengthLimitError; //! # use tower::{Service, ServiceExt, ServiceBuilder, BoxError}; -//! # use tower_http::limit::RequestBodyLimitLayer; -//! # use hyper::Body; +//! # use tower_http::{body::Limited, limit::RequestBodyLimitLayer}; +//! # use http_body_util::Full; +//! # use http_body_util::BodyExt; //! # //! # #[tokio::main] //! # async fn main() -> Result<(), BoxError> { -//! async fn handle(req: Request>) -> Result, BoxError> { -//! let data = match hyper::body::to_bytes(req.into_body()).await { -//! Ok(data) => data, +//! async fn handle(req: Request>>) -> Result>, BoxError> { +//! let data = match req.into_body().collect().await { +//! Ok(collected) => collected.to_bytes(), //! Err(err) => { //! if let Some(_) = err.downcast_ref::() { -//! let mut resp = Response::new(Body::empty()); +//! let mut resp = Response::new(Full::default()); //! *resp.status_mut() = StatusCode::PAYLOAD_TOO_LARGE; //! return Ok(resp); //! } else { @@ -92,7 +94,7 @@ //! } //! }; //! -//! Ok(Response::new(Body::empty())) +//! Ok(Response::new(Full::default())) //! } //! //! let mut svc = ServiceBuilder::new() @@ -101,14 +103,14 @@ //! .service_fn(handle); //! //! // Call the service. -//! let request = Request::new(Body::empty()); +//! let request = Request::new(Full::::default()); //! //! let response = svc.ready().await?.call(request).await?; //! //! assert_eq!(response.status(), StatusCode::OK); //! //! // Call the service with a body that is too large. -//! let request = Request::new(Body::from(Bytes::from(vec![0u8; 4097]))); +//! let request = Request::new(Full::::from(Bytes::from(vec![0u8; 4097]))); //! //! let response = svc.ready().await?.call(request).await?; //! From ec8fca4c3212706a9f1c8f2213c3a82d9d4e972e Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 10:53:23 +0100 Subject: [PATCH 24/56] lib --- tower-http/src/lib.rs | 71 +++++-------------------------------------- 1 file changed, 8 insertions(+), 63 deletions(-) diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index 3ca06448..e4aefee8 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -17,17 +17,18 @@ //! ```rust,no_run //! use tower_http::{ //! add_extension::AddExtensionLayer, -//! compression::CompressionLayer, +//! // compression::CompressionLayer, //! propagate_header::PropagateHeaderLayer, //! sensitive_headers::SetSensitiveRequestHeadersLayer, //! set_header::SetResponseHeaderLayer, //! trace::TraceLayer, //! validate_request::ValidateRequestHeaderLayer, //! }; -//! use tower::{ServiceBuilder, service_fn, make::Shared}; +//! use tower::{ServiceBuilder, service_fn, BoxError}; //! use http::{Request, Response, header::{HeaderName, CONTENT_TYPE, AUTHORIZATION}}; -//! use hyper::{Body, Error, server::Server, service::make_service_fn}; //! use std::{sync::Arc, net::SocketAddr, convert::Infallible, iter::once}; +//! use bytes::Bytes; +//! use http_body_util::Full; //! # struct DatabaseConnectionPool; //! # impl DatabaseConnectionPool { //! # fn new() -> DatabaseConnectionPool { DatabaseConnectionPool } @@ -37,7 +38,7 @@ //! //! // Our request handler. This is where we would implement the application logic //! // for responding to HTTP requests... -//! async fn handler(request: Request) -> Result, Error> { +//! async fn handler(request: Request>) -> Result>, BoxError> { //! // ... //! # todo!() //! } @@ -64,7 +65,7 @@ //! // Share an `Arc` with all requests //! .layer(AddExtensionLayer::new(Arc::new(state))) //! // Compress responses -//! .layer(CompressionLayer::new()) +//! // .layer(CompressionLayer::new()) //! // Propagate `X-Request-Id`s from requests to responses //! .layer(PropagateHeaderLayer::new(HeaderName::from_static("x-request-id"))) //! // If the response has a known size set the `Content-Length` header @@ -75,70 +76,14 @@ //! .layer(ValidateRequestHeaderLayer::accept("application/json")) //! // Wrap a `Service` in our middleware stack //! .service_fn(handler); -//! -//! // And run our service using `hyper` -//! let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); -//! Server::bind(&addr) -//! .serve(Shared::new(service)) -//! .await -//! .expect("server error"); +//! # let mut service = service; +//! # tower::Service::call(&mut service, Request::new(Full::default())); //! } //! ``` //! //! Keep in mind that while this example uses [hyper], tower-http supports any HTTP //! client/server implementation that uses the [http] and [http-body] crates. //! -//! # Example client -//! -//! tower-http middleware can also be applied to HTTP clients: -//! -//! ```rust,no_run -//! use tower_http::{ -//! decompression::DecompressionLayer, -//! set_header::SetRequestHeaderLayer, -//! trace::TraceLayer, -//! classify::StatusInRangeAsFailures, -//! }; -//! use tower::{ServiceBuilder, Service, ServiceExt}; -//! use hyper::Body; -//! use http::{Request, HeaderValue, header::USER_AGENT}; -//! -//! #[tokio::main] -//! async fn main() { -//! let mut client = ServiceBuilder::new() -//! // Add tracing and consider server errors and client -//! // errors as failures. -//! .layer(TraceLayer::new( -//! StatusInRangeAsFailures::new(400..=599).into_make_classifier() -//! )) -//! // Set a `User-Agent` header on all requests. -//! .layer(SetRequestHeaderLayer::overriding( -//! USER_AGENT, -//! HeaderValue::from_static("tower-http demo") -//! )) -//! // Decompress response bodies -//! .layer(DecompressionLayer::new()) -//! // Wrap a `hyper::Client` in our middleware stack. -//! // This is possible because `hyper::Client` implements -//! // `tower::Service`. -//! .service(hyper::Client::new()); -//! -//! // Make a request -//! let request = Request::builder() -//! .uri("http://example.com") -//! .body(Body::empty()) -//! .unwrap(); -//! -//! let response = client -//! .ready() -//! .await -//! .unwrap() -//! .call(request) -//! .await -//! .unwrap(); -//! } -//! ``` -//! //! # Feature Flags //! //! All middleware are disabled by default and can be enabled using [cargo features]. From c3fab2a6a2ca23788187d71b0d640d612ce3808e Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:00:10 +0100 Subject: [PATCH 25/56] cors --- tower-http/src/cors/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tower-http/src/cors/mod.rs b/tower-http/src/cors/mod.rs index 9a952a2c..d9d63c13 100644 --- a/tower-http/src/cors/mod.rs +++ b/tower-http/src/cors/mod.rs @@ -4,13 +4,14 @@ //! //! ``` //! use http::{Request, Response, Method, header}; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! use tower::{ServiceBuilder, ServiceExt, Service}; //! use tower_http::cors::{Any, CorsLayer}; //! use std::convert::Infallible; //! -//! async fn handle(request: Request) -> Result, Infallible> { -//! Ok(Response::new(Body::empty())) +//! async fn handle(request: Request>) -> Result>, Infallible> { +//! Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] @@ -27,7 +28,7 @@ //! //! let request = Request::builder() //! .header(header::ORIGIN, "https://example.com") -//! .body(Body::empty()) +//! .body(Full::default()) //! .unwrap(); //! //! let response = service From bec9c5de6bc768246924d55e795be861cf5b329f Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:01:58 +0100 Subject: [PATCH 26/56] classify --- tower-http/src/classify/mod.rs | 2 +- .../src/classify/status_in_range_is_error.rs | 28 ------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/tower-http/src/classify/mod.rs b/tower-http/src/classify/mod.rs index 417430c1..db0fbbf2 100644 --- a/tower-http/src/classify/mod.rs +++ b/tower-http/src/classify/mod.rs @@ -192,7 +192,7 @@ pub trait ClassifyResponse { /// ClassifyResponse, ClassifiedResponse /// }; /// use http::{Response, StatusCode}; - /// use http_body::Empty; + /// use http_body_util::Empty; /// use bytes::Bytes; /// /// fn transform_failure_class(class: ServerErrorsFailureClass) -> NewFailureClass { diff --git a/tower-http/src/classify/status_in_range_is_error.rs b/tower-http/src/classify/status_in_range_is_error.rs index ce3202a2..75bde023 100644 --- a/tower-http/src/classify/status_in_range_is_error.rs +++ b/tower-http/src/classify/status_in_range_is_error.rs @@ -4,34 +4,6 @@ use std::{fmt, ops::RangeInclusive}; /// Response classifier that considers responses with a status code within some range to be /// failures. -/// -/// # Example -/// -/// A client with tracing where server errors _and_ client errors are considered failures. -/// -/// ```no_run -/// use tower_http::{trace::TraceLayer, classify::StatusInRangeAsFailures}; -/// use tower::{ServiceBuilder, Service, ServiceExt}; -/// use hyper::{Client, Body}; -/// use http::{Request, Method}; -/// -/// # async fn foo() -> Result<(), tower::BoxError> { -/// let classifier = StatusInRangeAsFailures::new(400..=599); -/// -/// let mut client = ServiceBuilder::new() -/// .layer(TraceLayer::new(classifier.into_make_classifier())) -/// .service(Client::new()); -/// -/// let request = Request::builder() -/// .method(Method::GET) -/// .uri("https://example.com") -/// .body(Body::empty()) -/// .unwrap(); -/// -/// let response = client.ready().await?.call(request).await?; -/// # Ok(()) -/// # } -/// ``` #[derive(Debug, Clone)] pub struct StatusInRangeAsFailures { range: RangeInclusive, From 197e5b5abf64018d57d4911f8e35f0ff96c29317 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:02:52 +0100 Subject: [PATCH 27/56] add_extension --- tower-http/src/add_extension.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tower-http/src/add_extension.rs b/tower-http/src/add_extension.rs index eb028d99..095646df 100644 --- a/tower-http/src/add_extension.rs +++ b/tower-http/src/add_extension.rs @@ -8,7 +8,8 @@ //! use tower_http::add_extension::AddExtensionLayer; //! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; //! use http::{Request, Response}; -//! use hyper::Body; +//! use bytes::Bytes; +//! use http_body_util::Full; //! use std::{sync::Arc, convert::Infallible}; //! //! # struct DatabaseConnectionPool; @@ -21,11 +22,11 @@ //! pool: DatabaseConnectionPool, //! } //! -//! async fn handle(req: Request) -> Result, Infallible> { +//! async fn handle(req: Request>) -> Result>, Infallible> { //! // Grab the state from the request extensions. //! let state = req.extensions().get::>().unwrap(); //! -//! Ok(Response::new(Body::empty())) +//! Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] @@ -44,7 +45,7 @@ //! let response = service //! .ready() //! .await? -//! .call(Request::new(Body::empty())) +//! .call(Request::new(Full::default())) //! .await?; //! # Ok(()) //! # } From 27eda19611b69bf32b15e2af60dfb269943f9bc6 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:04:01 +0100 Subject: [PATCH 28/56] catch_panic --- tower-http/src/catch_panic.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tower-http/src/catch_panic.rs b/tower-http/src/catch_panic.rs index 7eac4f1e..a9957dd8 100644 --- a/tower-http/src/catch_panic.rs +++ b/tower-http/src/catch_panic.rs @@ -10,11 +10,12 @@ //! use std::convert::Infallible; //! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; //! use tower_http::catch_panic::CatchPanicLayer; -//! use hyper::Body; +//! use http_body_util::Full; +//! use bytes::Bytes; //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! async fn handle(req: Request) -> Result, Infallible> { +//! async fn handle(req: Request>) -> Result>, Infallible> { //! panic!("something went wrong...") //! } //! @@ -24,7 +25,7 @@ //! .service_fn(handle); //! //! // Call the service. -//! let request = Request::new(Body::empty()); +//! let request = Request::new(Full::default()); //! //! let response = svc.ready().await?.call(request).await?; //! @@ -41,15 +42,16 @@ //! use std::{any::Any, convert::Infallible}; //! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; //! use tower_http::catch_panic::CatchPanicLayer; -//! use hyper::Body; +//! use bytes::Bytes; +//! use http_body_util::Full; //! //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! async fn handle(req: Request) -> Result, Infallible> { +//! async fn handle(req: Request>) -> Result>, Infallible> { //! panic!("something went wrong...") //! } //! -//! fn handle_panic(err: Box) -> Response { +//! fn handle_panic(err: Box) -> Response> { //! let details = if let Some(s) = err.downcast_ref::() { //! s.clone() //! } else if let Some(s) = err.downcast_ref::<&str>() { @@ -69,7 +71,7 @@ //! Response::builder() //! .status(StatusCode::INTERNAL_SERVER_ERROR) //! .header(header::CONTENT_TYPE, "application/json") -//! .body(Body::from(body)) +//! .body(Full::from(body)) //! .unwrap() //! } //! From 0fe1db50d8a6cbb866444f385e04fd7b74282ab4 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:05:29 +0100 Subject: [PATCH 29/56] add_authorization --- tower-http/src/auth/add_authorization.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tower-http/src/auth/add_authorization.rs b/tower-http/src/auth/add_authorization.rs index f83f313a..f3fb9a84 100644 --- a/tower-http/src/auth/add_authorization.rs +++ b/tower-http/src/auth/add_authorization.rs @@ -7,15 +7,16 @@ //! ``` //! use tower_http::validate_request::{ValidateRequestHeader, ValidateRequestHeaderLayer}; //! use tower_http::auth::AddAuthorizationLayer; -//! use hyper::{Request, Response, Body, Error}; -//! use http::{StatusCode, header::AUTHORIZATION}; -//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; -//! # async fn handle(request: Request) -> Result, Error> { -//! # Ok(Response::new(Body::empty())) +//! use http::{Request, Response, StatusCode, header::AUTHORIZATION}; +//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError}; +//! use http_body_util::Full; +//! use bytes::Bytes; +//! # async fn handle(request: Request>) -> Result>, BoxError> { +//! # Ok(Response::new(Full::default())) //! # } //! //! # #[tokio::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! # let service_that_requires_auth = ValidateRequestHeader::basic( //! # tower::service_fn(handle), //! # "username", @@ -30,7 +31,7 @@ //! let response = client //! .ready() //! .await? -//! .call(Request::new(Body::empty())) +//! .call(Request::new(Full::default())) //! .await?; //! //! assert_eq!(StatusCode::OK, response.status()); From a556ad5d109183f0a305a2c2b074a37694746f46 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:09:55 +0100 Subject: [PATCH 30/56] require_authorization + async_require_authorization --- .../src/auth/async_require_authorization.rs | 32 ++++++++++--------- tower-http/src/auth/require_authorization.rs | 17 +++++----- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/tower-http/src/auth/async_require_authorization.rs b/tower-http/src/auth/async_require_authorization.rs index 735aaa1c..f4639d92 100644 --- a/tower-http/src/auth/async_require_authorization.rs +++ b/tower-http/src/auth/async_require_authorization.rs @@ -6,10 +6,11 @@ //! //! ``` //! use tower_http::auth::{AsyncRequireAuthorizationLayer, AsyncAuthorizeRequest}; -//! use hyper::{Request, Response, Body, Error}; -//! use http::{StatusCode, header::AUTHORIZATION}; -//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; +//! use http::{Request, Response, StatusCode, header::AUTHORIZATION}; +//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError}; //! use futures_util::future::BoxFuture; +//! use bytes::Bytes; +//! use http_body_util::Full; //! //! #[derive(Clone, Copy)] //! struct MyAuth; @@ -19,7 +20,7 @@ //! B: Send + Sync + 'static, //! { //! type RequestBody = B; -//! type ResponseBody = Body; +//! type ResponseBody = Full; //! type Future = BoxFuture<'static, Result, Response>>; //! //! fn authorize(&mut self, mut request: Request) -> Self::Future { @@ -33,7 +34,7 @@ //! } else { //! let unauthorized_response = Response::builder() //! .status(StatusCode::UNAUTHORIZED) -//! .body(Body::empty()) +//! .body(Full::::default()) //! .unwrap(); //! //! Err(unauthorized_response) @@ -50,7 +51,7 @@ //! #[derive(Debug)] //! struct UserId(String); //! -//! async fn handle(request: Request) -> Result, Error> { +//! async fn handle(request: Request>) -> Result>, BoxError> { //! // Access the `UserId` that was set in `on_authorized`. If `handle` gets called the //! // request was authorized and `UserId` will be present. //! let user_id = request @@ -60,11 +61,11 @@ //! //! println!("request from {:?}", user_id); //! -//! Ok(Response::new(Body::empty())) +//! Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! let service = ServiceBuilder::new() //! // Authorize requests using `MyAuth` //! .layer(AsyncRequireAuthorizationLayer::new(MyAuth)) @@ -77,10 +78,11 @@ //! //! ``` //! use tower_http::auth::{AsyncRequireAuthorizationLayer, AsyncAuthorizeRequest}; -//! use hyper::{Request, Response, Body, Error}; -//! use http::StatusCode; -//! use tower::{Service, ServiceExt, ServiceBuilder}; +//! use http::{Request, Response, StatusCode}; +//! use tower::{Service, ServiceExt, ServiceBuilder, BoxError}; //! use futures_util::future::BoxFuture; +//! use http_body_util::Full; +//! use bytes::Bytes; //! //! async fn check_auth(request: &Request) -> Option { //! // ... @@ -90,21 +92,21 @@ //! #[derive(Debug)] //! struct UserId(String); //! -//! async fn handle(request: Request) -> Result, Error> { +//! async fn handle(request: Request>) -> Result>, BoxError> { //! # todo!(); //! // ... //! } //! //! # #[tokio::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! let service = ServiceBuilder::new() -//! .layer(AsyncRequireAuthorizationLayer::new(|request: Request| async move { +//! .layer(AsyncRequireAuthorizationLayer::new(|request: Request>| async move { //! if let Some(user_id) = check_auth(&request).await { //! Ok(request) //! } else { //! let unauthorized_response = Response::builder() //! .status(StatusCode::UNAUTHORIZED) -//! .body(Body::empty()) +//! .body(Full::::default()) //! .unwrap(); //! //! Err(unauthorized_response) diff --git a/tower-http/src/auth/require_authorization.rs b/tower-http/src/auth/require_authorization.rs index a1f241f1..febb3b87 100644 --- a/tower-http/src/auth/require_authorization.rs +++ b/tower-http/src/auth/require_authorization.rs @@ -6,16 +6,17 @@ //! //! ``` //! use tower_http::validate_request::{ValidateRequest, ValidateRequestHeader, ValidateRequestHeaderLayer}; -//! use hyper::{Request, Response, Body, Error}; -//! use http::{StatusCode, header::AUTHORIZATION}; -//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; +//! use http::{Request, Response, StatusCode, header::AUTHORIZATION}; +//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError}; +//! use bytes::Bytes; +//! use http_body_util::Full; //! -//! async fn handle(request: Request) -> Result, Error> { -//! Ok(Response::new(Body::empty())) +//! async fn handle(request: Request>) -> Result>, BoxError> { +//! Ok(Response::new(Full::default())) //! } //! //! # #[tokio::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! let mut service = ServiceBuilder::new() //! // Require the `Authorization` header to be `Bearer passwordlol` //! .layer(ValidateRequestHeaderLayer::bearer("passwordlol")) @@ -24,7 +25,7 @@ //! // Requests with the correct token are allowed through //! let request = Request::builder() //! .header(AUTHORIZATION, "Bearer passwordlol") -//! .body(Body::empty()) +//! .body(Full::default()) //! .unwrap(); //! //! let response = service @@ -37,7 +38,7 @@ //! //! // Requests with an invalid token get a `401 Unauthorized` response //! let request = Request::builder() -//! .body(Body::empty()) +//! .body(Full::default()) //! .unwrap(); //! //! let response = service From 618df5a22824d8b9b36ce56b609bed246f2b7386 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:11:43 +0100 Subject: [PATCH 31/56] builder --- tower-http/src/builder.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tower-http/src/builder.rs b/tower-http/src/builder.rs index 0bb97e79..73d964b3 100644 --- a/tower-http/src/builder.rs +++ b/tower-http/src/builder.rs @@ -17,13 +17,14 @@ use tower_layer::Stack; /// /// ```rust /// use http::{Request, Response, header::HeaderName}; -/// use hyper::Body; +/// use bytes::Bytes; +/// use http_body_util::Full; /// use std::{time::Duration, convert::Infallible}; /// use tower::{ServiceBuilder, ServiceExt, Service}; /// use tower_http::ServiceBuilderExt; /// -/// async fn handle(request: Request) -> Result, Infallible> { -/// Ok(Response::new(Body::empty())) +/// async fn handle(request: Request>) -> Result>, Infallible> { +/// Ok(Response::new(Full::default())) /// } /// /// # #[tokio::main] @@ -33,11 +34,10 @@ use tower_layer::Stack; /// .timeout(Duration::from_secs(30)) /// // Methods from tower-http /// .trace_for_http() -/// .compression() /// .propagate_header(HeaderName::from_static("x-request-id")) /// .service_fn(handle); /// # let mut service = service; -/// # service.ready().await.unwrap().call(Request::new(Body::empty())).await.unwrap(); +/// # service.ready().await.unwrap().call(Request::new(Full::default())).await.unwrap(); /// # } /// ``` #[cfg(feature = "util")] From 19b765632bb22954411e9ab6a21fbc9f94792ede Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:12:02 +0100 Subject: [PATCH 32/56] format --- tower-http/src/content_encoding.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tower-http/src/content_encoding.rs b/tower-http/src/content_encoding.rs index a52a2ab0..9273a0f3 100644 --- a/tower-http/src/content_encoding.rs +++ b/tower-http/src/content_encoding.rs @@ -271,8 +271,7 @@ mod tests { #[test] fn no_accept_encoding_header() { - let encoding = - Encoding::from_headers(&http::HeaderMap::new(), SupportedEncodingsAll); + let encoding = Encoding::from_headers(&http::HeaderMap::new(), SupportedEncodingsAll); assert_eq!(Encoding::Identity, encoding); } From 83d4681197aa55772b6c034f62e962ffe6559e31 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:28:15 +0100 Subject: [PATCH 33/56] fixes --- tower-http/src/cors/allow_credentials.rs | 2 +- tower-http/src/cors/allow_private_network.rs | 13 +++++++++++-- tower-http/src/validate_request.rs | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tower-http/src/cors/allow_credentials.rs b/tower-http/src/cors/allow_credentials.rs index e489c570..de53ffed 100644 --- a/tower-http/src/cors/allow_credentials.rs +++ b/tower-http/src/cors/allow_credentials.rs @@ -57,7 +57,7 @@ impl AllowCredentials { AllowCredentialsInner::Predicate(c) => c(origin?, parts), }; - allow_creds.then(|| (header::ACCESS_CONTROL_ALLOW_CREDENTIALS, TRUE)) + allow_creds.then_some((header::ACCESS_CONTROL_ALLOW_CREDENTIALS, TRUE)) } } diff --git a/tower-http/src/cors/allow_private_network.rs b/tower-http/src/cors/allow_private_network.rs index 4163014d..9f97dc11 100644 --- a/tower-http/src/cors/allow_private_network.rs +++ b/tower-http/src/cors/allow_private_network.rs @@ -39,6 +39,10 @@ impl AllowPrivateNetwork { Self(AllowPrivateNetworkInner::Predicate(Arc::new(f))) } + #[allow( + clippy::declare_interior_mutable_const, + clippy::borrow_interior_mutable_const + )] pub(super) fn to_header( &self, origin: Option<&HeaderValue>, @@ -71,7 +75,7 @@ impl AllowPrivateNetwork { AllowPrivateNetworkInner::Predicate(c) => c(origin?, parts), }; - allow_private_network.then(|| (ALLOW_PRIVATE_NETWORK, TRUE)) + allow_private_network.then_some((ALLOW_PRIVATE_NETWORK, TRUE)) } } @@ -111,11 +115,16 @@ impl Default for AllowPrivateNetworkInner { #[cfg(test)] mod tests { + #![allow( + clippy::declare_interior_mutable_const, + clippy::borrow_interior_mutable_const + )] + use super::AllowPrivateNetwork; use crate::cors::CorsLayer; + use crate::test_helpers::Body; use http::{header::ORIGIN, request::Parts, HeaderName, HeaderValue, Request, Response}; - use hyper::Body; use tower::{BoxError, ServiceBuilder, ServiceExt}; use tower_service::Service; diff --git a/tower-http/src/validate_request.rs b/tower-http/src/validate_request.rs index 7e599dc6..327266af 100644 --- a/tower-http/src/validate_request.rs +++ b/tower-http/src/validate_request.rs @@ -385,7 +385,7 @@ where .into_iter() .filter_map(|header| header.to_str().ok()) .any(|h| { - MimeIter::new(&h) + MimeIter::new(h) .map(|mim| { if let Ok(mim) = mim { let typ = self.header_value.type_(); From fcdabf8d4ffbdabea541a3338cde22464fa2ab4f Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:35:50 +0100 Subject: [PATCH 34/56] fix docs --- tower-http/src/auth/add_authorization.rs | 4 ++-- tower-http/src/auth/require_authorization.rs | 4 ++-- tower-http/src/classify/grpc_errors_as_failures.rs | 4 ++-- tower-http/src/limit/mod.rs | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tower-http/src/auth/add_authorization.rs b/tower-http/src/auth/add_authorization.rs index c626ca8b..246c13b6 100644 --- a/tower-http/src/auth/add_authorization.rs +++ b/tower-http/src/auth/add_authorization.rs @@ -85,7 +85,7 @@ impl AddAuthorizationLayer { /// /// # Panics /// - /// Panics if the token is not a valid [`HeaderValue`](http::header::HeaderValue). + /// Panics if the token is not a valid [`HeaderValue`]. pub fn bearer(token: &str) -> Self { let value = HeaderValue::try_from(format!("Bearer {}", token)).expect("token is not valid header"); @@ -148,7 +148,7 @@ impl AddAuthorization { /// /// # Panics /// - /// Panics if the token is not a valid [`HeaderValue`](http::header::HeaderValue). + /// Panics if the token is not a valid [`HeaderValue`]. pub fn bearer(inner: S, token: &str) -> Self { AddAuthorizationLayer::bearer(token).layer(inner) } diff --git a/tower-http/src/auth/require_authorization.rs b/tower-http/src/auth/require_authorization.rs index ba1ceba5..d5c9508f 100644 --- a/tower-http/src/auth/require_authorization.rs +++ b/tower-http/src/auth/require_authorization.rs @@ -104,7 +104,7 @@ impl ValidateRequestHeader> { /// /// # Panics /// - /// Panics if the token is not a valid [`HeaderValue`](http::header::HeaderValue). + /// Panics if the token is not a valid [`HeaderValue`]. pub fn bearer(inner: S, token: &str) -> Self where ResBody: Body + Default, @@ -120,7 +120,7 @@ impl ValidateRequestHeaderLayer> { /// /// # Panics /// - /// Panics if the token is not a valid [`HeaderValue`](http::header::HeaderValue). + /// Panics if the token is not a valid [`HeaderValue`]. pub fn bearer(token: &str) -> Self where ResBody: Body + Default, diff --git a/tower-http/src/classify/grpc_errors_as_failures.rs b/tower-http/src/classify/grpc_errors_as_failures.rs index 103c0459..b88606b5 100644 --- a/tower-http/src/classify/grpc_errors_as_failures.rs +++ b/tower-http/src/classify/grpc_errors_as_failures.rs @@ -3,7 +3,7 @@ use bitflags::bitflags; use http::{HeaderMap, Response}; use std::{fmt, num::NonZeroI32}; -/// gRPC status codes. Used in [`GrpcErrorsAsFailures::success_codes`]. +/// gRPC status codes. /// /// These variants match the [gRPC status codes]. /// @@ -125,7 +125,7 @@ impl GrpcCodeBitmask { /// /// Responses are considered successful if /// -/// - `grpc-status` header value matches [`GrpcErrorsAsFailures::success_codes`] (only `Ok` by +/// - `grpc-status` header value contains a success value. /// default). /// - `grpc-status` header is missing. /// - `grpc-status` header value isn't a valid `String`. diff --git a/tower-http/src/limit/mod.rs b/tower-http/src/limit/mod.rs index 9f611a83..3f2fede3 100644 --- a/tower-http/src/limit/mod.rs +++ b/tower-http/src/limit/mod.rs @@ -59,8 +59,8 @@ //! //! If a `Content-Length` header is not present, then the body will be read //! until the configured limit has been reached. If the payload is larger than -//! the limit, the [`http_body::Limited`] body will return an error. This -//! error can be inspected to determine if it is a [`http_body::LengthLimitError`] +//! the limit, the [`http_body_util::Limited`] body will return an error. This +//! error can be inspected to determine if it is a [`http_body_util::LengthLimitError`] //! and return an appropriate response in such case. //! //! Note that no error will be generated if the body is never read. Similarly, @@ -125,7 +125,7 @@ //! If enforcement of body size limits is desired without preemptively //! handling requests with a `Content-Length` header indicating an over-sized //! request, consider using [`MapRequestBody`] to wrap the request body with -//! [`http_body::Limited`] and checking for [`http_body::LengthLimitError`] +//! [`http_body_util::Limited`] and checking for [`http_body_util::LengthLimitError`] //! like in the previous example. //! //! [`MapRequestBody`]: crate::map_request_body From b7b33bbc401753222d57f0ef5ec0fc08b97ea991 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 11:36:43 +0100 Subject: [PATCH 35/56] fix cargo hack --- tower-http/src/body.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tower-http/src/body.rs b/tower-http/src/body.rs index b40992a9..21a6119a 100644 --- a/tower-http/src/body.rs +++ b/tower-http/src/body.rs @@ -46,6 +46,7 @@ pin_project! { } impl Full { + #[allow(dead_code)] pub(crate) fn new(inner: http_body_util::Full) -> Self { Self { inner } } @@ -66,6 +67,7 @@ pin_project! { } impl Limited { + #[allow(dead_code)] pub(crate) fn new(inner: http_body_util::Limited) -> Self { Self { inner } } @@ -101,6 +103,7 @@ where } impl UnsyncBoxBody { + #[allow(dead_code)] pub(crate) fn new(inner: http_body_util::combinators::UnsyncBoxBody) -> Self { Self { inner } } From 58070136720b6a2be7ff7348a9ffebf4bb9d6db6 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 16:12:04 +0100 Subject: [PATCH 36/56] is this breaking patches in Cargo.toml? --- tower-http/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index fd844ec6..856328d8 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tower-http" description = "Tower middleware and utilities for HTTP clients and servers" -version = "0.4.2" +version = "0.4.4" authors = ["Tower Maintainers "] edition = "2018" license = "MIT" From 602f06f248211b6eb5af59e1c7ae10c9177e25da Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 15 Nov 2023 19:46:38 +0100 Subject: [PATCH 37/56] update to 1.0 of dependencies --- Cargo.toml | 4 ---- tower-http/Cargo.toml | 6 +++--- tower-http/src/auth/async_require_authorization.rs | 2 +- tower-http/src/follow_redirect/mod.rs | 1 + 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b116a373..fdbc1794 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,3 @@ members = [ "tower-http", "examples/*", ] - -[patch.crates-io] -# for `Frame::map_data` -http-body = { git = "https://github.com/hyperium/http-body", rev = "7bf321acbb422214c89933c103417bcfe3892aed" } diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index 856328d8..62ae7e2e 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -17,9 +17,9 @@ bitflags = "2.0.2" bytes = "1" futures-core = "0.3" futures-util = { version = "0.3.14", default_features = false, features = [] } -http = "0.2.9" -http-body = "1.0.0-rc.2" -http-body-util = "0.1.0-rc.3" +http = "1.0" +http-body = "1.0.0" +http-body-util = "0.1.0" pin-project-lite = "0.2.7" tower-layer = "0.3" tower-service = "0.3" diff --git a/tower-http/src/auth/async_require_authorization.rs b/tower-http/src/auth/async_require_authorization.rs index f4639d92..d66d1ee9 100644 --- a/tower-http/src/auth/async_require_authorization.rs +++ b/tower-http/src/auth/async_require_authorization.rs @@ -349,7 +349,7 @@ mod tests { } } - #[derive(Debug)] + #[derive(Clone, Debug)] struct UserId(String); #[tokio::test] diff --git a/tower-http/src/follow_redirect/mod.rs b/tower-http/src/follow_redirect/mod.rs index 6a4bebdb..454540a9 100644 --- a/tower-http/src/follow_redirect/mod.rs +++ b/tower-http/src/follow_redirect/mod.rs @@ -326,6 +326,7 @@ where /// /// The value differs from the original request's effective URI if the middleware has followed /// redirections. +#[derive(Clone)] pub struct RequestUri(pub Uri); #[derive(Debug)] From a93aab088d28144886edcc8d3204cf7e7fdec08c Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 11:10:19 +0100 Subject: [PATCH 38/56] porting compression like this seems to work \o/ --- tower-http/Cargo.toml | 12 +- tower-http/src/compression/mod.rs | 25 +++-- tower-http/src/compression_utils.rs | 167 ++++++++++++++++++---------- tower-http/src/lib.rs | 58 +++++----- tower-http/src/test_helpers.rs | 43 +++++++ 5 files changed, 199 insertions(+), 106 deletions(-) diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index 62ae7e2e..8a8b0553 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -60,7 +60,7 @@ full = [ "add-extension", "auth", "catch-panic", - # "compression-full", + "compression-full", "cors", # "decompression-full", "follow-redirect", @@ -104,11 +104,11 @@ trace = ["tracing"] util = ["tower"] validate-request = ["mime"] -# compression-br = ["async-compression/brotli", "tokio-util", "tokio"] -# compression-deflate = ["async-compression/zlib", "tokio-util", "tokio"] -# compression-full = ["compression-br", "compression-deflate", "compression-gzip", "compression-zstd"] -# compression-gzip = ["async-compression/gzip", "tokio-util", "tokio"] -# compression-zstd = ["async-compression/zstd", "tokio-util", "tokio"] +compression-br = ["async-compression/brotli", "tokio-util", "tokio"] +compression-deflate = ["async-compression/zlib", "tokio-util", "tokio"] +compression-full = ["compression-br", "compression-deflate", "compression-gzip", "compression-zstd"] +compression-gzip = ["async-compression/gzip", "tokio-util", "tokio"] +compression-zstd = ["async-compression/zstd", "tokio-util", "tokio"] # decompression-br = ["async-compression/brotli", "tokio-util", "tokio"] # decompression-deflate = ["async-compression/zlib", "tokio-util", "tokio"] diff --git a/tower-http/src/compression/mod.rs b/tower-http/src/compression/mod.rs index 756bd71d..ef7f76ac 100644 --- a/tower-http/src/compression/mod.rs +++ b/tower-http/src/compression/mod.rs @@ -87,12 +87,13 @@ mod tests { use crate::compression::predicate::SizeAbove; use super::*; - use crate::test_helpers::{Body, TowerHttpBodyExt}; + use crate::test_helpers::{Body, TowerHttpBodyExt, WithTrailers}; use async_compression::tokio::write::{BrotliDecoder, BrotliEncoder}; use bytes::BytesMut; use flate2::read::GzDecoder; use http::header::{ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_TYPE}; - use http::{Request, Response}; + use http::{HeaderMap, HeaderName, Request, Response}; + use http_body_util::BodyExt; use std::convert::Infallible; use std::io::Read; use std::sync::{Arc, RwLock}; @@ -126,13 +127,9 @@ mod tests { let res = svc.ready().await.unwrap().call(req).await.unwrap(); // read the compressed body - let mut body = res.into_body(); - let mut data = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk.unwrap(); - data.extend_from_slice(&chunk[..]); - } - let compressed_data = data.freeze().to_vec(); + let collected = res.into_body().collect().await.unwrap(); + let trailers = collected.trailers().cloned().unwrap(); + let compressed_data = collected.to_bytes(); // decompress the body // doing this with flate2 as that is much easier than async-compression and blocking during @@ -142,6 +139,9 @@ mod tests { decoder.read_to_string(&mut decompressed).unwrap(); assert_eq!(decompressed, "Hello, World!"); + + // trailers are maintained + assert_eq!(trailers["foo"], "bar"); } #[tokio::test] @@ -236,8 +236,11 @@ mod tests { assert_eq!(data, DATA.as_bytes()); } - async fn handle(_req: Request) -> Result, Infallible> { - Ok(Response::new(Body::from("Hello, World!"))) + async fn handle(_req: Request) -> Result>, Infallible> { + let mut trailers = HeaderMap::new(); + trailers.insert(HeaderName::from_static("foo"), "bar".parse().unwrap()); + let body = Body::from("Hello, World!").with_trailers(trailers); + Ok(Response::builder().body(body).unwrap()) } #[tokio::test] diff --git a/tower-http/src/compression_utils.rs b/tower-http/src/compression_utils.rs index f48b0741..9c69c2d2 100644 --- a/tower-http/src/compression_utils.rs +++ b/tower-http/src/compression_utils.rs @@ -1,13 +1,14 @@ //! Types used by compression and decompression middleware. use crate::{content_encoding::SupportedEncodings, BoxError}; -use bytes::Bytes; +use bytes::{Buf, Bytes, BytesMut}; use futures_core::Stream; use futures_util::ready; use http::HeaderValue; -use http_body::Body; +use http_body::{Body, Frame}; use pin_project_lite::pin_project; use std::{ + collections::VecDeque, io, pin::Pin, task::{Context, Poll}, @@ -152,6 +153,7 @@ pin_project! { pub(crate) struct WrapBody { #[pin] pub(crate) read: M::Output, + read_all_data: bool, } } @@ -175,7 +177,10 @@ impl WrapBody { // apply decorator to `AsyncRead` yielding another `AsyncRead` let read = M::apply(read, quality); - Self { read } + Self { + read, + read_all_data: false, + } } } @@ -192,71 +197,75 @@ where self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>>> { - // I'm not sure our previous body wrapping setup works. It assumes we can poll data and - // trailers separately, but we can't anymore - - todo!() - } - - // fn poll_data( - // self: Pin<&mut Self>, - // cx: &mut Context<'_>, - // ) -> Poll>> { - // let mut this = self.project(); - // let mut buf = BytesMut::new(); - - // let read = match ready!(poll_read_buf(this.read.as_mut(), cx, &mut buf)) { - // Ok(read) => read, - // Err(err) => { - // let body_error: Option = M::get_pin_mut(this.read) - // .get_pin_mut() - // .project() - // .error - // .take(); - - // if let Some(body_error) = 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(err.into()))); - // } - // } - // }; - - // if read == 0 { - // Poll::Ready(None) - // } else { - // Poll::Ready(Some(Ok(buf.freeze()))) - // } - // } - - // fn poll_trailers( - // self: Pin<&mut Self>, - // cx: &mut Context<'_>, - // ) -> Poll, Self::Error>> { - // let this = self.project(); - // let body = M::get_pin_mut(this.read) - // .get_pin_mut() - // .get_pin_mut() - // .get_pin_mut(); - // body.poll_trailers(cx).map_err(Into::into) - // } + let mut this = self.project(); + let mut buf = BytesMut::new(); + if !*this.read_all_data { + match tokio_util::io::poll_read_buf(this.read.as_mut(), cx, &mut buf) { + Poll::Ready(result) => { + match result { + Ok(read) => { + if read == 0 { + *this.read_all_data = true; + } else { + return Poll::Ready(Some(Ok(Frame::data(buf.freeze())))); + } + } + Err(err) => { + let body_error: Option = M::get_pin_mut(this.read) + .get_pin_mut() + .project() + .error + .take(); + + if let Some(body_error) = 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(err.into()))); + } + } + } + } + Poll::Pending => return Poll::Pending, + } + } + + let body = M::get_pin_mut(this.read).get_pin_mut().get_pin_mut(); + body.poll_frame(cx).map(|option| { + option.map(|result| { + result + .map(|frame| { + frame.map_data(|mut data| data.copy_to_bytes(data.remaining())) + }) + .map_err(|err| err.into()) + }) + }) + } } pin_project! { - // When https://github.com/hyperium/http-body/pull/36 is merged we can remove this - pub(crate) struct BodyIntoStream { + pub(crate) struct BodyIntoStream + where + B: Body, + { #[pin] body: B, + non_data_frames: VecDeque>, } } #[allow(dead_code)] -impl BodyIntoStream { +impl BodyIntoStream +where + B: Body, +{ pub(crate) fn new(body: B) -> Self { - Self { body } + Self { + body, + non_data_frames: Default::default(), + } } /// Get a reference to the inner body @@ -288,10 +297,13 @@ where fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { - match futures_util::ready!(self.as_mut().project().body.poll_frame(cx)) { + let this = self.as_mut().project(); + match futures_util::ready!(this.body.poll_frame(cx)) { Some(Ok(frame)) => match frame.into_data() { Ok(data) => return Poll::Ready(Some(Ok(data))), - Err(_frame) => {} + Err(frame) => { + this.non_data_frames.push_back(frame); + } }, Some(Err(err)) => return Poll::Ready(Some(Err(err))), None => return Poll::Ready(None), @@ -300,6 +312,41 @@ where } } +impl Body for BodyIntoStream +where + B: Body, +{ + type Data = B::Data; + type Error = B::Error; + + fn poll_frame( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + let this = self.project(); + + if let Some(frame) = futures_util::ready!(this.body.poll_frame(cx)) { + return Poll::Ready(Some(frame)); + } + + if let Some(frame) = this.non_data_frames.pop_back() { + return Poll::Ready(Some(Ok(frame))); + } + + Poll::Ready(None) + } + + #[inline] + fn is_end_stream(&self) -> bool { + self.body.is_end_stream() + } + + #[inline] + fn size_hint(&self) -> http_body::SizeHint { + self.body.size_hint() + } +} + pin_project! { pub(crate) struct StreamErrorIntoIoError { #[pin] diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index e4aefee8..f5dbf029 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -182,13 +182,13 @@ pub mod set_header; pub mod propagate_header; // TODO(david): bring this back -// #[cfg(any( -// feature = "compression-br", -// feature = "compression-deflate", -// feature = "compression-gzip", -// feature = "compression-zstd", -// ))] -// pub mod compression; +#[cfg(any( + feature = "compression-br", + feature = "compression-deflate", + feature = "compression-gzip", + feature = "compression-zstd", +))] +pub mod compression; #[cfg(feature = "add-extension")] pub mod add_extension; @@ -218,29 +218,29 @@ pub mod sensitive_headers; ))] mod content_encoding; -// #[cfg(any( -// feature = "compression-br", -// feature = "compression-deflate", -// feature = "compression-gzip", -// feature = "compression-zstd", -// feature = "decompression-br", -// feature = "decompression-deflate", -// feature = "decompression-gzip", -// feature = "decompression-zstd", -// ))] -// mod compression_utils; +#[cfg(any( + feature = "compression-br", + feature = "compression-deflate", + feature = "compression-gzip", + feature = "compression-zstd", + feature = "decompression-br", + feature = "decompression-deflate", + feature = "decompression-gzip", + feature = "decompression-zstd", +))] +mod compression_utils; -// #[cfg(any( -// feature = "compression-br", -// feature = "compression-deflate", -// feature = "compression-gzip", -// feature = "compression-zstd", -// feature = "decompression-br", -// feature = "decompression-deflate", -// feature = "decompression-gzip", -// feature = "decompression-zstd", -// ))] -// pub use compression_utils::CompressionLevel; +#[cfg(any( + feature = "compression-br", + feature = "compression-deflate", + feature = "compression-gzip", + feature = "compression-zstd", + feature = "decompression-br", + feature = "decompression-deflate", + feature = "decompression-gzip", + feature = "decompression-zstd", +))] +pub use compression_utils::CompressionLevel; #[cfg(feature = "map-response-body")] pub mod map_response_body; diff --git a/tower-http/src/test_helpers.rs b/tower-http/src/test_helpers.rs index 4ed0504b..864e5631 100644 --- a/tower-http/src/test_helpers.rs +++ b/tower-http/src/test_helpers.rs @@ -6,6 +6,7 @@ use std::{ use bytes::Bytes; use futures::TryStream; +use http::HeaderMap; use http_body::{Body as _, Frame}; use http_body_util::BodyExt; use pin_project_lite::pin_project; @@ -40,6 +41,13 @@ impl Body { stream: SyncWrapper::new(stream), }) } + + pub(crate) fn with_trailers(self, trailers: HeaderMap) -> WithTrailers { + WithTrailers { + inner: self, + trailers: Some(trailers), + } + } } impl Default for Body { @@ -126,6 +134,8 @@ where Ok(body.collect().await?.to_bytes()) } +// TODO(david): remove this and use `body.collect()` instead since that doesn't silently ignore +// trailers pub(crate) trait TowerHttpBodyExt: http_body::Body + Unpin { /// Returns future that resolves to next data chunk, if any. fn data(&mut self) -> Data<'_, Self> @@ -159,3 +169,36 @@ where } } } + +pin_project! { + pub(crate) struct WithTrailers { + #[pin] + inner: B, + trailers: Option, + } +} + +impl http_body::Body for WithTrailers +where + B: http_body::Body, +{ + type Data = B::Data; + type Error = B::Error; + + fn poll_frame( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + let this = self.project(); + match futures_util::ready!(this.inner.poll_frame(cx)) { + Some(frame) => Poll::Ready(Some(frame)), + None => { + if let Some(trailers) = this.trailers.take() { + Poll::Ready(Some(Ok(Frame::trailers(trailers)))) + } else { + Poll::Ready(None) + } + } + } + } +} From bb9153414eb8fd1bb0271282dfd565aff60ca005 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 11:24:53 +0100 Subject: [PATCH 39/56] use `BodyExt::collect` in tests --- tower-http/src/compression/layer.rs | 25 +++------- tower-http/src/compression/mod.rs | 47 +++++------------- tower-http/src/compression_utils.rs | 9 ++-- tower-http/src/services/fs/serve_dir/tests.rs | 37 +++++++------- tower-http/src/services/fs/serve_file.rs | 48 ++++++++----------- tower-http/src/test_helpers.rs | 40 +--------------- tower-http/src/timeout/body.rs | 11 +++-- 7 files changed, 70 insertions(+), 147 deletions(-) diff --git a/tower-http/src/compression/layer.rs b/tower-http/src/compression/layer.rs index 9cd1bbf5..5dcab99c 100644 --- a/tower-http/src/compression/layer.rs +++ b/tower-http/src/compression/layer.rs @@ -123,12 +123,11 @@ impl CompressionLayer { #[cfg(test)] mod tests { use super::*; - use crate::test_helpers::{Body, TowerHttpBodyExt}; + use crate::test_helpers::Body; use http::{header::ACCEPT_ENCODING, Request, Response}; - use tokio::fs::File; - // for Body::data - use bytes::{Bytes, BytesMut}; + use http_body_util::BodyExt; use std::convert::Infallible; + use tokio::fs::File; use tokio_util::io::ReaderStream; use tower::{Service, ServiceBuilder, ServiceExt}; @@ -165,13 +164,8 @@ mod tests { assert_eq!(response.headers()["content-encoding"], "deflate"); // Read the body - let mut body = response.into_body(); - let mut bytes = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk?; - bytes.extend_from_slice(&chunk[..]); - } - let bytes: Bytes = bytes.freeze(); + let body = response.into_body(); + let bytes = body.collect().await.unwrap().to_bytes(); let deflate_bytes_len = bytes.len(); @@ -195,13 +189,8 @@ mod tests { assert_eq!(response.headers()["content-encoding"], "br"); // Read the body - let mut body = response.into_body(); - let mut bytes = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk?; - bytes.extend_from_slice(&chunk[..]); - } - let bytes: Bytes = bytes.freeze(); + let body = response.into_body(); + let bytes = body.collect().await.unwrap().to_bytes(); let br_byte_length = bytes.len(); diff --git a/tower-http/src/compression/mod.rs b/tower-http/src/compression/mod.rs index ef7f76ac..5b4c630a 100644 --- a/tower-http/src/compression/mod.rs +++ b/tower-http/src/compression/mod.rs @@ -87,9 +87,8 @@ mod tests { use crate::compression::predicate::SizeAbove; use super::*; - use crate::test_helpers::{Body, TowerHttpBodyExt, WithTrailers}; + use crate::test_helpers::{Body, WithTrailers}; use async_compression::tokio::write::{BrotliDecoder, BrotliEncoder}; - use bytes::BytesMut; use flate2::read::GzDecoder; use http::header::{ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_TYPE}; use http::{HeaderMap, HeaderName, Request, Response}; @@ -157,13 +156,8 @@ mod tests { let res = svc.ready().await.unwrap().call(req).await.unwrap(); // read the compressed body - let mut body = res.into_body(); - let mut data = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk.unwrap(); - data.extend_from_slice(&chunk[..]); - } - let compressed_data = data.freeze().to_vec(); + let body = res.into_body(); + let compressed_data = body.collect().await.unwrap().to_bytes(); // decompress the body let decompressed = zstd::stream::decode_all(std::io::Cursor::new(compressed_data)).unwrap(); @@ -214,12 +208,8 @@ mod tests { ); // read the compressed body - let mut body = res.into_body(); - let mut data = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk.unwrap(); - data.extend_from_slice(&chunk[..]); - } + let body = res.into_body(); + let data = body.collect().await.unwrap().to_bytes(); // decompress the body let data = { @@ -282,12 +272,8 @@ mod tests { let res = svc.ready().await.unwrap().call(req).await.unwrap(); // read the uncompressed body - let mut body = res.into_body(); - let mut data = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk.unwrap(); - data.extend_from_slice(&chunk[..]); - } + let body = res.into_body(); + let data = body.collect().await.unwrap().to_bytes(); let still_uncompressed = String::from_utf8(data.to_vec()).unwrap(); assert_eq!(DATA, &still_uncompressed); @@ -299,12 +285,8 @@ mod tests { let res = svc.ready().await.unwrap().call(req).await.unwrap(); // read the compressed body - let mut body = res.into_body(); - let mut data = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk.unwrap(); - data.extend_from_slice(&chunk[..]); - } + let body = res.into_body(); + let data = body.collect().await.unwrap().to_bytes(); assert!(String::from_utf8(data.to_vec()).is_err()); } @@ -380,13 +362,8 @@ mod tests { let res = svc.ready().await.unwrap().call(req).await.unwrap(); // read the compressed body - let mut body = res.into_body(); - let mut data = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk.unwrap(); - data.extend_from_slice(&chunk[..]); - } - let compressed_data = data.freeze().to_vec(); + let body = res.into_body(); + let compressed_data = body.collect().await.unwrap().to_bytes(); // build the compressed body with the same quality level let compressed_with_level = { @@ -404,7 +381,7 @@ mod tests { }; assert_eq!( - compressed_data.as_slice(), + compressed_data, compressed_with_level.as_slice(), "Compression level is not respected" ); diff --git a/tower-http/src/compression_utils.rs b/tower-http/src/compression_utils.rs index 9c69c2d2..841c15b9 100644 --- a/tower-http/src/compression_utils.rs +++ b/tower-http/src/compression_utils.rs @@ -152,7 +152,9 @@ pin_project! { /// `Body` that has been decorated by an `AsyncRead` pub(crate) struct WrapBody { #[pin] - pub(crate) read: M::Output, + // rust-analyer thinks this field is private if its `pub(crate)` but works fine when its + // `pub` + pub read: M::Output, read_all_data: bool, } } @@ -236,9 +238,7 @@ where body.poll_frame(cx).map(|option| { option.map(|result| { result - .map(|frame| { - frame.map_data(|mut data| data.copy_to_bytes(data.remaining())) - }) + .map(|frame| frame.map_data(|mut data| data.copy_to_bytes(data.remaining()))) .map_err(|err| err.into()) }) }) @@ -419,6 +419,7 @@ pub enum CompressionLevel { Precise(i32), } +#[allow(clippy::derivable_impls)] impl Default for CompressionLevel { fn default() -> Self { CompressionLevel::Default diff --git a/tower-http/src/services/fs/serve_dir/tests.rs b/tower-http/src/services/fs/serve_dir/tests.rs index 8004a681..07ae110b 100644 --- a/tower-http/src/services/fs/serve_dir/tests.rs +++ b/tower-http/src/services/fs/serve_dir/tests.rs @@ -1,5 +1,5 @@ use crate::services::{ServeDir, ServeFile}; -use crate::test_helpers::{to_bytes, Body, TowerHttpBodyExt}; +use crate::test_helpers::{to_bytes, Body}; use brotli::BrotliDecompress; use bytes::Bytes; use flate2::bufread::{DeflateDecoder, GzDecoder}; @@ -7,6 +7,7 @@ use http::header::ALLOW; use http::{header, Method, Response}; use http::{Request, StatusCode}; use http_body::Body as HttpBody; +use http_body_util::BodyExt; use std::convert::Infallible; use std::io::Read; use tower::{service_fn, ServiceExt}; @@ -59,8 +60,7 @@ async fn head_request() { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-length"], "23"); - let body = res.into_body().data().await; - assert!(body.is_none()); + assert!(res.into_body().frame().await.is_none()); } #[tokio::test] @@ -79,8 +79,7 @@ async fn precompresed_head_request() { assert_eq!(res.headers()["content-encoding"], "gzip"); assert_eq!(res.headers()["content-length"], "59"); - let body = res.into_body().data().await; - assert!(body.is_none()); + assert!(res.into_body().frame().await.is_none()); } #[tokio::test] @@ -116,7 +115,7 @@ async fn precompressed_gzip() { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "gzip"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decoder = GzDecoder::new(&body[..]); let mut decompressed = String::new(); decoder.read_to_string(&mut decompressed).unwrap(); @@ -137,7 +136,7 @@ async fn precompressed_br() { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "br"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decompressed = Vec::new(); BrotliDecompress(&mut &body[..], &mut decompressed).unwrap(); let decompressed = String::from_utf8(decompressed.to_vec()).unwrap(); @@ -157,7 +156,7 @@ async fn precompressed_deflate() { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "deflate"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decoder = DeflateDecoder::new(&body[..]); let mut decompressed = String::new(); decoder.read_to_string(&mut decompressed).unwrap(); @@ -178,7 +177,7 @@ async fn unsupported_precompression_alogrithm_fallbacks_to_uncompressed() { assert_eq!(res.headers()["content-type"], "text/plain"); assert!(res.headers().get("content-encoding").is_none()); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let body = String::from_utf8(body.to_vec()).unwrap(); assert!(body.starts_with("\"This is a test file!\"")); } @@ -206,7 +205,7 @@ async fn only_precompressed_variant_existing() { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "gzip"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decoder = GzDecoder::new(&body[..]); let mut decompressed = String::new(); decoder.read_to_string(&mut decompressed).unwrap(); @@ -228,7 +227,7 @@ async fn missing_precompressed_variant_fallbacks_to_uncompressed() { // Uncompressed file is served because compressed version is missing assert!(res.headers().get("content-encoding").is_none()); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let body = String::from_utf8(body.to_vec()).unwrap(); assert!(body.starts_with("Test file!")); } @@ -250,7 +249,7 @@ async fn missing_precompressed_variant_fallbacks_to_uncompressed_for_head_reques // Uncompressed file is served because compressed version is missing assert!(res.headers().get("content-encoding").is_none()); - assert!(res.into_body().data().await.is_none()); + assert!(res.into_body().frame().await.is_none()); } #[tokio::test] @@ -346,7 +345,7 @@ async fn fallbacks_to_different_precompressed_variant_if_not_found_for_head_requ assert_eq!(res.headers()["content-encoding"], "br"); assert_eq!(res.headers()["content-length"], "15"); - assert!(res.into_body().data().await.is_none()); + assert!(res.into_body().frame().await.is_none()); } #[tokio::test] @@ -365,7 +364,7 @@ async fn fallbacks_to_different_precompressed_variant_if_not_found() { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "br"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decompressed = Vec::new(); BrotliDecompress(&mut &body[..], &mut decompressed).unwrap(); let decompressed = String::from_utf8(decompressed.to_vec()).unwrap(); @@ -583,8 +582,7 @@ async fn last_modified() { let res = svc.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::NOT_MODIFIED); - let body = res.into_body().data().await; - assert!(body.is_none()); + assert!(res.into_body().frame().await.is_none()); let svc = ServeDir::new(".."); let req = Request::builder() @@ -596,7 +594,7 @@ async fn last_modified() { let res = svc.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::OK); let readme_bytes = include_bytes!("../../../../../README.md"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); assert_eq!(body.as_ref(), readme_bytes); // -- If-Unmodified-Since @@ -610,7 +608,7 @@ async fn last_modified() { let res = svc.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::OK); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); assert_eq!(body.as_ref(), readme_bytes); let svc = ServeDir::new(".."); @@ -622,8 +620,7 @@ async fn last_modified() { let res = svc.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::PRECONDITION_FAILED); - let body = res.into_body().data().await; - assert!(body.is_none()); + assert!(res.into_body().frame().await.is_none()); } #[tokio::test] diff --git a/tower-http/src/services/fs/serve_file.rs b/tower-http/src/services/fs/serve_file.rs index 42a330dc..fc2553ea 100644 --- a/tower-http/src/services/fs/serve_file.rs +++ b/tower-http/src/services/fs/serve_file.rs @@ -129,13 +129,13 @@ where mod tests { use crate::services::ServeFile; use crate::test_helpers::Body; - use crate::test_helpers::TowerHttpBodyExt; use brotli::BrotliDecompress; use flate2::bufread::DeflateDecoder; use flate2::bufread::GzDecoder; use http::header; use http::Method; use http::{Request, StatusCode}; + use http_body_util::BodyExt; use mime::Mime; use std::io::Read; use std::str::FromStr; @@ -149,7 +149,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/markdown"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let body = String::from_utf8(body.to_vec()).unwrap(); assert!(body.starts_with("# Tower HTTP")); @@ -163,7 +163,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "image/jpg"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let body = String::from_utf8(body.to_vec()).unwrap(); assert!(body.starts_with("# Tower HTTP")); @@ -180,8 +180,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-length"], "23"); - let body = res.into_body().data().await; - assert!(body.is_none()); + assert!(res.into_body().frame().await.is_none()); } #[tokio::test] @@ -199,8 +198,7 @@ mod tests { assert_eq!(res.headers()["content-encoding"], "gzip"); assert_eq!(res.headers()["content-length"], "59"); - let body = res.into_body().data().await; - assert!(body.is_none()); + assert!(res.into_body().frame().await.is_none()); } #[tokio::test] @@ -216,7 +214,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "gzip"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decoder = GzDecoder::new(&body[..]); let mut decompressed = String::new(); decoder.read_to_string(&mut decompressed).unwrap(); @@ -236,7 +234,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/plain"); assert!(res.headers().get("content-encoding").is_none()); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let body = String::from_utf8(body.to_vec()).unwrap(); assert!(body.starts_with("\"This is a test file!\"")); } @@ -255,7 +253,7 @@ mod tests { // Uncompressed file is served because compressed version is missing assert!(res.headers().get("content-encoding").is_none()); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let body = String::from_utf8(body.to_vec()).unwrap(); assert!(body.starts_with("Test file!")); } @@ -276,8 +274,7 @@ mod tests { // Uncompressed file is served because compressed version is missing assert!(res.headers().get("content-encoding").is_none()); - let body = res.into_body().data().await; - assert!(body.is_none()); + assert!(res.into_body().frame().await.is_none()); } #[tokio::test] @@ -299,7 +296,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "gzip"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decoder = GzDecoder::new(&body[..]); let mut decompressed = String::new(); decoder.read_to_string(&mut decompressed).unwrap(); @@ -319,7 +316,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "br"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decompressed = Vec::new(); BrotliDecompress(&mut &body[..], &mut decompressed).unwrap(); let decompressed = String::from_utf8(decompressed.to_vec()).unwrap(); @@ -338,7 +335,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "deflate"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decoder = DeflateDecoder::new(&body[..]); let mut decompressed = String::new(); decoder.read_to_string(&mut decompressed).unwrap(); @@ -360,7 +357,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "gzip"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decoder = GzDecoder::new(&body[..]); let mut decompressed = String::new(); decoder.read_to_string(&mut decompressed).unwrap(); @@ -375,7 +372,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "br"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decompressed = Vec::new(); BrotliDecompress(&mut &body[..], &mut decompressed).unwrap(); let decompressed = String::from_utf8(decompressed.to_vec()).unwrap(); @@ -390,7 +387,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/markdown"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let body = String::from_utf8(body.to_vec()).unwrap(); assert!(body.starts_with("# Tower HTTP")); @@ -412,7 +409,7 @@ mod tests { assert_eq!(res.headers()["content-type"], "text/plain"); assert_eq!(res.headers()["content-encoding"], "br"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); let mut decompressed = Vec::new(); BrotliDecompress(&mut &body[..], &mut decompressed).unwrap(); let decompressed = String::from_utf8(decompressed.to_vec()).unwrap(); @@ -437,8 +434,7 @@ mod tests { assert_eq!(res.headers()["content-length"], "15"); assert_eq!(res.headers()["content-encoding"], "br"); - let body = res.into_body().data().await; - assert!(body.is_none()); + assert!(res.into_body().frame().await.is_none()); } #[tokio::test] @@ -489,8 +485,7 @@ mod tests { let res = svc.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::NOT_MODIFIED); - let body = res.into_body().data().await; - assert!(body.is_none()); + assert!(res.into_body().frame().await.is_none()); let svc = ServeFile::new("../README.md"); let req = Request::builder() @@ -501,7 +496,7 @@ mod tests { let res = svc.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::OK); let readme_bytes = include_bytes!("../../../../README.md"); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); assert_eq!(body.as_ref(), readme_bytes); // -- If-Unmodified-Since @@ -514,7 +509,7 @@ mod tests { let res = svc.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::OK); - let body = res.into_body().data().await.unwrap().unwrap(); + let body = res.into_body().collect().await.unwrap().to_bytes(); assert_eq!(body.as_ref(), readme_bytes); let svc = ServeFile::new("../README.md"); @@ -525,7 +520,6 @@ mod tests { let res = svc.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::PRECONDITION_FAILED); - let body = res.into_body().data().await; - assert!(body.is_none()); + assert!(res.into_body().frame().await.is_none()); } } diff --git a/tower-http/src/test_helpers.rs b/tower-http/src/test_helpers.rs index 864e5631..114b05b6 100644 --- a/tower-http/src/test_helpers.rs +++ b/tower-http/src/test_helpers.rs @@ -1,5 +1,4 @@ use std::{ - future::Future, pin::Pin, task::{Context, Poll}, }; @@ -7,7 +6,7 @@ use std::{ use bytes::Bytes; use futures::TryStream; use http::HeaderMap; -use http_body::{Body as _, Frame}; +use http_body::Frame; use http_body_util::BodyExt; use pin_project_lite::pin_project; use sync_wrapper::SyncWrapper; @@ -125,7 +124,6 @@ where } } -// copied from hyper pub(crate) async fn to_bytes(body: T) -> Result where T: http_body::Body, @@ -134,42 +132,6 @@ where Ok(body.collect().await?.to_bytes()) } -// TODO(david): remove this and use `body.collect()` instead since that doesn't silently ignore -// trailers -pub(crate) trait TowerHttpBodyExt: http_body::Body + Unpin { - /// Returns future that resolves to next data chunk, if any. - fn data(&mut self) -> Data<'_, Self> - where - Self: Unpin + Sized, - { - Data(self) - } -} - -impl TowerHttpBodyExt for B where B: http_body::Body + Unpin {} - -pub(crate) struct Data<'a, T>(pub(crate) &'a mut T); - -impl<'a, T> Future for Data<'a, T> -where - T: http_body::Body + Unpin, -{ - type Output = Option>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - match futures_util::ready!(Pin::new(&mut self.0).poll_frame(cx)) { - Some(Ok(frame)) => match frame.into_data() { - Ok(data) => return Poll::Ready(Some(Ok(data))), - Err(_frame) => {} - }, - Some(Err(err)) => return Poll::Ready(Some(Err(err))), - None => return Poll::Ready(None), - } - } - } -} - pin_project! { pub(crate) struct WithTrailers { #[pin] diff --git a/tower-http/src/timeout/body.rs b/tower-http/src/timeout/body.rs index 28214b6f..13dc5b3e 100644 --- a/tower-http/src/timeout/body.rs +++ b/tower-http/src/timeout/body.rs @@ -118,8 +118,6 @@ impl std::fmt::Display for TimeoutError { } #[cfg(test)] mod tests { - use crate::test_helpers::TowerHttpBodyExt; - use super::*; use bytes::Bytes; @@ -171,7 +169,12 @@ mod tests { }; let timeout_body = TimeoutBody::new(timeout_sleep, mock_body); - assert!(timeout_body.boxed().data().await.expect("no data").is_ok()); + assert!(timeout_body + .boxed() + .frame() + .await + .expect("no frame") + .is_ok()); } #[tokio::test] @@ -184,6 +187,6 @@ mod tests { }; let timeout_body = TimeoutBody::new(timeout_sleep, mock_body); - assert!(timeout_body.boxed().data().await.unwrap().is_err()); + assert!(timeout_body.boxed().frame().await.unwrap().is_err()); } } From d69d18c1d5dc65914978c7616042a3ee81cad493 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 11:27:12 +0100 Subject: [PATCH 40/56] compression is back! --- tower-http/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index f5dbf029..b9739845 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -181,7 +181,6 @@ pub mod set_header; #[cfg(feature = "propagate-header")] pub mod propagate_header; -// TODO(david): bring this back #[cfg(any( feature = "compression-br", feature = "compression-deflate", From 29e3d453e835feb5de26b14fd6ae616ddcd8f9b9 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 11:36:53 +0100 Subject: [PATCH 41/56] and thats decompression! --- tower-http/Cargo.toml | 12 ++--- tower-http/src/compression_utils.rs | 1 + tower-http/src/decompression/mod.rs | 53 ++++++++++----------- tower-http/src/decompression/request/mod.rs | 11 ++--- tower-http/src/lib.rs | 15 +++--- 5 files changed, 41 insertions(+), 51 deletions(-) diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index 8a8b0553..b3dbc7db 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -62,7 +62,7 @@ full = [ "catch-panic", "compression-full", "cors", - # "decompression-full", + "decompression-full", "follow-redirect", "fs", "limit", @@ -110,11 +110,11 @@ compression-full = ["compression-br", "compression-deflate", "compression-gzip", compression-gzip = ["async-compression/gzip", "tokio-util", "tokio"] compression-zstd = ["async-compression/zstd", "tokio-util", "tokio"] -# decompression-br = ["async-compression/brotli", "tokio-util", "tokio"] -# decompression-deflate = ["async-compression/zlib", "tokio-util", "tokio"] -# decompression-full = ["decompression-br", "decompression-deflate", "decompression-gzip", "decompression-zstd"] -# decompression-gzip = ["async-compression/gzip", "tokio-util", "tokio"] -# decompression-zstd = ["async-compression/zstd", "tokio-util", "tokio"] +decompression-br = ["async-compression/brotli", "tokio-util", "tokio"] +decompression-deflate = ["async-compression/zlib", "tokio-util", "tokio"] +decompression-full = ["decompression-br", "decompression-deflate", "decompression-gzip", "decompression-zstd"] +decompression-gzip = ["async-compression/gzip", "tokio-util", "tokio"] +decompression-zstd = ["async-compression/zstd", "tokio-util", "tokio"] [package.metadata.docs.rs] all-features = true diff --git a/tower-http/src/compression_utils.rs b/tower-http/src/compression_utils.rs index 841c15b9..e316d0fe 100644 --- a/tower-http/src/compression_utils.rs +++ b/tower-http/src/compression_utils.rs @@ -234,6 +234,7 @@ where } } + // poll any remaining frames, such as trailers let body = M::get_pin_mut(this.read).get_pin_mut().get_pin_mut(); body.poll_frame(cx).map(|option| { option.map(|result| { diff --git a/tower-http/src/decompression/mod.rs b/tower-http/src/decompression/mod.rs index 451c54fb..2d0925a0 100644 --- a/tower-http/src/decompression/mod.rs +++ b/tower-http/src/decompression/mod.rs @@ -119,13 +119,12 @@ mod tests { use std::io::Write; use super::*; - use crate::compression::Compression; use crate::test_helpers::Body; - use crate::test_helpers::TowerHttpBodyExt; - use bytes::BytesMut; - use http::Request; + use crate::{compression::Compression, test_helpers::WithTrailers}; use flate2::write::GzEncoder; use http::Response; + use http::{HeaderMap, HeaderName, Request}; + use http_body_util::BodyExt; use tower::{service_fn, Service, ServiceExt}; #[tokio::test] @@ -139,19 +138,22 @@ mod tests { let res = client.ready().await.unwrap().call(req).await.unwrap(); // read the body, it will be decompressed automatically - let mut body = res.into_body(); - let mut data = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk.unwrap(); - data.extend_from_slice(&chunk[..]); - } - let decompressed_data = String::from_utf8(data.freeze().to_vec()).unwrap(); + let body = res.into_body(); + let collected = body.collect().await.unwrap(); + let trailers = collected.trailers().cloned().unwrap(); + let decompressed_data = String::from_utf8(collected.to_bytes().to_vec()).unwrap(); assert_eq!(decompressed_data, "Hello, World!"); + + // maintains trailers + assert_eq!(trailers["foo"], "bar"); } - async fn handle(_req: Request) -> Result, Infallible> { - Ok(Response::new(Body::from("Hello, World!"))) + async fn handle(_req: Request) -> Result>, Infallible> { + let mut trailers = HeaderMap::new(); + trailers.insert(HeaderName::from_static("foo"), "bar".parse().unwrap()); + let body = Body::from("Hello, World!").with_trailers(trailers); + Ok(Response::builder().body(body).unwrap()) } #[tokio::test] @@ -165,22 +167,14 @@ mod tests { let res = client.ready().await.unwrap().call(req).await.unwrap(); // read the body, it will be decompressed automatically - let mut body = res.into_body(); - let mut data = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk.unwrap(); - data.extend_from_slice(&chunk[..]); - } - let decompressed_data = String::from_utf8(data.freeze().to_vec()).unwrap(); + let body = res.into_body(); + let decompressed_data = + String::from_utf8(body.collect().await.unwrap().to_bytes().to_vec()).unwrap(); assert_eq!(decompressed_data, "Hello, World!"); } - async fn handle(_req: Request) -> Result, Error> { - Ok(Response::new(Body::from("Hello, World!"))) - } - - async fn handle_multi_gz(_req: Request) -> Result, Error> { + async fn handle_multi_gz(_req: Request) -> Result, Infallible> { let mut buf = Vec::new(); let mut enc1 = GzEncoder::new(&mut buf, Default::default()); enc1.write_all(b"Hello, ").unwrap(); @@ -198,11 +192,12 @@ mod tests { #[allow(dead_code)] async fn is_compatible_with_hyper() { - let mut client = Decompression::new(Client::new()); + todo!() + // let mut client = Decompression::new(Client::new()); - let req = Request::new(Body::empty()); + // let req = Request::new(Body::empty()); - let _: Response> = - client.ready().await.unwrap().call(req).await.unwrap(); + // let _: Response> = + // client.ready().await.unwrap().call(req).await.unwrap(); } } diff --git a/tower-http/src/decompression/request/mod.rs b/tower-http/src/decompression/request/mod.rs index 88bfa632..da3d9409 100644 --- a/tower-http/src/decompression/request/mod.rs +++ b/tower-http/src/decompression/request/mod.rs @@ -5,11 +5,11 @@ pub(super) mod service; #[cfg(test)] mod tests { use super::service::RequestDecompression; + use crate::decompression::DecompressionBody; use crate::test_helpers::Body; - use crate::{decompression::DecompressionBody, test_helpers::TowerHttpBodyExt}; - use bytes::BytesMut; use flate2::{write::GzEncoder, Compression}; use http::{header, Request, Response, StatusCode}; + use http_body_util::BodyExt; use std::{convert::Infallible, io::Write}; use tower::{service_fn, Service, ServiceExt}; @@ -85,11 +85,6 @@ mod tests { } async fn read_body(body: &mut DecompressionBody) -> Vec { - let mut data = BytesMut::new(); - while let Some(chunk) = body.data().await { - let chunk = chunk.unwrap(); - data.extend_from_slice(&chunk[..]); - } - data.freeze().to_vec() + body.collect().await.unwrap().to_bytes().to_vec() } } diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index b9739845..d53381f5 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -195,14 +195,13 @@ pub mod add_extension; #[cfg(feature = "sensitive-headers")] pub mod sensitive_headers; -// TODO(david): bring this back -// #[cfg(any( -// feature = "decompression-br", -// feature = "decompression-deflate", -// feature = "decompression-gzip", -// feature = "decompression-zstd", -// ))] -// pub mod decompression; +#[cfg(any( + feature = "decompression-br", + feature = "decompression-deflate", + feature = "decompression-gzip", + feature = "decompression-zstd", +))] +pub mod decompression; #[cfg(any( feature = "compression-br", From 30a8cadece58f195517bc3dd26a526a3f3a93ca1 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 11:38:17 +0100 Subject: [PATCH 42/56] remove hyper specific test --- tower-http/src/decompression/mod.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tower-http/src/decompression/mod.rs b/tower-http/src/decompression/mod.rs index 2d0925a0..42a49b00 100644 --- a/tower-http/src/decompression/mod.rs +++ b/tower-http/src/decompression/mod.rs @@ -189,15 +189,4 @@ mod tests { .insert("content-encoding", "gzip".parse().unwrap()); Ok(res) } - - #[allow(dead_code)] - async fn is_compatible_with_hyper() { - todo!() - // let mut client = Decompression::new(Client::new()); - - // let req = Request::new(Body::empty()); - - // let _: Response> = - // client.ready().await.unwrap().call(req).await.unwrap(); - } } From 3b41aabe45362f0ca176ae9f5135138e8e075d2c Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 11:45:22 +0100 Subject: [PATCH 43/56] Update examples/axum-key-value-store/src/main.rs Co-authored-by: Jonas Platte --- examples/axum-key-value-store/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/axum-key-value-store/src/main.rs b/examples/axum-key-value-store/src/main.rs index 0379e89d..a5e5a327 100644 --- a/examples/axum-key-value-store/src/main.rs +++ b/examples/axum-key-value-store/src/main.rs @@ -1,5 +1,5 @@ fn main() { - eprint!("this example has not yet been updated to hyper 1.0"); + eprintln!("this example has not yet been updated to hyper 1.0"); } /* From 43ba8d881fb604475d5d80c5115b21678f6f7a8a Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 11:56:06 +0100 Subject: [PATCH 44/56] bring back client examples --- tower-http/Cargo.toml | 7 +-- .../src/classify/status_in_range_is_error.rs | 31 +++++++++++ tower-http/src/lib.rs | 54 +++++++++++++++++++ 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/tower-http/Cargo.toml b/tower-http/Cargo.toml index b3dbc7db..46cd7e1b 100644 --- a/tower-http/Cargo.toml +++ b/tower-http/Cargo.toml @@ -41,17 +41,18 @@ uuid = { version = "1.0", features = ["v4"], optional = true } [dev-dependencies] async-trait = "0.1" +brotli = "3" bytes = "1" flate2 = "1.0" -brotli = "3" futures = "0.3" +hyper-util = { version = "0.1", features = ["client-legacy", "http1", "tokio"] } once_cell = "1" +serde_json = "1.0" +sync_wrapper = "0.1.1" tokio = { version = "1", features = ["full"] } tower = { version = "0.4.10", features = ["buffer", "util", "retry", "make", "timeout"] } tracing-subscriber = "0.3" uuid = { version = "1.0", features = ["v4"] } -serde_json = "1.0" -sync_wrapper = "0.1.1" zstd = "0.12" [features] diff --git a/tower-http/src/classify/status_in_range_is_error.rs b/tower-http/src/classify/status_in_range_is_error.rs index 75bde023..934d08c5 100644 --- a/tower-http/src/classify/status_in_range_is_error.rs +++ b/tower-http/src/classify/status_in_range_is_error.rs @@ -4,6 +4,37 @@ use std::{fmt, ops::RangeInclusive}; /// Response classifier that considers responses with a status code within some range to be /// failures. +/// +/// # Example +/// +/// A client with tracing where server errors _and_ client errors are considered failures. +/// +/// ```no_run +/// use tower_http::{trace::TraceLayer, classify::StatusInRangeAsFailures}; +/// use tower::{ServiceBuilder, Service, ServiceExt}; +/// use http::{Request, Method}; +/// use http_body_util::Full; +/// use bytes::Bytes; +/// use hyper_util::{rt::TokioExecutor, client::legacy::Client}; +/// +/// # async fn foo() -> Result<(), tower::BoxError> { +/// let classifier = StatusInRangeAsFailures::new(400..=599); +/// +/// let client = Client::builder(TokioExecutor::new()).build_http(); +/// let mut client = ServiceBuilder::new() +/// .layer(TraceLayer::new(classifier.into_make_classifier())) +/// .service(client); +/// +/// let request = Request::builder() +/// .method(Method::GET) +/// .uri("https://example.com") +/// .body(Full::::default()) +/// .unwrap(); +/// +/// let response = client.ready().await?.call(request).await?; +/// # Ok(()) +/// # } +/// ``` #[derive(Debug, Clone)] pub struct StatusInRangeAsFailures { range: RangeInclusive, diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index d53381f5..99691b7f 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -84,6 +84,60 @@ //! Keep in mind that while this example uses [hyper], tower-http supports any HTTP //! client/server implementation that uses the [http] and [http-body] crates. //! +//! # Example client +//! +//! tower-http middleware can also be applied to HTTP clients: +//! +//! ```rust,no_run +//! use tower_http::{ +//! decompression::DecompressionLayer, +//! set_header::SetRequestHeaderLayer, +//! trace::TraceLayer, +//! classify::StatusInRangeAsFailures, +//! }; +//! use tower::{ServiceBuilder, Service, ServiceExt}; +//! use hyper_util::{rt::TokioExecutor, client::legacy::Client}; +//! use http_body_util::Full; +//! use bytes::Bytes; +//! use http::{Request, HeaderValue, header::USER_AGENT}; +//! +//! #[tokio::main] +//! async fn main() { +//! let client = Client::builder(TokioExecutor::new()).build_http(); +//! let mut client = ServiceBuilder::new() +//! // Add tracing and consider server errors and client +//! // errors as failures. +//! .layer(TraceLayer::new( +//! StatusInRangeAsFailures::new(400..=599).into_make_classifier() +//! )) +//! // Set a `User-Agent` header on all requests. +//! .layer(SetRequestHeaderLayer::overriding( +//! USER_AGENT, +//! HeaderValue::from_static("tower-http demo") +//! )) +//! // Decompress response bodies +//! .layer(DecompressionLayer::new()) +//! // Wrap a `Client` in our middleware stack. +//! // This is possible because `Client` implements +//! // `tower::Service`. +//! .service(client); +//! +//! // Make a request +//! let request = Request::builder() +//! .uri("http://example.com") +//! .body(Full::::default()) +//! .unwrap(); +//! +//! let response = client +//! .ready() +//! .await +//! .unwrap() +//! .call(request) +//! .await +//! .unwrap(); +//! } +//! ``` +//! //! # Feature Flags //! //! All middleware are disabled by default and can be enabled using [cargo features]. From 76de6cc5e94fd04b38cc9d15e84b9f1eb3cc3b0a Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 12:33:19 +0100 Subject: [PATCH 45/56] fix doc tests --- .../src/auth/async_require_authorization.rs | 2 +- tower-http/src/compression/mod.rs | 31 +++++++------ tower-http/src/decompression/mod.rs | 44 ++++++++----------- 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/tower-http/src/auth/async_require_authorization.rs b/tower-http/src/auth/async_require_authorization.rs index d66d1ee9..76bcc376 100644 --- a/tower-http/src/auth/async_require_authorization.rs +++ b/tower-http/src/auth/async_require_authorization.rs @@ -48,7 +48,7 @@ //! # None //! } //! -//! #[derive(Debug)] +//! #[derive(Debug, Clone)] //! struct UserId(String); //! //! async fn handle(request: Request>) -> Result>, BoxError> { diff --git a/tower-http/src/compression/mod.rs b/tower-http/src/compression/mod.rs index 5b4c630a..e3145a8c 100644 --- a/tower-http/src/compression/mod.rs +++ b/tower-http/src/compression/mod.rs @@ -7,23 +7,30 @@ //! ```rust //! use bytes::{Bytes, BytesMut}; //! use http::{Request, Response, header::ACCEPT_ENCODING}; -//! use http_body::Body as _; // for Body::data -//! use hyper::Body; +//! use http_body_util::{Full, BodyExt, StreamBody, combinators::UnsyncBoxBody}; +//! use http_body::Frame; //! use std::convert::Infallible; //! use tokio::fs::{self, File}; //! use tokio_util::io::ReaderStream; //! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; //! use tower_http::{compression::CompressionLayer, BoxError}; +//! use futures_util::TryStreamExt; +//! +//! type BoxBody = UnsyncBoxBody; //! //! # #[tokio::main] //! # async fn main() -> Result<(), BoxError> { -//! async fn handle(req: Request) -> Result, Infallible> { +//! async fn handle(req: Request>) -> Result, Infallible> { //! // Open the file. //! let file = File::open("Cargo.toml").await.expect("file missing"); -//! // Convert the file into a `Stream`. +//! // Convert the file into a `Stream` of `Bytes`. //! let stream = ReaderStream::new(file); +//! // Convert the stream into a stream of data `Frame`s. +//! let stream = stream.map_ok(Frame::data); //! // Convert the `Stream` into a `Body`. -//! let body = Body::wrap_stream(stream); +//! let body = StreamBody::new(stream); +//! // Erase the type because its very hard to name in the function signature. +//! let body = body.boxed_unsync(); //! // Create response. //! Ok(Response::new(body)) //! } @@ -36,7 +43,7 @@ //! // Call the service. //! let request = Request::builder() //! .header(ACCEPT_ENCODING, "gzip") -//! .body(Body::empty())?; +//! .body(Full::::default())?; //! //! let response = service //! .ready() @@ -47,13 +54,11 @@ //! assert_eq!(response.headers()["content-encoding"], "gzip"); //! //! // Read the body -//! let mut body = response.into_body(); -//! let mut bytes = BytesMut::new(); -//! while let Some(chunk) = body.data().await { -//! let chunk = chunk?; -//! bytes.extend_from_slice(&chunk[..]); -//! } -//! let bytes: Bytes = bytes.freeze(); +//! let bytes = response +//! .into_body() +//! .collect() +//! .await? +//! .to_bytes(); //! //! // The compressed body should be smaller 🤞 //! let uncompressed_len = fs::read_to_string("Cargo.toml").await?.len(); diff --git a/tower-http/src/decompression/mod.rs b/tower-http/src/decompression/mod.rs index 42a49b00..afe5b031 100644 --- a/tower-http/src/decompression/mod.rs +++ b/tower-http/src/decompression/mod.rs @@ -3,12 +3,12 @@ //! # Examples //! //! #### Request +//! //! ```rust -//! use bytes::BytesMut; +//! use bytes::Bytes; //! use flate2::{write::GzEncoder, Compression}; //! use http::{header, HeaderValue, Request, Response}; -//! use http_body::Body as _; // for Body::data -//! use hyper::Body; +//! use http_body_util::{Full, BodyExt}; //! use std::{error::Error, io::Write}; //! use tower::{Service, ServiceBuilder, service_fn, ServiceExt}; //! use tower_http::{BoxError, decompression::{DecompressionBody, RequestDecompressionLayer}}; @@ -20,7 +20,7 @@ //! encoder.write_all(b"Hello?")?; //! let request = Request::builder() //! .header(header::CONTENT_ENCODING, "gzip") -//! .body(Body::from(encoder.finish()?))?; +//! .body(Full::from(encoder.finish()?))?; //! //! // Our HTTP server //! let mut server = ServiceBuilder::new() @@ -32,33 +32,31 @@ //! let _response = server.ready().await?.call(request).await?; //! //! // Handler receives request whose body is decoded when read -//! async fn handler(mut req: Request>) -> Result, BoxError>{ -//! let mut data = BytesMut::new(); -//! while let Some(chunk) = req.body_mut().data().await { -//! let chunk = chunk?; -//! data.extend_from_slice(&chunk[..]); -//! } -//! assert_eq!(data.freeze().to_vec(), b"Hello?"); -//! Ok(Response::new(Body::from("Hello, World!"))) +//! async fn handler( +//! mut req: Request>>, +//! ) -> Result>, BoxError>{ +//! let data = req.into_body().collect().await?.to_bytes(); +//! assert_eq!(&data[..], b"Hello?"); +//! Ok(Response::new(Full::from("Hello, World!"))) //! } //! # Ok(()) //! # } //! ``` //! //! #### Response +//! //! ```rust -//! use bytes::BytesMut; +//! use bytes::Bytes; //! use http::{Request, Response}; -//! use http_body::Body as _; // for Body::data -//! use hyper::Body; +//! use http_body_util::{Full, BodyExt}; //! use std::convert::Infallible; //! use tower::{Service, ServiceExt, ServiceBuilder, service_fn}; //! use tower_http::{compression::Compression, decompression::DecompressionLayer, BoxError}; //! # //! # #[tokio::main] //! # async fn main() -> Result<(), tower_http::BoxError> { -//! # async fn handle(req: Request) -> Result, Infallible> { -//! # let body = Body::from("Hello, World!"); +//! # async fn handle(req: Request>) -> Result>, Infallible> { +//! # let body = Full::from("Hello, World!"); //! # Ok(Response::new(body)) //! # } //! @@ -74,7 +72,7 @@ //! // Call the service. //! // //! // `DecompressionLayer` takes care of setting `Accept-Encoding`. -//! let request = Request::new(Body::empty()); +//! let request = Request::new(Full::::default()); //! //! let response = client //! .ready() @@ -83,13 +81,9 @@ //! .await?; //! //! // Read the body -//! let mut body = response.into_body(); -//! let mut bytes = BytesMut::new(); -//! while let Some(chunk) = body.data().await { -//! let chunk = chunk?; -//! bytes.extend_from_slice(&chunk[..]); -//! } -//! let body = String::from_utf8(bytes.to_vec()).map_err(Into::::into)?; +//! let body = response.into_body(); +//! let bytes = body.collect().await?.to_bytes().to_vec(); +//! let body = String::from_utf8(bytes).map_err(Into::::into)?; //! //! assert_eq!(body, "Hello, World!"); //! # From ccb3e997f800be307cc03ca33124c9815eed3205 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 12:44:15 +0100 Subject: [PATCH 46/56] forgot ServiceBuilderExt --- tower-http/src/builder.rs | 88 +++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/tower-http/src/builder.rs b/tower-http/src/builder.rs index 73d964b3..d8d14aa0 100644 --- a/tower-http/src/builder.rs +++ b/tower-http/src/builder.rs @@ -88,31 +88,31 @@ pub trait ServiceBuilderExt: crate::sealed::Sealed + Sized { f: F, ) -> ServiceBuilder, L>>; - ///// Compresses response bodies. - ///// - ///// See [`tower_http::compression`] for more details. - ///// - ///// [`tower_http::compression`]: crate::compression - //#[cfg(any( - // feature = "compression-br", - // feature = "compression-deflate", - // feature = "compression-gzip", - // feature = "compression-zstd", - //))] - //fn compression(self) -> ServiceBuilder>; - - ///// Decompress response bodies. - ///// - ///// See [`tower_http::decompression`] for more details. - ///// - ///// [`tower_http::decompression`]: crate::decompression - //#[cfg(any( - // feature = "decompression-br", - // feature = "decompression-deflate", - // feature = "decompression-gzip", - // feature = "decompression-zstd", - //))] - //fn decompression(self) -> ServiceBuilder>; + /// Compresses response bodies. + /// + /// See [`tower_http::compression`] for more details. + /// + /// [`tower_http::compression`]: crate::compression + #[cfg(any( + feature = "compression-br", + feature = "compression-deflate", + feature = "compression-gzip", + feature = "compression-zstd", + ))] + fn compression(self) -> ServiceBuilder>; + + /// Decompress response bodies. + /// + /// See [`tower_http::decompression`] for more details. + /// + /// [`tower_http::decompression`]: crate::decompression + #[cfg(any( + feature = "decompression-br", + feature = "decompression-deflate", + feature = "decompression-gzip", + feature = "decompression-zstd", + ))] + fn decompression(self) -> ServiceBuilder>; /// High level tracing that classifies responses using HTTP status codes. /// @@ -404,25 +404,25 @@ impl ServiceBuilderExt for ServiceBuilder { self.layer(crate::map_response_body::MapResponseBodyLayer::new(f)) } - // #[cfg(any( - // feature = "compression-br", - // feature = "compression-deflate", - // feature = "compression-gzip", - // feature = "compression-zstd", - // ))] - // fn compression(self) -> ServiceBuilder> { - // self.layer(crate::compression::CompressionLayer::new()) - // } - - // #[cfg(any( - // feature = "decompression-br", - // feature = "decompression-deflate", - // feature = "decompression-gzip", - // feature = "decompression-zstd", - // ))] - // fn decompression(self) -> ServiceBuilder> { - // self.layer(crate::decompression::DecompressionLayer::new()) - // } + #[cfg(any( + feature = "compression-br", + feature = "compression-deflate", + feature = "compression-gzip", + feature = "compression-zstd", + ))] + fn compression(self) -> ServiceBuilder> { + self.layer(crate::compression::CompressionLayer::new()) + } + + #[cfg(any( + feature = "decompression-br", + feature = "decompression-deflate", + feature = "decompression-gzip", + feature = "decompression-zstd", + ))] + fn decompression(self) -> ServiceBuilder> { + self.layer(crate::decompression::DecompressionLayer::new()) + } #[cfg(feature = "trace")] fn trace_for_http( From 77ba9a8d03f238bb92f11ad1e7d751a9f25c0adb Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 12:46:32 +0100 Subject: [PATCH 47/56] changelog --- tower-http/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tower-http/CHANGELOG.md b/tower-http/CHANGELOG.md index 1c3b55d7..01961c2b 100644 --- a/tower-http/CHANGELOG.md +++ b/tower-http/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Changed - Bump Minimum Supported Rust Version to 1.66 ([#433]) +- Update to http-body 1.0 ([#348]) +- Update to http 1.0 ([#348]) ## Removed @@ -26,6 +28,7 @@ http-range-header to `0.4` [#418]: https://github.com/tower-rs/tower-http/pull/418 [#433]: https://github.com/tower-rs/tower-http/pull/433 +[#348]: https://github.com/tower-rs/tower-http/pull/348 # 0.4.2 (July 19, 2023) From 6adc41c8dad4617d9a7103b708a7517b305bfb9b Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 20 Nov 2023 15:42:21 +0100 Subject: [PATCH 48/56] Fix potential ordering issues with `BodyIntoStream` --- tower-http/src/compression_utils.rs | 38 ++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/tower-http/src/compression_utils.rs b/tower-http/src/compression_utils.rs index 9a741484..2d221992 100644 --- a/tower-http/src/compression_utils.rs +++ b/tower-http/src/compression_utils.rs @@ -7,7 +7,6 @@ use http::HeaderValue; use http_body::{Body, Frame}; use pin_project_lite::pin_project; use std::{ - collections::VecDeque, io, pin::Pin, task::{ready, Context, Poll}, @@ -252,7 +251,8 @@ pin_project! { { #[pin] body: B, - non_data_frames: VecDeque>, + yielded_all_data: bool, + non_data_frame: Option>, } } @@ -264,7 +264,8 @@ where pub(crate) fn new(body: B) -> Self { Self { body, - non_data_frames: Default::default(), + yielded_all_data: false, + non_data_frame: None, } } @@ -298,15 +299,23 @@ where fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { let this = self.as_mut().project(); + + if *this.yielded_all_data { + return Poll::Ready(None); + } + match futures_util::ready!(this.body.poll_frame(cx)) { Some(Ok(frame)) => match frame.into_data() { Ok(data) => return Poll::Ready(Some(Ok(data))), Err(frame) => { - this.non_data_frames.push_back(frame); + *this.yielded_all_data = true; + *this.non_data_frame = Some(frame); } }, Some(Err(err)) => return Poll::Ready(Some(Err(err))), - None => return Poll::Ready(None), + None => { + *this.yielded_all_data = true; + } } } } @@ -320,20 +329,25 @@ where type Error = B::Error; fn poll_frame( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>>> { - let this = self.project(); - - if let Some(frame) = futures_util::ready!(this.body.poll_frame(cx)) { - return Poll::Ready(Some(frame)); + // First drive the stream impl. This conume all data frames and buffer at most one trailers + // frame. + if let Some(frame) = futures_util::ready!(self.as_mut().poll_next(cx)) { + return Poll::Ready(Some(frame.map(Frame::data))); } - if let Some(frame) = this.non_data_frames.pop_back() { + let this = self.project(); + + // Yield the trailers frame `poll_next` hit. + if let Some(frame) = this.non_data_frame.take() { return Poll::Ready(Some(Ok(frame))); } - Poll::Ready(None) + // Yield any remaining frames in the body. There shouldn't be any after the trailers but + // you never know. + this.body.poll_frame(cx) } #[inline] From aa9ad9c2bd7e9340b28c6a00266853859d068103 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 20 Nov 2023 15:54:11 +0100 Subject: [PATCH 49/56] don't delegate `is_end_stream` --- tower-http/src/compression_utils.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tower-http/src/compression_utils.rs b/tower-http/src/compression_utils.rs index 2d221992..0a17432b 100644 --- a/tower-http/src/compression_utils.rs +++ b/tower-http/src/compression_utils.rs @@ -350,11 +350,6 @@ where this.body.poll_frame(cx) } - #[inline] - fn is_end_stream(&self) -> bool { - self.body.is_end_stream() - } - #[inline] fn size_hint(&self) -> http_body::SizeHint { self.body.size_hint() From 298ba9a80efe0b2947c0621c424012bb0b266bbe Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 20 Nov 2023 18:41:06 +0100 Subject: [PATCH 50/56] fix typos --- tower-http/src/body.rs | 3 ++- tower-http/src/compression_utils.rs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tower-http/src/body.rs b/tower-http/src/body.rs index 21a6119a..815a0d10 100644 --- a/tower-http/src/body.rs +++ b/tower-http/src/body.rs @@ -3,7 +3,8 @@ //! All these are wrappers around other body types. You shouldn't have to use them in your code. //! Use `http-body-util` instead. //! -//! They exist because we cannot expose types from `http-body-util` in `tower-http`s public API. +//! They exist because we don't want to expose types from `http-body-util` in `tower-http`s public +//! API. #![allow(missing_docs)] diff --git a/tower-http/src/compression_utils.rs b/tower-http/src/compression_utils.rs index 0a17432b..a7a328fb 100644 --- a/tower-http/src/compression_utils.rs +++ b/tower-http/src/compression_utils.rs @@ -332,9 +332,9 @@ where mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>>> { - // First drive the stream impl. This conume all data frames and buffer at most one trailers - // frame. - if let Some(frame) = futures_util::ready!(self.as_mut().poll_next(cx)) { + // First drive the stream impl. This consumes all data frames and buffer at most one + // trailers frame. + if let Some(frame) = std::task::ready!(self.as_mut().poll_next(cx)) { return Poll::Ready(Some(frame.map(Frame::data))); } From cc08044e011dfc29122be0971a017df5d27d2e9f Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 20 Nov 2023 18:41:28 +0100 Subject: [PATCH 51/56] use std::task::ready --- tower-http/src/compression_utils.rs | 2 +- tower-http/src/services/fs/mod.rs | 2 +- tower-http/src/test_helpers.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tower-http/src/compression_utils.rs b/tower-http/src/compression_utils.rs index a7a328fb..801883e8 100644 --- a/tower-http/src/compression_utils.rs +++ b/tower-http/src/compression_utils.rs @@ -304,7 +304,7 @@ where return Poll::Ready(None); } - match futures_util::ready!(this.body.poll_frame(cx)) { + match std::task::ready!(this.body.poll_frame(cx)) { Some(Ok(frame)) => match frame.into_data() { Ok(data) => return Poll::Ready(Some(Ok(data))), Err(frame) => { diff --git a/tower-http/src/services/fs/mod.rs b/tower-http/src/services/fs/mod.rs index 22856f5d..32dd6f1c 100644 --- a/tower-http/src/services/fs/mod.rs +++ b/tower-http/src/services/fs/mod.rs @@ -70,7 +70,7 @@ where self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>>> { - match futures_util::ready!(self.project().reader.poll_next(cx)) { + match std::task::ready!(self.project().reader.poll_next(cx)) { Some(Ok(chunk)) => Poll::Ready(Some(Ok(Frame::data(chunk)))), Some(Err(err)) => Poll::Ready(Some(Err(err))), None => Poll::Ready(None), diff --git a/tower-http/src/test_helpers.rs b/tower-http/src/test_helpers.rs index a3dd0f56..6add4233 100644 --- a/tower-http/src/test_helpers.rs +++ b/tower-http/src/test_helpers.rs @@ -116,7 +116,7 @@ where cx: &mut Context<'_>, ) -> Poll, Self::Error>>> { let stream = self.project().stream.get_pin_mut(); - match futures_util::ready!(stream.try_poll_next(cx)) { + match std::task::ready!(stream.try_poll_next(cx)) { Some(Ok(chunk)) => Poll::Ready(Some(Ok(Frame::data(chunk.into())))), Some(Err(err)) => Poll::Ready(Some(Err(err.into()))), None => Poll::Ready(None), @@ -152,7 +152,7 @@ where cx: &mut Context<'_>, ) -> Poll, Self::Error>>> { let this = self.project(); - match futures_util::ready!(this.inner.poll_frame(cx)) { + match std::task::ready!(this.inner.poll_frame(cx)) { Some(frame) => Poll::Ready(Some(frame)), None => { if let Some(trailers) = this.trailers.take() { From 2813604f77a12e2541b37749016edf9de3443795 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 20 Nov 2023 18:42:22 +0100 Subject: [PATCH 52/56] uncomment CompressionLayer in examples --- tower-http/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index 99691b7f..600f70ff 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -17,7 +17,7 @@ //! ```rust,no_run //! use tower_http::{ //! add_extension::AddExtensionLayer, -//! // compression::CompressionLayer, +//! compression::CompressionLayer, //! propagate_header::PropagateHeaderLayer, //! sensitive_headers::SetSensitiveRequestHeadersLayer, //! set_header::SetResponseHeaderLayer, @@ -65,7 +65,7 @@ //! // Share an `Arc` with all requests //! .layer(AddExtensionLayer::new(Arc::new(state))) //! // Compress responses -//! // .layer(CompressionLayer::new()) +//! .layer(CompressionLayer::new()) //! // Propagate `X-Request-Id`s from requests to responses //! .layer(PropagateHeaderLayer::new(HeaderName::from_static("x-request-id"))) //! // If the response has a known size set the `Content-Length` header From 4a68a6dfc713fbe63692f903d9d55333e6e30850 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 20 Nov 2023 18:43:04 +0100 Subject: [PATCH 53/56] Derive default --- tower-http/src/compression_utils.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tower-http/src/compression_utils.rs b/tower-http/src/compression_utils.rs index 801883e8..1d851e67 100644 --- a/tower-http/src/compression_utils.rs +++ b/tower-http/src/compression_utils.rs @@ -413,13 +413,14 @@ pub(crate) const SENTINEL_ERROR_CODE: i32 = -837459418; /// Level of compression data should be compressed with. #[non_exhaustive] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)] pub enum CompressionLevel { /// Fastest quality of compression, usually produces bigger size. Fastest, /// Best quality of compression, usually produces the smallest size. Best, /// Default quality of compression defined by the selected compression algorithm. + #[default] Default, /// Precise quality based on the underlying compression algorithms' /// qualities. The interpretation of this depends on the algorithm chosen @@ -428,13 +429,6 @@ pub enum CompressionLevel { Precise(i32), } -#[allow(clippy::derivable_impls)] -impl Default for CompressionLevel { - fn default() -> Self { - CompressionLevel::Default - } -} - #[cfg(any( feature = "compression-br", feature = "compression-gzip", From 677012b1b1f78dc2680411904afc80a2ac5093a6 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 20 Nov 2023 23:14:34 +0100 Subject: [PATCH 54/56] format --- tower-http/src/decompression/request/future.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tower-http/src/decompression/request/future.rs b/tower-http/src/decompression/request/future.rs index 10ef906d..bdb22f8b 100644 --- a/tower-http/src/decompression/request/future.rs +++ b/tower-http/src/decompression/request/future.rs @@ -75,11 +75,9 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.project().kind.project() { - StateProj::Inner { fut } => fut - .poll(cx) - .map_ok(|res| { - res.map(|body| UnsyncBoxBody::new(body.map_err(Into::into).boxed_unsync())) - }), + StateProj::Inner { fut } => fut.poll(cx).map_ok(|res| { + res.map(|body| UnsyncBoxBody::new(body.map_err(Into::into).boxed_unsync())) + }), StateProj::Unsupported { accept } => { let res = Response::builder() .header( From ec644712f78964a844daa6cd46c821a8a66c42cb Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Tue, 21 Nov 2023 14:36:01 +0100 Subject: [PATCH 55/56] bring back hyper compat test --- tower-http/src/decompression/mod.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tower-http/src/decompression/mod.rs b/tower-http/src/decompression/mod.rs index afe5b031..708df439 100644 --- a/tower-http/src/decompression/mod.rs +++ b/tower-http/src/decompression/mod.rs @@ -183,4 +183,17 @@ mod tests { .insert("content-encoding", "gzip".parse().unwrap()); Ok(res) } + + #[allow(dead_code)] + async fn is_compatible_with_hyper() { + let client = + hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + .build_http(); + let mut client = Decompression::new(client); + + let req = Request::new(Body::empty()); + + let _: Response> = + client.ready().await.unwrap().call(req).await.unwrap(); + } } From bd4005113565c3e2f1acdbd41377c9f469388374 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Tue, 21 Nov 2023 14:36:10 +0100 Subject: [PATCH 56/56] remove lint. Lets just add this back later. Wanna merge now --- tower-http/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tower-http/src/lib.rs b/tower-http/src/lib.rs index 600f70ff..4c731e83 100644 --- a/tower-http/src/lib.rs +++ b/tower-http/src/lib.rs @@ -209,7 +209,7 @@ nonstandard_style, missing_docs )] -#![deny(unreachable_pub, private_in_public)] +#![deny(unreachable_pub)] #![allow( elided_lifetimes_in_paths, // TODO: Remove this once the MSRV bumps to 1.42.0 or above.