From 807aa59fbb3da05caf18603c4a3a1b23f38aa230 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 25 Nov 2022 21:29:55 +0100 Subject: [PATCH 01/57] Start working on v0.7.0 From 8238ca72dd1460641b6e129a3dc5bbf10d5d9b5c Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 27 Nov 2022 11:57:47 +0100 Subject: [PATCH 02/57] Add `axum_core::body::Body` (#1584) --- axum-core/src/body.rs | 94 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index 9f25408936..601e18a88a 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -3,7 +3,11 @@ use crate::{BoxError, Error}; use bytes::Bytes; use bytes::{Buf, BufMut}; -use http_body::Body; +use futures_util::stream::Stream; +use http::HeaderMap; +use http_body::Body as _; +use std::pin::Pin; +use std::task::{Context, Poll}; /// A boxed [`Body`] trait object. /// @@ -55,7 +59,7 @@ where // THE SOFTWARE. pub(crate) async fn to_bytes(body: T) -> Result where - T: Body, + T: http_body::Body, { futures_util::pin_mut!(body); @@ -85,6 +89,92 @@ where Ok(vec.into()) } +/// The body type used in axum requests and responses. +#[derive(Debug)] +pub struct Body(BoxBody); + +impl Body { + /// Create a new `Body` that wraps another [`http_body::Body`]. + pub fn new(body: B) -> Self + where + B: http_body::Body + Send + 'static, + B::Error: Into, + { + try_downcast(body).unwrap_or_else(|body| Self(boxed(body))) + } + + /// Create an empty body. + pub fn empty() -> Self { + Self::new(http_body::Empty::new()) + } +} + +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 = Error; + + #[inline] + fn poll_data( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> std::task::Poll>> { + Pin::new(&mut self.0).poll_data(cx) + } + + #[inline] + fn poll_trailers( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> std::task::Poll, Self::Error>> { + Pin::new(&mut self.0).poll_trailers(cx) + } + + #[inline] + fn size_hint(&self) -> http_body::SizeHint { + self.0.size_hint() + } + + #[inline] + fn is_end_stream(&self) -> bool { + self.0.is_end_stream() + } +} + +impl Stream for Body { + type Item = Result; + + #[inline] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_data(cx) + } +} + #[test] fn test_try_downcast() { assert_eq!(try_downcast::(5_u32), Err(5_u32)); From 280efc418d86a7463983e892553b71f69bb67910 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 16 Feb 2023 23:32:05 +0100 Subject: [PATCH 03/57] Change `sse::Event::json_data` to use `axum_core::Error` as its error type (#1762) --- axum/CHANGELOG.md | 4 +++- axum/Cargo.toml | 1 - axum/src/response/sse.rs | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 684a9db4b2..4fddb6a7e2 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased -- None. +- **breaking:** Change `sse::Event::json_data` to use `axum_core::Error` as its error type ([#1762]) + +[#1762]: https://github.com/tokio-rs/axum/pull/1762 # 0.6.12 (22. March, 2023) diff --git a/axum/Cargo.toml b/axum/Cargo.toml index b4198500bd..cfb651bf61 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -186,7 +186,6 @@ allowed = [ "http_body", "hyper", "serde", - "serde_json", "tower_layer", "tower_service", ] diff --git a/axum/src/response/sse.rs b/axum/src/response/sse.rs index 2e9e28535e..1b205a93e0 100644 --- a/axum/src/response/sse.rs +++ b/axum/src/response/sse.rs @@ -212,7 +212,7 @@ impl Event { /// /// [`MessageEvent`'s data field]: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/data #[cfg(feature = "json")] - pub fn json_data(mut self, data: T) -> serde_json::Result + pub fn json_data(mut self, data: T) -> Result where T: serde::Serialize, { @@ -221,7 +221,7 @@ impl Event { } self.buffer.extend_from_slice(b"data:"); - serde_json::to_writer((&mut self.buffer).writer(), &data)?; + serde_json::to_writer((&mut self.buffer).writer(), &data).map_err(axum_core::Error::new)?; self.buffer.put_u8(b'\n'); self.flags.insert(EventFlags::HAS_DATA); From f902f7470fa8d1f505716c6a69e3cc95e9b38af7 Mon Sep 17 00:00:00 2001 From: Michael Scofield Date: Fri, 24 Feb 2023 22:35:31 +0100 Subject: [PATCH 04/57] Fix typo in extract::ws (#1664) Co-authored-by: David Pedersen --- axum/CHANGELOG.md | 3 +++ axum/src/extract/ws.rs | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 4fddb6a7e2..d8bd1b7ed4 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -8,8 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased - **breaking:** Change `sse::Event::json_data` to use `axum_core::Error` as its error type ([#1762]) +- **breaking:** Rename `DefaultOnFailedUpdgrade` to `DefaultOnFailedUpgrade` ([#1664]) +- **breaking:** Rename `OnFailedUpdgrade` to `OnFailedUpgrade` ([#1664]) [#1762]: https://github.com/tokio-rs/axum/pull/1762 +[#1664]: https://github.com/tokio-rs/axum/pull/1664 # 0.6.12 (22. March, 2023) diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 482e691665..ae4cbf0743 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -135,7 +135,7 @@ use tokio_tungstenite::{ /// /// See the [module docs](self) for an example. #[cfg_attr(docsrs, doc(cfg(feature = "ws")))] -pub struct WebSocketUpgrade { +pub struct WebSocketUpgrade { config: WebSocketConfig, /// The chosen protocol sent in the `Sec-WebSocket-Protocol` header of the response. protocol: Option, @@ -268,7 +268,7 @@ impl WebSocketUpgrade { /// ``` pub fn on_failed_upgrade(self, callback: C) -> WebSocketUpgrade where - C: OnFailedUpdgrade, + C: OnFailedUpgrade, { WebSocketUpgrade { config: self.config, @@ -287,7 +287,7 @@ impl WebSocketUpgrade { where C: FnOnce(WebSocket) -> Fut + Send + 'static, Fut: Future + Send + 'static, - F: OnFailedUpdgrade, + F: OnFailedUpgrade, { let on_upgrade = self.on_upgrade; let config = self.config; @@ -339,12 +339,12 @@ impl WebSocketUpgrade { /// What to do when a connection upgrade fails. /// /// See [`WebSocketUpgrade::on_failed_upgrade`] for more details. -pub trait OnFailedUpdgrade: Send + 'static { +pub trait OnFailedUpgrade: Send + 'static { /// Call the callback. fn call(self, error: Error); } -impl OnFailedUpdgrade for F +impl OnFailedUpgrade for F where F: FnOnce(Error) + Send + 'static, { @@ -353,20 +353,20 @@ where } } -/// The default `OnFailedUpdgrade` used by `WebSocketUpgrade`. +/// The default `OnFailedUpgrade` used by `WebSocketUpgrade`. /// /// It simply ignores the error. #[non_exhaustive] #[derive(Debug)] -pub struct DefaultOnFailedUpdgrade; +pub struct DefaultOnFailedUpgrade; -impl OnFailedUpdgrade for DefaultOnFailedUpdgrade { +impl OnFailedUpgrade for DefaultOnFailedUpgrade { #[inline] fn call(self, _error: Error) {} } #[async_trait] -impl FromRequestParts for WebSocketUpgrade +impl FromRequestParts for WebSocketUpgrade where S: Send + Sync, { @@ -407,7 +407,7 @@ where sec_websocket_key, on_upgrade, sec_websocket_protocol, - on_failed_upgrade: DefaultOnFailedUpdgrade, + on_failed_upgrade: DefaultOnFailedUpgrade, }) } } From a4b7d3843cf755c26a545a8e1828c35bed9ed649 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 12 Mar 2023 16:37:32 +0100 Subject: [PATCH 05/57] Remove `B` type param (#1751) Co-authored-by: Jonas Platte Co-authored-by: Michael Scofield --- axum-core/src/ext_traits/request.rs | 89 +++--- axum-core/src/extract/default_body_limit.rs | 10 +- axum-core/src/extract/mod.rs | 67 +--- axum-core/src/extract/rejection.rs | 8 +- axum-core/src/extract/request_parts.rs | 26 +- axum-core/src/extract/tuple.rs | 12 +- axum-core/src/response/into_response.rs | 4 +- axum-extra/src/extract/cookie/mod.rs | 4 +- axum-extra/src/extract/form.rs | 11 +- axum-extra/src/extract/multipart.rs | 20 +- axum-extra/src/extract/with_rejection.rs | 10 +- axum-extra/src/handler/mod.rs | 33 +- axum-extra/src/handler/or.rs | 29 +- axum-extra/src/json_lines.rs | 39 +-- axum-extra/src/protobuf.rs | 12 +- axum-extra/src/routing/mod.rs | 51 ++- axum-extra/src/routing/resource.rs | 38 +-- axum-macros/CHANGELOG.md | 5 +- axum-macros/src/debug_handler.rs | 29 +- axum-macros/src/from_request.rs | 41 +-- axum-macros/src/lib.rs | 17 +- .../fail/argument_not_extractor.stderr | 2 +- .../debug_handler/fail/duplicate_args.rs | 5 +- .../debug_handler/fail/duplicate_args.stderr | 10 +- .../debug_handler/fail/extract_self_mut.rs | 5 +- .../fail/extract_self_mut.stderr | 4 +- .../debug_handler/fail/extract_self_ref.rs | 5 +- .../fail/extract_self_ref.stderr | 4 +- .../debug_handler/fail/invalid_attrs.stderr | 2 +- .../fail/json_not_deserialize.stderr | 2 +- .../pass/different_request_body_type.rs | 10 - .../tests/debug_handler/pass/self_receiver.rs | 10 +- .../tests/debug_handler/pass/set_state.rs | 5 +- .../debug_handler/pass/state_and_body.rs | 6 +- .../from_request/fail/generic_without_via.rs | 4 +- .../fail/generic_without_via.stderr | 34 +- .../fail/generic_without_via_rejection.rs | 4 +- .../fail/generic_without_via_rejection.stderr | 34 +- ...rride_rejection_on_enum_without_via.stderr | 24 +- .../state_infer_multiple_different_types.rs | 2 +- .../tests/from_request/pass/container.rs | 3 +- .../tests/from_request/pass/empty_named.rs | 2 +- .../tests/from_request/pass/empty_tuple.rs | 2 +- .../tests/from_request/pass/enum_via.rs | 4 +- .../tests/from_request/pass/enum_via_parts.rs | 4 +- axum-macros/tests/from_request/pass/named.rs | 3 +- .../tests/from_request/pass/named_via.rs | 3 +- .../from_request/pass/override_rejection.rs | 6 +- .../tests/from_request/pass/state_cookie.rs | 2 +- .../tests/from_request/pass/state_infer.rs | 2 +- .../from_request/pass/state_infer_multiple.rs | 2 +- axum-macros/tests/from_request/pass/tuple.rs | 2 +- .../pass/tuple_same_type_twice.rs | 2 +- .../pass/tuple_same_type_twice_via.rs | 2 +- .../tests/from_request/pass/tuple_via.rs | 2 +- axum-macros/tests/from_request/pass/unit.rs | 2 +- .../typed_path/pass/customize_rejection.rs | 2 +- .../typed_path/pass/named_fields_struct.rs | 2 +- .../tests/typed_path/pass/option_result.rs | 2 +- .../tests/typed_path/pass/tuple_struct.rs | 2 +- .../tests/typed_path/pass/unit_struct.rs | 2 +- .../tests/typed_path/pass/wildcards.rs | 2 +- axum/CHANGELOG.md | 24 +- axum/src/body/mod.rs | 5 +- axum/src/body/stream_body.rs | 2 +- axum/src/boxed.rs | 101 +++--- axum/src/docs/extract.md | 102 +----- axum/src/docs/middleware.md | 12 +- axum/src/extract/mod.rs | 2 +- axum/src/extract/multipart.rs | 26 +- axum/src/extract/query.rs | 7 +- axum/src/extract/raw_form.rs | 26 +- axum/src/extract/request_parts.rs | 99 +----- axum/src/form.rs | 24 +- axum/src/handler/future.rs | 17 +- axum/src/handler/mod.rs | 74 ++--- axum/src/handler/service.rs | 31 +- axum/src/json.rs | 20 +- axum/src/middleware/from_fn.rs | 9 +- axum/src/middleware/map_request.rs | 9 +- axum/src/response/mod.rs | 6 +- axum/src/routing/method_routing.rs | 195 +++++------- axum/src/routing/mod.rs | 300 +++++++++++++----- axum/src/routing/route.rs | 68 ++-- axum/src/routing/tests/fallback.rs | 4 +- axum/src/routing/tests/mod.rs | 17 +- axum/src/routing/tests/nest.rs | 2 +- .../src/main.rs | 23 +- .../src/custom_extractor.rs | 8 +- examples/http-proxy/src/main.rs | 3 +- examples/hyper-1-0/src/main.rs | 9 +- examples/low-level-openssl/src/main.rs | 4 +- examples/low-level-rustls/src/main.rs | 8 +- .../src/main.rs | 10 +- examples/reverse-proxy/src/main.rs | 12 +- examples/static-file-server/src/main.rs | 5 +- examples/stream-to-file/src/main.rs | 10 +- examples/testing/src/main.rs | 25 +- examples/validator/src/main.rs | 8 +- 99 files changed, 947 insertions(+), 1142 deletions(-) delete mode 100644 axum-macros/tests/debug_handler/pass/different_request_body_type.rs diff --git a/axum-core/src/ext_traits/request.rs b/axum-core/src/ext_traits/request.rs index e49ba9216c..1c69abd54e 100644 --- a/axum-core/src/ext_traits/request.rs +++ b/axum-core/src/ext_traits/request.rs @@ -1,15 +1,16 @@ +use crate::body::Body; use crate::extract::{DefaultBodyLimitKind, FromRequest, FromRequestParts}; use futures_util::future::BoxFuture; use http::Request; use http_body::Limited; mod sealed { - pub trait Sealed {} - impl Sealed for http::Request {} + pub trait Sealed {} + impl Sealed for http::Request {} } /// Extension trait that adds additional methods to [`Request`]. -pub trait RequestExt: sealed::Sealed + Sized { +pub trait RequestExt: sealed::Sealed + Sized { /// Apply an extractor to this `Request`. /// /// This is just a convenience for `E::from_request(req, &())`. @@ -23,6 +24,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// use axum::{ /// async_trait, /// extract::FromRequest, + /// body::Body, /// http::{header::CONTENT_TYPE, Request, StatusCode}, /// response::{IntoResponse, Response}, /// Form, Json, RequestExt, @@ -31,17 +33,16 @@ pub trait RequestExt: sealed::Sealed + Sized { /// struct FormOrJson(T); /// /// #[async_trait] - /// impl FromRequest for FormOrJson + /// impl FromRequest for FormOrJson /// where - /// Json: FromRequest<(), B>, - /// Form: FromRequest<(), B>, + /// Json: FromRequest<()>, + /// Form: FromRequest<()>, /// T: 'static, - /// B: Send + 'static, /// S: Send + Sync, /// { /// type Rejection = Response; /// - /// async fn from_request(req: Request, _state: &S) -> Result { + /// async fn from_request(req: Request, _state: &S) -> Result { /// let content_type = req /// .headers() /// .get(CONTENT_TYPE) @@ -70,7 +71,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// ``` fn extract(self) -> BoxFuture<'static, Result> where - E: FromRequest<(), B, M> + 'static, + E: FromRequest<(), M> + 'static, M: 'static; /// Apply an extractor that requires some state to this `Request`. @@ -85,6 +86,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// ``` /// use axum::{ /// async_trait, + /// body::Body, /// extract::{FromRef, FromRequest}, /// http::Request, /// RequestExt, @@ -95,15 +97,14 @@ pub trait RequestExt: sealed::Sealed + Sized { /// } /// /// #[async_trait] - /// impl FromRequest for MyExtractor + /// impl FromRequest for MyExtractor /// where /// String: FromRef, /// S: Send + Sync, - /// B: Send + 'static, /// { /// type Rejection = std::convert::Infallible; /// - /// async fn from_request(req: Request, state: &S) -> Result { + /// async fn from_request(req: Request, state: &S) -> Result { /// let requires_state = req.extract_with_state::(state).await?; /// /// Ok(Self { requires_state }) @@ -114,22 +115,21 @@ pub trait RequestExt: sealed::Sealed + Sized { /// struct RequiresState { /* ... */ } /// /// #[async_trait] - /// impl FromRequest for RequiresState + /// impl FromRequest for RequiresState /// where /// String: FromRef, /// S: Send + Sync, - /// B: Send + 'static, /// { /// // ... /// # type Rejection = std::convert::Infallible; - /// # async fn from_request(req: Request, _state: &S) -> Result { + /// # async fn from_request(req: Request, _state: &S) -> Result { /// # todo!() /// # } /// } /// ``` fn extract_with_state(self, state: &S) -> BoxFuture<'_, Result> where - E: FromRequest + 'static, + E: FromRequest + 'static, S: Send + Sync; /// Apply a parts extractor to this `Request`. @@ -145,6 +145,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// headers::{authorization::Bearer, Authorization}, /// http::Request, /// response::{IntoResponse, Response}, + /// body::Body, /// Json, RequestExt, TypedHeader, /// }; /// @@ -154,16 +155,15 @@ pub trait RequestExt: sealed::Sealed + Sized { /// } /// /// #[async_trait] - /// impl FromRequest for MyExtractor + /// impl FromRequest for MyExtractor /// where - /// B: Send + 'static, /// S: Send + Sync, - /// Json: FromRequest<(), B>, + /// Json: FromRequest<()>, /// T: 'static, /// { /// type Rejection = Response; /// - /// async fn from_request(mut req: Request, _state: &S) -> Result { + /// async fn from_request(mut req: Request, _state: &S) -> Result { /// let TypedHeader(auth_header) = req /// .extract_parts::>>() /// .await @@ -197,6 +197,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// extract::{FromRef, FromRequest, FromRequestParts}, /// http::{request::Parts, Request}, /// response::{IntoResponse, Response}, + /// body::Body, /// Json, RequestExt, /// }; /// @@ -206,17 +207,16 @@ pub trait RequestExt: sealed::Sealed + Sized { /// } /// /// #[async_trait] - /// impl FromRequest for MyExtractor + /// impl FromRequest for MyExtractor /// where /// String: FromRef, - /// Json: FromRequest<(), B>, + /// Json: FromRequest<()>, /// T: 'static, /// S: Send + Sync, - /// B: Send + 'static, /// { /// type Rejection = Response; /// - /// async fn from_request(mut req: Request, state: &S) -> Result { + /// async fn from_request(mut req: Request, state: &S) -> Result { /// let requires_state = req /// .extract_parts_with_state::(state) /// .await @@ -260,21 +260,18 @@ pub trait RequestExt: sealed::Sealed + Sized { /// Apply the [default body limit](crate::extract::DefaultBodyLimit). /// /// If it is disabled, return the request as-is in `Err`. - fn with_limited_body(self) -> Result>, Request>; + fn with_limited_body(self) -> Result>, Request>; /// Consumes the request, returning the body wrapped in [`Limited`] if a /// [default limit](crate::extract::DefaultBodyLimit) is in place, or not wrapped if the /// default limit is disabled. - fn into_limited_body(self) -> Result, B>; + fn into_limited_body(self) -> Result, Body>; } -impl RequestExt for Request -where - B: Send + 'static, -{ +impl RequestExt for Request { fn extract(self) -> BoxFuture<'static, Result> where - E: FromRequest<(), B, M> + 'static, + E: FromRequest<(), M> + 'static, M: 'static, { self.extract_with_state(&()) @@ -282,7 +279,7 @@ where fn extract_with_state(self, state: &S) -> BoxFuture<'_, Result> where - E: FromRequest + 'static, + E: FromRequest + 'static, S: Send + Sync, { E::from_request(self, state) @@ -324,7 +321,7 @@ where }) } - fn with_limited_body(self) -> Result>, Request> { + fn with_limited_body(self) -> Result>, Request> { // update docs in `axum-core/src/extract/default_body_limit.rs` and // `axum/src/docs/extract.md` if this changes const DEFAULT_LIMIT: usize = 2_097_152; // 2 mb @@ -338,7 +335,7 @@ where } } - fn into_limited_body(self) -> Result, B> { + fn into_limited_body(self) -> Result, Body> { self.with_limited_body() .map(Request::into_body) .map_err(Request::into_body) @@ -354,11 +351,10 @@ mod tests { }; use async_trait::async_trait; use http::Method; - use hyper::Body; #[tokio::test] async fn extract_without_state() { - let req = Request::new(()); + let req = Request::new(Body::empty()); let method: Method = req.extract().await.unwrap(); @@ -376,7 +372,7 @@ mod tests { #[tokio::test] async fn extract_with_state() { - let req = Request::new(()); + let req = Request::new(Body::empty()); let state = "state".to_owned(); @@ -387,7 +383,10 @@ mod tests { #[tokio::test] async fn extract_parts_without_state() { - let mut req = Request::builder().header("x-foo", "foo").body(()).unwrap(); + let mut req = Request::builder() + .header("x-foo", "foo") + .body(Body::empty()) + .unwrap(); let method: Method = req.extract_parts().await.unwrap(); @@ -397,7 +396,10 @@ mod tests { #[tokio::test] async fn extract_parts_with_state() { - let mut req = Request::builder().header("x-foo", "foo").body(()).unwrap(); + let mut req = Request::builder() + .header("x-foo", "foo") + .body(Body::empty()) + .unwrap(); let state = "state".to_owned(); @@ -417,15 +419,14 @@ mod tests { } #[async_trait] - impl FromRequest for WorksForCustomExtractor + impl FromRequest for WorksForCustomExtractor where S: Send + Sync, - B: Send + 'static, - String: FromRef + FromRequest<(), B>, + String: FromRef + FromRequest<()>, { - type Rejection = >::Rejection; + type Rejection = >::Rejection; - async fn from_request(mut req: Request, state: &S) -> Result { + async fn from_request(mut req: Request, state: &S) -> Result { let RequiresState(from_state) = req.extract_parts_with_state(state).await.unwrap(); let method = req.extract_parts().await.unwrap(); let body = req.extract().await?; diff --git a/axum-core/src/extract/default_body_limit.rs b/axum-core/src/extract/default_body_limit.rs index 7b37f1edab..f27e23a3c7 100644 --- a/axum-core/src/extract/default_body_limit.rs +++ b/axum-core/src/extract/default_body_limit.rs @@ -41,7 +41,7 @@ use tower_layer::Layer; /// post(|request: Request| async {}), /// ) /// .layer(DefaultBodyLimit::max(1024)); -/// # let _: Router<(), _> = app; +/// # let _: Router = app; /// ``` /// /// ``` @@ -54,10 +54,10 @@ use tower_layer::Layer; /// "/", /// // `RequestBodyLimitLayer` changes the request body type to `Limited` /// // extracting a different body type wont work -/// post(|request: Request>| async {}), +/// post(|request: Request| async {}), /// ) /// .layer(RequestBodyLimitLayer::new(1024)); -/// # let _: Router<(), _> = app; +/// # let _: Router = app; /// ``` /// /// In general using `DefaultBodyLimit` is recommended but if you need to use third party @@ -105,7 +105,7 @@ impl DefaultBodyLimit { /// use tower_http::limit::RequestBodyLimitLayer; /// use http_body::Limited; /// - /// let app: Router<(), Limited> = Router::new() + /// let app: Router<()> = Router::new() /// .route("/", get(|body: Bytes| async {})) /// // Disable the default limit /// .layer(DefaultBodyLimit::disable()) @@ -140,7 +140,7 @@ impl DefaultBodyLimit { /// use tower_http::limit::RequestBodyLimitLayer; /// use http_body::Limited; /// - /// let app: Router<(), Limited> = Router::new() + /// let app: Router<()> = Router::new() /// .route("/", get(|body: Bytes| async {})) /// // Replace the default of 2MB with 1024 bytes. /// .layer(DefaultBodyLimit::max(1024)); diff --git a/axum-core/src/extract/mod.rs b/axum-core/src/extract/mod.rs index 1113a1ee7a..c8b6f39463 100644 --- a/axum-core/src/extract/mod.rs +++ b/axum-core/src/extract/mod.rs @@ -4,7 +4,7 @@ //! //! [`axum::extract`]: https://docs.rs/axum/latest/axum/extract/index.html -use crate::response::IntoResponse; +use crate::{body::Body, response::IntoResponse}; use async_trait::async_trait; use http::{request::Parts, Request}; use std::convert::Infallible; @@ -64,48 +64,6 @@ pub trait FromRequestParts: Sized { /// /// See [`axum::extract`] for more general docs about extractors. /// -/// # What is the `B` type parameter? -/// -/// `FromRequest` is generic over the request body (the `B` in -/// [`http::Request`]). This is to allow `FromRequest` to be usable with any -/// type of request body. This is necessary because some middleware change the -/// request body, for example to add timeouts. -/// -/// If you're writing your own `FromRequest` that wont be used outside your -/// application, and not using any middleware that changes the request body, you -/// can most likely use `axum::body::Body`. -/// -/// If you're writing a library that's intended for others to use, it's recommended -/// to keep the generic type parameter: -/// -/// ```rust -/// use axum::{ -/// async_trait, -/// extract::FromRequest, -/// http::{self, Request}, -/// }; -/// -/// struct MyExtractor; -/// -/// #[async_trait] -/// impl FromRequest for MyExtractor -/// where -/// // these bounds are required by `async_trait` -/// B: Send + 'static, -/// S: Send + Sync, -/// { -/// type Rejection = http::StatusCode; -/// -/// async fn from_request(req: Request, state: &S) -> Result { -/// // ... -/// # unimplemented!() -/// } -/// } -/// ``` -/// -/// This ensures your extractor is as flexible as possible. -/// -/// [`http::Request`]: http::Request /// [`axum::extract`]: https://docs.rs/axum/0.6.0/axum/extract/index.html #[async_trait] #[cfg_attr( @@ -114,25 +72,24 @@ pub trait FromRequestParts: Sized { note = "Function argument is not a valid axum extractor. \nSee `https://docs.rs/axum/latest/axum/extract/index.html` for details", ) )] -pub trait FromRequest: Sized { +pub trait FromRequest: Sized { /// If the extractor fails it'll use this "rejection" type. A rejection is /// a kind of error that can be converted into a response. type Rejection: IntoResponse; /// Perform the extraction. - async fn from_request(req: Request, state: &S) -> Result; + async fn from_request(req: Request, state: &S) -> Result; } #[async_trait] -impl FromRequest for T +impl FromRequest for T where - B: Send + 'static, S: Send + Sync, T: FromRequestParts, { type Rejection = >::Rejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let (mut parts, _) = req.into_parts(); Self::from_request_parts(&mut parts, state).await } @@ -155,15 +112,14 @@ where } #[async_trait] -impl FromRequest for Option +impl FromRequest for Option where - T: FromRequest, - B: Send + 'static, + T: FromRequest, S: Send + Sync, { type Rejection = Infallible; - async fn from_request(req: Request, state: &S) -> Result, Self::Rejection> { + async fn from_request(req: Request, state: &S) -> Result, Self::Rejection> { Ok(T::from_request(req, state).await.ok()) } } @@ -182,15 +138,14 @@ where } #[async_trait] -impl FromRequest for Result +impl FromRequest for Result where - T: FromRequest, - B: Send + 'static, + T: FromRequest, S: Send + Sync, { type Rejection = Infallible; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { Ok(T::from_request(req, state).await) } } diff --git a/axum-core/src/extract/rejection.rs b/axum-core/src/extract/rejection.rs index 09d701911a..037f4217a3 100644 --- a/axum-core/src/extract/rejection.rs +++ b/axum-core/src/extract/rejection.rs @@ -1,6 +1,6 @@ //! Rejection response types. -use crate::BoxError; +use crate::{BoxError, Error}; composite_rejection! { /// Rejection type for extractors that buffer the request body. Used if the @@ -16,7 +16,11 @@ impl FailedToBufferBody { where E: Into, { - match err.into().downcast::() { + let box_error = match err.into().downcast::() { + Ok(err) => err.into_inner(), + Err(err) => err, + }; + match box_error.downcast::() { Ok(err) => Self::LengthLimitError(LengthLimitError::from_err(err)), Err(err) => Self::UnknownBodyError(UnknownBodyError::from_err(err)), } diff --git a/axum-core/src/extract/request_parts.rs b/axum-core/src/extract/request_parts.rs index 05d7d7277b..80ad5e78a9 100644 --- a/axum-core/src/extract/request_parts.rs +++ b/axum-core/src/extract/request_parts.rs @@ -1,19 +1,18 @@ use super::{rejection::*, FromRequest, FromRequestParts}; -use crate::{BoxError, RequestExt}; +use crate::{body::Body, RequestExt}; use async_trait::async_trait; use bytes::Bytes; use http::{request::Parts, HeaderMap, Method, Request, Uri, Version}; use std::convert::Infallible; #[async_trait] -impl FromRequest for Request +impl FromRequest for Request where - B: Send, S: Send + Sync, { type Rejection = Infallible; - async fn from_request(req: Request, _: &S) -> Result { + async fn from_request(req: Request, _: &S) -> Result { Ok(req) } } @@ -72,16 +71,13 @@ where } #[async_trait] -impl FromRequest for Bytes +impl FromRequest for Bytes where - B: http_body::Body + Send + 'static, - B::Data: Send, - B::Error: Into, S: Send + Sync, { type Rejection = BytesRejection; - async fn from_request(req: Request, _: &S) -> Result { + async fn from_request(req: Request, _: &S) -> Result { let bytes = match req.into_limited_body() { Ok(limited_body) => crate::body::to_bytes(limited_body) .await @@ -96,16 +92,13 @@ where } #[async_trait] -impl FromRequest for String +impl FromRequest for String where - B: http_body::Body + Send + 'static, - B::Data: Send, - B::Error: Into, S: Send + Sync, { type Rejection = StringRejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let bytes = Bytes::from_request(req, state) .await .map_err(|err| match err { @@ -123,14 +116,13 @@ where } #[async_trait] -impl FromRequest for Parts +impl FromRequest for Parts where - B: Send + 'static, S: Send + Sync, { type Rejection = Infallible; - async fn from_request(req: Request, _: &S) -> Result { + async fn from_request(req: Request, _: &S) -> Result { Ok(req.into_parts().0) } } diff --git a/axum-core/src/extract/tuple.rs b/axum-core/src/extract/tuple.rs index 728135b2a0..14d1e1ddd3 100644 --- a/axum-core/src/extract/tuple.rs +++ b/axum-core/src/extract/tuple.rs @@ -1,4 +1,5 @@ use super::{FromRequest, FromRequestParts}; +use crate::body::Body; use crate::response::{IntoResponse, Response}; use async_trait::async_trait; use http::request::{Parts, Request}; @@ -45,19 +46,18 @@ macro_rules! impl_from_request { } // This impl must not be generic over M, otherwise it would conflict with the blanket - // implementation of `FromRequest` for `T: FromRequestParts`. + // implementation of `FromRequest` for `T: FromRequestParts`. #[async_trait] #[allow(non_snake_case, unused_mut, unused_variables)] - impl FromRequest for ($($ty,)* $last,) + impl FromRequest for ($($ty,)* $last,) where $( $ty: FromRequestParts + Send, )* - $last: FromRequest + Send, - B: Send + 'static, + $last: FromRequest + Send, S: Send + Sync, { type Rejection = Response; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let (mut parts, body) = req.into_parts(); $( @@ -85,7 +85,7 @@ mod tests { fn assert_from_request() where - T: FromRequest<(), http_body::Full, M>, + T: FromRequest<(), M>, { } diff --git a/axum-core/src/response/into_response.rs b/axum-core/src/response/into_response.rs index f19974cfb7..82efaabed2 100644 --- a/axum-core/src/response/into_response.rs +++ b/axum-core/src/response/into_response.rs @@ -119,9 +119,7 @@ use std::{ /// /// // `MyBody` can now be returned from handlers. /// let app = Router::new().route("/", get(|| async { MyBody })); -/// # async { -/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` pub trait IntoResponse { /// Create a response. diff --git a/axum-extra/src/extract/cookie/mod.rs b/axum-extra/src/extract/cookie/mod.rs index 175bf9e60d..5cd021fa70 100644 --- a/axum-extra/src/extract/cookie/mod.rs +++ b/axum-extra/src/extract/cookie/mod.rs @@ -255,7 +255,7 @@ mod tests { custom_key: CustomKey(Key::generate()), }; - let app = Router::<_, Body>::new() + let app = Router::new() .route("/set", get(set_cookie)) .route("/get", get(get_cookie)) .route("/remove", get(remove_cookie)) @@ -352,7 +352,7 @@ mod tests { custom_key: CustomKey(Key::generate()), }; - let app = Router::<_, Body>::new() + let app = Router::new() .route("/get", get(get_cookie)) .with_state(state); diff --git a/axum-extra/src/extract/form.rs b/axum-extra/src/extract/form.rs index df33e5cade..1f660c4511 100644 --- a/axum-extra/src/extract/form.rs +++ b/axum-extra/src/extract/form.rs @@ -1,9 +1,9 @@ use axum::{ async_trait, - body::HttpBody, + body::Body, extract::{rejection::RawFormRejection, FromRequest, RawForm}, response::{IntoResponse, Response}, - BoxError, Error, RequestExt, + Error, RequestExt, }; use http::{Request, StatusCode}; use serde::de::DeserializeOwned; @@ -52,17 +52,14 @@ impl Deref for Form { } #[async_trait] -impl FromRequest for Form +impl FromRequest for Form where T: DeserializeOwned, - B: HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, S: Send + Sync, { type Rejection = FormRejection; - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { let RawForm(bytes) = req .extract() .await diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 03f7fc540d..09f45a1821 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -4,8 +4,8 @@ use axum::{ async_trait, - body::{Bytes, HttpBody}, - extract::{BodyStream, FromRequest}, + body::{Body, Bytes}, + extract::FromRequest, response::{IntoResponse, Response}, BoxError, RequestExt, }; @@ -91,22 +91,18 @@ pub struct Multipart { } #[async_trait] -impl FromRequest for Multipart +impl FromRequest for Multipart where - B: HttpBody + Send + 'static, - B::Data: Into, - B::Error: Into, S: Send + Sync, { type Rejection = MultipartRejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?; - let stream_result = match req.with_limited_body() { - Ok(limited) => BodyStream::from_request(limited, state).await, - Err(unlimited) => BodyStream::from_request(unlimited, state).await, + let stream = match req.with_limited_body() { + Ok(limited) => Body::new(limited), + Err(unlimited) => unlimited.into_body(), }; - let stream = stream_result.unwrap_or_else(|err| match err {}); let multipart = multer::Multipart::new(stream, boundary); Ok(Self { inner: multipart }) } @@ -447,7 +443,7 @@ mod tests { // No need for this to be a #[test], we just want to make sure it compiles fn _multipart_from_request_limited() { async fn handler(_: Multipart) {} - let _app: Router<(), http_body::Limited> = Router::new().route("/", post(handler)); + let _app: Router<()> = Router::new().route("/", post(handler)); } #[tokio::test] diff --git a/axum-extra/src/extract/with_rejection.rs b/axum-extra/src/extract/with_rejection.rs index f3a0f04e87..c5b9c326f7 100644 --- a/axum-extra/src/extract/with_rejection.rs +++ b/axum-extra/src/extract/with_rejection.rs @@ -1,4 +1,5 @@ use axum::async_trait; +use axum::body::Body; use axum::extract::{FromRequest, FromRequestParts}; use axum::response::IntoResponse; use http::request::Parts; @@ -109,16 +110,15 @@ impl DerefMut for WithRejection { } #[async_trait] -impl FromRequest for WithRejection +impl FromRequest for WithRejection where - B: Send + 'static, S: Send + Sync, - E: FromRequest, + E: FromRequest, R: From + IntoResponse, { type Rejection = R; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let extractor = E::from_request(req, state).await?; Ok(WithRejection(extractor, PhantomData)) } @@ -180,7 +180,7 @@ mod tests { } } - let req = Request::new(()); + let req = Request::new(Body::empty()); let result = WithRejection::::from_request(req, &()).await; assert!(matches!(result, Err(TestRejection))); diff --git a/axum-extra/src/handler/mod.rs b/axum-extra/src/handler/mod.rs index 305c9c8ccf..275f8418cd 100644 --- a/axum-extra/src/handler/mod.rs +++ b/axum-extra/src/handler/mod.rs @@ -1,5 +1,6 @@ //! Additional handler utilities. +use axum::body::Body; use axum::{ extract::FromRequest, handler::Handler, @@ -19,15 +20,15 @@ pub use self::or::Or; /// /// The drawbacks of this trait is that you cannot apply middleware to individual handlers like you /// can with [`Handler::layer`]. -pub trait HandlerCallWithExtractors: Sized { +pub trait HandlerCallWithExtractors: Sized { /// The type of future calling this handler returns. type Future: Future + Send + 'static; /// Call the handler with the extracted inputs. - fn call(self, extractors: T, state: S) -> >::Future; + fn call(self, extractors: T, state: S) -> >::Future; /// Conver this `HandlerCallWithExtractors` into [`Handler`]. - fn into_handler(self) -> IntoHandler { + fn into_handler(self) -> IntoHandler { IntoHandler { handler: self, _marker: PhantomData, @@ -102,9 +103,9 @@ pub trait HandlerCallWithExtractors: Sized { /// ); /// # let _: Router = app; /// ``` - fn or(self, rhs: R) -> Or + fn or(self, rhs: R) -> Or where - R: HandlerCallWithExtractors, + R: HandlerCallWithExtractors, { Or { lhs: self, @@ -117,7 +118,7 @@ pub trait HandlerCallWithExtractors: Sized { macro_rules! impl_handler_call_with { ( $($ty:ident),* $(,)? ) => { #[allow(non_snake_case)] - impl HandlerCallWithExtractors<($($ty,)*), S, B> for F + impl HandlerCallWithExtractors<($($ty,)*), S> for F where F: FnOnce($($ty,)*) -> Fut, Fut: Future + Send + 'static, @@ -130,7 +131,7 @@ macro_rules! impl_handler_call_with { self, ($($ty,)*): ($($ty,)*), _state: S, - ) -> >::Future { + ) -> >::Future { self($($ty,)*).map(IntoResponse::into_response) } } @@ -159,22 +160,22 @@ impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, /// /// Created with [`HandlerCallWithExtractors::into_handler`]. #[allow(missing_debug_implementations)] -pub struct IntoHandler { +pub struct IntoHandler { handler: H, - _marker: PhantomData (T, S, B)>, + _marker: PhantomData (T, S)>, } -impl Handler for IntoHandler +impl Handler for IntoHandler where - H: HandlerCallWithExtractors + Clone + Send + 'static, - T: FromRequest + Send + 'static, + H: HandlerCallWithExtractors + Clone + Send + 'static, + T: FromRequest + Send + 'static, T::Rejection: Send, - B: Send + 'static, S: Send + Sync + 'static, { type Future = BoxFuture<'static, Response>; - fn call(self, req: http::Request, state: S) -> Self::Future { + fn call(self, req: http::Request, state: S) -> Self::Future { + let req = req.map(Body::new); Box::pin(async move { match T::from_request(req, &state).await { Ok(t) => self.handler.call(t, state).await, @@ -184,9 +185,9 @@ where } } -impl Copy for IntoHandler where H: Copy {} +impl Copy for IntoHandler where H: Copy {} -impl Clone for IntoHandler +impl Clone for IntoHandler where H: Clone, { diff --git a/axum-extra/src/handler/or.rs b/axum-extra/src/handler/or.rs index 2ef24e7145..b23ae5d4ee 100644 --- a/axum-extra/src/handler/or.rs +++ b/axum-extra/src/handler/or.rs @@ -1,6 +1,7 @@ use super::HandlerCallWithExtractors; use crate::either::Either; use axum::{ + body::Body, extract::{FromRequest, FromRequestParts}, handler::Handler, http::Request, @@ -14,19 +15,18 @@ use std::{future::Future, marker::PhantomData}; /// /// Created with [`HandlerCallWithExtractors::or`](super::HandlerCallWithExtractors::or). #[allow(missing_debug_implementations)] -pub struct Or { +pub struct Or { pub(super) lhs: L, pub(super) rhs: R, - pub(super) _marker: PhantomData (Lt, Rt, S, B)>, + pub(super) _marker: PhantomData (Lt, Rt, S)>, } -impl HandlerCallWithExtractors, S, B> for Or +impl HandlerCallWithExtractors, S> for Or where - L: HandlerCallWithExtractors + Send + 'static, - R: HandlerCallWithExtractors + Send + 'static, + L: HandlerCallWithExtractors + Send + 'static, + R: HandlerCallWithExtractors + Send + 'static, Rt: Send + 'static, Lt: Send + 'static, - B: Send + 'static, { // this puts `futures_util` in our public API but thats fine in axum-extra type Future = EitherFuture< @@ -38,7 +38,7 @@ where self, extractors: Either, state: S, - ) -> , S, B>>::Future { + ) -> , S>>::Future { match extractors { Either::E1(lt) => self .lhs @@ -54,21 +54,20 @@ where } } -impl Handler<(M, Lt, Rt), S, B> for Or +impl Handler<(M, Lt, Rt), S> for Or where - L: HandlerCallWithExtractors + Clone + Send + 'static, - R: HandlerCallWithExtractors + Clone + Send + 'static, + L: HandlerCallWithExtractors + Clone + Send + 'static, + R: HandlerCallWithExtractors + Clone + Send + 'static, Lt: FromRequestParts + Send + 'static, - Rt: FromRequest + Send + 'static, + Rt: FromRequest + Send + 'static, Lt::Rejection: Send, Rt::Rejection: Send, - B: Send + 'static, S: Send + Sync + 'static, { // this puts `futures_util` in our public API but thats fine in axum-extra type Future = BoxFuture<'static, Response>; - fn call(self, req: Request, state: S) -> Self::Future { + fn call(self, req: Request, state: S) -> Self::Future { Box::pin(async move { let (mut parts, body) = req.into_parts(); @@ -86,14 +85,14 @@ where } } -impl Copy for Or +impl Copy for Or where L: Copy, R: Copy, { } -impl Clone for Or +impl Clone for Or where L: Clone, R: Clone, diff --git a/axum-extra/src/json_lines.rs b/axum-extra/src/json_lines.rs index 8b04825f2c..912529dd6d 100644 --- a/axum-extra/src/json_lines.rs +++ b/axum-extra/src/json_lines.rs @@ -2,12 +2,12 @@ use axum::{ async_trait, - body::{HttpBody, StreamBody}, + body::{Body, StreamBody}, extract::FromRequest, response::{IntoResponse, Response}, BoxError, }; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::{BufMut, BytesMut}; use futures_util::stream::{BoxStream, Stream, TryStream, TryStreamExt}; use http::Request; use pin_project_lite::pin_project; @@ -101,26 +101,19 @@ impl JsonLines { } #[async_trait] -impl FromRequest for JsonLines +impl FromRequest for JsonLines where - B: HttpBody + Send + 'static, - B::Data: Into, - B::Error: Into, T: DeserializeOwned, S: Send + Sync, { type Rejection = Infallible; - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { // `Stream::lines` isn't a thing so we have to convert it into an `AsyncRead` // so we can call `AsyncRead::lines` and then convert it back to a `Stream` - let body = BodyStream { - body: req.into_body(), - }; + let body = req.into_body(); - let stream = body - .map_ok(Into::into) - .map_err(|err| io::Error::new(io::ErrorKind::Other, err)); + let stream = TryStreamExt::map_err(body, |err| io::Error::new(io::ErrorKind::Other, err)); let read = StreamReader::new(stream); let lines_stream = LinesStream::new(read.lines()); @@ -140,26 +133,6 @@ where } } -// like `axum::extract::BodyStream` except it doesn't box the inner body -// we don't need that since we box the final stream in `Inner::Extractor` -pin_project! { - struct BodyStream { - #[pin] - body: B, - } -} - -impl Stream for BodyStream -where - B: HttpBody + Send + 'static, -{ - type Item = Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.project().body.poll_data(cx) - } -} - impl Stream for JsonLines { type Item = Result; diff --git a/axum-extra/src/protobuf.rs b/axum-extra/src/protobuf.rs index 6da01175e6..f55423ac30 100644 --- a/axum-extra/src/protobuf.rs +++ b/axum-extra/src/protobuf.rs @@ -2,12 +2,11 @@ use axum::{ async_trait, - body::{Bytes, HttpBody}, + body::Body, extract::{rejection::BytesRejection, FromRequest}, response::{IntoResponse, Response}, - BoxError, }; -use bytes::BytesMut; +use bytes::{Bytes, BytesMut}; use http::{Request, StatusCode}; use prost::Message; use std::ops::{Deref, DerefMut}; @@ -98,17 +97,14 @@ use std::ops::{Deref, DerefMut}; pub struct Protobuf(pub T); #[async_trait] -impl FromRequest for Protobuf +impl FromRequest for Protobuf where T: Message + Default, - B: HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, S: Send + Sync, { type Rejection = ProtobufRejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let mut bytes = Bytes::from_request(req, state).await?; match T::decode(&mut bytes) { diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index f65ddf5c92..47d2e84a86 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -1,6 +1,7 @@ //! Additional types for defining routes. use axum::{ + body::Body, http::Request, response::{IntoResponse, Redirect, Response}, routing::{any, MethodRouter}, @@ -26,7 +27,7 @@ pub use axum_macros::TypedPath; pub use self::typed::{SecondElementIs, TypedPath}; /// Extension trait that adds additional methods to [`Router`]. -pub trait RouterExt: sealed::Sealed { +pub trait RouterExt: sealed::Sealed { /// Add a typed `GET` route to the router. /// /// The path will be inferred from the first argument to the handler function which must @@ -36,7 +37,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_get(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath; @@ -49,7 +50,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_delete(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath; @@ -62,7 +63,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_head(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath; @@ -75,7 +76,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_options(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath; @@ -88,7 +89,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_patch(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath; @@ -101,7 +102,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_post(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath; @@ -114,7 +115,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_put(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath; @@ -127,7 +128,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_trace(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath; @@ -156,7 +157,7 @@ pub trait RouterExt: sealed::Sealed { /// .route_with_tsr("/bar/", get(|| async {})); /// # let _: Router = app; /// ``` - fn route_with_tsr(self, path: &str, method_router: MethodRouter) -> Self + fn route_with_tsr(self, path: &str, method_router: MethodRouter) -> Self where Self: Sized; @@ -165,21 +166,20 @@ pub trait RouterExt: sealed::Sealed { /// This works like [`RouterExt::route_with_tsr`] but accepts any [`Service`]. fn route_service_with_tsr(self, path: &str, service: T) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service, Error = Infallible> + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, Self: Sized; } -impl RouterExt for Router +impl RouterExt for Router where - B: axum::body::HttpBody + Send + 'static, S: Clone + Send + Sync + 'static, { #[cfg(feature = "typed-routing")] fn typed_get(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath, { @@ -189,7 +189,7 @@ where #[cfg(feature = "typed-routing")] fn typed_delete(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath, { @@ -199,7 +199,7 @@ where #[cfg(feature = "typed-routing")] fn typed_head(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath, { @@ -209,7 +209,7 @@ where #[cfg(feature = "typed-routing")] fn typed_options(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath, { @@ -219,7 +219,7 @@ where #[cfg(feature = "typed-routing")] fn typed_patch(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath, { @@ -229,7 +229,7 @@ where #[cfg(feature = "typed-routing")] fn typed_post(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath, { @@ -239,7 +239,7 @@ where #[cfg(feature = "typed-routing")] fn typed_put(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath, { @@ -249,7 +249,7 @@ where #[cfg(feature = "typed-routing")] fn typed_trace(self, handler: H) -> Self where - H: axum::handler::Handler, + H: axum::handler::Handler, T: SecondElementIs

+ 'static, P: TypedPath, { @@ -257,7 +257,7 @@ where } #[track_caller] - fn route_with_tsr(mut self, path: &str, method_router: MethodRouter) -> Self + fn route_with_tsr(mut self, path: &str, method_router: MethodRouter) -> Self where Self: Sized, { @@ -269,7 +269,7 @@ where #[track_caller] fn route_service_with_tsr(mut self, path: &str, service: T) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service, Error = Infallible> + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, Self: Sized, @@ -287,9 +287,8 @@ fn validate_tsr_path(path: &str) { } } -fn add_tsr_redirect_route(router: Router, path: &str) -> Router +fn add_tsr_redirect_route(router: Router, path: &str) -> Router where - B: axum::body::HttpBody + Send + 'static, S: Clone + Send + Sync + 'static, { async fn redirect_handler(uri: Uri) -> Response { @@ -337,7 +336,7 @@ where mod sealed { pub trait Sealed {} - impl Sealed for axum::Router {} + impl Sealed for axum::Router {} } #[cfg(test)] diff --git a/axum-extra/src/routing/resource.rs b/axum-extra/src/routing/resource.rs index a4d350bbea..2ab3b9d9e8 100644 --- a/axum-extra/src/routing/resource.rs +++ b/axum-extra/src/routing/resource.rs @@ -1,5 +1,4 @@ use axum::{ - body::Body, handler::Handler, routing::{delete, get, on, post, MethodFilter, MethodRouter}, Router, @@ -34,14 +33,13 @@ use axum::{ /// ``` #[derive(Debug)] #[must_use] -pub struct Resource { +pub struct Resource { pub(crate) name: String, - pub(crate) router: Router, + pub(crate) router: Router, } -impl Resource +impl Resource where - B: axum::body::HttpBody + Send + 'static, S: Clone + Send + Sync + 'static, { /// Create a `Resource` with the given name. @@ -57,7 +55,7 @@ where /// Add a handler at `GET /{resource_name}`. pub fn index(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = self.index_create_path(); @@ -67,7 +65,7 @@ where /// Add a handler at `POST /{resource_name}`. pub fn create(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = self.index_create_path(); @@ -77,7 +75,7 @@ where /// Add a handler at `GET /{resource_name}/new`. pub fn new(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = format!("/{}/new", self.name); @@ -87,7 +85,7 @@ where /// Add a handler at `GET /{resource_name}/:{resource_name}_id`. pub fn show(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = self.show_update_destroy_path(); @@ -97,7 +95,7 @@ where /// Add a handler at `GET /{resource_name}/:{resource_name}_id/edit`. pub fn edit(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = format!("/{0}/:{0}_id/edit", self.name); @@ -107,7 +105,7 @@ where /// Add a handler at `PUT or PATCH /resource_name/:{resource_name}_id`. pub fn update(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = self.show_update_destroy_path(); @@ -117,7 +115,7 @@ where /// Add a handler at `DELETE /{resource_name}/:{resource_name}_id`. pub fn destroy(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = self.show_update_destroy_path(); @@ -132,14 +130,14 @@ where format!("/{0}/:{0}_id", self.name) } - fn route(mut self, path: &str, method_router: MethodRouter) -> Self { + fn route(mut self, path: &str, method_router: MethodRouter) -> Self { self.router = self.router.route(path, method_router); self } } -impl From> for Router { - fn from(resource: Resource) -> Self { +impl From> for Router { + fn from(resource: Resource) -> Self { resource.router } } @@ -148,9 +146,9 @@ impl From> for Router { mod tests { #[allow(unused_imports)] use super::*; - use axum::{extract::Path, http::Method, Router}; + use axum::{body::Body, extract::Path, http::Method, Router}; use http::Request; - use tower::{Service, ServiceExt}; + use tower::ServiceExt; #[tokio::test] async fn works() { @@ -208,10 +206,8 @@ mod tests { async fn call_route(app: &mut Router, method: Method, uri: &str) -> String { let res = app - .ready() - .await - .unwrap() - .call( + .clone() + .oneshot( Request::builder() .method(method) .uri(uri) diff --git a/axum-macros/CHANGELOG.md b/axum-macros/CHANGELOG.md index 2e04bd75ea..1326807fab 100644 --- a/axum-macros/CHANGELOG.md +++ b/axum-macros/CHANGELOG.md @@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased -- None. +- **breaking:** `#[debug_handler]` no longer accepts a `body = _` argument. The + body type is always `axum::body::Body` ([#1751]) + +[#1751]: https://github.com/tokio-rs/axum/pull/1751 # 0.3.7 (22. March, 2023) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index 9b42a1af90..d44371f95a 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -6,14 +6,10 @@ use crate::{ }; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; -use syn::{parse::Parse, parse_quote, spanned::Spanned, FnArg, ItemFn, Token, Type}; +use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn, Token, Type}; pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream { - let Attrs { body_ty, state_ty } = attr; - - let body_ty = body_ty - .map(second) - .unwrap_or_else(|| parse_quote!(axum::body::Body)); + let Attrs { state_ty } = attr; let mut state_ty = state_ty.map(second); @@ -57,7 +53,7 @@ pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream { } } else { let check_inputs_impls_from_request = - check_inputs_impls_from_request(&item_fn, &body_ty, state_ty); + check_inputs_impls_from_request(&item_fn, state_ty); quote! { #check_inputs_impls_from_request @@ -88,20 +84,16 @@ mod kw { } pub(crate) struct Attrs { - body_ty: Option<(kw::body, Type)>, state_ty: Option<(kw::state, Type)>, } impl Parse for Attrs { fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut body_ty = None; let mut state_ty = None; while !input.is_empty() { let lh = input.lookahead1(); - if lh.peek(kw::body) { - parse_assignment_attribute(input, &mut body_ty)?; - } else if lh.peek(kw::state) { + if lh.peek(kw::state) { parse_assignment_attribute(input, &mut state_ty)?; } else { return Err(lh.error()); @@ -110,7 +102,7 @@ impl Parse for Attrs { let _ = input.parse::(); } - Ok(Self { body_ty, state_ty }) + Ok(Self { state_ty }) } } @@ -183,11 +175,7 @@ fn is_self_pat_type(typed: &syn::PatType) -> bool { ident == "self" } -fn check_inputs_impls_from_request( - item_fn: &ItemFn, - body_ty: &Type, - state_ty: Type, -) -> TokenStream { +fn check_inputs_impls_from_request(item_fn: &ItemFn, state_ty: Type) -> TokenStream { let takes_self = item_fn.sig.inputs.first().map_or(false, |arg| match arg { FnArg::Receiver(_) => true, FnArg::Typed(typed) => is_self_pat_type(typed), @@ -266,11 +254,11 @@ fn check_inputs_impls_from_request( } } else if consumes_request { quote_spanned! {span=> - #ty: ::axum::extract::FromRequest<#state_ty, #body_ty> + Send + #ty: ::axum::extract::FromRequest<#state_ty> + Send } } else { quote_spanned! {span=> - #ty: ::axum::extract::FromRequest<#state_ty, #body_ty, M> + Send + #ty: ::axum::extract::FromRequest<#state_ty, M> + Send } }; @@ -379,7 +367,6 @@ fn request_consuming_type_name(ty: &Type) -> Option<&'static str> { let type_name = match &*ident.to_string() { "Json" => "Json<_>", - "BodyStream" => "BodyStream", "RawBody" => "RawBody<_>", "RawForm" => "RawForm", "Multipart" => "Multipart", diff --git a/axum-macros/src/from_request.rs b/axum-macros/src/from_request.rs index 7f6e76c9f7..e9608e7f62 100644 --- a/axum-macros/src/from_request.rs +++ b/axum-macros/src/from_request.rs @@ -19,13 +19,6 @@ pub(crate) enum Trait { } impl Trait { - fn body_type(&self) -> impl Iterator { - match self { - Trait::FromRequest => Some(parse_quote!(B)).into_iter(), - Trait::FromRequestParts => None.into_iter(), - } - } - fn via_marker_type(&self) -> Option { match self { Trait::FromRequest => Some(parse_quote!(M)), @@ -370,14 +363,12 @@ fn impl_struct_by_extracting_each_field( quote!(::axum::response::Response) }; - let impl_generics = tr - .body_type() - .chain(state.impl_generics()) + let impl_generics = state + .impl_generics() .collect::>(); let trait_generics = state .trait_generics() - .chain(tr.body_type()) .collect::>(); let state_bounds = state.bounds(); @@ -388,15 +379,12 @@ fn impl_struct_by_extracting_each_field( #[automatically_derived] impl<#impl_generics> ::axum::extract::FromRequest<#trait_generics> for #ident where - B: ::axum::body::HttpBody + ::std::marker::Send + 'static, - B::Data: ::std::marker::Send, - B::Error: ::std::convert::Into<::axum::BoxError>, #state_bounds { type Rejection = #rejection_ident; async fn from_request( - mut req: ::axum::http::Request, + mut req: ::axum::http::Request<::axum::body::Body>, state: &#state, ) -> ::std::result::Result { #trait_fn_body @@ -749,7 +737,7 @@ fn impl_struct_by_extracting_all_at_once( // struct AppState {} // ``` // - // we need to implement `impl FromRequest` but only for + // we need to implement `impl FromRequest` but only for // - `#[derive(FromRequest)]`, not `#[derive(FromRequestParts)]` // - `State`, not other extractors // @@ -760,16 +748,15 @@ fn impl_struct_by_extracting_all_at_once( None }; - let impl_generics = tr - .body_type() - .chain(via_marker_type.clone()) + let impl_generics = via_marker_type + .iter() + .cloned() .chain(state.impl_generics()) .chain(generic_ident.is_some().then(|| parse_quote!(T))) .collect::>(); let trait_generics = state .trait_generics() - .chain(tr.body_type()) .chain(via_marker_type) .collect::>(); @@ -828,13 +815,12 @@ fn impl_struct_by_extracting_all_at_once( where #via_path<#via_type_generics>: ::axum::extract::FromRequest<#trait_generics>, #rejection_bound - B: ::std::marker::Send + 'static, #state_bounds { type Rejection = #associated_rejection_type; async fn from_request( - req: ::axum::http::Request, + req: ::axum::http::Request<::axum::body::Body>, state: &#state, ) -> ::std::result::Result { ::axum::extract::FromRequest::from_request(req, state) @@ -923,14 +909,12 @@ fn impl_enum_by_extracting_all_at_once( let path_span = path.span(); - let impl_generics = tr - .body_type() - .chain(state.impl_generics()) + let impl_generics = state + .impl_generics() .collect::>(); let trait_generics = state .trait_generics() - .chain(tr.body_type()) .collect::>(); let state_bounds = state.bounds(); @@ -942,15 +926,12 @@ fn impl_enum_by_extracting_all_at_once( #[automatically_derived] impl<#impl_generics> ::axum::extract::FromRequest<#trait_generics> for #ident where - B: ::axum::body::HttpBody + ::std::marker::Send + 'static, - B::Data: ::std::marker::Send, - B::Error: ::std::convert::Into<::axum::BoxError>, #state_bounds { type Rejection = #associated_rejection_type; async fn from_request( - req: ::axum::http::Request, + req: ::axum::http::Request<::axum::body::Body>, state: &#state, ) -> ::std::result::Result { ::axum::extract::FromRequest::from_request(req, state) diff --git a/axum-macros/src/lib.rs b/axum-macros/src/lib.rs index 584d83153f..3327f0b565 100644 --- a/axum-macros/src/lib.rs +++ b/axum-macros/src/lib.rs @@ -147,7 +147,7 @@ use from_request::Trait::{FromRequest, FromRequestParts}; /// ``` /// pub struct ViaExtractor(pub T); /// -/// // impl FromRequest for ViaExtractor { ... } +/// // impl FromRequest for ViaExtractor { ... } /// ``` /// /// More complex via extractors are not supported and require writing a manual implementation. @@ -480,21 +480,6 @@ pub fn derive_from_request_parts(item: TokenStream) -> TokenStream { /// } /// ``` /// -/// # Changing request body type -/// -/// By default `#[debug_handler]` assumes your request body type is `axum::body::Body`. This will -/// work for most extractors but, for example, it wont work for `Request`, -/// which only implements `FromRequest` and _not_ `FromRequest`. -/// -/// To work around that the request body type can be customized like so: -/// -/// ``` -/// use axum::{body::BoxBody, http::Request, debug_handler}; -/// -/// #[debug_handler(body = BoxBody)] -/// async fn handler(request: Request) {} -/// ``` -/// /// # Changing state type /// /// By default `#[debug_handler]` assumes your state type is `()` unless your handler has a diff --git a/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr b/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr index 360163cade..8be92b7913 100644 --- a/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr +++ b/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr @@ -16,7 +16,7 @@ error[E0277]: the trait bound `bool: FromRequestParts<()>` is not satisfied <(T1, T2, T3, T4, T5, T6, T7) as FromRequestParts> <(T1, T2, T3, T4, T5, T6, T7, T8) as FromRequestParts> and 26 others - = note: required for `bool` to implement `FromRequest<(), Body, axum_core::extract::private::ViaParts>` + = note: required for `bool` to implement `FromRequest<(), axum_core::extract::private::ViaParts>` note: required by a bound in `__axum_macros_check_handler_0_from_request_check` --> tests/debug_handler/fail/argument_not_extractor.rs:4:23 | diff --git a/axum-macros/tests/debug_handler/fail/duplicate_args.rs b/axum-macros/tests/debug_handler/fail/duplicate_args.rs index dca335bd3c..4fc7c90fd6 100644 --- a/axum-macros/tests/debug_handler/fail/duplicate_args.rs +++ b/axum-macros/tests/debug_handler/fail/duplicate_args.rs @@ -1,9 +1,6 @@ use axum_macros::debug_handler; -#[debug_handler(body = BoxBody, body = BoxBody)] -async fn handler() {} - #[debug_handler(state = (), state = ())] -async fn handler_2() {} +async fn handler() {} fn main() {} diff --git a/axum-macros/tests/debug_handler/fail/duplicate_args.stderr b/axum-macros/tests/debug_handler/fail/duplicate_args.stderr index 694b6cb3ee..bed70f774a 100644 --- a/axum-macros/tests/debug_handler/fail/duplicate_args.stderr +++ b/axum-macros/tests/debug_handler/fail/duplicate_args.stderr @@ -1,11 +1,5 @@ -error: `body` specified more than once - --> tests/debug_handler/fail/duplicate_args.rs:3:33 - | -3 | #[debug_handler(body = BoxBody, body = BoxBody)] - | ^^^^ - error: `state` specified more than once - --> tests/debug_handler/fail/duplicate_args.rs:6:29 + --> tests/debug_handler/fail/duplicate_args.rs:3:29 | -6 | #[debug_handler(state = (), state = ())] +3 | #[debug_handler(state = (), state = ())] | ^^^^^ diff --git a/axum-macros/tests/debug_handler/fail/extract_self_mut.rs b/axum-macros/tests/debug_handler/fail/extract_self_mut.rs index d20426e22f..1d45b50494 100644 --- a/axum-macros/tests/debug_handler/fail/extract_self_mut.rs +++ b/axum-macros/tests/debug_handler/fail/extract_self_mut.rs @@ -8,14 +8,13 @@ use axum_macros::debug_handler; struct A; #[async_trait] -impl FromRequest for A +impl FromRequest for A where - B: Send + 'static, S: Send + Sync, { type Rejection = (); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { unimplemented!() } } diff --git a/axum-macros/tests/debug_handler/fail/extract_self_mut.stderr b/axum-macros/tests/debug_handler/fail/extract_self_mut.stderr index 1e1a9ec384..3d80dffbca 100644 --- a/axum-macros/tests/debug_handler/fail/extract_self_mut.stderr +++ b/axum-macros/tests/debug_handler/fail/extract_self_mut.stderr @@ -1,5 +1,5 @@ error: Handlers must only take owned values - --> tests/debug_handler/fail/extract_self_mut.rs:25:22 + --> tests/debug_handler/fail/extract_self_mut.rs:24:22 | -25 | async fn handler(&mut self) {} +24 | async fn handler(&mut self) {} | ^^^^^^^^^ diff --git a/axum-macros/tests/debug_handler/fail/extract_self_ref.rs b/axum-macros/tests/debug_handler/fail/extract_self_ref.rs index 77940e2996..e5c3bb032d 100644 --- a/axum-macros/tests/debug_handler/fail/extract_self_ref.rs +++ b/axum-macros/tests/debug_handler/fail/extract_self_ref.rs @@ -8,14 +8,13 @@ use axum_macros::debug_handler; struct A; #[async_trait] -impl FromRequest for A +impl FromRequest for A where - B: Send + 'static, S: Send + Sync, { type Rejection = (); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { unimplemented!() } } diff --git a/axum-macros/tests/debug_handler/fail/extract_self_ref.stderr b/axum-macros/tests/debug_handler/fail/extract_self_ref.stderr index 79f9d190f5..82d9a89ff5 100644 --- a/axum-macros/tests/debug_handler/fail/extract_self_ref.stderr +++ b/axum-macros/tests/debug_handler/fail/extract_self_ref.stderr @@ -1,5 +1,5 @@ error: Handlers must only take owned values - --> tests/debug_handler/fail/extract_self_ref.rs:25:22 + --> tests/debug_handler/fail/extract_self_ref.rs:24:22 | -25 | async fn handler(&self) {} +24 | async fn handler(&self) {} | ^^^^^ diff --git a/axum-macros/tests/debug_handler/fail/invalid_attrs.stderr b/axum-macros/tests/debug_handler/fail/invalid_attrs.stderr index 93514ebfe5..d2db79b336 100644 --- a/axum-macros/tests/debug_handler/fail/invalid_attrs.stderr +++ b/axum-macros/tests/debug_handler/fail/invalid_attrs.stderr @@ -1,4 +1,4 @@ -error: expected `body` or `state` +error: expected `state` --> tests/debug_handler/fail/invalid_attrs.rs:3:17 | 3 | #[debug_handler(foo)] diff --git a/axum-macros/tests/debug_handler/fail/json_not_deserialize.stderr b/axum-macros/tests/debug_handler/fail/json_not_deserialize.stderr index e93ae20ccd..6868e400b2 100644 --- a/axum-macros/tests/debug_handler/fail/json_not_deserialize.stderr +++ b/axum-macros/tests/debug_handler/fail/json_not_deserialize.stderr @@ -15,6 +15,6 @@ error[E0277]: the trait bound `for<'de> Struct: serde::de::Deserialize<'de>` is (T0, T1, T2, T3) and $N others = note: required for `Struct` to implement `serde::de::DeserializeOwned` - = note: required for `Json` to implement `FromRequest<(), Body>` + = note: required for `Json` to implement `FromRequest<()>` = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable diff --git a/axum-macros/tests/debug_handler/pass/different_request_body_type.rs b/axum-macros/tests/debug_handler/pass/different_request_body_type.rs deleted file mode 100644 index 715e5aec19..0000000000 --- a/axum-macros/tests/debug_handler/pass/different_request_body_type.rs +++ /dev/null @@ -1,10 +0,0 @@ -use axum::{body::BoxBody, http::Request}; -use axum_macros::debug_handler; - -#[debug_handler(body = BoxBody)] -async fn handler(_: Request) {} - -#[debug_handler(body = axum::body::BoxBody,)] -async fn handler_with_trailing_comma_and_type_path(_: Request) {} - -fn main() {} diff --git a/axum-macros/tests/debug_handler/pass/self_receiver.rs b/axum-macros/tests/debug_handler/pass/self_receiver.rs index e7bf81ce6c..2b4dfa4d6b 100644 --- a/axum-macros/tests/debug_handler/pass/self_receiver.rs +++ b/axum-macros/tests/debug_handler/pass/self_receiver.rs @@ -8,27 +8,25 @@ use axum_macros::debug_handler; struct A; #[async_trait] -impl FromRequest for A +impl FromRequest for A where - B: Send + 'static, S: Send + Sync, { type Rejection = (); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { unimplemented!() } } #[async_trait] -impl FromRequest for Box +impl FromRequest for Box where - B: Send + 'static, S: Send + Sync, { type Rejection = (); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { unimplemented!() } } diff --git a/axum-macros/tests/debug_handler/pass/set_state.rs b/axum-macros/tests/debug_handler/pass/set_state.rs index 5c84dbd25b..7c06742e54 100644 --- a/axum-macros/tests/debug_handler/pass/set_state.rs +++ b/axum-macros/tests/debug_handler/pass/set_state.rs @@ -12,15 +12,14 @@ struct AppState; struct A; #[async_trait] -impl FromRequest for A +impl FromRequest for A where - B: Send + 'static, S: Send + Sync, AppState: FromRef, { type Rejection = (); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { unimplemented!() } } diff --git a/axum-macros/tests/debug_handler/pass/state_and_body.rs b/axum-macros/tests/debug_handler/pass/state_and_body.rs index 7e1525f524..fea3700745 100644 --- a/axum-macros/tests/debug_handler/pass/state_and_body.rs +++ b/axum-macros/tests/debug_handler/pass/state_and_body.rs @@ -1,8 +1,8 @@ use axum_macros::debug_handler; -use axum::{body::BoxBody, extract::State, http::Request}; +use axum::{extract::State, http::Request}; -#[debug_handler(state = AppState, body = BoxBody)] -async fn handler(_: State, _: Request) {} +#[debug_handler(state = AppState)] +async fn handler(_: State, _: Request) {} #[derive(Clone)] struct AppState; diff --git a/axum-macros/tests/from_request/fail/generic_without_via.rs b/axum-macros/tests/from_request/fail/generic_without_via.rs index 38eaa437a3..f0d54acfa9 100644 --- a/axum-macros/tests/from_request/fail/generic_without_via.rs +++ b/axum-macros/tests/from_request/fail/generic_without_via.rs @@ -1,4 +1,4 @@ -use axum::{body::Body, routing::get, Router}; +use axum::{routing::get, Router}; use axum_macros::FromRequest; #[derive(FromRequest, Clone)] @@ -7,5 +7,5 @@ struct Extractor(T); async fn foo(_: Extractor<()>) {} fn main() { - Router::<(), Body>::new().route("/", get(foo)); + _ = Router::<()>::new().route("/", get(foo)); } diff --git a/axum-macros/tests/from_request/fail/generic_without_via.stderr b/axum-macros/tests/from_request/fail/generic_without_via.stderr index 7630c9bf75..e875b443b7 100644 --- a/axum-macros/tests/from_request/fail/generic_without_via.stderr +++ b/axum-macros/tests/from_request/fail/generic_without_via.stderr @@ -4,21 +4,21 @@ error: #[derive(FromRequest)] only supports generics when used with #[from_reque 5 | struct Extractor(T); | ^ -error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future {foo}: Handler<_, _, _>` is not satisfied - --> tests/from_request/fail/generic_without_via.rs:10:46 - | -10 | Router::<(), Body>::new().route("/", get(foo)); - | --- ^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future {foo}` - | | - | required by a bound introduced by this call - | - = note: Consider using `#[axum::debug_handler]` to improve the error message - = help: the following other types implement trait `Handler`: - as Handler> - as Handler<(), S, B>> +error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future {foo}: Handler<_, _>` is not satisfied + --> tests/from_request/fail/generic_without_via.rs:10:44 + | +10 | _ = Router::<()>::new().route("/", get(foo)); + | --- ^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future {foo}` + | | + | required by a bound introduced by this call + | + = note: Consider using `#[axum::debug_handler]` to improve the error message + = help: the following other types implement trait `Handler`: + as Handler> + as Handler<(), S>> note: required by a bound in `axum::routing::get` - --> $WORKSPACE/axum/src/routing/method_routing.rs - | - | top_level_handler_fn!(get, GET); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get` - = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) + --> $WORKSPACE/axum/src/routing/method_routing.rs + | + | top_level_handler_fn!(get, GET); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get` + = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/axum-macros/tests/from_request/fail/generic_without_via_rejection.rs b/axum-macros/tests/from_request/fail/generic_without_via_rejection.rs index 38d6b0910b..b1ce072cb4 100644 --- a/axum-macros/tests/from_request/fail/generic_without_via_rejection.rs +++ b/axum-macros/tests/from_request/fail/generic_without_via_rejection.rs @@ -1,4 +1,4 @@ -use axum::{body::Body, routing::get, Router}; +use axum::{routing::get, Router}; use axum_macros::FromRequest; #[derive(FromRequest, Clone)] @@ -8,5 +8,5 @@ struct Extractor(T); async fn foo(_: Extractor<()>) {} fn main() { - Router::<(), Body>::new().route("/", get(foo)); + _ = Router::<()>::new().route("/", get(foo)); } diff --git a/axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr b/axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr index 65f0e64d74..167b4a3681 100644 --- a/axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr +++ b/axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr @@ -4,21 +4,21 @@ error: #[derive(FromRequest)] only supports generics when used with #[from_reque 6 | struct Extractor(T); | ^ -error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future {foo}: Handler<_, _, _>` is not satisfied - --> tests/from_request/fail/generic_without_via_rejection.rs:11:46 - | -11 | Router::<(), Body>::new().route("/", get(foo)); - | --- ^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future {foo}` - | | - | required by a bound introduced by this call - | - = note: Consider using `#[axum::debug_handler]` to improve the error message - = help: the following other types implement trait `Handler`: - as Handler> - as Handler<(), S, B>> +error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future {foo}: Handler<_, _>` is not satisfied + --> tests/from_request/fail/generic_without_via_rejection.rs:11:44 + | +11 | _ = Router::<()>::new().route("/", get(foo)); + | --- ^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future {foo}` + | | + | required by a bound introduced by this call + | + = note: Consider using `#[axum::debug_handler]` to improve the error message + = help: the following other types implement trait `Handler`: + as Handler> + as Handler<(), S>> note: required by a bound in `axum::routing::get` - --> $WORKSPACE/axum/src/routing/method_routing.rs - | - | top_level_handler_fn!(get, GET); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get` - = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) + --> $WORKSPACE/axum/src/routing/method_routing.rs + | + | top_level_handler_fn!(get, GET); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get` + = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/axum-macros/tests/from_request/fail/override_rejection_on_enum_without_via.stderr b/axum-macros/tests/from_request/fail/override_rejection_on_enum_without_via.stderr index d5a11d81d0..7ed7dea626 100644 --- a/axum-macros/tests/from_request/fail/override_rejection_on_enum_without_via.stderr +++ b/axum-macros/tests/from_request/fail/override_rejection_on_enum_without_via.stderr @@ -4,18 +4,18 @@ error: cannot use `rejection` without `via` 18 | #[from_request(rejection(MyRejection))] | ^^^^^^^^^ -error[E0277]: the trait bound `fn(MyExtractor) -> impl Future {handler}: Handler<_, _, _>` is not satisfied +error[E0277]: the trait bound `fn(MyExtractor) -> impl Future {handler}: Handler<_, _>` is not satisfied --> tests/from_request/fail/override_rejection_on_enum_without_via.rs:10:50 | 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); - | --- ^^^^^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(MyExtractor) -> impl Future {handler}` + | --- ^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(MyExtractor) -> impl Future {handler}` | | | required by a bound introduced by this call | = note: Consider using `#[axum::debug_handler]` to improve the error message - = help: the following other types implement trait `Handler`: - as Handler> - as Handler<(), S, B>> + = help: the following other types implement trait `Handler`: + as Handler> + as Handler<(), S>> note: required by a bound in `axum::routing::get` --> $WORKSPACE/axum/src/routing/method_routing.rs | @@ -23,21 +23,21 @@ note: required by a bound in `axum::routing::get` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get` = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `fn(Result) -> impl Future {handler_result}: Handler<_, _, _>` is not satisfied +error[E0277]: the trait bound `fn(Result) -> impl Future {handler_result}: Handler<_, _>` is not satisfied --> tests/from_request/fail/override_rejection_on_enum_without_via.rs:10:64 | 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); - | ---- ^^^^^^^^^^^^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(Result) -> impl Future {handler_result}` + | ---- ^^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Result) -> impl Future {handler_result}` | | | required by a bound introduced by this call | = note: Consider using `#[axum::debug_handler]` to improve the error message - = help: the following other types implement trait `Handler`: - as Handler> - as Handler<(), S, B>> -note: required by a bound in `MethodRouter::::post` + = help: the following other types implement trait `Handler`: + as Handler> + as Handler<(), S>> +note: required by a bound in `MethodRouter::::post` --> $WORKSPACE/axum/src/routing/method_routing.rs | | chained_handler_fn!(post, POST); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `MethodRouter::::post` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `MethodRouter::::post` = note: this error originates in the macro `chained_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/axum-macros/tests/from_request/fail/state_infer_multiple_different_types.rs b/axum-macros/tests/from_request/fail/state_infer_multiple_different_types.rs index 57400377ef..6533d3276a 100644 --- a/axum-macros/tests/from_request/fail/state_infer_multiple_different_types.rs +++ b/axum-macros/tests/from_request/fail/state_infer_multiple_different_types.rs @@ -15,7 +15,7 @@ struct OtherState {} fn assert_from_request() where - Extractor: axum::extract::FromRequest, + Extractor: axum::extract::FromRequest, { } diff --git a/axum-macros/tests/from_request/pass/container.rs b/axum-macros/tests/from_request/pass/container.rs index 6e62c569a4..9d4e0666e9 100644 --- a/axum-macros/tests/from_request/pass/container.rs +++ b/axum-macros/tests/from_request/pass/container.rs @@ -1,5 +1,4 @@ use axum::{ - body::Body, extract::{FromRequest, Json}, response::Response, }; @@ -15,7 +14,7 @@ struct Extractor { fn assert_from_request() where - Extractor: FromRequest<(), Body, Rejection = Response>, + Extractor: FromRequest<(), Rejection = Response>, { } diff --git a/axum-macros/tests/from_request/pass/empty_named.rs b/axum-macros/tests/from_request/pass/empty_named.rs index eec021d0f5..63af28f5d0 100644 --- a/axum-macros/tests/from_request/pass/empty_named.rs +++ b/axum-macros/tests/from_request/pass/empty_named.rs @@ -5,7 +5,7 @@ struct Extractor {} fn assert_from_request() where - Extractor: axum::extract::FromRequest<(), axum::body::Body, Rejection = std::convert::Infallible>, + Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>, { } diff --git a/axum-macros/tests/from_request/pass/empty_tuple.rs b/axum-macros/tests/from_request/pass/empty_tuple.rs index 3d8bcd25c0..b740cb8374 100644 --- a/axum-macros/tests/from_request/pass/empty_tuple.rs +++ b/axum-macros/tests/from_request/pass/empty_tuple.rs @@ -5,7 +5,7 @@ struct Extractor(); fn assert_from_request() where - Extractor: axum::extract::FromRequest<(), axum::body::Body, Rejection = std::convert::Infallible>, + Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>, { } diff --git a/axum-macros/tests/from_request/pass/enum_via.rs b/axum-macros/tests/from_request/pass/enum_via.rs index c68b9796a7..17c6ba80f3 100644 --- a/axum-macros/tests/from_request/pass/enum_via.rs +++ b/axum-macros/tests/from_request/pass/enum_via.rs @@ -1,4 +1,4 @@ -use axum::{body::Body, routing::get, Extension, Router}; +use axum::{routing::get, Extension, Router}; use axum_macros::FromRequest; #[derive(FromRequest, Clone)] @@ -8,5 +8,5 @@ enum Extractor {} async fn foo(_: Extractor) {} fn main() { - Router::<(), Body>::new().route("/", get(foo)); + _ = Router::<()>::new().route("/", get(foo)); } diff --git a/axum-macros/tests/from_request/pass/enum_via_parts.rs b/axum-macros/tests/from_request/pass/enum_via_parts.rs index 5e18d9228d..049598c339 100644 --- a/axum-macros/tests/from_request/pass/enum_via_parts.rs +++ b/axum-macros/tests/from_request/pass/enum_via_parts.rs @@ -1,4 +1,4 @@ -use axum::{body::Body, routing::get, Extension, Router}; +use axum::{routing::get, Extension, Router}; use axum_macros::FromRequestParts; #[derive(FromRequestParts, Clone)] @@ -8,5 +8,5 @@ enum Extractor {} async fn foo(_: Extractor) {} fn main() { - Router::<(), Body>::new().route("/", get(foo)); + _ = Router::<()>::new().route("/", get(foo)); } diff --git a/axum-macros/tests/from_request/pass/named.rs b/axum-macros/tests/from_request/pass/named.rs index e042477b90..e775d41d84 100644 --- a/axum-macros/tests/from_request/pass/named.rs +++ b/axum-macros/tests/from_request/pass/named.rs @@ -1,5 +1,4 @@ use axum::{ - body::Body, extract::{FromRequest, TypedHeader, rejection::TypedHeaderRejection}, response::Response, headers::{self, UserAgent}, @@ -17,7 +16,7 @@ struct Extractor { fn assert_from_request() where - Extractor: FromRequest<(), Body, Rejection = Response>, + Extractor: FromRequest<(), Rejection = Response>, { } diff --git a/axum-macros/tests/from_request/pass/named_via.rs b/axum-macros/tests/from_request/pass/named_via.rs index 41cc361556..be2e8c67a6 100644 --- a/axum-macros/tests/from_request/pass/named_via.rs +++ b/axum-macros/tests/from_request/pass/named_via.rs @@ -1,5 +1,4 @@ use axum::{ - body::Body, response::Response, extract::{ rejection::TypedHeaderRejection, @@ -24,7 +23,7 @@ struct Extractor { fn assert_from_request() where - Extractor: FromRequest<(), Body, Rejection = Response>, + Extractor: FromRequest<(), Rejection = Response>, { } diff --git a/axum-macros/tests/from_request/pass/override_rejection.rs b/axum-macros/tests/from_request/pass/override_rejection.rs index 0147c9a8b3..db341b792e 100644 --- a/axum-macros/tests/from_request/pass/override_rejection.rs +++ b/axum-macros/tests/from_request/pass/override_rejection.rs @@ -4,6 +4,7 @@ use axum::{ http::{StatusCode, Request}, response::{IntoResponse, Response}, routing::get, + body::Body, Extension, Router, }; @@ -27,15 +28,14 @@ struct MyExtractor { struct OtherExtractor; #[async_trait] -impl FromRequest for OtherExtractor +impl FromRequest for OtherExtractor where - B: Send + 'static, S: Send + Sync, { // this rejection doesn't implement `Display` and `Error` type Rejection = (StatusCode, String); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { todo!() } } diff --git a/axum-macros/tests/from_request/pass/state_cookie.rs b/axum-macros/tests/from_request/pass/state_cookie.rs index a4f46c6acd..6e2aa1f4ed 100644 --- a/axum-macros/tests/from_request/pass/state_cookie.rs +++ b/axum-macros/tests/from_request/pass/state_cookie.rs @@ -20,7 +20,7 @@ impl FromRef for Key { fn assert_from_request() where - Extractor: axum::extract::FromRequest, + Extractor: axum::extract::FromRequest, { } diff --git a/axum-macros/tests/from_request/pass/state_infer.rs b/axum-macros/tests/from_request/pass/state_infer.rs index 5290614991..07545ab074 100644 --- a/axum-macros/tests/from_request/pass/state_infer.rs +++ b/axum-macros/tests/from_request/pass/state_infer.rs @@ -11,7 +11,7 @@ struct AppState {} fn assert_from_request() where - Extractor: axum::extract::FromRequest, + Extractor: axum::extract::FromRequest, { } diff --git a/axum-macros/tests/from_request/pass/state_infer_multiple.rs b/axum-macros/tests/from_request/pass/state_infer_multiple.rs index 6729e61572..cb8de1d59c 100644 --- a/axum-macros/tests/from_request/pass/state_infer_multiple.rs +++ b/axum-macros/tests/from_request/pass/state_infer_multiple.rs @@ -12,7 +12,7 @@ struct AppState {} fn assert_from_request() where - Extractor: axum::extract::FromRequest, + Extractor: axum::extract::FromRequest, { } diff --git a/axum-macros/tests/from_request/pass/tuple.rs b/axum-macros/tests/from_request/pass/tuple.rs index 2af407d0f9..85a409817e 100644 --- a/axum-macros/tests/from_request/pass/tuple.rs +++ b/axum-macros/tests/from_request/pass/tuple.rs @@ -5,7 +5,7 @@ struct Extractor(axum::http::HeaderMap, String); fn assert_from_request() where - Extractor: axum::extract::FromRequest<(), axum::body::Body>, + Extractor: axum::extract::FromRequest<()>, { } diff --git a/axum-macros/tests/from_request/pass/tuple_same_type_twice.rs b/axum-macros/tests/from_request/pass/tuple_same_type_twice.rs index 227e4a3c8f..343563ddb6 100644 --- a/axum-macros/tests/from_request/pass/tuple_same_type_twice.rs +++ b/axum-macros/tests/from_request/pass/tuple_same_type_twice.rs @@ -13,7 +13,7 @@ struct Payload {} fn assert_from_request() where - Extractor: axum::extract::FromRequest<(), axum::body::Body>, + Extractor: axum::extract::FromRequest<()>, { } diff --git a/axum-macros/tests/from_request/pass/tuple_same_type_twice_via.rs b/axum-macros/tests/from_request/pass/tuple_same_type_twice_via.rs index 82342c56c5..ab0ee467f6 100644 --- a/axum-macros/tests/from_request/pass/tuple_same_type_twice_via.rs +++ b/axum-macros/tests/from_request/pass/tuple_same_type_twice_via.rs @@ -14,7 +14,7 @@ struct Payload {} fn assert_from_request() where - Extractor: axum::extract::FromRequest<(), axum::body::Body, Rejection = Response>, + Extractor: axum::extract::FromRequest<(), Rejection = Response>, { } diff --git a/axum-macros/tests/from_request/pass/tuple_via.rs b/axum-macros/tests/from_request/pass/tuple_via.rs index 03a9e3610c..3b62287ed8 100644 --- a/axum-macros/tests/from_request/pass/tuple_via.rs +++ b/axum-macros/tests/from_request/pass/tuple_via.rs @@ -9,7 +9,7 @@ struct State; fn assert_from_request() where - Extractor: axum::extract::FromRequest<(), axum::body::Body>, + Extractor: axum::extract::FromRequest<()>, { } diff --git a/axum-macros/tests/from_request/pass/unit.rs b/axum-macros/tests/from_request/pass/unit.rs index 3e5d986917..9a4dc1dd44 100644 --- a/axum-macros/tests/from_request/pass/unit.rs +++ b/axum-macros/tests/from_request/pass/unit.rs @@ -5,7 +5,7 @@ struct Extractor; fn assert_from_request() where - Extractor: axum::extract::FromRequest<(), axum::body::Body, Rejection = std::convert::Infallible>, + Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>, { } diff --git a/axum-macros/tests/typed_path/pass/customize_rejection.rs b/axum-macros/tests/typed_path/pass/customize_rejection.rs index 40f3ec0ada..01f11fc94c 100644 --- a/axum-macros/tests/typed_path/pass/customize_rejection.rs +++ b/axum-macros/tests/typed_path/pass/customize_rejection.rs @@ -40,7 +40,7 @@ impl Default for MyRejection { } fn main() { - axum::Router::<(), axum::body::Body>::new() + _ = axum::Router::<()>::new() .typed_get(|_: Result| async {}) .typed_post(|_: Result| async {}) .typed_put(|_: Result| async {}); diff --git a/axum-macros/tests/typed_path/pass/named_fields_struct.rs b/axum-macros/tests/typed_path/pass/named_fields_struct.rs index 6119304080..042936fe02 100644 --- a/axum-macros/tests/typed_path/pass/named_fields_struct.rs +++ b/axum-macros/tests/typed_path/pass/named_fields_struct.rs @@ -9,7 +9,7 @@ struct MyPath { } fn main() { - axum::Router::<(), axum::body::Body>::new().route("/", axum::routing::get(|_: MyPath| async {})); + _ = axum::Router::<()>::new().route("/", axum::routing::get(|_: MyPath| async {})); assert_eq!(MyPath::PATH, "/users/:user_id/teams/:team_id"); assert_eq!( diff --git a/axum-macros/tests/typed_path/pass/option_result.rs b/axum-macros/tests/typed_path/pass/option_result.rs index bd4c6dc282..1bd2359010 100644 --- a/axum-macros/tests/typed_path/pass/option_result.rs +++ b/axum-macros/tests/typed_path/pass/option_result.rs @@ -19,7 +19,7 @@ struct UsersIndex; async fn result_handler_unit_struct(_: Result) {} fn main() { - axum::Router::<(), axum::body::Body>::new() + _ = axum::Router::<()>::new() .typed_get(option_handler) .typed_post(result_handler) .typed_post(result_handler_unit_struct); diff --git a/axum-macros/tests/typed_path/pass/tuple_struct.rs b/axum-macros/tests/typed_path/pass/tuple_struct.rs index 4f8fa17eeb..3ee8370402 100644 --- a/axum-macros/tests/typed_path/pass/tuple_struct.rs +++ b/axum-macros/tests/typed_path/pass/tuple_struct.rs @@ -8,7 +8,7 @@ pub type Result = std::result::Result; struct MyPath(u32, u32); fn main() { - axum::Router::<(), axum::body::Body>::new().route("/", axum::routing::get(|_: MyPath| async {})); + _ = axum::Router::<()>::new().route("/", axum::routing::get(|_: MyPath| async {})); assert_eq!(MyPath::PATH, "/users/:user_id/teams/:team_id"); assert_eq!(format!("{}", MyPath(1, 2)), "/users/1/teams/2"); diff --git a/axum-macros/tests/typed_path/pass/unit_struct.rs b/axum-macros/tests/typed_path/pass/unit_struct.rs index 0ba27f81ac..f3bb164075 100644 --- a/axum-macros/tests/typed_path/pass/unit_struct.rs +++ b/axum-macros/tests/typed_path/pass/unit_struct.rs @@ -5,7 +5,7 @@ use axum_extra::routing::TypedPath; struct MyPath; fn main() { - axum::Router::<(), axum::body::Body>::new() + _ = axum::Router::<()>::new() .route("/", axum::routing::get(|_: MyPath| async {})); assert_eq!(MyPath::PATH, "/users"); diff --git a/axum-macros/tests/typed_path/pass/wildcards.rs b/axum-macros/tests/typed_path/pass/wildcards.rs index e7794fc895..98aa5f5153 100644 --- a/axum-macros/tests/typed_path/pass/wildcards.rs +++ b/axum-macros/tests/typed_path/pass/wildcards.rs @@ -8,5 +8,5 @@ struct MyPath { } fn main() { - axum::Router::<(), axum::body::Body>::new().typed_get(|_: MyPath| async {}); + _ = axum::Router::<()>::new().typed_get(|_: MyPath| async {}); } diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index d8bd1b7ed4..3db88eb8b4 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -7,12 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased +- **breaking:** The following types/traits are no longer generic over the request body + (i.e. the `B` type param has been removed) ([#1751]): + - `FromRequest` + - `FromRequestParts` + - `Handler` + - `HandlerService` + - `HandlerWithoutStateExt` + - `Layered` + - `LayeredFuture` + - `MethodRouter` + - `RequestExt` + - `Route` + - `RouteFuture` + - `Router` +- **breaking:** axum no longer re-exports `hyper::Body` as that type is removed + in hyper 1.0. Instead axum has its own body type at `axum::body::Body` ([#1751]) +- **breaking:** `extract::BodyStream` has been removed as `body::Body` + implements `Stream` and `FromRequest` directly ([#1751]) - **breaking:** Change `sse::Event::json_data` to use `axum_core::Error` as its error type ([#1762]) - **breaking:** Rename `DefaultOnFailedUpdgrade` to `DefaultOnFailedUpgrade` ([#1664]) - **breaking:** Rename `OnFailedUpdgrade` to `OnFailedUpgrade` ([#1664]) +- **added:** Add `Router::as_service` and `Router::into_service` to workaround + type inference issues when calling `ServiceExt` methods on a `Router` ([#1835]) -[#1762]: https://github.com/tokio-rs/axum/pull/1762 [#1664]: https://github.com/tokio-rs/axum/pull/1664 +[#1751]: https://github.com/tokio-rs/axum/pull/1751 +[#1762]: https://github.com/tokio-rs/axum/pull/1762 +[#1835]: https://github.com/tokio-rs/axum/pull/1835 # 0.6.12 (22. March, 2023) diff --git a/axum/src/body/mod.rs b/axum/src/body/mod.rs index 4eceec0ced..9e1e826fff 100644 --- a/axum/src/body/mod.rs +++ b/axum/src/body/mod.rs @@ -7,11 +7,8 @@ pub use self::stream_body::StreamBody; #[doc(no_inline)] pub use http_body::{Body as HttpBody, Empty, Full}; -#[doc(no_inline)] -pub use hyper::body::Body; - #[doc(no_inline)] pub use bytes::Bytes; #[doc(inline)] -pub use axum_core::body::{boxed, BoxBody}; +pub use axum_core::body::{boxed, Body, BoxBody}; diff --git a/axum/src/body/stream_body.rs b/axum/src/body/stream_body.rs index 1c2955eaa9..df782c6987 100644 --- a/axum/src/body/stream_body.rs +++ b/axum/src/body/stream_body.rs @@ -21,7 +21,7 @@ pin_project! { /// /// The purpose of this type is to be used in responses. If you want to /// extract the request body as a stream consider using - /// [`BodyStream`](crate::extract::BodyStream). + /// [`Body`](crate::body::Body). /// /// # Example /// diff --git a/axum/src/boxed.rs b/axum/src/boxed.rs index 6aaea39a66..54a1a89d14 100644 --- a/axum/src/boxed.rs +++ b/axum/src/boxed.rs @@ -1,27 +1,25 @@ use std::{convert::Infallible, fmt}; +use axum_core::body::Body; use http::Request; use tower::Service; use crate::{ - body::HttpBody, handler::Handler, routing::{future::RouteFuture, Route}, Router, }; -pub(crate) struct BoxedIntoRoute(Box>); +pub(crate) struct BoxedIntoRoute(Box>); -impl BoxedIntoRoute +impl BoxedIntoRoute where S: Clone + Send + Sync + 'static, - B: Send + 'static, { pub(crate) fn from_handler(handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, - B: HttpBody, { Self(Box::new(MakeErasedHandler { handler, @@ -29,9 +27,8 @@ where })) } - pub(crate) fn from_router(router: Router) -> Self + pub(crate) fn from_router(router: Router) -> Self where - B: HttpBody + Send + 'static, S: Clone + Send + Sync + 'static, { Self(Box::new(MakeErasedRouter { @@ -42,21 +39,19 @@ where pub(crate) fn call_with_state( self, - request: Request, + request: Request, state: S, - ) -> RouteFuture { + ) -> RouteFuture { self.0.call_with_state(request, state) } } -impl BoxedIntoRoute { - pub(crate) fn map(self, f: F) -> BoxedIntoRoute +impl BoxedIntoRoute { + pub(crate) fn map(self, f: F) -> BoxedIntoRoute where S: 'static, - B: 'static, E: 'static, - F: FnOnce(Route) -> Route + Clone + Send + 'static, - B2: HttpBody + 'static, + F: FnOnce(Route) -> Route + Clone + Send + 'static, E2: 'static, { BoxedIntoRoute(Box::new(Map { @@ -65,60 +60,59 @@ impl BoxedIntoRoute { })) } - pub(crate) fn into_route(self, state: S) -> Route { + pub(crate) fn into_route(self, state: S) -> Route { self.0.into_route(state) } } -impl Clone for BoxedIntoRoute { +impl Clone for BoxedIntoRoute { fn clone(&self) -> Self { Self(self.0.clone_box()) } } -impl fmt::Debug for BoxedIntoRoute { +impl fmt::Debug for BoxedIntoRoute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("BoxedIntoRoute").finish() } } -pub(crate) trait ErasedIntoRoute: Send { - fn clone_box(&self) -> Box>; +pub(crate) trait ErasedIntoRoute: Send { + fn clone_box(&self) -> Box>; - fn into_route(self: Box, state: S) -> Route; + fn into_route(self: Box, state: S) -> Route; - fn call_with_state(self: Box, request: Request, state: S) -> RouteFuture; + fn call_with_state(self: Box, request: Request, state: S) -> RouteFuture; } -pub(crate) struct MakeErasedHandler { +pub(crate) struct MakeErasedHandler { pub(crate) handler: H, - pub(crate) into_route: fn(H, S) -> Route, + pub(crate) into_route: fn(H, S) -> Route, } -impl ErasedIntoRoute for MakeErasedHandler +impl ErasedIntoRoute for MakeErasedHandler where H: Clone + Send + 'static, S: 'static, - B: HttpBody + 'static, { - fn clone_box(&self) -> Box> { + fn clone_box(&self) -> Box> { Box::new(self.clone()) } - fn into_route(self: Box, state: S) -> Route { + fn into_route(self: Box, state: S) -> Route { (self.into_route)(self.handler, state) } fn call_with_state( self: Box, - request: Request, + request: Request, state: S, - ) -> RouteFuture { + ) -> RouteFuture { self.into_route(state).call(request) } } -impl Clone for MakeErasedHandler +impl Clone for MakeErasedHandler where H: Clone, { @@ -130,34 +124,33 @@ where } } -pub(crate) struct MakeErasedRouter { - pub(crate) router: Router, - pub(crate) into_route: fn(Router, S) -> Route, +pub(crate) struct MakeErasedRouter { + pub(crate) router: Router, + pub(crate) into_route: fn(Router, S) -> Route, } -impl ErasedIntoRoute for MakeErasedRouter +impl ErasedIntoRoute for MakeErasedRouter where S: Clone + Send + Sync + 'static, - B: HttpBody + Send + 'static, { - fn clone_box(&self) -> Box> { + fn clone_box(&self) -> Box> { Box::new(self.clone()) } - fn into_route(self: Box, state: S) -> Route { + fn into_route(self: Box, state: S) -> Route { (self.into_route)(self.router, state) } fn call_with_state( mut self: Box, - request: Request, + request: Request, state: S, - ) -> RouteFuture { + ) -> RouteFuture { self.router.call_with_state(request, state) } } -impl Clone for MakeErasedRouter +impl Clone for MakeErasedRouter where S: Clone, { @@ -169,44 +162,42 @@ where } } -pub(crate) struct Map { - pub(crate) inner: Box>, - pub(crate) layer: Box>, +pub(crate) struct Map { + pub(crate) inner: Box>, + pub(crate) layer: Box>, } -impl ErasedIntoRoute for Map +impl ErasedIntoRoute for Map where S: 'static, - B: 'static, E: 'static, - B2: HttpBody + 'static, E2: 'static, { - fn clone_box(&self) -> Box> { + fn clone_box(&self) -> Box> { Box::new(Self { inner: self.inner.clone_box(), layer: self.layer.clone_box(), }) } - fn into_route(self: Box, state: S) -> Route { + fn into_route(self: Box, state: S) -> Route { (self.layer)(self.inner.into_route(state)) } - fn call_with_state(self: Box, request: Request, state: S) -> RouteFuture { + fn call_with_state(self: Box, request: Request, state: S) -> RouteFuture { (self.layer)(self.inner.into_route(state)).call(request) } } -pub(crate) trait LayerFn: FnOnce(Route) -> Route + Send { - fn clone_box(&self) -> Box>; +pub(crate) trait LayerFn: FnOnce(Route) -> Route + Send { + fn clone_box(&self) -> Box>; } -impl LayerFn for F +impl LayerFn for F where - F: FnOnce(Route) -> Route + Clone + Send + 'static, + F: FnOnce(Route) -> Route + Clone + Send + 'static, { - fn clone_box(&self) -> Box> { + fn clone_box(&self) -> Box> { Box::new(self.clone()) } } diff --git a/axum/src/docs/extract.md b/axum/src/docs/extract.md index 7e9b59f19c..d03f142b2b 100644 --- a/axum/src/docs/extract.md +++ b/axum/src/docs/extract.md @@ -187,7 +187,7 @@ async fn handler( // ... } # -# let _: axum::routing::MethodRouter = axum::routing::get(handler); +# let _: axum::routing::MethodRouter = axum::routing::get(handler); ``` We get a compile error if `String` isn't the last extractor: @@ -465,7 +465,7 @@ use axum::{ async_trait, extract::FromRequest, response::{Response, IntoResponse}, - body::Bytes, + body::{Bytes, Body}, routing::get, Router, http::{ @@ -478,15 +478,14 @@ use axum::{ struct ValidatedBody(Bytes); #[async_trait] -impl FromRequest for ValidatedBody +impl FromRequest for ValidatedBody where - Bytes: FromRequest, - B: Send + 'static, + Bytes: FromRequest, S: Send + Sync, { type Rejection = Response; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let body = Bytes::from_request(req, state) .await .map_err(IntoResponse::into_response)?; @@ -519,6 +518,7 @@ use axum::{ routing::get, extract::{FromRequest, FromRequestParts}, http::{Request, request::Parts}, + body::Body, async_trait, }; use std::convert::Infallible; @@ -528,14 +528,13 @@ struct MyExtractor; // `MyExtractor` implements both `FromRequest` #[async_trait] -impl FromRequest for MyExtractor +impl FromRequest for MyExtractor where S: Send + Sync, - B: Send + 'static, { type Rejection = Infallible; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { // ... # todo!() } @@ -638,81 +637,6 @@ For security reasons, [`Bytes`] will, by default, not accept bodies larger than For more details, including how to disable this limit, see [`DefaultBodyLimit`]. -# Request body extractors - -Most of the time your request body type will be [`body::Body`] (a re-export -of [`hyper::Body`]), which is directly supported by all extractors. - -However if you're applying a tower middleware that changes the request body type -you might have to apply a different body type to some extractors: - -```rust -use std::{ - task::{Context, Poll}, - pin::Pin, -}; -use tower_http::map_request_body::MapRequestBodyLayer; -use axum::{ - extract::{self, BodyStream}, - body::{Body, HttpBody}, - routing::get, - http::{header::HeaderMap, Request}, - Router, -}; - -struct MyBody(B); - -impl HttpBody for MyBody -where - B: HttpBody + Unpin, -{ - type Data = B::Data; - type Error = B::Error; - - fn poll_data( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>> { - Pin::new(&mut self.0).poll_data(cx) - } - - fn poll_trailers( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - Pin::new(&mut self.0).poll_trailers(cx) - } -} - -let app = Router::new() - .route( - "/string", - // `String` works directly with any body type - get(|_: String| async {}) - ) - .route( - "/body", - // `extract::Body` defaults to `axum::body::Body` - // but can be customized - get(|_: extract::RawBody>| async {}) - ) - .route( - "/body-stream", - // same for `extract::BodyStream` - get(|_: extract::BodyStream| async {}), - ) - .route( - // and `Request<_>` - "/request", - get(|_: Request>| async {}) - ) - // middleware that changes the request body type - .layer(MapRequestBodyLayer::new(MyBody)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; -``` - # Running extractors from middleware Extractors can also be run from middleware: @@ -758,7 +682,7 @@ fn token_is_valid(token: &str) -> bool { } let app = Router::new().layer(middleware::from_fn(auth_middleware)); -# let _: Router<()> = app; +# let _: Router = app; ``` # Wrapping extractors @@ -770,6 +694,7 @@ may or may not consume the request body) you should implement both ```rust use axum::{ Router, + body::Body, routing::get, extract::{FromRequest, FromRequestParts}, http::{Request, HeaderMap, request::Parts}, @@ -805,15 +730,14 @@ where // and `FromRequest` #[async_trait] -impl FromRequest for Timing +impl FromRequest for Timing where - B: Send + 'static, S: Send + Sync, - T: FromRequest, + T: FromRequest, { type Rejection = T::Rejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let start = Instant::now(); let extractor = T::from_request(req, state).await?; let duration = start.elapsed(); diff --git a/axum/src/docs/middleware.md b/axum/src/docs/middleware.md index 90fa2cac48..05f17fa1a9 100644 --- a/axum/src/docs/middleware.md +++ b/axum/src/docs/middleware.md @@ -97,7 +97,7 @@ let app = Router::new() .layer(layer_one) .layer(layer_two) .layer(layer_three); -# let _: Router<(), axum::body::Body> = app; +# let _: Router = app; ``` Think of the middleware as being layered like an onion where each new layer @@ -156,7 +156,7 @@ let app = Router::new() .layer(layer_two) .layer(layer_three), ); -# let _: Router<(), axum::body::Body> = app; +# let _: Router = app; ``` `ServiceBuilder` works by composing all layers into one such that they run top @@ -523,7 +523,7 @@ async fn handler( let app = Router::new() .route("/", get(handler)) .route_layer(middleware::from_fn(auth)); -# let _: Router<()> = app; +# let _: Router = app; ``` [Response extensions] can also be used but note that request extensions are not @@ -549,13 +549,13 @@ use axum::{ http::Request, }; -async fn rewrite_request_uri(req: Request, next: Next) -> Response { +fn rewrite_request_uri(req: Request) -> Request { // ... - # next.run(req).await + # req } // this can be any `tower::Layer` -let middleware = axum::middleware::from_fn(rewrite_request_uri); +let middleware = tower::util::MapRequestLayer::new(rewrite_request_uri); let app = Router::new(); diff --git a/axum/src/extract/mod.rs b/axum/src/extract/mod.rs index cb4ebcd92c..b93cc7f459 100644 --- a/axum/src/extract/mod.rs +++ b/axum/src/extract/mod.rs @@ -29,7 +29,7 @@ pub use self::{ path::{Path, RawPathParams}, raw_form::RawForm, raw_query::RawQuery, - request_parts::{BodyStream, RawBody}, + request_parts::RawBody, state::State, }; diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 3923600a34..2587610e96 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -2,11 +2,11 @@ //! //! See [`Multipart`] for more details. -use super::{BodyStream, FromRequest}; -use crate::body::{Bytes, HttpBody}; -use crate::BoxError; +use super::FromRequest; +use crate::body::Bytes; use async_trait::async_trait; use axum_core::response::{IntoResponse, Response}; +use axum_core::body::Body; use axum_core::RequestExt; use futures_util::stream::Stream; use http::header::{HeaderMap, CONTENT_TYPE}; @@ -57,22 +57,18 @@ pub struct Multipart { } #[async_trait] -impl FromRequest for Multipart +impl FromRequest for Multipart where - B: HttpBody + Send + 'static, - B::Data: Into, - B::Error: Into, S: Send + Sync, { type Rejection = MultipartRejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?; - let stream_result = match req.with_limited_body() { - Ok(limited) => BodyStream::from_request(limited, state).await, - Err(unlimited) => BodyStream::from_request(unlimited, state).await, + let stream = match req.with_limited_body() { + Ok(limited) => Body::new(limited), + Err(unlimited) => unlimited.into_body(), }; - let stream = stream_result.unwrap_or_else(|err| match err {}); let multipart = multer::Multipart::new(stream, boundary); Ok(Self { inner: multipart }) } @@ -303,7 +299,7 @@ mod tests { use axum_core::extract::DefaultBodyLimit; use super::*; - use crate::{body::Body, response::IntoResponse, routing::post, test_helpers::*, Router}; + use crate::{response::IntoResponse, routing::post, test_helpers::*, Router}; #[crate::test] async fn content_type_with_encoding() { @@ -339,7 +335,9 @@ mod tests { // No need for this to be a #[test], we just want to make sure it compiles fn _multipart_from_request_limited() { async fn handler(_: Multipart) {} - let _app: Router<(), http_body::Limited> = Router::new().route("/", post(handler)); + let _app: Router = Router::new() + .route("/", post(handler)) + .layer(tower_http::limit::RequestBodyLimitLayer::new(1024)); } #[crate::test] diff --git a/axum/src/extract/query.rs b/axum/src/extract/query.rs index 10b523a432..c8e25e702f 100644 --- a/axum/src/extract/query.rs +++ b/axum/src/extract/query.rs @@ -78,7 +78,7 @@ mod tests { use crate::{routing::get, test_helpers::TestClient, Router}; use super::*; - use axum_core::extract::FromRequest; + use axum_core::{body::Body, extract::FromRequest}; use http::{Request, StatusCode}; use serde::Deserialize; use std::fmt::Debug; @@ -87,7 +87,10 @@ mod tests { where T: DeserializeOwned + PartialEq + Debug, { - let req = Request::builder().uri(uri.as_ref()).body(()).unwrap(); + let req = Request::builder() + .uri(uri.as_ref()) + .body(Body::empty()) + .unwrap(); assert_eq!(Query::::from_request(req, &()).await.unwrap().0, value); } diff --git a/axum/src/extract/raw_form.rs b/axum/src/extract/raw_form.rs index 830d8b62ae..ad74686817 100644 --- a/axum/src/extract/raw_form.rs +++ b/axum/src/extract/raw_form.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use axum_core::extract::FromRequest; +use axum_core::{body::Body, extract::FromRequest}; use bytes::{Bytes, BytesMut}; use http::{Method, Request}; @@ -8,8 +8,6 @@ use super::{ rejection::{InvalidFormContentType, RawFormRejection}, }; -use crate::{body::HttpBody, BoxError}; - /// Extractor that extracts raw form requests. /// /// For `GET` requests it will extract the raw query. For other methods it extracts the raw @@ -35,16 +33,13 @@ use crate::{body::HttpBody, BoxError}; pub struct RawForm(pub Bytes); #[async_trait] -impl FromRequest for RawForm +impl FromRequest for RawForm where - B: HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, S: Send + Sync, { type Rejection = RawFormRejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { if req.method() == Method::GET { let mut bytes = BytesMut::new(); @@ -65,20 +60,15 @@ where #[cfg(test)] mod tests { + use axum_core::body::Body; use http::{header::CONTENT_TYPE, Request}; use super::{InvalidFormContentType, RawForm, RawFormRejection}; - use crate::{ - body::{Bytes, Empty, Full}, - extract::FromRequest, - }; + use crate::extract::FromRequest; async fn check_query(uri: &str, value: &[u8]) { - let req = Request::builder() - .uri(uri) - .body(Empty::::new()) - .unwrap(); + let req = Request::builder().uri(uri).body(Body::empty()).unwrap(); assert_eq!(RawForm::from_request(req, &()).await.unwrap().0, value); } @@ -86,7 +76,7 @@ mod tests { async fn check_body(body: &'static [u8]) { let req = Request::post("http://example.com/test") .header(CONTENT_TYPE, mime::APPLICATION_WWW_FORM_URLENCODED.as_ref()) - .body(Full::new(Bytes::from(body))) + .body(Body::from(body)) .unwrap(); assert_eq!(RawForm::from_request(req, &()).await.unwrap().0, body); @@ -109,7 +99,7 @@ mod tests { #[crate::test] async fn test_incorrect_content_type() { let req = Request::post("http://example.com/test") - .body(Full::::from(Bytes::from("page=0&size=10"))) + .body(Body::from("page=0&size=10")) .unwrap(); assert!(matches!( diff --git a/axum/src/extract/request_parts.rs b/axum/src/extract/request_parts.rs index b3a28bc429..5e83553cc0 100644 --- a/axum/src/extract/request_parts.rs +++ b/axum/src/extract/request_parts.rs @@ -1,18 +1,8 @@ use super::{Extension, FromRequest, FromRequestParts}; -use crate::{ - body::{Body, Bytes, HttpBody}, - BoxError, Error, -}; +use crate::body::Body; use async_trait::async_trait; -use futures_util::stream::Stream; use http::{request::Parts, Request, Uri}; -use std::{ - convert::Infallible, - fmt, - pin::Pin, - task::{Context, Poll}, -}; -use sync_wrapper::SyncWrapper; +use std::convert::Infallible; /// Extractor that gets the original request URI regardless of nesting. /// @@ -101,82 +91,6 @@ where } } -/// Extractor that extracts the request body as a [`Stream`]. -/// -/// Since extracting the request body requires consuming it, the `BodyStream` extractor must be -/// *last* if there are multiple extractors in a handler. -/// See ["the order of extractors"][order-of-extractors] -/// -/// [order-of-extractors]: crate::extract#the-order-of-extractors -/// -/// # Example -/// -/// ```rust,no_run -/// use axum::{ -/// extract::BodyStream, -/// routing::get, -/// Router, -/// }; -/// use futures_util::StreamExt; -/// -/// async fn handler(mut stream: BodyStream) { -/// while let Some(chunk) = stream.next().await { -/// // ... -/// } -/// } -/// -/// let app = Router::new().route("/users", get(handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; -/// ``` -/// -/// [`Stream`]: https://docs.rs/futures/latest/futures/stream/trait.Stream.html -/// [`body::Body`]: crate::body::Body -pub struct BodyStream( - SyncWrapper + Send + 'static>>>, -); - -impl Stream for BodyStream { - type Item = Result; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(self.0.get_mut()).poll_data(cx) - } -} - -#[async_trait] -impl FromRequest for BodyStream -where - B: HttpBody + Send + 'static, - B::Data: Into, - B::Error: Into, - S: Send + Sync, -{ - type Rejection = Infallible; - - async fn from_request(req: Request, _state: &S) -> Result { - let body = req - .into_body() - .map_data(Into::into) - .map_err(|err| Error::new(err.into())); - let stream = BodyStream(SyncWrapper::new(Box::pin(body))); - Ok(stream) - } -} - -impl fmt::Debug for BodyStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("BodyStream").finish() - } -} - -#[test] -fn body_stream_traits() { - crate::test_helpers::assert_send::(); - crate::test_helpers::assert_sync::(); -} - /// Extractor that extracts the raw request body. /// /// Since extracting the raw request body requires consuming it, the `RawBody` extractor must be @@ -205,18 +119,17 @@ fn body_stream_traits() { /// ``` /// /// [`body::Body`]: crate::body::Body -#[derive(Debug, Default, Clone)] -pub struct RawBody(pub B); +#[derive(Debug, Default)] +pub struct RawBody(pub Body); #[async_trait] -impl FromRequest for RawBody +impl FromRequest for RawBody where - B: Send, S: Send + Sync, { type Rejection = Infallible; - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { Ok(Self(req.into_body())) } } diff --git a/axum/src/form.rs b/axum/src/form.rs index 5318dd01f1..731362f55e 100644 --- a/axum/src/form.rs +++ b/axum/src/form.rs @@ -1,7 +1,6 @@ -use crate::body::HttpBody; use crate::extract::{rejection::*, FromRequest, RawForm}; -use crate::BoxError; use async_trait::async_trait; +use axum_core::body::Body; use axum_core::response::{IntoResponse, Response}; use axum_core::RequestExt; use http::header::CONTENT_TYPE; @@ -65,17 +64,14 @@ use std::ops::Deref; pub struct Form(pub T); #[async_trait] -impl FromRequest for Form +impl FromRequest for Form where T: DeserializeOwned, - B: HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, S: Send + Sync, { type Rejection = FormRejection; - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { let is_get_or_head = req.method() == http::Method::GET || req.method() == http::Method::HEAD; @@ -127,12 +123,11 @@ impl Deref for Form { mod tests { use super::*; use crate::{ - body::{Empty, Full}, + body::Body, routing::{on, MethodFilter}, test_helpers::TestClient, Router, }; - use bytes::Bytes; use http::{header::CONTENT_TYPE, Method, Request}; use mime::APPLICATION_WWW_FORM_URLENCODED; use serde::{Deserialize, Serialize}; @@ -147,7 +142,7 @@ mod tests { async fn check_query(uri: impl AsRef, value: T) { let req = Request::builder() .uri(uri.as_ref()) - .body(Empty::::new()) + .body(Body::empty()) .unwrap(); assert_eq!(Form::::from_request(req, &()).await.unwrap().0, value); } @@ -157,9 +152,7 @@ mod tests { .uri("http://example.com/test") .method(Method::POST) .header(CONTENT_TYPE, APPLICATION_WWW_FORM_URLENCODED.as_ref()) - .body(Full::::new( - serde_urlencoded::to_string(&value).unwrap().into(), - )) + .body(Body::from(serde_urlencoded::to_string(&value).unwrap())) .unwrap(); assert_eq!(Form::::from_request(req, &()).await.unwrap().0, value); } @@ -221,13 +214,12 @@ mod tests { .uri("http://example.com/test") .method(Method::POST) .header(CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) - .body(Full::::new( + .body(Body::from( serde_urlencoded::to_string(&Pagination { size: Some(10), page: None, }) - .unwrap() - .into(), + .unwrap(), )) .unwrap(); assert!(matches!( diff --git a/axum/src/handler/future.rs b/axum/src/handler/future.rs index 59487c31b2..48f51a004a 100644 --- a/axum/src/handler/future.rs +++ b/axum/src/handler/future.rs @@ -1,5 +1,6 @@ //! Handler future types. +use crate::body::Body; use crate::response::Response; use futures_util::future::Map; use http::Request; @@ -19,29 +20,29 @@ opaque_future! { pin_project! { /// The response future for [`Layered`](super::Layered). - pub struct LayeredFuture + pub struct LayeredFuture where - S: Service>, + S: Service>, { #[pin] - inner: Map>, fn(Result) -> Response>, + inner: Map>, fn(Result) -> Response>, } } -impl LayeredFuture +impl LayeredFuture where - S: Service>, + S: Service>, { pub(super) fn new( - inner: Map>, fn(Result) -> Response>, + inner: Map>, fn(Result) -> Response>, ) -> Self { Self { inner } } } -impl Future for LayeredFuture +impl Future for LayeredFuture where - S: Service>, + S: Service>, { type Output = Response; diff --git a/axum/src/handler/mod.rs b/axum/src/handler/mod.rs index 338eea623c..eb539e5367 100644 --- a/axum/src/handler/mod.rs +++ b/axum/src/handler/mod.rs @@ -37,11 +37,11 @@ #[cfg(feature = "tokio")] use crate::extract::connect_info::IntoMakeServiceWithConnectInfo; use crate::{ - body::Body, extract::{FromRequest, FromRequestParts}, response::{IntoResponse, Response}, routing::IntoMakeService, }; +use axum_core::body::Body; use http::Request; use std::{convert::Infallible, fmt, future::Future, marker::PhantomData, pin::Pin}; use tower::ServiceExt; @@ -99,12 +99,12 @@ pub use self::service::HandlerService; note = "Consider using `#[axum::debug_handler]` to improve the error message" ) )] -pub trait Handler: Clone + Send + Sized + 'static { +pub trait Handler: Clone + Send + Sized + 'static { /// The type of future calling this handler returns. type Future: Future + Send + 'static; /// Call the handler with the given request. - fn call(self, req: Request, state: S) -> Self::Future; + fn call(self, req: Request, state: S) -> Self::Future; /// Apply a [`tower::Layer`] to the handler. /// @@ -142,10 +142,10 @@ pub trait Handler: Clone + Send + Sized + 'static { /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` - fn layer(self, layer: L) -> Layered + fn layer(self, layer: L) -> Layered where - L: Layer> + Clone, - L::Service: Service>, + L: Layer> + Clone, + L::Service: Service>, { Layered { layer, @@ -155,21 +155,20 @@ pub trait Handler: Clone + Send + Sized + 'static { } /// Convert the handler into a [`Service`] by providing the state - fn with_state(self, state: S) -> HandlerService { + fn with_state(self, state: S) -> HandlerService { HandlerService::new(self, state) } } -impl Handler<((),), S, B> for F +impl Handler<((),), S> for F where F: FnOnce() -> Fut + Clone + Send + 'static, Fut: Future + Send, Res: IntoResponse, - B: Send + 'static, { type Future = Pin + Send>>; - fn call(self, _req: Request, _state: S) -> Self::Future { + fn call(self, _req: Request, _state: S) -> Self::Future { Box::pin(async move { self().await.into_response() }) } } @@ -179,19 +178,18 @@ macro_rules! impl_handler { [$($ty:ident),*], $last:ident ) => { #[allow(non_snake_case, unused_mut)] - impl Handler<(M, $($ty,)* $last,), S, B> for F + impl Handler<(M, $($ty,)* $last,), S> for F where F: FnOnce($($ty,)* $last,) -> Fut + Clone + Send + 'static, Fut: Future + Send, - B: Send + 'static, S: Send + Sync + 'static, Res: IntoResponse, $( $ty: FromRequestParts + Send, )* - $last: FromRequest + Send, + $last: FromRequest + Send, { type Future = Pin + Send>>; - fn call(self, req: Request, state: S) -> Self::Future { + fn call(self, req: Request, state: S) -> Self::Future { Box::pin(async move { let (mut parts, body) = req.into_parts(); let state = &state; @@ -224,13 +222,13 @@ all_the_tuples!(impl_handler); /// A [`Service`] created from a [`Handler`] by applying a Tower middleware. /// /// Created with [`Handler::layer`]. See that method for more details. -pub struct Layered { +pub struct Layered { layer: L, handler: H, - _marker: PhantomData (T, S, B, B2)>, + _marker: PhantomData (T, S)>, } -impl fmt::Debug for Layered +impl fmt::Debug for Layered where L: fmt::Debug, { @@ -241,7 +239,7 @@ where } } -impl Clone for Layered +impl Clone for Layered where L: Clone, H: Clone, @@ -255,21 +253,19 @@ where } } -impl Handler for Layered +impl Handler for Layered where - L: Layer> + Clone + Send + 'static, - H: Handler, - L::Service: Service, Error = Infallible> + Clone + Send + 'static, - >>::Response: IntoResponse, - >>::Future: Send, + L: Layer> + Clone + Send + 'static, + H: Handler, + L::Service: Service, Error = Infallible> + Clone + Send + 'static, + >>::Response: IntoResponse, + >>::Future: Send, T: 'static, S: 'static, - B: Send + 'static, - B2: Send + 'static, { - type Future = future::LayeredFuture; + type Future = future::LayeredFuture; - fn call(self, req: Request, state: S) -> Self::Future { + fn call(self, req: Request, state: S) -> Self::Future { use futures_util::future::{FutureExt, Map}; let svc = self.handler.with_state(state); @@ -279,8 +275,8 @@ where _, fn( Result< - >>::Response, - >>::Error, + >>::Response, + >>::Error, >, ) -> _, > = svc.oneshot(req).map(|result| match result { @@ -297,16 +293,16 @@ where /// This provides convenience methods to convert the [`Handler`] into a [`Service`] or [`MakeService`]. /// /// [`MakeService`]: tower::make::MakeService -pub trait HandlerWithoutStateExt: Handler { +pub trait HandlerWithoutStateExt: Handler { /// Convert the handler into a [`Service`] and no state. - fn into_service(self) -> HandlerService; + fn into_service(self) -> HandlerService; /// Convert the handler into a [`MakeService`] and no state. /// /// See [`HandlerService::into_make_service`] for more details. /// /// [`MakeService`]: tower::make::MakeService - fn into_make_service(self) -> IntoMakeService>; + fn into_make_service(self) -> IntoMakeService>; /// Convert the handler into a [`MakeService`] which stores information /// about the incoming connection and has no state. @@ -317,25 +313,25 @@ pub trait HandlerWithoutStateExt: Handler { #[cfg(feature = "tokio")] fn into_make_service_with_connect_info( self, - ) -> IntoMakeServiceWithConnectInfo, C>; + ) -> IntoMakeServiceWithConnectInfo, C>; } -impl HandlerWithoutStateExt for H +impl HandlerWithoutStateExt for H where - H: Handler, + H: Handler, { - fn into_service(self) -> HandlerService { + fn into_service(self) -> HandlerService { self.with_state(()) } - fn into_make_service(self) -> IntoMakeService> { + fn into_make_service(self) -> IntoMakeService> { self.into_service().into_make_service() } #[cfg(feature = "tokio")] fn into_make_service_with_connect_info( self, - ) -> IntoMakeServiceWithConnectInfo, C> { + ) -> IntoMakeServiceWithConnectInfo, C> { self.into_service().into_make_service_with_connect_info() } } diff --git a/axum/src/handler/service.rs b/axum/src/handler/service.rs index 52fd5de67d..86b93afeb3 100644 --- a/axum/src/handler/service.rs +++ b/axum/src/handler/service.rs @@ -1,8 +1,10 @@ use super::Handler; +use crate::body::{Body, Bytes, HttpBody}; #[cfg(feature = "tokio")] use crate::extract::connect_info::IntoMakeServiceWithConnectInfo; use crate::response::Response; use crate::routing::IntoMakeService; +use crate::BoxError; use http::Request; use std::{ convert::Infallible, @@ -17,13 +19,13 @@ use tower_service::Service; /// Created with [`Handler::with_state`] or [`HandlerWithoutStateExt::into_service`]. /// /// [`HandlerWithoutStateExt::into_service`]: super::HandlerWithoutStateExt::into_service -pub struct HandlerService { +pub struct HandlerService { handler: H, state: S, - _marker: PhantomData (T, B)>, + _marker: PhantomData T>, } -impl HandlerService { +impl HandlerService { /// Get a reference to the state. pub fn state(&self) -> &S { &self.state @@ -61,7 +63,7 @@ impl HandlerService { /// ``` /// /// [`MakeService`]: tower::make::MakeService - pub fn into_make_service(self) -> IntoMakeService> { + pub fn into_make_service(self) -> IntoMakeService> { IntoMakeService::new(self) } @@ -104,7 +106,7 @@ impl HandlerService { #[cfg(feature = "tokio")] pub fn into_make_service_with_connect_info( self, - ) -> IntoMakeServiceWithConnectInfo, C> { + ) -> IntoMakeServiceWithConnectInfo, C> { IntoMakeServiceWithConnectInfo::new(self) } } @@ -112,11 +114,11 @@ impl HandlerService { #[test] fn traits() { use crate::test_helpers::*; - assert_send::>(); - assert_sync::>(); + assert_send::>(); + assert_sync::>(); } -impl HandlerService { +impl HandlerService { pub(super) fn new(handler: H, state: S) -> Self { Self { handler, @@ -126,13 +128,13 @@ impl HandlerService { } } -impl fmt::Debug for HandlerService { +impl fmt::Debug for HandlerService { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("IntoService").finish_non_exhaustive() } } -impl Clone for HandlerService +impl Clone for HandlerService where H: Clone, S: Clone, @@ -146,10 +148,11 @@ where } } -impl Service> for HandlerService +impl Service> for HandlerService where - H: Handler + Clone + Send + 'static, - B: Send + 'static, + H: Handler + Clone + Send + 'static, + B: HttpBody + Send + 'static, + B::Error: Into, S: Clone + Send + Sync, { type Response = Response; @@ -167,6 +170,8 @@ where fn call(&mut self, req: Request) -> Self::Future { use futures_util::future::FutureExt; + let req = req.map(Body::new); + let handler = self.handler.clone(); let future = Handler::call(handler, req, self.state.clone()); let future = future.map(Ok as _); diff --git a/axum/src/json.rs b/axum/src/json.rs index e6d8588cfd..a93a97b8a8 100644 --- a/axum/src/json.rs +++ b/axum/src/json.rs @@ -1,11 +1,10 @@ -use crate::{ - body::{Bytes, HttpBody}, - extract::{rejection::*, FromRequest}, - BoxError, -}; +use crate::extract::{rejection::*, FromRequest}; use async_trait::async_trait; -use axum_core::response::{IntoResponse, Response}; -use bytes::{BufMut, BytesMut}; +use axum_core::{ + body::Body, + response::{IntoResponse, Response}, +}; +use bytes::{BufMut, Bytes, BytesMut}; use http::{ header::{self, HeaderMap, HeaderValue}, Request, StatusCode, @@ -101,17 +100,14 @@ use std::ops::{Deref, DerefMut}; pub struct Json(pub T); #[async_trait] -impl FromRequest for Json +impl FromRequest for Json where T: DeserializeOwned, - B: HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, S: Send + Sync, { type Rejection = JsonRejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { if json_content_type(req.headers()) { let bytes = Bytes::from_request(req, state).await?; let deserializer = &mut serde_json::Deserializer::from_slice(&bytes); diff --git a/axum/src/middleware/from_fn.rs b/axum/src/middleware/from_fn.rs index f380a580ad..ba595f338f 100644 --- a/axum/src/middleware/from_fn.rs +++ b/axum/src/middleware/from_fn.rs @@ -1,4 +1,6 @@ +use crate::body::{Body, Bytes, HttpBody}; use crate::response::{IntoResponse, Response}; +use crate::BoxError; use axum_core::extract::{FromRequest, FromRequestParts}; use futures_util::future::BoxFuture; use http::Request; @@ -247,7 +249,7 @@ macro_rules! impl_service { where F: FnMut($($ty,)* $last, Next) -> Fut + Clone + Send + 'static, $( $ty: FromRequestParts + Send, )* - $last: FromRequest + Send, + $last: FromRequest + Send, Fut: Future + Send + 'static, Out: IntoResponse + 'static, I: Service, Error = Infallible> @@ -256,7 +258,8 @@ macro_rules! impl_service { + 'static, I::Response: IntoResponse, I::Future: Send + 'static, - B: Send + 'static, + B: HttpBody + Send + 'static, + B::Error: Into, S: Clone + Send + Sync + 'static, { type Response = Response; @@ -268,6 +271,8 @@ macro_rules! impl_service { } fn call(&mut self, req: Request) -> Self::Future { + let req = req.map(Body::new); + let not_ready_inner = self.inner.clone(); let ready_inner = std::mem::replace(&mut self.inner, not_ready_inner); diff --git a/axum/src/middleware/map_request.rs b/axum/src/middleware/map_request.rs index 5d1801ac7c..d36a7cc958 100644 --- a/axum/src/middleware/map_request.rs +++ b/axum/src/middleware/map_request.rs @@ -1,4 +1,6 @@ +use crate::body::{Body, Bytes, HttpBody}; use crate::response::{IntoResponse, Response}; +use crate::BoxError; use axum_core::extract::{FromRequest, FromRequestParts}; use futures_util::future::BoxFuture; use http::Request; @@ -251,7 +253,7 @@ macro_rules! impl_service { where F: FnMut($($ty,)* $last) -> Fut + Clone + Send + 'static, $( $ty: FromRequestParts + Send, )* - $last: FromRequest + Send, + $last: FromRequest + Send, Fut: Future + Send + 'static, Fut::Output: IntoMapRequestResult + Send + 'static, I: Service, Error = Infallible> @@ -260,7 +262,8 @@ macro_rules! impl_service { + 'static, I::Response: IntoResponse, I::Future: Send + 'static, - B: Send + 'static, + B: HttpBody + Send + 'static, + B::Error: Into, S: Clone + Send + Sync + 'static, { type Response = Response; @@ -272,6 +275,8 @@ macro_rules! impl_service { } fn call(&mut self, req: Request) -> Self::Future { + let req = req.map(Body::new); + let not_ready_inner = self.inner.clone(); let mut ready_inner = std::mem::replace(&mut self.inner, not_ready_inner); diff --git a/axum/src/response/mod.rs b/axum/src/response/mod.rs index 2c149748a6..95bcb762e2 100644 --- a/axum/src/response/mod.rs +++ b/axum/src/response/mod.rs @@ -67,7 +67,7 @@ impl From for Html { #[cfg(test)] mod tests { use crate::extract::Extension; - use crate::{body::Body, routing::get, Router}; + use crate::{routing::get, Router}; use axum_core::response::IntoResponse; use http::HeaderMap; use http::{StatusCode, Uri}; @@ -99,7 +99,7 @@ mod tests { } } - _ = Router::<(), Body>::new() + _ = Router::<()>::new() .route("/", get(impl_trait_ok)) .route("/", get(impl_trait_err)) .route("/", get(impl_trait_both)) @@ -209,7 +209,7 @@ mod tests { ) } - _ = Router::<(), Body>::new() + _ = Router::<()>::new() .route("/", get(status)) .route("/", get(status_headermap)) .route("/", get(status_header_array)) diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index cd94290227..7d982f4232 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -12,7 +12,7 @@ use crate::{ response::Response, routing::{future::RouteFuture, Fallback, MethodFilter, Route}, }; -use axum_core::response::IntoResponse; +use axum_core::{response::IntoResponse, BoxError}; use bytes::BytesMut; use std::{ convert::Infallible, @@ -37,10 +37,10 @@ macro_rules! top_level_service_fn { /// http::Request, /// Router, /// routing::get_service, + /// body::Body, /// }; /// use http::Response; /// use std::convert::Infallible; - /// use hyper::Body; /// /// let service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) @@ -78,12 +78,11 @@ macro_rules! top_level_service_fn { $name:ident, $method:ident ) => { $(#[$m])+ - pub fn $name(svc: T) -> MethodRouter + pub fn $name(svc: T) -> MethodRouter where - T: Service> + Clone + Send + 'static, + T: Service> + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, - B: HttpBody + Send + 'static, S: Clone, { on_service(MethodFilter::$method, svc) @@ -140,10 +139,9 @@ macro_rules! top_level_handler_fn { $name:ident, $method:ident ) => { $(#[$m])+ - pub fn $name(handler: H) -> MethodRouter + pub fn $name(handler: H) -> MethodRouter where - H: Handler, - B: HttpBody + Send + 'static, + H: Handler, T: 'static, S: Clone + Send + Sync + 'static, { @@ -166,10 +164,10 @@ macro_rules! chained_service_fn { /// http::Request, /// Router, /// routing::post_service, + /// body::Body, /// }; /// use http::Response; /// use std::convert::Infallible; - /// use hyper::Body; /// /// let service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) @@ -215,7 +213,7 @@ macro_rules! chained_service_fn { #[track_caller] pub fn $name(self, svc: T) -> Self where - T: Service, Error = E> + T: Service, Error = E> + Clone + Send + 'static, @@ -279,7 +277,7 @@ macro_rules! chained_handler_fn { #[track_caller] pub fn $name(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, S: Send + Sync + 'static, { @@ -306,11 +304,11 @@ top_level_service_fn!(trace_service, TRACE); /// http::Request, /// routing::on, /// Router, +/// body::Body, /// routing::{MethodFilter, on_service}, /// }; /// use http::Response; /// use std::convert::Infallible; -/// use hyper::Body; /// /// let service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) @@ -322,12 +320,11 @@ top_level_service_fn!(trace_service, TRACE); /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` -pub fn on_service(filter: MethodFilter, svc: T) -> MethodRouter +pub fn on_service(filter: MethodFilter, svc: T) -> MethodRouter where - T: Service> + Clone + Send + 'static, + T: Service> + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, - B: HttpBody + Send + 'static, S: Clone, { MethodRouter::new().on_service(filter, svc) @@ -342,10 +339,10 @@ where /// http::Request, /// Router, /// routing::any_service, +/// body::Body, /// }; /// use http::Response; /// use std::convert::Infallible; -/// use hyper::Body; /// /// let service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) @@ -365,10 +362,10 @@ where /// http::Request, /// Router, /// routing::any_service, +/// body::Body, /// }; /// use http::Response; /// use std::convert::Infallible; -/// use hyper::Body; /// /// let service = tower::service_fn(|request: Request| async { /// # Ok::<_, Infallible>(Response::new(Body::empty())) @@ -386,12 +383,11 @@ where /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` -pub fn any_service(svc: T) -> MethodRouter +pub fn any_service(svc: T) -> MethodRouter where - T: Service> + Clone + Send + 'static, + T: Service> + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, - B: HttpBody + Send + 'static, S: Clone, { MethodRouter::new() @@ -427,10 +423,9 @@ top_level_handler_fn!(trace, TRACE); /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` -pub fn on(filter: MethodFilter, handler: H) -> MethodRouter +pub fn on(filter: MethodFilter, handler: H) -> MethodRouter where - H: Handler, - B: HttpBody + Send + 'static, + H: Handler, T: 'static, S: Clone + Send + Sync + 'static, { @@ -474,10 +469,9 @@ where /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` -pub fn any(handler: H) -> MethodRouter +pub fn any(handler: H) -> MethodRouter where - H: Handler, - B: HttpBody + Send + 'static, + H: Handler, T: 'static, S: Clone + Send + Sync + 'static, { @@ -514,16 +508,16 @@ where /// {} /// ``` #[must_use] -pub struct MethodRouter { - get: MethodEndpoint, - head: MethodEndpoint, - delete: MethodEndpoint, - options: MethodEndpoint, - patch: MethodEndpoint, - post: MethodEndpoint, - put: MethodEndpoint, - trace: MethodEndpoint, - fallback: Fallback, +pub struct MethodRouter { + get: MethodEndpoint, + head: MethodEndpoint, + delete: MethodEndpoint, + options: MethodEndpoint, + patch: MethodEndpoint, + post: MethodEndpoint, + put: MethodEndpoint, + trace: MethodEndpoint, + fallback: Fallback, allow_header: AllowHeader, } @@ -553,7 +547,7 @@ impl AllowHeader { } } -impl fmt::Debug for MethodRouter { +impl fmt::Debug for MethodRouter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MethodRouter") .field("get", &self.get) @@ -570,9 +564,8 @@ impl fmt::Debug for MethodRouter { } } -impl MethodRouter +impl MethodRouter where - B: HttpBody + Send + 'static, S: Clone, { /// Chain an additional handler that will accept requests matching the given @@ -601,7 +594,7 @@ where #[track_caller] pub fn on(self, filter: MethodFilter, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, S: Send + Sync + 'static, { @@ -623,7 +616,7 @@ where /// Add a fallback [`Handler`] to the router. pub fn fallback(mut self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, S: Send + Sync + 'static, { @@ -632,10 +625,7 @@ where } } -impl MethodRouter<(), B, Infallible> -where - B: HttpBody + Send + 'static, -{ +impl MethodRouter<(), Infallible> { /// Convert the handler into a [`MakeService`]. /// /// This allows you to serve a single handler if you don't need any routing: @@ -706,15 +696,14 @@ where } } -impl MethodRouter +impl MethodRouter where - B: HttpBody + Send + 'static, S: Clone, { /// Create a default `MethodRouter` that will respond with `405 Method Not Allowed` to all /// requests. pub fn new() -> Self { - let fallback = Route::new(service_fn(|_: Request| async { + let fallback = Route::new(service_fn(|_: Request| async { Ok(StatusCode::METHOD_NOT_ALLOWED.into_response()) })); @@ -733,7 +722,7 @@ where } /// Provide the state for the router. - pub fn with_state(self, state: S) -> MethodRouter { + pub fn with_state(self, state: S) -> MethodRouter { MethodRouter { get: self.get.with_state(&state), head: self.head.with_state(&state), @@ -758,10 +747,10 @@ where /// http::Request, /// Router, /// routing::{MethodFilter, on_service}, + /// body::Body, /// }; /// use http::Response; /// use std::convert::Infallible; - /// use hyper::Body; /// /// let service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) @@ -776,7 +765,7 @@ where #[track_caller] pub fn on_service(self, filter: MethodFilter, svc: T) -> Self where - T: Service, Error = E> + Clone + Send + 'static, + T: Service, Error = E> + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, { @@ -784,19 +773,19 @@ where } #[track_caller] - fn on_endpoint(mut self, filter: MethodFilter, endpoint: MethodEndpoint) -> Self { + fn on_endpoint(mut self, filter: MethodFilter, endpoint: MethodEndpoint) -> Self { // written as a separate function to generate less IR #[track_caller] - fn set_endpoint( + fn set_endpoint( method_name: &str, - out: &mut MethodEndpoint, - endpoint: &MethodEndpoint, + out: &mut MethodEndpoint, + endpoint: &MethodEndpoint, endpoint_filter: MethodFilter, filter: MethodFilter, allow_header: &mut AllowHeader, methods: &[&'static str], ) where - MethodEndpoint: Clone, + MethodEndpoint: Clone, S: Clone, { if endpoint_filter.contains(filter) { @@ -908,7 +897,7 @@ where #[doc = include_str!("../docs/method_routing/fallback.md")] pub fn fallback_service(mut self, svc: T) -> Self where - T: Service, Error = E> + Clone + Send + 'static, + T: Service, Error = E> + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, { @@ -917,19 +906,18 @@ where } #[doc = include_str!("../docs/method_routing/layer.md")] - pub fn layer(self, layer: L) -> MethodRouter + pub fn layer(self, layer: L) -> MethodRouter where - L: Layer> + Clone + Send + 'static, - L::Service: Service> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Error: Into + 'static, - >>::Future: Send + 'static, + L: Layer> + Clone + Send + 'static, + L::Service: Service> + Clone + Send + 'static, + >>::Response: IntoResponse + 'static, + >>::Error: Into + 'static, + >>::Future: Send + 'static, E: 'static, S: 'static, - NewReqBody: HttpBody + 'static, NewError: 'static, { - let layer_fn = move |route: Route| route.layer(layer.clone()); + let layer_fn = move |route: Route| route.layer(layer.clone()); MethodRouter { get: self.get.map(layer_fn.clone()), @@ -947,12 +935,12 @@ where #[doc = include_str!("../docs/method_routing/route_layer.md")] #[track_caller] - pub fn route_layer(mut self, layer: L) -> MethodRouter + pub fn route_layer(mut self, layer: L) -> MethodRouter where - L: Layer> + Clone + Send + 'static, - L::Service: Service, Error = E> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Future: Send + 'static, + L: Layer> + Clone + Send + 'static, + L::Service: Service, Error = E> + Clone + Send + 'static, + >>::Response: IntoResponse + 'static, + >>::Future: Send + 'static, E: 'static, S: 'static, { @@ -990,19 +978,15 @@ where } #[track_caller] - pub(crate) fn merge_for_path( - mut self, - path: Option<&str>, - other: MethodRouter, - ) -> Self { + pub(crate) fn merge_for_path(mut self, path: Option<&str>, other: MethodRouter) -> Self { // written using inner functions to generate less IR #[track_caller] - fn merge_inner( + fn merge_inner( path: Option<&str>, name: &str, - first: MethodEndpoint, - second: MethodEndpoint, - ) -> MethodEndpoint { + first: MethodEndpoint, + second: MethodEndpoint, + ) -> MethodEndpoint { match (first, second) { (MethodEndpoint::None, MethodEndpoint::None) => MethodEndpoint::None, (pick, MethodEndpoint::None) | (MethodEndpoint::None, pick) => pick, @@ -1042,22 +1026,21 @@ where #[doc = include_str!("../docs/method_routing/merge.md")] #[track_caller] - pub fn merge(self, other: MethodRouter) -> Self { + pub fn merge(self, other: MethodRouter) -> Self { self.merge_for_path(None, other) } /// Apply a [`HandleErrorLayer`]. /// /// This is a convenience method for doing `self.layer(HandleErrorLayer::new(f))`. - pub fn handle_error(self, f: F) -> MethodRouter + pub fn handle_error(self, f: F) -> MethodRouter where F: Clone + Send + Sync + 'static, - HandleError, F, T>: Service, Error = Infallible>, - , F, T> as Service>>::Future: Send, - , F, T> as Service>>::Response: IntoResponse + Send, + HandleError, F, T>: Service, Error = Infallible>, + , F, T> as Service>>::Future: Send, + , F, T> as Service>>::Response: IntoResponse + Send, T: 'static, E: 'static, - B: 'static, S: 'static, { self.layer(HandleErrorLayer::new(f)) @@ -1068,7 +1051,7 @@ where self } - pub(crate) fn call_with_state(&mut self, req: Request, state: S) -> RouteFuture { + pub(crate) fn call_with_state(&mut self, req: Request, state: S) -> RouteFuture { macro_rules! call { ( $req:expr, @@ -1157,7 +1140,7 @@ fn append_allow_header(allow_header: &mut AllowHeader, method: &'static str) { } } -impl Clone for MethodRouter { +impl Clone for MethodRouter { fn clone(&self) -> Self { Self { get: self.get.clone(), @@ -1174,9 +1157,8 @@ impl Clone for MethodRouter { } } -impl Default for MethodRouter +impl Default for MethodRouter where - B: HttpBody + Send + 'static, S: Clone, { fn default() -> Self { @@ -1184,13 +1166,13 @@ where } } -enum MethodEndpoint { +enum MethodEndpoint { None, - Route(Route), - BoxedHandler(BoxedIntoRoute), + Route(Route), + BoxedHandler(BoxedIntoRoute), } -impl MethodEndpoint +impl MethodEndpoint where S: Clone, { @@ -1202,13 +1184,11 @@ where matches!(self, Self::None) } - fn map(self, f: F) -> MethodEndpoint + fn map(self, f: F) -> MethodEndpoint where S: 'static, - B: 'static, E: 'static, - F: FnOnce(Route) -> Route + Clone + Send + 'static, - B2: HttpBody + 'static, + F: FnOnce(Route) -> Route + Clone + Send + 'static, E2: 'static, { match self { @@ -1218,7 +1198,7 @@ where } } - fn with_state(self, state: &S) -> MethodEndpoint { + fn with_state(self, state: &S) -> MethodEndpoint { match self { MethodEndpoint::None => MethodEndpoint::None, MethodEndpoint::Route(route) => MethodEndpoint::Route(route), @@ -1229,7 +1209,7 @@ where } } -impl Clone for MethodEndpoint { +impl Clone for MethodEndpoint { fn clone(&self) -> Self { match self { Self::None => Self::None, @@ -1239,7 +1219,7 @@ impl Clone for MethodEndpoint { } } -impl fmt::Debug for MethodEndpoint { +impl fmt::Debug for MethodEndpoint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::None => f.debug_tuple("None").finish(), @@ -1249,13 +1229,14 @@ impl fmt::Debug for MethodEndpoint { } } -impl Service> for MethodRouter<(), B, E> +impl Service> for MethodRouter<(), E> where - B: HttpBody + Send + 'static, + B: HttpBody + Send + 'static, + B::Error: Into, { type Response = Response; type Error = E; - type Future = RouteFuture; + type Future = RouteFuture; #[inline] fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { @@ -1264,18 +1245,18 @@ where #[inline] fn call(&mut self, req: Request) -> Self::Future { + let req = req.map(Body::new); self.call_with_state(req, ()) } } -impl Handler<(), S, B> for MethodRouter +impl Handler<(), S> for MethodRouter where S: Clone + 'static, - B: HttpBody + Send + 'static, { - type Future = InfallibleRouteFuture; + type Future = InfallibleRouteFuture; - fn call(mut self, req: Request, state: S) -> Self::Future { + fn call(mut self, req: Request, state: S) -> Self::Future { InfallibleRouteFuture::new(self.call_with_state(req, state)) } } diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 7322401c45..e789a56bb0 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -16,6 +16,7 @@ use std::{ collections::HashMap, convert::Infallible, fmt, + marker::PhantomData, sync::Arc, task::{Context, Poll}, }; @@ -49,14 +50,14 @@ pub(crate) struct RouteId(u32); /// The router type for composing handlers and services. #[must_use] -pub struct Router { - routes: HashMap>, +pub struct Router { + routes: HashMap>, node: Arc, - fallback: Fallback, + fallback: Fallback, prev_route_id: RouteId, } -impl Clone for Router { +impl Clone for Router { fn clone(&self) -> Self { Self { routes: self.routes.clone(), @@ -67,9 +68,8 @@ impl Clone for Router { } } -impl Default for Router +impl Default for Router where - B: HttpBody + Send + 'static, S: Clone + Send + Sync + 'static, { fn default() -> Self { @@ -77,7 +77,7 @@ where } } -impl fmt::Debug for Router { +impl fmt::Debug for Router { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Router") .field("routes", &self.routes) @@ -91,9 +91,8 @@ impl fmt::Debug for Router { pub(crate) const NEST_TAIL_PARAM: &str = "__private__axum_nest_tail_param"; pub(crate) const NEST_TAIL_PARAM_CAPTURE: &str = "/*__private__axum_nest_tail_param"; -impl Router +impl Router where - B: HttpBody + Send + 'static, S: Clone + Send + Sync + 'static, { /// Create a new `Router`. @@ -111,7 +110,7 @@ where #[doc = include_str!("../docs/routing/route.md")] #[track_caller] - pub fn route(mut self, path: &str, method_router: MethodRouter) -> Self { + pub fn route(mut self, path: &str, method_router: MethodRouter) -> Self { #[track_caller] fn validate_path(path: &str) { if path.is_empty() { @@ -153,11 +152,11 @@ where #[doc = include_str!("../docs/routing/route_service.md")] pub fn route_service(self, path: &str, service: T) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service, Error = Infallible> + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, { - let service = match try_downcast::, _>(service) { + let service = match try_downcast::, _>(service) { Ok(_) => { panic!( "Invalid route: `Router::route_service` cannot be used with `Router`s. \ @@ -171,7 +170,7 @@ where } #[track_caller] - fn route_endpoint(mut self, path: &str, endpoint: Endpoint) -> Self { + fn route_endpoint(mut self, path: &str, endpoint: Endpoint) -> Self { if path.is_empty() { panic!("Paths must start with a `/`. Use \"/\" for root routes"); } else if !path.starts_with('/') { @@ -196,15 +195,15 @@ where #[doc = include_str!("../docs/routing/nest.md")] #[track_caller] - pub fn nest(self, path: &str, router: Router) -> Self { - self.nest_endpoint(path, RouterOrService::<_, _, NotFound>::Router(router)) + pub fn nest(self, path: &str, router: Router) -> Self { + self.nest_endpoint(path, RouterOrService::<_, NotFound>::Router(router)) } /// Like [`nest`](Self::nest), but accepts an arbitrary `Service`. #[track_caller] pub fn nest_service(self, path: &str, svc: T) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service, Error = Infallible> + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, { @@ -212,13 +211,9 @@ where } #[track_caller] - fn nest_endpoint( - mut self, - mut path: &str, - router_or_service: RouterOrService, - ) -> Self + fn nest_endpoint(mut self, mut path: &str, router_or_service: RouterOrService) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service, Error = Infallible> + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, { @@ -269,7 +264,7 @@ where #[track_caller] pub fn merge(mut self, other: R) -> Self where - R: Into>, + R: Into>, { let Router { routes, @@ -301,14 +296,13 @@ where } #[doc = include_str!("../docs/routing/layer.md")] - pub fn layer(self, layer: L) -> Router + pub fn layer(self, layer: L) -> Router where - L: Layer> + Clone + Send + 'static, - L::Service: Service> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Error: Into + 'static, - >>::Future: Send + 'static, - NewReqBody: HttpBody + 'static, + L: Layer + Clone + Send + 'static, + L::Service: Service> + Clone + Send + 'static, + >>::Response: IntoResponse + 'static, + >>::Error: Into + 'static, + >>::Future: Send + 'static, { let routes = self .routes @@ -333,11 +327,11 @@ where #[track_caller] pub fn route_layer(self, layer: L) -> Self where - L: Layer> + Clone + Send + 'static, - L::Service: Service> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Error: Into + 'static, - >>::Future: Send + 'static, + L: Layer + Clone + Send + 'static, + L::Service: Service> + Clone + Send + 'static, + >>::Response: IntoResponse + 'static, + >>::Error: Into + 'static, + >>::Future: Send + 'static, { if self.routes.is_empty() { panic!( @@ -366,7 +360,7 @@ where #[doc = include_str!("../docs/routing/fallback.md")] pub fn fallback(mut self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { self.fallback = Fallback::BoxedHandler(BoxedIntoRoute::from_handler(handler)); @@ -378,7 +372,7 @@ where /// See [`Router::fallback`] for more details. pub fn fallback_service(mut self, svc: T) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service, Error = Infallible> + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, { @@ -387,12 +381,12 @@ where } #[doc = include_str!("../docs/routing/with_state.md")] - pub fn with_state(self, state: S) -> Router { + pub fn with_state(self, state: S) -> Router { let routes = self .routes .into_iter() .map(|(id, endpoint)| { - let endpoint: Endpoint = match endpoint { + let endpoint: Endpoint = match endpoint { Endpoint::MethodRouter(method_router) => { Endpoint::MethodRouter(method_router.with_state(state.clone())) } @@ -417,9 +411,9 @@ where pub(crate) fn call_with_state( &mut self, - mut req: Request, + mut req: Request, state: S, - ) -> RouteFuture { + ) -> RouteFuture { #[cfg(feature = "original-uri")] { use crate::extract::OriginalUri; @@ -477,8 +471,7 @@ where | MatchError::MissingTrailingSlash, ) => match &mut self.fallback { Fallback::Default(fallback) => { - if let Some(super_fallback) = req.extensions_mut().remove::>() - { + if let Some(super_fallback) = req.extensions_mut().remove::() { let mut super_fallback = super_fallback.0.into_inner(); super_fallback.call(req) } else { @@ -500,12 +493,81 @@ where self.prev_route_id = RouteId(next_id); self.prev_route_id } + + /// Convert the router into a borrowed [`Service`] with a fixed request body type, to aid type + /// inference. + /// + /// In some cases when calling methods from [`tower::ServiceExt`] on a [`Router`] you might get + /// type inference errors along the lines of + /// + /// ```not_rust + /// let response = router.ready().await?.call(request).await?; + /// ^^^^^ cannot infer type for type parameter `B` + /// ``` + /// + /// This happens because `Router` implements [`Service`] with `impl Service> for Router<()>`. + /// + /// For example: + /// + /// ```compile_fail + /// use axum::{ + /// Router, + /// routing::get, + /// http::Request, + /// body::Body, + /// }; + /// use tower::{Service, ServiceExt}; + /// + /// # async fn async_main() -> Result<(), Box> { + /// let mut router = Router::new().route("/", get(|| async {})); + /// let request = Request::new(Body::empty()); + /// let response = router.ready().await?.call(request).await?; + /// # Ok(()) + /// # } + /// ``` + /// + /// Calling `Router::as_service` fixes that: + /// + /// ``` + /// use axum::{ + /// Router, + /// routing::get, + /// http::Request, + /// body::Body, + /// }; + /// use tower::{Service, ServiceExt}; + /// + /// # async fn async_main() -> Result<(), Box> { + /// let mut router = Router::new().route("/", get(|| async {})); + /// let request = Request::new(Body::empty()); + /// let response = router.as_service().ready().await?.call(request).await?; + /// # Ok(()) + /// # } + /// ``` + /// + /// This is mainly used when calling `Router` in tests. It shouldn't be necessary when running + /// the `Router` normally via [`Router::into_make_service`]. + pub fn as_service(&mut self) -> RouterAsService<'_, B, S> { + RouterAsService { + router: self, + _marker: PhantomData, + } + } + + /// Convert the router into an owned [`Service`] with a fixed request body type, to aid type + /// inference. + /// + /// This is the same as [`Router::as_service`] instead it returns an owned [`Service`]. See + /// that method for more details. + pub fn into_service(self) -> RouterIntoService { + RouterIntoService { + router: self, + _marker: PhantomData, + } + } } -impl Router<(), B> -where - B: HttpBody + Send + 'static, -{ +impl Router { /// Convert this router into a [`MakeService`], that is a [`Service`] whose /// response is another service. /// @@ -544,13 +606,14 @@ where } } -impl Service> for Router<(), B> +impl Service> for Router<()> where - B: HttpBody + Send + 'static, + B: HttpBody + Send + 'static, + B::Error: Into, { type Response = Response; type Error = Infallible; - type Future = RouteFuture; + type Future = RouteFuture; #[inline] fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { @@ -559,10 +622,89 @@ where #[inline] fn call(&mut self, req: Request) -> Self::Future { + let req = req.map(Body::new); self.call_with_state(req, ()) } } +/// A [`Router`] converted into a borrowed [`Service`] with a fixed body type. +/// +/// See [`Router::as_service`] for more details. +pub struct RouterAsService<'a, B, S = ()> { + router: &'a mut Router, + _marker: PhantomData, +} + +impl<'a, B> Service> for RouterAsService<'a, B, ()> +where + B: HttpBody + Send + 'static, + B::Error: Into, +{ + type Response = Response; + type Error = Infallible; + type Future = RouteFuture; + + #[inline] + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + >>::poll_ready(self.router, cx) + } + + #[inline] + fn call(&mut self, req: Request) -> Self::Future { + self.router.call(req) + } +} + +impl<'a, B, S> fmt::Debug for RouterAsService<'a, B, S> +where + S: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RouterAsService") + .field("router", &self.router) + .finish() + } +} + +/// A [`Router`] converted into an owned [`Service`] with a fixed body type. +/// +/// See [`Router::into_service`] for more details. +pub struct RouterIntoService { + router: Router, + _marker: PhantomData, +} + +impl Service> for RouterIntoService +where + B: HttpBody + Send + 'static, + B::Error: Into, +{ + type Response = Response; + type Error = Infallible; + type Future = RouteFuture; + + #[inline] + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + >>::poll_ready(&mut self.router, cx) + } + + #[inline] + fn call(&mut self, req: Request) -> Self::Future { + self.router.call(req) + } +} + +impl fmt::Debug for RouterIntoService +where + S: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RouterIntoService") + .field("router", &self.router) + .finish() + } +} + /// Wrapper around `matchit::Router` that supports merging two `Router`s. #[derive(Clone, Default)] struct Node { @@ -604,13 +746,13 @@ impl fmt::Debug for Node { } } -enum Fallback { - Default(Route), - Service(Route), - BoxedHandler(BoxedIntoRoute), +enum Fallback { + Default(Route), + Service(Route), + BoxedHandler(BoxedIntoRoute), } -impl Fallback +impl Fallback where S: Clone, { @@ -622,13 +764,11 @@ where } } - fn map(self, f: F) -> Fallback + fn map(self, f: F) -> Fallback where S: 'static, - B: 'static, E: 'static, - F: FnOnce(Route) -> Route + Clone + Send + 'static, - B2: HttpBody + 'static, + F: FnOnce(Route) -> Route + Clone + Send + 'static, E2: 'static, { match self { @@ -638,7 +778,7 @@ where } } - fn with_state(self, state: S) -> Fallback { + fn with_state(self, state: S) -> Fallback { match self { Fallback::Default(route) => Fallback::Default(route), Fallback::Service(route) => Fallback::Service(route), @@ -647,7 +787,7 @@ where } } -impl Clone for Fallback { +impl Clone for Fallback { fn clone(&self) -> Self { match self { Self::Default(inner) => Self::Default(inner.clone()), @@ -657,7 +797,7 @@ impl Clone for Fallback { } } -impl fmt::Debug for Fallback { +impl fmt::Debug for Fallback { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Default(inner) => f.debug_tuple("Default").field(inner).finish(), @@ -668,25 +808,23 @@ impl fmt::Debug for Fallback { } #[allow(clippy::large_enum_variant)] -enum Endpoint { - MethodRouter(MethodRouter), - Route(Route), - NestedRouter(BoxedIntoRoute), +enum Endpoint { + MethodRouter(MethodRouter), + Route(Route), + NestedRouter(BoxedIntoRoute), } -impl Endpoint +impl Endpoint where - B: HttpBody + Send + 'static, S: Clone + Send + Sync + 'static, { - fn layer(self, layer: L) -> Endpoint + fn layer(self, layer: L) -> Endpoint where - L: Layer> + Clone + Send + 'static, - L::Service: Service> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Error: Into + 'static, - >>::Future: Send + 'static, - NewReqBody: HttpBody + 'static, + L: Layer + Clone + Send + 'static, + L::Service: Service> + Clone + Send + 'static, + >>::Response: IntoResponse + 'static, + >>::Error: Into + 'static, + >>::Future: Send + 'static, { match self { Endpoint::MethodRouter(method_router) => { @@ -700,7 +838,7 @@ where } } -impl Clone for Endpoint { +impl Clone for Endpoint { fn clone(&self) -> Self { match self { Self::MethodRouter(inner) => Self::MethodRouter(inner.clone()), @@ -710,7 +848,7 @@ impl Clone for Endpoint { } } -impl fmt::Debug for Endpoint { +impl fmt::Debug for Endpoint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::MethodRouter(method_router) => { @@ -722,16 +860,16 @@ impl fmt::Debug for Endpoint { } } -enum RouterOrService { - Router(Router), +enum RouterOrService { + Router(Router), Service(T), } -struct SuperFallback(SyncWrapper>); +struct SuperFallback(SyncWrapper); #[test] #[allow(warnings)] fn traits() { use crate::test_helpers::*; - assert_send::>(); + assert_send::>(); } diff --git a/axum/src/routing/route.rs b/axum/src/routing/route.rs index 1667db1607..335c87df9e 100644 --- a/axum/src/routing/route.rs +++ b/axum/src/routing/route.rs @@ -27,12 +27,12 @@ use tower_service::Service; /// /// You normally shouldn't need to care about this type. It's used in /// [`Router::layer`](super::Router::layer). -pub struct Route(BoxCloneService, Response, E>); +pub struct Route(BoxCloneService, Response, E>); -impl Route { +impl Route { pub(crate) fn new(svc: T) -> Self where - T: Service, Error = E> + Clone + Send + 'static, + T: Service, Error = E> + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, { @@ -43,22 +43,22 @@ impl Route { pub(crate) fn oneshot_inner( &mut self, - req: Request, - ) -> Oneshot, Response, E>, Request> { + req: Request, + ) -> Oneshot, Response, E>, Request> { self.0.clone().oneshot(req) } - pub(crate) fn layer(self, layer: L) -> Route + pub(crate) fn layer(self, layer: L) -> Route where - L: Layer> + Clone + Send + 'static, - L::Service: Service> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Error: Into + 'static, - >>::Future: Send + 'static, - NewReqBody: 'static, + L: Layer> + Clone + Send + 'static, + L::Service: Service> + Clone + Send + 'static, + >>::Response: IntoResponse + 'static, + >>::Error: Into + 'static, + >>::Future: Send + 'static, NewError: 'static, { let layer = ServiceBuilder::new() + .map_request(|req: Request<_>| req.map(Body::new)) .map_err(Into::into) .layer(MapResponseLayer::new(IntoResponse::into_response)) .layer(layer) @@ -68,25 +68,26 @@ impl Route { } } -impl Clone for Route { +impl Clone for Route { fn clone(&self) -> Self { Self(self.0.clone()) } } -impl fmt::Debug for Route { +impl fmt::Debug for Route { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Route").finish() } } -impl Service> for Route +impl Service> for Route where - B: HttpBody, + B: HttpBody + Send + 'static, + B::Error: Into, { type Response = Response; type Error = E; - type Future = RouteFuture; + type Future = RouteFuture; #[inline] fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { @@ -95,15 +96,16 @@ where #[inline] fn call(&mut self, req: Request) -> Self::Future { + let req = req.map(Body::new); RouteFuture::from_future(self.oneshot_inner(req)) } } pin_project! { /// Response future for [`Route`]. - pub struct RouteFuture { + pub struct RouteFuture { #[pin] - kind: RouteFutureKind, + kind: RouteFutureKind, strip_body: bool, allow_header: Option, } @@ -111,12 +113,12 @@ pin_project! { pin_project! { #[project = RouteFutureKindProj] - enum RouteFutureKind { + enum RouteFutureKind { Future { #[pin] future: Oneshot< - BoxCloneService, Response, E>, - Request, + BoxCloneService, Response, E>, + Request, >, }, Response { @@ -125,9 +127,9 @@ pin_project! { } } -impl RouteFuture { +impl RouteFuture { pub(crate) fn from_future( - future: Oneshot, Response, E>, Request>, + future: Oneshot, Response, E>, Request>, ) -> Self { Self { kind: RouteFutureKind::Future { future }, @@ -147,10 +149,7 @@ impl RouteFuture { } } -impl Future for RouteFuture -where - B: HttpBody, -{ +impl Future for RouteFuture { type Output = Result; #[inline] @@ -217,22 +216,19 @@ fn set_content_length(size_hint: http_body::SizeHint, headers: &mut HeaderMap) { pin_project! { /// A [`RouteFuture`] that always yields a [`Response`]. - pub struct InfallibleRouteFuture { + pub struct InfallibleRouteFuture { #[pin] - future: RouteFuture, + future: RouteFuture, } } -impl InfallibleRouteFuture { - pub(crate) fn new(future: RouteFuture) -> Self { +impl InfallibleRouteFuture { + pub(crate) fn new(future: RouteFuture) -> Self { Self { future } } } -impl Future for InfallibleRouteFuture -where - B: HttpBody, -{ +impl Future for InfallibleRouteFuture { type Output = Response; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/axum/src/routing/tests/fallback.rs b/axum/src/routing/tests/fallback.rs index 65a6791ace..ca0dbf8649 100644 --- a/axum/src/routing/tests/fallback.rs +++ b/axum/src/routing/tests/fallback.rs @@ -172,7 +172,7 @@ async fn fallback_inherited_into_nested_router_service() { .with_state("inner"); // with a different state - let app = Router::<()>::new() + let app = Router::new() .nest_service("/foo", inner) .fallback(outer_fallback); @@ -194,7 +194,7 @@ async fn fallback_inherited_into_nested_opaque_service() { .boxed_clone(); // with a different state - let app = Router::<()>::new() + let app = Router::new() .nest_service("/foo", inner) .fallback(outer_fallback); diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index bed48415d3..c60d8a9df3 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -1,5 +1,5 @@ use crate::{ - body::{Bytes, Empty}, + body::{Body, Bytes, Empty}, error_handling::HandleErrorLayer, extract::{self, DefaultBodyLimit, FromRef, Path, State}, handler::{Handler, HandlerWithoutStateExt}, @@ -10,7 +10,6 @@ use crate::{ }; use futures_util::stream::StreamExt; use http::{header::ALLOW, header::CONTENT_LENGTH, HeaderMap, Request, Response, StatusCode, Uri}; -use hyper::Body; use serde_json::json; use std::{ convert::Infallible, @@ -199,7 +198,7 @@ async fn middleware_on_single_route() { #[crate::test] async fn service_in_bottom() { async fn handler(_req: Request) -> Result, Infallible> { - Ok(Response::new(hyper::Body::empty())) + Ok(Response::new(Body::empty())) } let app = Router::new().route("/", get_service(service_fn(handler))); @@ -649,7 +648,7 @@ async fn body_limited_by_default() { println!("calling {uri}"); let stream = futures_util::stream::repeat("a".repeat(1000)).map(Ok::<_, hyper::Error>); - let body = Body::wrap_stream(stream); + let body = reqwest::Body::wrap_stream(stream); let res_future = client .post(uri) @@ -673,7 +672,7 @@ async fn disabling_the_default_limit() { let client = TestClient::new(app); // `DEFAULT_LIMIT` is 2mb so make a body larger than that - let body = Body::from("a".repeat(3_000_000)); + let body = reqwest::Body::from("a".repeat(3_000_000)); let res = client.post("/").body(body).send().await; @@ -714,14 +713,14 @@ async fn changing_the_default_limit() { let res = client .post("/") - .body(Body::from("a".repeat(new_limit))) + .body(reqwest::Body::from("a".repeat(new_limit))) .send() .await; assert_eq!(res.status(), StatusCode::OK); let res = client .post("/") - .body(Body::from("a".repeat(new_limit + 1))) + .body(reqwest::Body::from("a".repeat(new_limit + 1))) .send() .await; assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE); @@ -745,7 +744,7 @@ async fn limited_body_with_streaming_body() { let stream = futures_util::stream::iter(vec![Ok::<_, hyper::Error>("a".repeat(LIMIT))]); let res = client .post("/") - .body(Body::wrap_stream(stream)) + .body(reqwest::Body::wrap_stream(stream)) .send() .await; assert_eq!(res.status(), StatusCode::OK); @@ -753,7 +752,7 @@ async fn limited_body_with_streaming_body() { let stream = futures_util::stream::iter(vec![Ok::<_, hyper::Error>("a".repeat(LIMIT * 2))]); let res = client .post("/") - .body(Body::wrap_stream(stream)) + .body(reqwest::Body::wrap_stream(stream)) .send() .await; assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE); diff --git a/axum/src/routing/tests/nest.rs b/axum/src/routing/tests/nest.rs index e0fb6b6e83..7a3f38033f 100644 --- a/axum/src/routing/tests/nest.rs +++ b/axum/src/routing/tests/nest.rs @@ -257,7 +257,7 @@ async fn multiple_top_level_nests() { #[crate::test] #[should_panic(expected = "Invalid route: nested routes cannot contain wildcards (*)")] async fn nest_cannot_contain_wildcards() { - _ = Router::<(), Body>::new().nest("/one/*rest", Router::new()); + _ = Router::<()>::new().nest("/one/*rest", Router::new()); } #[crate::test] diff --git a/examples/consume-body-in-extractor-or-middleware/src/main.rs b/examples/consume-body-in-extractor-or-middleware/src/main.rs index 548e4f0e5a..25819a2278 100644 --- a/examples/consume-body-in-extractor-or-middleware/src/main.rs +++ b/examples/consume-body-in-extractor-or-middleware/src/main.rs @@ -6,7 +6,7 @@ use axum::{ async_trait, - body::{self, BoxBody, Bytes, Full}, + body::{Body, Bytes}, extract::FromRequest, http::{Request, StatusCode}, middleware::{self, Next}, @@ -16,7 +16,6 @@ use axum::{ }; use std::net::SocketAddr; use tower::ServiceBuilder; -use tower_http::ServiceBuilderExt; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -29,11 +28,9 @@ async fn main() { .with(tracing_subscriber::fmt::layer()) .init(); - let app = Router::new().route("/", post(handler)).layer( - ServiceBuilder::new() - .map_request_body(body::boxed) - .layer(middleware::from_fn(print_request_body)), - ); + let app = Router::new() + .route("/", post(handler)) + .layer(ServiceBuilder::new().layer(middleware::from_fn(print_request_body))); let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); tracing::debug!("listening on {}", addr); @@ -45,8 +42,8 @@ async fn main() { // middleware that shows how to consume the request body upfront async fn print_request_body( - request: Request, - next: Next, + request: Request, + next: Next, ) -> Result { let request = buffer_request_body(request).await?; @@ -55,7 +52,7 @@ async fn print_request_body( // the trick is to take the request apart, buffer the body, do what you need to do, then put // the request back together -async fn buffer_request_body(request: Request) -> Result, Response> { +async fn buffer_request_body(request: Request) -> Result, Response> { let (parts, body) = request.into_parts(); // this wont work if the body is an long running stream @@ -65,7 +62,7 @@ async fn buffer_request_body(request: Request) -> Result FromRequest for BufferRequestBody +impl FromRequest for BufferRequestBody where S: Send + Sync, { type Rejection = Response; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let body = Bytes::from_request(req, state) .await .map_err(|err| err.into_response())?; diff --git a/examples/customize-extractor-error/src/custom_extractor.rs b/examples/customize-extractor-error/src/custom_extractor.rs index 4351384b95..6a39ffbd22 100644 --- a/examples/customize-extractor-error/src/custom_extractor.rs +++ b/examples/customize-extractor-error/src/custom_extractor.rs @@ -6,6 +6,7 @@ //! - Complexity: Manually implementing `FromRequest` results on more complex code use axum::{ async_trait, + body::Body, extract::{rejection::JsonRejection, FromRequest, MatchedPath}, http::Request, http::StatusCode, @@ -22,15 +23,14 @@ pub async fn handler(Json(value): Json) -> impl IntoResponse { pub struct Json(pub T); #[async_trait] -impl FromRequest for Json +impl FromRequest for Json where - axum::Json: FromRequest, + axum::Json: FromRequest, S: Send + Sync, - B: Send + 'static, { type Rejection = (StatusCode, axum::Json); - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let (mut parts, body) = req.into_parts(); // We can use other extractors to provide better rejection messages. diff --git a/examples/http-proxy/src/main.rs b/examples/http-proxy/src/main.rs index 27d3b85046..73abf78f18 100644 --- a/examples/http-proxy/src/main.rs +++ b/examples/http-proxy/src/main.rs @@ -37,8 +37,9 @@ async fn main() { let router_svc = Router::new().route("/", get(|| async { "Hello, World!" })); - let service = tower::service_fn(move |req: Request| { + let service = tower::service_fn(move |req: Request<_>| { let router_svc = router_svc.clone(); + let req = req.map(Body::new); async move { if req.method() == Method::CONNECT { proxy(req).await diff --git a/examples/hyper-1-0/src/main.rs b/examples/hyper-1-0/src/main.rs index 72c651cce4..51493d4450 100644 --- a/examples/hyper-1-0/src/main.rs +++ b/examples/hyper-1-0/src/main.rs @@ -8,13 +8,11 @@ use axum::{routing::get, Router}; use std::net::SocketAddr; use tokio::net::TcpListener; use tower_http::trace::TraceLayer; -use tower_hyper_http_body_compat::{ - HttpBody1ToHttpBody04, TowerService03HttpServiceAsHyper1HttpService, -}; +use tower_hyper_http_body_compat::TowerService03HttpServiceAsHyper1HttpService; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; // this is hyper 1.0 -use hyper::{body::Incoming, server::conn::http1}; +use hyper::server::conn::http1; #[tokio::main] async fn main() { @@ -26,8 +24,7 @@ async fn main() { .with(tracing_subscriber::fmt::layer()) .init(); - // you have to use `HttpBody1ToHttpBody04` as the second type parameter to `Router` - let app: Router<_, HttpBody1ToHttpBody04> = Router::new() + let app = Router::new() .route("/", get(|| async { "Hello, World!" })) // we can still add regular tower middleware .layer(TraceLayer::new_for_http()); diff --git a/examples/low-level-openssl/src/main.rs b/examples/low-level-openssl/src/main.rs index 707f6c0a2d..c40e24997c 100644 --- a/examples/low-level-openssl/src/main.rs +++ b/examples/low-level-openssl/src/main.rs @@ -1,7 +1,7 @@ use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod}; use tokio_openssl::SslStream; -use axum::{extract::ConnectInfo, routing::get, Router}; +use axum::{body::Body, extract::ConnectInfo, http::Request, routing::get, Router}; use futures_util::future::poll_fn; use hyper::server::{ accept::Accept, @@ -68,7 +68,7 @@ async fn main() { let protocol = protocol.clone(); - let svc = app.make_service(&stream); + let svc = MakeService::<_, Request>::make_service(&mut app, &stream); tokio::spawn(async move { let ssl = Ssl::new(acceptor.context()).unwrap(); diff --git a/examples/low-level-rustls/src/main.rs b/examples/low-level-rustls/src/main.rs index 22176844a5..afe6a037e9 100644 --- a/examples/low-level-rustls/src/main.rs +++ b/examples/low-level-rustls/src/main.rs @@ -4,7 +4,7 @@ //! cargo run -p example-low-level-rustls //! ``` -use axum::{extract::ConnectInfo, routing::get, Router}; +use axum::{body::Body, extract::ConnectInfo, http::Request, routing::get, Router}; use futures_util::future::poll_fn; use hyper::server::{ accept::Accept, @@ -24,7 +24,7 @@ use tokio_rustls::{ rustls::{Certificate, PrivateKey, ServerConfig}, TlsAcceptor, }; -use tower::MakeService; +use tower::make::MakeService; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -53,7 +53,7 @@ async fn main() { let protocol = Arc::new(Http::new()); - let mut app = Router::new() + let mut app = Router::<()>::new() .route("/", get(handler)) .into_make_service_with_connect_info::(); @@ -67,7 +67,7 @@ async fn main() { let protocol = protocol.clone(); - let svc = app.make_service(&stream); + let svc = MakeService::<_, Request>::make_service(&mut app, &stream); tokio::spawn(async move { if let Ok(stream) = acceptor.accept(stream).await { diff --git a/examples/parse-body-based-on-content-type/src/main.rs b/examples/parse-body-based-on-content-type/src/main.rs index 017622380a..d66791bcdd 100644 --- a/examples/parse-body-based-on-content-type/src/main.rs +++ b/examples/parse-body-based-on-content-type/src/main.rs @@ -8,6 +8,7 @@ use axum::{ async_trait, + body::Body, extract::FromRequest, http::{header::CONTENT_TYPE, Request, StatusCode}, response::{IntoResponse, Response}, @@ -51,17 +52,16 @@ async fn handler(JsonOrForm(payload): JsonOrForm) { struct JsonOrForm(T); #[async_trait] -impl FromRequest for JsonOrForm +impl FromRequest for JsonOrForm where - B: Send + 'static, S: Send + Sync, - Json: FromRequest<(), B>, - Form: FromRequest<(), B>, + Json: FromRequest<()>, + Form: FromRequest<()>, T: 'static, { type Rejection = Response; - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { let content_type_header = req.headers().get(CONTENT_TYPE); let content_type = content_type_header.and_then(|value| value.to_str().ok()); diff --git a/examples/reverse-proxy/src/main.rs b/examples/reverse-proxy/src/main.rs index d97dcf44a0..634a6a0440 100644 --- a/examples/reverse-proxy/src/main.rs +++ b/examples/reverse-proxy/src/main.rs @@ -8,12 +8,14 @@ //! ``` use axum::{ + body::Body, extract::State, - http::{uri::Uri, Request, Response}, + http::{uri::Uri, Request}, + response::{IntoResponse, Response}, routing::get, Router, }; -use hyper::{client::HttpConnector, Body}; +use hyper::client::HttpConnector; use std::net::SocketAddr; type Client = hyper::client::Client; @@ -22,7 +24,7 @@ type Client = hyper::client::Client; async fn main() { tokio::spawn(server()); - let client = Client::new(); + let client: Client = hyper::Client::builder().build(HttpConnector::new()); let app = Router::new().route("/", get(handler)).with_state(client); @@ -34,7 +36,7 @@ async fn main() { .unwrap(); } -async fn handler(State(client): State, mut req: Request) -> Response { +async fn handler(State(client): State, mut req: Request) -> Response { let path = req.uri().path(); let path_query = req .uri() @@ -46,7 +48,7 @@ async fn handler(State(client): State, mut req: Request) -> Respon *req.uri_mut() = Uri::try_from(uri).unwrap(); - client.request(req).await.unwrap() + client.request(req).await.unwrap().into_response() } async fn server() { diff --git a/examples/static-file-server/src/main.rs b/examples/static-file-server/src/main.rs index d1eac21f69..50857d9611 100644 --- a/examples/static-file-server/src/main.rs +++ b/examples/static-file-server/src/main.rs @@ -71,7 +71,10 @@ fn using_serve_dir_with_handler_as_service() -> Router { (StatusCode::NOT_FOUND, "Not found") } - let serve_dir = ServeDir::new("assets").not_found_service(handle_404.into_service()); + // you can convert handler function to service + let service = handle_404.into_service(); + + let serve_dir = ServeDir::new("assets").not_found_service(service); Router::new() .route("/foo", get(|| async { "Hi from /foo" })) diff --git a/examples/stream-to-file/src/main.rs b/examples/stream-to-file/src/main.rs index 585ad3de6c..e58fd1497f 100644 --- a/examples/stream-to-file/src/main.rs +++ b/examples/stream-to-file/src/main.rs @@ -5,9 +5,9 @@ //! ``` use axum::{ - body::Bytes, - extract::{BodyStream, Multipart, Path}, - http::StatusCode, + body::{Body, Bytes}, + extract::{Multipart, Path}, + http::{Request, StatusCode}, response::{Html, Redirect}, routing::{get, post}, BoxError, Router, @@ -52,9 +52,9 @@ async fn main() { // POST'ing to `/file/foo.txt` will create a file called `foo.txt`. async fn save_request_body( Path(file_name): Path, - body: BodyStream, + request: Request, ) -> Result<(), (StatusCode, String)> { - stream_to_file(&file_name, body).await + stream_to_file(&file_name, request.into_body()).await } // Handler that returns HTML for a multipart form. diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index b5f7466b46..02079eb856 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -148,7 +148,7 @@ mod tests { .request( Request::builder() .uri(format!("http://{}", addr)) - .body(Body::empty()) + .body(hyper::Body::empty()) .unwrap(), ) .await @@ -165,11 +165,21 @@ mod tests { let mut app = app(); let request = Request::builder().uri("/").body(Body::empty()).unwrap(); - let response = app.ready().await.unwrap().call(request).await.unwrap(); + let response = ServiceExt::>::ready(&mut app) + .await + .unwrap() + .call(request) + .await + .unwrap(); assert_eq!(response.status(), StatusCode::OK); let request = Request::builder().uri("/").body(Body::empty()).unwrap(); - let response = app.ready().await.unwrap().call(request).await.unwrap(); + let response = ServiceExt::>::ready(&mut app) + .await + .unwrap() + .call(request) + .await + .unwrap(); assert_eq!(response.status(), StatusCode::OK); } @@ -186,7 +196,14 @@ mod tests { .uri("/requires-connect-into") .body(Body::empty()) .unwrap(); - let response = app.ready().await.unwrap().call(request).await.unwrap(); + let response = app + .as_service() + .ready() + .await + .unwrap() + .call(request) + .await + .unwrap(); assert_eq!(response.status(), StatusCode::OK); } } diff --git a/examples/validator/src/main.rs b/examples/validator/src/main.rs index ecfe9509d3..8545a3e9ce 100644 --- a/examples/validator/src/main.rs +++ b/examples/validator/src/main.rs @@ -12,6 +12,7 @@ use async_trait::async_trait; use axum::{ + body::Body, extract::{rejection::FormRejection, Form, FromRequest}, http::{Request, StatusCode}, response::{Html, IntoResponse, Response}, @@ -61,16 +62,15 @@ async fn handler(ValidatedForm(input): ValidatedForm) -> Html pub struct ValidatedForm(pub T); #[async_trait] -impl FromRequest for ValidatedForm +impl FromRequest for ValidatedForm where T: DeserializeOwned + Validate, S: Send + Sync, - Form: FromRequest, - B: Send + 'static, + Form: FromRequest, { type Rejection = ServerError; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let Form(value) = Form::::from_request(req, state).await?; value.validate()?; Ok(ValidatedForm(value)) From 0d50d17304d0ca03f0bc1f71e86efdc9e81b6532 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Tue, 14 Mar 2023 09:13:19 +0100 Subject: [PATCH 06/57] Add `Body::from_stream` (#1848) --- axum-core/Cargo.toml | 2 + axum-core/src/body.rs | 61 ++++++++++++++++++++++++++ axum-extra/src/body/async_read_body.rs | 30 +++++-------- axum-extra/src/extract/multipart.rs | 6 +-- axum-extra/src/json_lines.rs | 4 +- axum/src/body/mod.rs | 4 -- axum/src/extract/multipart.rs | 2 +- axum/src/test_helpers/mod.rs | 1 - 8 files changed, 80 insertions(+), 30 deletions(-) diff --git a/axum-core/Cargo.toml b/axum-core/Cargo.toml index 5fb2312438..129d212ce1 100644 --- a/axum-core/Cargo.toml +++ b/axum-core/Cargo.toml @@ -21,6 +21,8 @@ futures-util = { version = "0.3", default-features = false, features = ["alloc"] http = "0.2.7" http-body = "0.4.5" mime = "0.3.16" +pin-project-lite = "0.2.7" +sync_wrapper = "0.1.1" tower-layer = "0.3" tower-service = "0.3" diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index 601e18a88a..f3c165857f 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -1,13 +1,17 @@ //! HTTP body utilities. +use crate::response::{IntoResponse, Response}; use crate::{BoxError, Error}; use bytes::Bytes; use bytes::{Buf, BufMut}; use futures_util::stream::Stream; +use futures_util::TryStream; use http::HeaderMap; use http_body::Body as _; +use pin_project_lite::pin_project; use std::pin::Pin; use std::task::{Context, Poll}; +use sync_wrapper::SyncWrapper; /// A boxed [`Body`] trait object. /// @@ -107,6 +111,20 @@ impl Body { pub fn empty() -> Self { Self::new(http_body::Empty::new()) } + + /// Create a new `Body` from a [`Stream`]. + /// + /// [`Stream`]: futures_util::stream::Stream + pub 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 { @@ -175,6 +193,49 @@ impl Stream for Body { } } +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 = Error; + + 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(Error::new(err)))), + None => Poll::Ready(None), + } + } + + #[inline] + fn poll_trailers( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll, Self::Error>> { + Poll::Ready(Ok(None)) + } +} + +impl IntoResponse for Body { + fn into_response(self) -> Response { + Response::new(self.0) + } +} + #[test] fn test_try_downcast() { assert_eq!(try_downcast::(5_u32), Err(5_u32)); diff --git a/axum-extra/src/body/async_read_body.rs b/axum-extra/src/body/async_read_body.rs index 5ea0fc59c5..ce87e43693 100644 --- a/axum-extra/src/body/async_read_body.rs +++ b/axum-extra/src/body/async_read_body.rs @@ -1,5 +1,5 @@ use axum::{ - body::{self, Bytes, HttpBody, StreamBody}, + body::{Body, Bytes, HttpBody}, http::HeaderMap, response::{IntoResponse, Response}, Error, @@ -47,28 +47,25 @@ pin_project! { #[cfg(feature = "async-read-body")] #[derive(Debug)] #[must_use] - pub struct AsyncReadBody { + pub struct AsyncReadBody { #[pin] - read: StreamBody>, + body: Body, } } -impl AsyncReadBody { +impl AsyncReadBody { /// Create a new `AsyncReadBody`. - pub fn new(read: R) -> Self + pub fn new(read: R) -> Self where R: AsyncRead + Send + 'static, { Self { - read: StreamBody::new(ReaderStream::new(read)), + body: Body::from_stream(ReaderStream::new(read)), } } } -impl HttpBody for AsyncReadBody -where - R: AsyncRead + Send + 'static, -{ +impl HttpBody for AsyncReadBody { type Data = Bytes; type Error = Error; @@ -76,22 +73,19 @@ where self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - self.project().read.poll_data(cx) + self.project().body.poll_data(cx) } fn poll_trailers( self: Pin<&mut Self>, - _cx: &mut Context<'_>, + cx: &mut Context<'_>, ) -> Poll, Self::Error>> { - Poll::Ready(Ok(None)) + self.project().body.poll_trailers(cx) } } -impl IntoResponse for AsyncReadBody -where - R: AsyncRead + Send + 'static, -{ +impl IntoResponse for AsyncReadBody { fn into_response(self) -> Response { - Response::new(body::boxed(self)) + self.body.into_response() } } diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 09f45a1821..403e183041 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -7,7 +7,7 @@ use axum::{ body::{Body, Bytes}, extract::FromRequest, response::{IntoResponse, Response}, - BoxError, RequestExt, + RequestExt, }; use futures_util::stream::Stream; use http::{ @@ -405,9 +405,7 @@ impl std::error::Error for InvalidBoundary {} mod tests { use super::*; use crate::test_helpers::*; - use axum::{ - body::Body, extract::DefaultBodyLimit, response::IntoResponse, routing::post, Router, - }; + use axum::{extract::DefaultBodyLimit, response::IntoResponse, routing::post, Router}; #[tokio::test] async fn content_type_with_encoding() { diff --git a/axum-extra/src/json_lines.rs b/axum-extra/src/json_lines.rs index 912529dd6d..973e418e39 100644 --- a/axum-extra/src/json_lines.rs +++ b/axum-extra/src/json_lines.rs @@ -2,7 +2,7 @@ use axum::{ async_trait, - body::{Body, StreamBody}, + body::Body, extract::FromRequest, response::{IntoResponse, Response}, BoxError, @@ -166,7 +166,7 @@ where buf.write_all(b"\n")?; Ok::<_, BoxError>(buf.into_inner().freeze()) }); - let stream = StreamBody::new(stream); + let stream = Body::from_stream(stream); // there is no consensus around mime type yet // https://github.com/wardi/jsonlines/issues/36 diff --git a/axum/src/body/mod.rs b/axum/src/body/mod.rs index 9e1e826fff..a1243ffd87 100644 --- a/axum/src/body/mod.rs +++ b/axum/src/body/mod.rs @@ -1,9 +1,5 @@ //! HTTP body utilities. -mod stream_body; - -pub use self::stream_body::StreamBody; - #[doc(no_inline)] pub use http_body::{Body as HttpBody, Empty, Full}; diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 2587610e96..484d3796b7 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -5,8 +5,8 @@ use super::FromRequest; use crate::body::Bytes; use async_trait::async_trait; -use axum_core::response::{IntoResponse, Response}; use axum_core::body::Body; +use axum_core::response::{IntoResponse, Response}; use axum_core::RequestExt; use futures_util::stream::Stream; use http::header::{HeaderMap, CONTENT_TYPE}; diff --git a/axum/src/test_helpers/mod.rs b/axum/src/test_helpers/mod.rs index 0b60ee9007..7c48916f20 100644 --- a/axum/src/test_helpers/mod.rs +++ b/axum/src/test_helpers/mod.rs @@ -7,6 +7,5 @@ pub(crate) use self::test_client::*; pub(crate) fn assert_send() {} pub(crate) fn assert_sync() {} -pub(crate) fn assert_unpin() {} pub(crate) struct NotSendSync(*const ()); From 2ae0cdf7cf1d26f8069d82968b9c6ac3411c2d71 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 20 Mar 2023 21:02:40 +0100 Subject: [PATCH 07/57] Remove `B` type param: Follow ups (#1789) Co-authored-by: Jonas Platte Co-authored-by: Michael Scofield --- axum-core/src/body.rs | 16 +--- axum-core/src/ext_traits/request.rs | 35 ++++----- axum-core/src/extract/default_body_limit.rs | 9 +-- axum-core/src/extract/mod.rs | 14 ++-- axum-core/src/extract/request_parts.rs | 26 +++++-- axum-core/src/extract/tuple.rs | 7 +- axum-core/src/response/into_response.rs | 32 ++++---- axum-core/src/response/mod.rs | 6 +- axum-extra/src/extract/cached.rs | 1 - axum-extra/src/extract/form.rs | 7 +- axum-extra/src/extract/with_rejection.rs | 7 +- axum-extra/src/handler/mod.rs | 3 +- axum-extra/src/handler/or.rs | 6 +- axum-extra/src/json_lines.rs | 5 +- axum-extra/src/protobuf.rs | 25 +++--- axum-extra/src/routing/mod.rs | 7 +- axum-macros/CHANGELOG.md | 1 + axum-macros/src/debug_handler.rs | 3 +- .../debug_handler/fail/extract_self_mut.rs | 5 +- .../fail/extract_self_mut.stderr | 4 +- .../debug_handler/fail/extract_self_ref.rs | 5 +- .../fail/extract_self_ref.stderr | 4 +- .../tests/debug_handler/pass/request_last.rs | 4 +- .../tests/debug_handler/pass/self_receiver.rs | 7 +- .../tests/debug_handler/pass/set_state.rs | 5 +- .../debug_handler/pass/state_and_body.rs | 4 +- .../from_request/pass/override_rejection.rs | 6 +- axum/CHANGELOG.md | 21 +++-- axum/src/body/mod.rs | 4 +- axum/src/body/stream_body.rs | 5 +- axum/src/boxed.rs | 25 ++---- axum/src/docs/extract.md | 38 ++++----- axum/src/docs/middleware.md | 19 +++-- axum/src/docs/response.md | 6 +- axum/src/docs/routing/route_service.md | 8 +- axum/src/extract/matched_path.rs | 8 +- axum/src/extract/mod.rs | 3 +- axum/src/extract/multipart.rs | 6 +- axum/src/extract/raw_form.rs | 6 +- axum/src/extract/request_parts.rs | 48 +----------- axum/src/extract/ws.rs | 9 +-- axum/src/form.rs | 13 ++-- axum/src/handler/future.rs | 13 ++-- axum/src/handler/mod.rs | 36 ++++----- axum/src/json.rs | 10 +-- axum/src/middleware/from_fn.rs | 78 +++++++------------ axum/src/response/mod.rs | 4 +- axum/src/response/sse.rs | 8 +- axum/src/routing/into_make_service.rs | 3 +- axum/src/routing/method_routing.rs | 78 +++++++++---------- axum/src/routing/mod.rs | 40 +++++----- axum/src/routing/route.rs | 30 +++---- axum/src/routing/tests/get_to_head.rs | 2 +- axum/src/routing/tests/merge.rs | 6 +- axum/src/routing/tests/mod.rs | 46 +++++------ axum/src/routing/tests/nest.rs | 8 +- axum/src/test_helpers/test_client.rs | 7 +- .../src/main.rs | 13 ++-- .../src/custom_extractor.rs | 6 +- examples/http-proxy/src/main.rs | 9 ++- examples/low-level-rustls/src/main.rs | 4 +- .../src/main.rs | 7 +- examples/print-request-response/src/main.rs | 7 +- examples/prometheus-metrics/src/main.rs | 5 +- .../src/multiplex_service.rs | 18 +++-- examples/reverse-proxy/src/main.rs | 6 +- examples/static-file-server/src/main.rs | 8 +- examples/stream-to-file/src/main.rs | 8 +- examples/testing/src/main.rs | 15 ++-- examples/validator/src/main.rs | 7 +- 70 files changed, 428 insertions(+), 527 deletions(-) diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index f3c165857f..89e69218bd 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -1,6 +1,5 @@ //! HTTP body utilities. -use crate::response::{IntoResponse, Response}; use crate::{BoxError, Error}; use bytes::Bytes; use bytes::{Buf, BufMut}; @@ -13,14 +12,9 @@ use std::pin::Pin; use std::task::{Context, Poll}; use sync_wrapper::SyncWrapper; -/// A boxed [`Body`] trait object. -/// -/// This is used in axum as the response body type for applications. It's -/// necessary to unify multiple response bodies types into one. -pub type BoxBody = http_body::combinators::UnsyncBoxBody; +type BoxBody = http_body::combinators::UnsyncBoxBody; -/// Convert a [`http_body::Body`] into a [`BoxBody`]. -pub fn boxed(body: B) -> BoxBody +fn boxed(body: B) -> BoxBody where B: http_body::Body + Send + 'static, B::Error: Into, @@ -230,12 +224,6 @@ where } } -impl IntoResponse for Body { - fn into_response(self) -> Response { - Response::new(self.0) - } -} - #[test] fn test_try_downcast() { assert_eq!(try_downcast::(5_u32), Err(5_u32)); diff --git a/axum-core/src/ext_traits/request.rs b/axum-core/src/ext_traits/request.rs index 1c69abd54e..df41a3c943 100644 --- a/axum-core/src/ext_traits/request.rs +++ b/axum-core/src/ext_traits/request.rs @@ -1,7 +1,6 @@ use crate::body::Body; -use crate::extract::{DefaultBodyLimitKind, FromRequest, FromRequestParts}; +use crate::extract::{DefaultBodyLimitKind, FromRequest, FromRequestParts, Request}; use futures_util::future::BoxFuture; -use http::Request; use http_body::Limited; mod sealed { @@ -23,9 +22,9 @@ pub trait RequestExt: sealed::Sealed + Sized { /// ``` /// use axum::{ /// async_trait, - /// extract::FromRequest, + /// extract::{Request, FromRequest}, /// body::Body, - /// http::{header::CONTENT_TYPE, Request, StatusCode}, + /// http::{header::CONTENT_TYPE, StatusCode}, /// response::{IntoResponse, Response}, /// Form, Json, RequestExt, /// }; @@ -42,7 +41,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// { /// type Rejection = Response; /// - /// async fn from_request(req: Request, _state: &S) -> Result { + /// async fn from_request(req: Request, _state: &S) -> Result { /// let content_type = req /// .headers() /// .get(CONTENT_TYPE) @@ -87,8 +86,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// use axum::{ /// async_trait, /// body::Body, - /// extract::{FromRef, FromRequest}, - /// http::Request, + /// extract::{Request, FromRef, FromRequest}, /// RequestExt, /// }; /// @@ -104,7 +102,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// { /// type Rejection = std::convert::Infallible; /// - /// async fn from_request(req: Request, state: &S) -> Result { + /// async fn from_request(req: Request, state: &S) -> Result { /// let requires_state = req.extract_with_state::(state).await?; /// /// Ok(Self { requires_state }) @@ -122,7 +120,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// { /// // ... /// # type Rejection = std::convert::Infallible; - /// # async fn from_request(req: Request, _state: &S) -> Result { + /// # async fn from_request(req: Request, _state: &S) -> Result { /// # todo!() /// # } /// } @@ -141,9 +139,8 @@ pub trait RequestExt: sealed::Sealed + Sized { /// ``` /// use axum::{ /// async_trait, - /// extract::FromRequest, + /// extract::{Request, FromRequest}, /// headers::{authorization::Bearer, Authorization}, - /// http::Request, /// response::{IntoResponse, Response}, /// body::Body, /// Json, RequestExt, TypedHeader, @@ -163,7 +160,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// { /// type Rejection = Response; /// - /// async fn from_request(mut req: Request, _state: &S) -> Result { + /// async fn from_request(mut req: Request, _state: &S) -> Result { /// let TypedHeader(auth_header) = req /// .extract_parts::>>() /// .await @@ -194,8 +191,8 @@ pub trait RequestExt: sealed::Sealed + Sized { /// ``` /// use axum::{ /// async_trait, - /// extract::{FromRef, FromRequest, FromRequestParts}, - /// http::{request::Parts, Request}, + /// extract::{Request, FromRef, FromRequest, FromRequestParts}, + /// http::request::Parts, /// response::{IntoResponse, Response}, /// body::Body, /// Json, RequestExt, @@ -216,7 +213,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// { /// type Rejection = Response; /// - /// async fn from_request(mut req: Request, state: &S) -> Result { + /// async fn from_request(mut req: Request, state: &S) -> Result { /// let requires_state = req /// .extract_parts_with_state::(state) /// .await @@ -260,7 +257,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// Apply the [default body limit](crate::extract::DefaultBodyLimit). /// /// If it is disabled, return the request as-is in `Err`. - fn with_limited_body(self) -> Result>, Request>; + fn with_limited_body(self) -> Result>, Request>; /// Consumes the request, returning the body wrapped in [`Limited`] if a /// [default limit](crate::extract::DefaultBodyLimit) is in place, or not wrapped if the @@ -268,7 +265,7 @@ pub trait RequestExt: sealed::Sealed + Sized { fn into_limited_body(self) -> Result, Body>; } -impl RequestExt for Request { +impl RequestExt for Request { fn extract(self) -> BoxFuture<'static, Result> where E: FromRequest<(), M> + 'static, @@ -321,7 +318,7 @@ impl RequestExt for Request { }) } - fn with_limited_body(self) -> Result>, Request> { + fn with_limited_body(self) -> Result>, Request> { // update docs in `axum-core/src/extract/default_body_limit.rs` and // `axum/src/docs/extract.md` if this changes const DEFAULT_LIMIT: usize = 2_097_152; // 2 mb @@ -426,7 +423,7 @@ mod tests { { type Rejection = >::Rejection; - async fn from_request(mut req: Request, state: &S) -> Result { + async fn from_request(mut req: Request, state: &S) -> Result { let RequiresState(from_state) = req.extract_parts_with_state(state).await.unwrap(); let method = req.extract_parts().await.unwrap(); let body = req.extract().await?; diff --git a/axum-core/src/extract/default_body_limit.rs b/axum-core/src/extract/default_body_limit.rs index f27e23a3c7..5a2cd971b5 100644 --- a/axum-core/src/extract/default_body_limit.rs +++ b/axum-core/src/extract/default_body_limit.rs @@ -30,22 +30,21 @@ use tower_layer::Layer; /// Router, /// routing::post, /// body::Body, -/// extract::{DefaultBodyLimit, RawBody}, -/// http::Request, +/// extract::{Request, DefaultBodyLimit}, /// }; /// /// let app = Router::new() /// .route( /// "/", /// // even with `DefaultBodyLimit` the request body is still just `Body` -/// post(|request: Request| async {}), +/// post(|request: Request| async {}), /// ) /// .layer(DefaultBodyLimit::max(1024)); /// # let _: Router = app; /// ``` /// /// ``` -/// use axum::{Router, routing::post, body::Body, extract::RawBody, http::Request}; +/// use axum::{Router, routing::post, body::Body, extract::Request}; /// use tower_http::limit::RequestBodyLimitLayer; /// use http_body::Limited; /// @@ -54,7 +53,7 @@ use tower_layer::Layer; /// "/", /// // `RequestBodyLimitLayer` changes the request body type to `Limited` /// // extracting a different body type wont work -/// post(|request: Request| async {}), +/// post(|request: Request| async {}), /// ) /// .layer(RequestBodyLimitLayer::new(1024)); /// # let _: Router = app; diff --git a/axum-core/src/extract/mod.rs b/axum-core/src/extract/mod.rs index c8b6f39463..758e5a5203 100644 --- a/axum-core/src/extract/mod.rs +++ b/axum-core/src/extract/mod.rs @@ -6,7 +6,7 @@ use crate::{body::Body, response::IntoResponse}; use async_trait::async_trait; -use http::{request::Parts, Request}; +use http::request::Parts; use std::convert::Infallible; pub mod rejection; @@ -19,6 +19,10 @@ mod tuple; pub(crate) use self::default_body_limit::DefaultBodyLimitKind; pub use self::{default_body_limit::DefaultBodyLimit, from_ref::FromRef}; +/// Type alias for [`http::Request`] whose body type defaults to [`Body`], the most common body +/// type used with axum. +pub type Request = http::Request; + mod private { #[derive(Debug, Clone, Copy)] pub enum ViaParts {} @@ -78,7 +82,7 @@ pub trait FromRequest: Sized { type Rejection: IntoResponse; /// Perform the extraction. - async fn from_request(req: Request, state: &S) -> Result; + async fn from_request(req: Request, state: &S) -> Result; } #[async_trait] @@ -89,7 +93,7 @@ where { type Rejection = >::Rejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let (mut parts, _) = req.into_parts(); Self::from_request_parts(&mut parts, state).await } @@ -119,7 +123,7 @@ where { type Rejection = Infallible; - async fn from_request(req: Request, state: &S) -> Result, Self::Rejection> { + async fn from_request(req: Request, state: &S) -> Result, Self::Rejection> { Ok(T::from_request(req, state).await.ok()) } } @@ -145,7 +149,7 @@ where { type Rejection = Infallible; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { Ok(T::from_request(req, state).await) } } diff --git a/axum-core/src/extract/request_parts.rs b/axum-core/src/extract/request_parts.rs index 80ad5e78a9..98943bea35 100644 --- a/axum-core/src/extract/request_parts.rs +++ b/axum-core/src/extract/request_parts.rs @@ -1,18 +1,18 @@ -use super::{rejection::*, FromRequest, FromRequestParts}; +use super::{rejection::*, FromRequest, FromRequestParts, Request}; use crate::{body::Body, RequestExt}; use async_trait::async_trait; use bytes::Bytes; -use http::{request::Parts, HeaderMap, Method, Request, Uri, Version}; +use http::{request::Parts, HeaderMap, Method, Uri, Version}; use std::convert::Infallible; #[async_trait] -impl FromRequest for Request +impl FromRequest for Request where S: Send + Sync, { type Rejection = Infallible; - async fn from_request(req: Request, _: &S) -> Result { + async fn from_request(req: Request, _: &S) -> Result { Ok(req) } } @@ -77,7 +77,7 @@ where { type Rejection = BytesRejection; - async fn from_request(req: Request, _: &S) -> Result { + async fn from_request(req: Request, _: &S) -> Result { let bytes = match req.into_limited_body() { Ok(limited_body) => crate::body::to_bytes(limited_body) .await @@ -98,7 +98,7 @@ where { type Rejection = StringRejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let bytes = Bytes::from_request(req, state) .await .map_err(|err| match err { @@ -122,7 +122,19 @@ where { type Rejection = Infallible; - async fn from_request(req: Request, _: &S) -> Result { + async fn from_request(req: Request, _: &S) -> Result { Ok(req.into_parts().0) } } + +#[async_trait] +impl FromRequest for Body +where + S: Send + Sync, +{ + type Rejection = Infallible; + + async fn from_request(req: Request, _: &S) -> Result { + Ok(req.into_body()) + } +} diff --git a/axum-core/src/extract/tuple.rs b/axum-core/src/extract/tuple.rs index 14d1e1ddd3..021b9616df 100644 --- a/axum-core/src/extract/tuple.rs +++ b/axum-core/src/extract/tuple.rs @@ -1,8 +1,7 @@ -use super::{FromRequest, FromRequestParts}; -use crate::body::Body; +use super::{FromRequest, FromRequestParts, Request}; use crate::response::{IntoResponse, Response}; use async_trait::async_trait; -use http::request::{Parts, Request}; +use http::request::Parts; use std::convert::Infallible; #[async_trait] @@ -57,7 +56,7 @@ macro_rules! impl_from_request { { type Rejection = Response; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let (mut parts, body) = req.into_parts(); $( diff --git a/axum-core/src/response/into_response.rs b/axum-core/src/response/into_response.rs index 82efaabed2..8a40641a04 100644 --- a/axum-core/src/response/into_response.rs +++ b/axum-core/src/response/into_response.rs @@ -1,5 +1,5 @@ use super::{IntoResponseParts, Response, ResponseParts}; -use crate::{body, BoxError}; +use crate::{body::Body, BoxError}; use bytes::{buf::Chain, Buf, Bytes, BytesMut}; use http::{ header::{self, HeaderMap, HeaderName, HeaderValue}, @@ -74,9 +74,9 @@ use std::{ /// body, /// routing::get, /// response::{IntoResponse, Response}, +/// body::Body, /// Router, /// }; -/// use http_body::Body; /// use http::HeaderMap; /// use bytes::Bytes; /// use std::{ @@ -89,7 +89,7 @@ use std::{ /// /// // First implement `Body` for `MyBody`. This could for example use /// // some custom streaming protocol. -/// impl Body for MyBody { +/// impl http_body::Body for MyBody { /// type Data = Bytes; /// type Error = Infallible; /// @@ -113,7 +113,7 @@ use std::{ /// // Now we can implement `IntoResponse` directly for `MyBody` /// impl IntoResponse for MyBody { /// fn into_response(self) -> Response { -/// Response::new(body::boxed(self)) +/// Response::new(Body::new(self)) /// } /// } /// @@ -165,25 +165,31 @@ where B::Error: Into, { fn into_response(self) -> Response { - self.map(body::boxed) + self.map(Body::new) } } impl IntoResponse for http::response::Parts { fn into_response(self) -> Response { - Response::from_parts(self, body::boxed(Empty::new())) + Response::from_parts(self, Body::empty()) } } impl IntoResponse for Full { fn into_response(self) -> Response { - Response::new(body::boxed(self)) + Response::new(Body::new(self)) } } impl IntoResponse for Empty { fn into_response(self) -> Response { - Response::new(body::boxed(self)) + Response::new(Body::new(self)) + } +} + +impl IntoResponse for Body { + fn into_response(self) -> Response { + Response::new(self) } } @@ -192,7 +198,7 @@ where E: Into + 'static, { fn into_response(self) -> Response { - Response::new(body::boxed(self)) + Response::new(Body::new(self)) } } @@ -201,7 +207,7 @@ where E: Into + 'static, { fn into_response(self) -> Response { - Response::new(body::boxed(self)) + Response::new(Body::new(self)) } } @@ -212,7 +218,7 @@ where B::Error: Into, { fn into_response(self) -> Response { - Response::new(body::boxed(self)) + Response::new(Body::new(self)) } } @@ -223,7 +229,7 @@ where E: Into, { fn into_response(self) -> Response { - Response::new(body::boxed(self)) + Response::new(Body::new(self)) } } @@ -274,7 +280,7 @@ where { fn into_response(self) -> Response { let (first, second) = self.into_inner(); - let mut res = Response::new(body::boxed(BytesChainBody { + let mut res = Response::new(Body::new(BytesChainBody { first: Some(first), second: Some(second), })); diff --git a/axum-core/src/response/mod.rs b/axum-core/src/response/mod.rs index d66dfec510..b06f072f19 100644 --- a/axum-core/src/response/mod.rs +++ b/axum-core/src/response/mod.rs @@ -4,7 +4,7 @@ //! //! [`axum::response`]: https://docs.rs/axum/latest/axum/response/index.html -use crate::body::BoxBody; +use crate::body::Body; mod append_headers; mod into_response; @@ -16,9 +16,9 @@ pub use self::{ into_response_parts::{IntoResponseParts, ResponseParts, TryIntoHeaderError}, }; -/// Type alias for [`http::Response`] whose body type defaults to [`BoxBody`], the most common body +/// Type alias for [`http::Response`] whose body type defaults to [`Body`], the most common body /// type used with axum. -pub type Response = http::Response; +pub type Response = http::Response; /// An [`IntoResponse`]-based result type that uses [`ErrorResponse`] as the error type. /// diff --git a/axum-extra/src/extract/cached.rs b/axum-extra/src/extract/cached.rs index 03565c7728..c544d6ddb1 100644 --- a/axum-extra/src/extract/cached.rs +++ b/axum-extra/src/extract/cached.rs @@ -22,7 +22,6 @@ use std::ops::{Deref, DerefMut}; /// use axum::{ /// async_trait, /// extract::FromRequestParts, -/// body::BoxBody, /// response::{IntoResponse, Response}, /// http::{StatusCode, request::Parts}, /// }; diff --git a/axum-extra/src/extract/form.rs b/axum-extra/src/extract/form.rs index 1f660c4511..5c813d393e 100644 --- a/axum-extra/src/extract/form.rs +++ b/axum-extra/src/extract/form.rs @@ -1,11 +1,10 @@ use axum::{ async_trait, - body::Body, - extract::{rejection::RawFormRejection, FromRequest, RawForm}, + extract::{rejection::RawFormRejection, FromRequest, RawForm, Request}, response::{IntoResponse, Response}, Error, RequestExt, }; -use http::{Request, StatusCode}; +use http::StatusCode; use serde::de::DeserializeOwned; use std::{fmt, ops::Deref}; @@ -59,7 +58,7 @@ where { type Rejection = FormRejection; - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { let RawForm(bytes) = req .extract() .await diff --git a/axum-extra/src/extract/with_rejection.rs b/axum-extra/src/extract/with_rejection.rs index c5b9c326f7..1227a1ab13 100644 --- a/axum-extra/src/extract/with_rejection.rs +++ b/axum-extra/src/extract/with_rejection.rs @@ -1,9 +1,7 @@ use axum::async_trait; -use axum::body::Body; -use axum::extract::{FromRequest, FromRequestParts}; +use axum::extract::{FromRequest, FromRequestParts, Request}; use axum::response::IntoResponse; use http::request::Parts; -use http::Request; use std::fmt::Debug; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; @@ -118,7 +116,7 @@ where { type Rejection = R; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let extractor = E::from_request(req, state).await?; Ok(WithRejection(extractor, PhantomData)) } @@ -141,6 +139,7 @@ where #[cfg(test)] mod tests { + use axum::body::Body; use axum::extract::FromRequestParts; use axum::http::Request; use axum::response::Response; diff --git a/axum-extra/src/handler/mod.rs b/axum-extra/src/handler/mod.rs index 275f8418cd..d017b41ef6 100644 --- a/axum-extra/src/handler/mod.rs +++ b/axum-extra/src/handler/mod.rs @@ -1,6 +1,7 @@ //! Additional handler utilities. use axum::body::Body; +use axum::extract::Request; use axum::{ extract::FromRequest, handler::Handler, @@ -174,7 +175,7 @@ where { type Future = BoxFuture<'static, Response>; - fn call(self, req: http::Request, state: S) -> Self::Future { + fn call(self, req: Request, state: S) -> Self::Future { let req = req.map(Body::new); Box::pin(async move { match T::from_request(req, &state).await { diff --git a/axum-extra/src/handler/or.rs b/axum-extra/src/handler/or.rs index b23ae5d4ee..7b78fe3d73 100644 --- a/axum-extra/src/handler/or.rs +++ b/axum-extra/src/handler/or.rs @@ -1,10 +1,8 @@ use super::HandlerCallWithExtractors; use crate::either::Either; use axum::{ - body::Body, - extract::{FromRequest, FromRequestParts}, + extract::{FromRequest, FromRequestParts, Request}, handler::Handler, - http::Request, response::{IntoResponse, Response}, }; use futures_util::future::{BoxFuture, Either as EitherFuture, FutureExt, Map}; @@ -67,7 +65,7 @@ where // this puts `futures_util` in our public API but thats fine in axum-extra type Future = BoxFuture<'static, Response>; - fn call(self, req: Request, state: S) -> Self::Future { + fn call(self, req: Request, state: S) -> Self::Future { Box::pin(async move { let (mut parts, body) = req.into_parts(); diff --git a/axum-extra/src/json_lines.rs b/axum-extra/src/json_lines.rs index 973e418e39..7cf47b04a3 100644 --- a/axum-extra/src/json_lines.rs +++ b/axum-extra/src/json_lines.rs @@ -3,13 +3,12 @@ use axum::{ async_trait, body::Body, - extract::FromRequest, + extract::{FromRequest, Request}, response::{IntoResponse, Response}, BoxError, }; use bytes::{BufMut, BytesMut}; use futures_util::stream::{BoxStream, Stream, TryStream, TryStreamExt}; -use http::Request; use pin_project_lite::pin_project; use serde::{de::DeserializeOwned, Serialize}; use std::{ @@ -108,7 +107,7 @@ where { type Rejection = Infallible; - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { // `Stream::lines` isn't a thing so we have to convert it into an `AsyncRead` // so we can call `AsyncRead::lines` and then convert it back to a `Stream` let body = req.into_body(); diff --git a/axum-extra/src/protobuf.rs b/axum-extra/src/protobuf.rs index f55423ac30..870fdb59ad 100644 --- a/axum-extra/src/protobuf.rs +++ b/axum-extra/src/protobuf.rs @@ -2,12 +2,11 @@ use axum::{ async_trait, - body::Body, - extract::{rejection::BytesRejection, FromRequest}, + extract::{rejection::BytesRejection, FromRequest, Request}, response::{IntoResponse, Response}, }; use bytes::{Bytes, BytesMut}; -use http::{Request, StatusCode}; +use http::StatusCode; use prost::Message; use std::ops::{Deref, DerefMut}; @@ -104,7 +103,7 @@ where { type Rejection = ProtobufRejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let mut bytes = Bytes::from_request(req, state).await?; match T::decode(&mut bytes) { @@ -286,16 +285,16 @@ mod tests { result: String, } - let app = Router::new().route( - "/", - post(|input: Protobuf| async move { - let output = Output { - result: input.foo.to_owned(), - }; + #[axum::debug_handler] + async fn handler(input: Protobuf) -> Protobuf { + let output = Output { + result: input.foo.to_owned(), + }; - Protobuf(output) - }), - ); + Protobuf(output) + } + + let app = Router::new().route("/", post(handler)); let input = Input { foo: "bar".to_owned(), diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index 47d2e84a86..40cb336df8 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -1,8 +1,7 @@ //! Additional types for defining routes. use axum::{ - body::Body, - http::Request, + extract::Request, response::{IntoResponse, Redirect, Response}, routing::{any, MethodRouter}, Router, @@ -166,7 +165,7 @@ pub trait RouterExt: sealed::Sealed { /// This works like [`RouterExt::route_with_tsr`] but accepts any [`Service`]. fn route_service_with_tsr(self, path: &str, service: T) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, Self: Sized; @@ -269,7 +268,7 @@ where #[track_caller] fn route_service_with_tsr(mut self, path: &str, service: T) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, Self: Sized, diff --git a/axum-macros/CHANGELOG.md b/axum-macros/CHANGELOG.md index 1326807fab..5d5d5645a2 100644 --- a/axum-macros/CHANGELOG.md +++ b/axum-macros/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 request-consuming extractors ([#1826]) [#1826]: https://github.com/tokio-rs/axum/pull/1826 +[#1751]: https://github.com/tokio-rs/axum/pull/1751 # 0.3.5 (03. March, 2023) diff --git a/axum-macros/src/debug_handler.rs b/axum-macros/src/debug_handler.rs index d44371f95a..0f361a10cb 100644 --- a/axum-macros/src/debug_handler.rs +++ b/axum-macros/src/debug_handler.rs @@ -43,10 +43,9 @@ pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream { err.unwrap_or_else(|| { let state_ty = state_ty.unwrap_or_else(|| syn::parse_quote!(())); - let check_input_order = check_input_order(&item_fn); let check_future_send = check_future_send(&item_fn); - if let Some(check_input_order) = check_input_order { + if let Some(check_input_order) = check_input_order(&item_fn) { quote! { #check_input_order #check_future_send diff --git a/axum-macros/tests/debug_handler/fail/extract_self_mut.rs b/axum-macros/tests/debug_handler/fail/extract_self_mut.rs index 1d45b50494..21ae99d6b8 100644 --- a/axum-macros/tests/debug_handler/fail/extract_self_mut.rs +++ b/axum-macros/tests/debug_handler/fail/extract_self_mut.rs @@ -1,7 +1,6 @@ use axum::{ async_trait, - extract::FromRequest, - http::Request, + extract::{Request, FromRequest}, }; use axum_macros::debug_handler; @@ -14,7 +13,7 @@ where { type Rejection = (); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { unimplemented!() } } diff --git a/axum-macros/tests/debug_handler/fail/extract_self_mut.stderr b/axum-macros/tests/debug_handler/fail/extract_self_mut.stderr index 3d80dffbca..595786bf4e 100644 --- a/axum-macros/tests/debug_handler/fail/extract_self_mut.stderr +++ b/axum-macros/tests/debug_handler/fail/extract_self_mut.stderr @@ -1,5 +1,5 @@ error: Handlers must only take owned values - --> tests/debug_handler/fail/extract_self_mut.rs:24:22 + --> tests/debug_handler/fail/extract_self_mut.rs:23:22 | -24 | async fn handler(&mut self) {} +23 | async fn handler(&mut self) {} | ^^^^^^^^^ diff --git a/axum-macros/tests/debug_handler/fail/extract_self_ref.rs b/axum-macros/tests/debug_handler/fail/extract_self_ref.rs index e5c3bb032d..8e32811994 100644 --- a/axum-macros/tests/debug_handler/fail/extract_self_ref.rs +++ b/axum-macros/tests/debug_handler/fail/extract_self_ref.rs @@ -1,7 +1,6 @@ use axum::{ async_trait, - extract::FromRequest, - http::Request, + extract::{Request, FromRequest}, }; use axum_macros::debug_handler; @@ -14,7 +13,7 @@ where { type Rejection = (); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { unimplemented!() } } diff --git a/axum-macros/tests/debug_handler/fail/extract_self_ref.stderr b/axum-macros/tests/debug_handler/fail/extract_self_ref.stderr index 82d9a89ff5..4c0b4950c7 100644 --- a/axum-macros/tests/debug_handler/fail/extract_self_ref.stderr +++ b/axum-macros/tests/debug_handler/fail/extract_self_ref.stderr @@ -1,5 +1,5 @@ error: Handlers must only take owned values - --> tests/debug_handler/fail/extract_self_ref.rs:24:22 + --> tests/debug_handler/fail/extract_self_ref.rs:23:22 | -24 | async fn handler(&self) {} +23 | async fn handler(&self) {} | ^^^^^ diff --git a/axum-macros/tests/debug_handler/pass/request_last.rs b/axum-macros/tests/debug_handler/pass/request_last.rs index bbfb53d28e..da1531f458 100644 --- a/axum-macros/tests/debug_handler/pass/request_last.rs +++ b/axum-macros/tests/debug_handler/pass/request_last.rs @@ -1,7 +1,7 @@ -use axum::{body::Body, extract::Extension, http::Request}; +use axum::extract::{Extension, Request}; use axum_macros::debug_handler; #[debug_handler] -async fn handler(_: Extension, _: Request) {} +async fn handler(_: Extension, _: Request) {} fn main() {} diff --git a/axum-macros/tests/debug_handler/pass/self_receiver.rs b/axum-macros/tests/debug_handler/pass/self_receiver.rs index 2b4dfa4d6b..9b72284502 100644 --- a/axum-macros/tests/debug_handler/pass/self_receiver.rs +++ b/axum-macros/tests/debug_handler/pass/self_receiver.rs @@ -1,7 +1,6 @@ use axum::{ async_trait, - extract::FromRequest, - http::Request, + extract::{Request, FromRequest}, }; use axum_macros::debug_handler; @@ -14,7 +13,7 @@ where { type Rejection = (); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { unimplemented!() } } @@ -26,7 +25,7 @@ where { type Rejection = (); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { unimplemented!() } } diff --git a/axum-macros/tests/debug_handler/pass/set_state.rs b/axum-macros/tests/debug_handler/pass/set_state.rs index 7c06742e54..60a7a3304e 100644 --- a/axum-macros/tests/debug_handler/pass/set_state.rs +++ b/axum-macros/tests/debug_handler/pass/set_state.rs @@ -1,7 +1,6 @@ use axum_macros::debug_handler; -use axum::extract::{FromRef, FromRequest}; +use axum::extract::{Request, FromRef, FromRequest}; use axum::async_trait; -use axum::http::Request; #[debug_handler(state = AppState)] async fn handler(_: A) {} @@ -19,7 +18,7 @@ where { type Rejection = (); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { unimplemented!() } } diff --git a/axum-macros/tests/debug_handler/pass/state_and_body.rs b/axum-macros/tests/debug_handler/pass/state_and_body.rs index fea3700745..f348360b3a 100644 --- a/axum-macros/tests/debug_handler/pass/state_and_body.rs +++ b/axum-macros/tests/debug_handler/pass/state_and_body.rs @@ -1,8 +1,8 @@ use axum_macros::debug_handler; -use axum::{extract::State, http::Request}; +use axum::{extract::State, extract::Request}; #[debug_handler(state = AppState)] -async fn handler(_: State, _: Request) {} +async fn handler(_: State, _: Request) {} #[derive(Clone)] struct AppState; diff --git a/axum-macros/tests/from_request/pass/override_rejection.rs b/axum-macros/tests/from_request/pass/override_rejection.rs index db341b792e..779058b9fc 100644 --- a/axum-macros/tests/from_request/pass/override_rejection.rs +++ b/axum-macros/tests/from_request/pass/override_rejection.rs @@ -1,7 +1,7 @@ use axum::{ async_trait, - extract::{rejection::ExtensionRejection, FromRequest}, - http::{StatusCode, Request}, + extract::{Request, rejection::ExtensionRejection, FromRequest}, + http::StatusCode, response::{IntoResponse, Response}, routing::get, body::Body, @@ -35,7 +35,7 @@ where // this rejection doesn't implement `Display` and `Error` type Rejection = (StatusCode, String); - async fn from_request(_req: Request, _state: &S) -> Result { + async fn from_request(_req: Request, _state: &S) -> Result { todo!() } } diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 3db88eb8b4..c12a24cfb6 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -8,18 +8,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased - **breaking:** The following types/traits are no longer generic over the request body - (i.e. the `B` type param has been removed) ([#1751]): - - `FromRequest` + (i.e. the `B` type param has been removed) ([#1751] and [#1789]): - `FromRequestParts` - - `Handler` + - `FromRequest` - `HandlerService` - `HandlerWithoutStateExt` - - `Layered` + - `Handler` - `LayeredFuture` + - `Layered` - `MethodRouter` + - `Next` - `RequestExt` - - `Route` - `RouteFuture` + - `Route` - `Router` - **breaking:** axum no longer re-exports `hyper::Body` as that type is removed in hyper 1.0. Instead axum has its own body type at `axum::body::Body` ([#1751]) @@ -28,6 +29,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **breaking:** Change `sse::Event::json_data` to use `axum_core::Error` as its error type ([#1762]) - **breaking:** Rename `DefaultOnFailedUpdgrade` to `DefaultOnFailedUpgrade` ([#1664]) - **breaking:** Rename `OnFailedUpdgrade` to `OnFailedUpgrade` ([#1664]) +- **breaking:** Removed re-exports of `Empty` and `Full`. Use + `axum::body::Body::empty` and `axum::body::Body::from` respectively ([#1789]) +- **breaking:** The response returned by `IntoResponse::into_response` must use + `axum::body::Body` as the body type. `axum::response::Response` does this + ([#1789]) +- **breaking:** Removed the `BoxBody` type alias and its `box_body` + constructor. Use `axum::body::Body::new` instead ([#1789]) +- **breaking:** Remove `RawBody` extractor. `axum::body::Body` implements `FromRequest` directly ([#1789]) +- **added:** Add `axum::extract::Request` type alias where the body is `axum::body::Body` ([#1789]) - **added:** Add `Router::as_service` and `Router::into_service` to workaround type inference issues when calling `ServiceExt` methods on a `Router` ([#1835]) @@ -35,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#1751]: https://github.com/tokio-rs/axum/pull/1751 [#1762]: https://github.com/tokio-rs/axum/pull/1762 [#1835]: https://github.com/tokio-rs/axum/pull/1835 +[#1789]: https://github.com/tokio-rs/axum/pull/1789 # 0.6.12 (22. March, 2023) diff --git a/axum/src/body/mod.rs b/axum/src/body/mod.rs index a1243ffd87..0e3d478179 100644 --- a/axum/src/body/mod.rs +++ b/axum/src/body/mod.rs @@ -1,10 +1,10 @@ //! HTTP body utilities. #[doc(no_inline)] -pub use http_body::{Body as HttpBody, Empty, Full}; +pub use http_body::Body as HttpBody; #[doc(no_inline)] pub use bytes::Bytes; #[doc(inline)] -pub use axum_core::body::{boxed, Body, BoxBody}; +pub use axum_core::body::Body; diff --git a/axum/src/body/stream_body.rs b/axum/src/body/stream_body.rs index df782c6987..6331567a74 100644 --- a/axum/src/body/stream_body.rs +++ b/axum/src/body/stream_body.rs @@ -1,8 +1,9 @@ use crate::{ - body::{self, Bytes, HttpBody}, + body::{Bytes, HttpBody}, response::{IntoResponse, Response}, BoxError, Error, }; +use axum_core::body::Body; use futures_util::{ ready, stream::{self, TryStream}, @@ -93,7 +94,7 @@ where S::Error: Into, { fn into_response(self) -> Response { - Response::new(body::boxed(self)) + Response::new(Body::new(self)) } } diff --git a/axum/src/boxed.rs b/axum/src/boxed.rs index 54a1a89d14..92ab1c54c6 100644 --- a/axum/src/boxed.rs +++ b/axum/src/boxed.rs @@ -1,7 +1,6 @@ use std::{convert::Infallible, fmt}; -use axum_core::body::Body; -use http::Request; +use crate::extract::Request; use tower::Service; use crate::{ @@ -37,11 +36,7 @@ where })) } - pub(crate) fn call_with_state( - self, - request: Request, - state: S, - ) -> RouteFuture { + pub(crate) fn call_with_state(self, request: Request, state: S) -> RouteFuture { self.0.call_with_state(request, state) } } @@ -82,7 +77,7 @@ pub(crate) trait ErasedIntoRoute: Send { fn into_route(self: Box, state: S) -> Route; - fn call_with_state(self: Box, request: Request, state: S) -> RouteFuture; + fn call_with_state(self: Box, request: Request, state: S) -> RouteFuture; } pub(crate) struct MakeErasedHandler { @@ -103,11 +98,7 @@ where (self.into_route)(self.handler, state) } - fn call_with_state( - self: Box, - request: Request, - state: S, - ) -> RouteFuture { + fn call_with_state(self: Box, request: Request, state: S) -> RouteFuture { self.into_route(state).call(request) } } @@ -141,11 +132,7 @@ where (self.into_route)(self.router, state) } - fn call_with_state( - mut self: Box, - request: Request, - state: S, - ) -> RouteFuture { + fn call_with_state(mut self: Box, request: Request, state: S) -> RouteFuture { self.router.call_with_state(request, state) } } @@ -184,7 +171,7 @@ where (self.layer)(self.inner.into_route(state)) } - fn call_with_state(self: Box, request: Request, state: S) -> RouteFuture { + fn call_with_state(self: Box, request: Request, state: S) -> RouteFuture { (self.layer)(self.inner.into_route(state)).call(request) } } diff --git a/axum/src/docs/extract.md b/axum/src/docs/extract.md index d03f142b2b..70bf645bca 100644 --- a/axum/src/docs/extract.md +++ b/axum/src/docs/extract.md @@ -57,10 +57,10 @@ Some commonly used extractors are: ```rust,no_run use axum::{ - extract::{Json, TypedHeader, Path, Extension, Query}, + extract::{Request, Json, TypedHeader, Path, Extension, Query}, routing::post, headers::UserAgent, - http::{Request, header::HeaderMap}, + http::header::HeaderMap, body::{Bytes, Body}, Router, }; @@ -91,7 +91,7 @@ async fn bytes(body: Bytes) {} async fn json(Json(payload): Json) {} // `Request` gives you the whole request for maximum control -async fn request(request: Request) {} +async fn request(request: Request) {} // `Extension` extracts data from "request extensions" // This is commonly used to share state with handlers @@ -463,7 +463,7 @@ If your extractor needs to consume the request body you must implement [`FromReq ```rust,no_run use axum::{ async_trait, - extract::FromRequest, + extract::{Request, FromRequest}, response::{Response, IntoResponse}, body::{Bytes, Body}, routing::get, @@ -471,7 +471,6 @@ use axum::{ http::{ StatusCode, header::{HeaderValue, USER_AGENT}, - Request, }, }; @@ -485,7 +484,7 @@ where { type Rejection = Response; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let body = Bytes::from_request(req, state) .await .map_err(IntoResponse::into_response)?; @@ -516,8 +515,8 @@ wrapping another extractor: use axum::{ Router, routing::get, - extract::{FromRequest, FromRequestParts}, - http::{Request, request::Parts}, + extract::{FromRequest, Request, FromRequestParts}, + http::request::Parts, body::Body, async_trait, }; @@ -534,7 +533,7 @@ where { type Rejection = Infallible; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { // ... # todo!() } @@ -644,20 +643,17 @@ Extractors can also be run from middleware: ```rust use axum::{ middleware::{self, Next}, - extract::{TypedHeader, FromRequestParts}, - http::{Request, StatusCode}, + extract::{TypedHeader, Request, FromRequestParts}, + http::StatusCode, response::Response, headers::authorization::{Authorization, Bearer}, RequestPartsExt, Router, }; -async fn auth_middleware( - request: Request, - next: Next, -) -> Result -where - B: Send, -{ +async fn auth_middleware( + request: Request, + next: Next, +) -> Result { // running extractors requires a `axum::http::request::Parts` let (mut parts, body) = request.into_parts(); @@ -696,8 +692,8 @@ use axum::{ Router, body::Body, routing::get, - extract::{FromRequest, FromRequestParts}, - http::{Request, HeaderMap, request::Parts}, + extract::{Request, FromRequest, FromRequestParts}, + http::{HeaderMap, request::Parts}, async_trait, }; use std::time::{Instant, Duration}; @@ -737,7 +733,7 @@ where { type Rejection = T::Rejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let start = Instant::now(); let extractor = T::from_request(req, state).await?; let duration = start.elapsed(); diff --git a/axum/src/docs/middleware.md b/axum/src/docs/middleware.md index 05f17fa1a9..6aca7a3ead 100644 --- a/axum/src/docs/middleware.md +++ b/axum/src/docs/middleware.md @@ -223,7 +223,7 @@ A decent template for such a middleware could be: use axum::{ response::Response, body::Body, - http::Request, + extract::Request, }; use futures_util::future::BoxFuture; use tower::{Service, Layer}; @@ -245,9 +245,9 @@ struct MyMiddleware { inner: S, } -impl Service> for MyMiddleware +impl Service for MyMiddleware where - S: Service, Response = Response> + Send + 'static, + S: Service + Send + 'static, S::Future: Send + 'static, { type Response = S::Response; @@ -259,7 +259,7 @@ where self.inner.poll_ready(cx) } - fn call(&mut self, request: Request) -> Self::Future { + fn call(&mut self, request: Request) -> Self::Future { let future = self.inner.call(request); Box::pin(async move { let response: Response = future.await?; @@ -406,8 +406,7 @@ use axum::{ routing::get, middleware::{self, Next}, response::Response, - extract::State, - http::Request, + extract::{State, Request}, }; use tower::{Layer, Service}; use std::task::{Context, Poll}; @@ -477,17 +476,17 @@ State can be passed from middleware to handlers using [request extensions]: ```rust use axum::{ Router, - http::{Request, StatusCode}, + http::StatusCode, routing::get, response::{IntoResponse, Response}, middleware::{self, Next}, - extract::Extension, + extract::{Request, Extension}, }; #[derive(Clone)] struct CurrentUser { /* ... */ } -async fn auth(mut req: Request, next: Next) -> Result { +async fn auth(mut req: Request, next: Next) -> Result { let auth_header = req.headers() .get(http::header::AUTHORIZATION) .and_then(|header| header.to_str().ok()); @@ -546,7 +545,7 @@ use axum::{ ServiceExt, // for `into_make_service` response::Response, middleware::Next, - http::Request, + extract::Request, }; fn rewrite_request_uri(req: Request) -> Request { diff --git a/axum/src/docs/response.md b/axum/src/docs/response.md index 2afe476046..4e1d50b5e2 100644 --- a/axum/src/docs/response.md +++ b/axum/src/docs/response.md @@ -171,15 +171,15 @@ Use [`Response`](crate::response::Response) for more low level control: use axum::{ Json, response::{IntoResponse, Response}, - body::{Full, Bytes}, + body::Body, http::StatusCode, }; -async fn response() -> Response> { +async fn response() -> Response { Response::builder() .status(StatusCode::NOT_FOUND) .header("x-foo", "custom header") - .body(Full::from("not found")) + .body(Body::from("not found")) .unwrap() } ``` diff --git a/axum/src/docs/routing/route_service.md b/axum/src/docs/routing/route_service.md index 1be229e4c6..190e0183a3 100644 --- a/axum/src/docs/routing/route_service.md +++ b/axum/src/docs/routing/route_service.md @@ -7,7 +7,8 @@ use axum::{ Router, body::Body, routing::{any_service, get_service}, - http::{Request, StatusCode}, + extract::Request, + http::StatusCode, error_handling::HandleErrorLayer, }; use tower_http::services::ServeFile; @@ -22,7 +23,7 @@ let app = Router::new() // Services whose response body is not `axum::body::BoxBody` // can be wrapped in `axum::routing::any_service` (or one of the other routing filters) // to have the response body mapped - any_service(service_fn(|_: Request| async { + any_service(service_fn(|_: Request| async { let res = Response::new(Body::from("Hi from `GET /`")); Ok::<_, Infallible>(res) })) @@ -31,9 +32,8 @@ let app = Router::new() "/foo", // This service's response body is `axum::body::BoxBody` so // it can be routed to directly. - service_fn(|req: Request| async move { + service_fn(|req: Request| async move { let body = Body::from(format!("Hi from `{} /foo`", req.method())); - let body = axum::body::boxed(body); let res = Response::new(body); Ok::<_, Infallible>(res) }) diff --git a/axum/src/extract/matched_path.rs b/axum/src/extract/matched_path.rs index e51b38a40c..ff109e67a1 100644 --- a/axum/src/extract/matched_path.rs +++ b/axum/src/extract/matched_path.rs @@ -35,8 +35,7 @@ use std::{collections::HashMap, sync::Arc}; /// ``` /// use axum::{ /// Router, -/// extract::MatchedPath, -/// http::Request, +/// extract::{Request, MatchedPath}, /// routing::get, /// }; /// use tower_http::trace::TraceLayer; @@ -65,13 +64,12 @@ use std::{collections::HashMap, sync::Arc}; /// Router, /// RequestExt, /// routing::get, -/// extract::{MatchedPath, rejection::MatchedPathRejection}, +/// extract::{Request, MatchedPath, rejection::MatchedPathRejection}, /// middleware::map_request, -/// http::Request, /// body::Body, /// }; /// -/// async fn access_matched_path(mut request: Request) -> Request { +/// async fn access_matched_path(mut request: Request) -> Request { /// // if `/foo/bar` is called this will be `Err(_)` since that matches /// // a nested route /// let matched_path: Result = diff --git a/axum/src/extract/mod.rs b/axum/src/extract/mod.rs index b93cc7f459..19d46cb5e6 100644 --- a/axum/src/extract/mod.rs +++ b/axum/src/extract/mod.rs @@ -17,7 +17,7 @@ mod request_parts; mod state; #[doc(inline)] -pub use axum_core::extract::{DefaultBodyLimit, FromRef, FromRequest, FromRequestParts}; +pub use axum_core::extract::{DefaultBodyLimit, FromRef, FromRequest, FromRequestParts, Request}; #[cfg(feature = "macros")] pub use axum_macros::{FromRef, FromRequest, FromRequestParts}; @@ -29,7 +29,6 @@ pub use self::{ path::{Path, RawPathParams}, raw_form::RawForm, raw_query::RawQuery, - request_parts::RawBody, state::State, }; diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 484d3796b7..83499f4319 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -2,7 +2,7 @@ //! //! See [`Multipart`] for more details. -use super::FromRequest; +use super::{FromRequest, Request}; use crate::body::Bytes; use async_trait::async_trait; use axum_core::body::Body; @@ -10,7 +10,7 @@ use axum_core::response::{IntoResponse, Response}; use axum_core::RequestExt; use futures_util::stream::Stream; use http::header::{HeaderMap, CONTENT_TYPE}; -use http::{Request, StatusCode}; +use http::StatusCode; use std::error::Error; use std::{ fmt, @@ -63,7 +63,7 @@ where { type Rejection = MultipartRejection; - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?; let stream = match req.with_limited_body() { Ok(limited) => Body::new(limited), diff --git a/axum/src/extract/raw_form.rs b/axum/src/extract/raw_form.rs index ad74686817..b1a6f97336 100644 --- a/axum/src/extract/raw_form.rs +++ b/axum/src/extract/raw_form.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; -use axum_core::{body::Body, extract::FromRequest}; +use axum_core::extract::{FromRequest, Request}; use bytes::{Bytes, BytesMut}; -use http::{Method, Request}; +use http::Method; use super::{ has_content_type, @@ -39,7 +39,7 @@ where { type Rejection = RawFormRejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { if req.method() == Method::GET { let mut bytes = BytesMut::new(); diff --git a/axum/src/extract/request_parts.rs b/axum/src/extract/request_parts.rs index 5e83553cc0..c5e5aa9b8b 100644 --- a/axum/src/extract/request_parts.rs +++ b/axum/src/extract/request_parts.rs @@ -1,7 +1,6 @@ -use super::{Extension, FromRequest, FromRequestParts}; -use crate::body::Body; +use super::{Extension, FromRequestParts}; use async_trait::async_trait; -use http::{request::Parts, Request, Uri}; +use http::{request::Parts, Uri}; use std::convert::Infallible; /// Extractor that gets the original request URI regardless of nesting. @@ -91,49 +90,6 @@ where } } -/// Extractor that extracts the raw request body. -/// -/// Since extracting the raw request body requires consuming it, the `RawBody` extractor must be -/// *last* if there are multiple extractors in a handler. See ["the order of extractors"][order-of-extractors] -/// -/// [order-of-extractors]: crate::extract#the-order-of-extractors -/// -/// # Example -/// -/// ```rust,no_run -/// use axum::{ -/// extract::RawBody, -/// routing::get, -/// Router, -/// }; -/// use futures_util::StreamExt; -/// -/// async fn handler(RawBody(body): RawBody) { -/// // ... -/// } -/// -/// let app = Router::new().route("/users", get(handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; -/// ``` -/// -/// [`body::Body`]: crate::body::Body -#[derive(Debug, Default)] -pub struct RawBody(pub Body); - -#[async_trait] -impl FromRequest for RawBody -where - S: Send + Sync, -{ - type Rejection = Infallible; - - async fn from_request(req: Request, _state: &S) -> Result { - Ok(Self(req.into_body())) - } -} - #[cfg(test)] mod tests { use crate::{extract::Extension, routing::get, test_helpers::*, Router}; diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index ae4cbf0743..18616ca966 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -96,12 +96,9 @@ use self::rejection::*; use super::FromRequestParts; -use crate::{ - body::{self, Bytes}, - response::Response, - Error, -}; +use crate::{body::Bytes, response::Response, Error}; use async_trait::async_trait; +use axum_core::body::Body; use futures_util::{ sink::{Sink, SinkExt}, stream::{Stream, StreamExt}, @@ -332,7 +329,7 @@ impl WebSocketUpgrade { builder = builder.header(header::SEC_WEBSOCKET_PROTOCOL, protocol); } - builder.body(body::boxed(body::Empty::new())).unwrap() + builder.body(Body::empty()).unwrap() } } diff --git a/axum/src/form.rs b/axum/src/form.rs index 731362f55e..35c3c0113a 100644 --- a/axum/src/form.rs +++ b/axum/src/form.rs @@ -1,10 +1,10 @@ +use crate::extract::Request; use crate::extract::{rejection::*, FromRequest, RawForm}; use async_trait::async_trait; -use axum_core::body::Body; use axum_core::response::{IntoResponse, Response}; use axum_core::RequestExt; use http::header::CONTENT_TYPE; -use http::{Request, StatusCode}; +use http::StatusCode; use serde::de::DeserializeOwned; use serde::Serialize; use std::ops::Deref; @@ -71,7 +71,7 @@ where { type Rejection = FormRejection; - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { let is_get_or_head = req.method() == http::Method::GET || req.method() == http::Method::HEAD; @@ -121,14 +121,15 @@ impl Deref for Form { #[cfg(test)] mod tests { - use super::*; use crate::{ - body::Body, routing::{on, MethodFilter}, test_helpers::TestClient, Router, }; - use http::{header::CONTENT_TYPE, Method, Request}; + + use super::*; + use axum_core::body::Body; + use http::{Method, Request}; use mime::APPLICATION_WWW_FORM_URLENCODED; use serde::{Deserialize, Serialize}; use std::fmt::Debug; diff --git a/axum/src/handler/future.rs b/axum/src/handler/future.rs index 48f51a004a..751984d0c6 100644 --- a/axum/src/handler/future.rs +++ b/axum/src/handler/future.rs @@ -1,9 +1,8 @@ //! Handler future types. -use crate::body::Body; use crate::response::Response; +use axum_core::extract::Request; use futures_util::future::Map; -use http::Request; use pin_project_lite::pin_project; use std::{convert::Infallible, future::Future, pin::Pin, task::Context}; use tower::util::Oneshot; @@ -22,19 +21,19 @@ pin_project! { /// The response future for [`Layered`](super::Layered). pub struct LayeredFuture where - S: Service>, + S: Service, { #[pin] - inner: Map>, fn(Result) -> Response>, + inner: Map, fn(Result) -> Response>, } } impl LayeredFuture where - S: Service>, + S: Service, { pub(super) fn new( - inner: Map>, fn(Result) -> Response>, + inner: Map, fn(Result) -> Response>, ) -> Self { Self { inner } } @@ -42,7 +41,7 @@ where impl Future for LayeredFuture where - S: Service>, + S: Service, { type Output = Response; diff --git a/axum/src/handler/mod.rs b/axum/src/handler/mod.rs index eb539e5367..69e7879c1a 100644 --- a/axum/src/handler/mod.rs +++ b/axum/src/handler/mod.rs @@ -37,12 +37,10 @@ #[cfg(feature = "tokio")] use crate::extract::connect_info::IntoMakeServiceWithConnectInfo; use crate::{ - extract::{FromRequest, FromRequestParts}, + extract::{FromRequest, FromRequestParts, Request}, response::{IntoResponse, Response}, routing::IntoMakeService, }; -use axum_core::body::Body; -use http::Request; use std::{convert::Infallible, fmt, future::Future, marker::PhantomData, pin::Pin}; use tower::ServiceExt; use tower_layer::Layer; @@ -68,9 +66,8 @@ pub use self::service::HandlerService; /// ``` /// use tower::Service; /// use axum::{ -/// extract::State, +/// extract::{State, Request}, /// body::Body, -/// http::Request, /// handler::{HandlerWithoutStateExt, Handler}, /// }; /// @@ -89,7 +86,7 @@ pub use self::service::HandlerService; /// // helper to check that a value implements `Service` /// fn assert_service(service: S) /// where -/// S: Service>, +/// S: Service, /// {} /// ``` #[doc = include_str!("../docs/debugging_handler_type_errors.md")] @@ -104,7 +101,7 @@ pub trait Handler: Clone + Send + Sized + 'static { type Future: Future + Send + 'static; /// Call the handler with the given request. - fn call(self, req: Request, state: S) -> Self::Future; + fn call(self, req: Request, state: S) -> Self::Future; /// Apply a [`tower::Layer`] to the handler. /// @@ -145,7 +142,7 @@ pub trait Handler: Clone + Send + Sized + 'static { fn layer(self, layer: L) -> Layered where L: Layer> + Clone, - L::Service: Service>, + L::Service: Service, { Layered { layer, @@ -168,7 +165,7 @@ where { type Future = Pin + Send>>; - fn call(self, _req: Request, _state: S) -> Self::Future { + fn call(self, _req: Request, _state: S) -> Self::Future { Box::pin(async move { self().await.into_response() }) } } @@ -189,7 +186,7 @@ macro_rules! impl_handler { { type Future = Pin + Send>>; - fn call(self, req: Request, state: S) -> Self::Future { + fn call(self, req: Request, state: S) -> Self::Future { Box::pin(async move { let (mut parts, body) = req.into_parts(); let state = &state; @@ -257,15 +254,15 @@ impl Handler for Layered where L: Layer> + Clone + Send + 'static, H: Handler, - L::Service: Service, Error = Infallible> + Clone + Send + 'static, - >>::Response: IntoResponse, - >>::Future: Send, + L::Service: Service + Clone + Send + 'static, + >::Response: IntoResponse, + >::Future: Send, T: 'static, S: 'static, { type Future = future::LayeredFuture; - fn call(self, req: Request, state: S) -> Self::Future { + fn call(self, req: Request, state: S) -> Self::Future { use futures_util::future::{FutureExt, Map}; let svc = self.handler.with_state(state); @@ -275,8 +272,8 @@ where _, fn( Result< - >>::Response, - >>::Error, + >::Response, + >::Error, >, ) -> _, > = svc.oneshot(req).map(|result| match result { @@ -339,7 +336,8 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{body, extract::State, test_helpers::*}; + use crate::{extract::State, test_helpers::*}; + use axum_core::body::Body; use http::StatusCode; use std::time::Duration; use tower_http::{ @@ -371,10 +369,10 @@ mod tests { .layer(( RequestBodyLimitLayer::new(1024), TimeoutLayer::new(Duration::from_secs(10)), - MapResponseBodyLayer::new(body::boxed), + MapResponseBodyLayer::new(Body::new), CompressionLayer::new(), )) - .layer(MapRequestBodyLayer::new(body::boxed)) + .layer(MapRequestBodyLayer::new(Body::new)) .with_state("foo"); let client = TestClient::new(svc); diff --git a/axum/src/json.rs b/axum/src/json.rs index a93a97b8a8..5d97537bb6 100644 --- a/axum/src/json.rs +++ b/axum/src/json.rs @@ -1,13 +1,11 @@ +use crate::extract::Request; use crate::extract::{rejection::*, FromRequest}; use async_trait::async_trait; -use axum_core::{ - body::Body, - response::{IntoResponse, Response}, -}; +use axum_core::response::{IntoResponse, Response}; use bytes::{BufMut, Bytes, BytesMut}; use http::{ header::{self, HeaderMap, HeaderValue}, - Request, StatusCode, + StatusCode, }; use serde::{de::DeserializeOwned, Serialize}; use std::ops::{Deref, DerefMut}; @@ -107,7 +105,7 @@ where { type Rejection = JsonRejection; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { if json_content_type(req.headers()) { let bytes = Bytes::from_request(req, state).await?; let deserializer = &mut serde_json::Deserializer::from_slice(&bytes); diff --git a/axum/src/middleware/from_fn.rs b/axum/src/middleware/from_fn.rs index ba595f338f..c2cf5fe659 100644 --- a/axum/src/middleware/from_fn.rs +++ b/axum/src/middleware/from_fn.rs @@ -1,9 +1,6 @@ -use crate::body::{Body, Bytes, HttpBody}; use crate::response::{IntoResponse, Response}; -use crate::BoxError; -use axum_core::extract::{FromRequest, FromRequestParts}; +use axum_core::extract::{FromRequest, FromRequestParts, Request}; use futures_util::future::BoxFuture; -use http::Request; use std::{ any::type_name, convert::Infallible, @@ -23,7 +20,7 @@ use tower_service::Service; /// /// 1. Be an `async fn`. /// 2. Take one or more [extractors] as the first arguments. -/// 3. Take [`Next`](Next) as the final argument. +/// 3. Take [`Next`](Next) as the final argument. /// 4. Return something that implements [`IntoResponse`]. /// /// Note that this function doesn't support extracting [`State`]. For that, use [`from_fn_with_state`]. @@ -33,15 +30,16 @@ use tower_service::Service; /// ```rust /// use axum::{ /// Router, -/// http::{self, Request}, +/// http, /// routing::get, /// response::Response, /// middleware::{self, Next}, +/// extract::Request, /// }; /// -/// async fn my_middleware( -/// request: Request, -/// next: Next, +/// async fn my_middleware( +/// request: Request, +/// next: Next, /// ) -> Response { /// // do something with `request`... /// @@ -63,23 +61,22 @@ use tower_service::Service; /// ```rust /// use axum::{ /// Router, -/// extract::TypedHeader, +/// extract::{Request, TypedHeader}, /// http::StatusCode, /// headers::authorization::{Authorization, Bearer}, -/// http::Request, /// middleware::{self, Next}, /// response::Response, /// routing::get, /// }; /// -/// async fn auth( +/// async fn auth( /// // run the `TypedHeader` extractor /// TypedHeader(auth): TypedHeader>, /// // you can also add more extractors here but the last /// // extractor must implement `FromRequest` which /// // `Request` does -/// request: Request, -/// next: Next, +/// request: Request, +/// next: Next, /// ) -> Result { /// if token_is_valid(auth.token()) { /// let response = next.run(request).await; @@ -115,23 +112,23 @@ pub fn from_fn(f: F) -> FromFnLayer { /// ```rust /// use axum::{ /// Router, -/// http::{Request, StatusCode}, +/// http::StatusCode, /// routing::get, /// response::{IntoResponse, Response}, /// middleware::{self, Next}, -/// extract::State, +/// extract::{Request, State}, /// }; /// /// #[derive(Clone)] /// struct AppState { /* ... */ } /// -/// async fn my_middleware( +/// async fn my_middleware( /// State(state): State, /// // you can add more extractors here but the last /// // extractor must implement `FromRequest` which /// // `Request` does -/// request: Request, -/// next: Next, +/// request: Request, +/// next: Next, /// ) -> Response { /// // do something with `request`... /// @@ -245,21 +242,19 @@ macro_rules! impl_service { [$($ty:ident),*], $last:ident ) => { #[allow(non_snake_case, unused_mut)] - impl Service> for FromFn + impl Service for FromFn where - F: FnMut($($ty,)* $last, Next) -> Fut + Clone + Send + 'static, + F: FnMut($($ty,)* $last, Next) -> Fut + Clone + Send + 'static, $( $ty: FromRequestParts + Send, )* $last: FromRequest + Send, Fut: Future + Send + 'static, Out: IntoResponse + 'static, - I: Service, Error = Infallible> + I: Service + Clone + Send + 'static, I::Response: IntoResponse, I::Future: Send + 'static, - B: HttpBody + Send + 'static, - B::Error: Into, S: Clone + Send + Sync + 'static, { type Response = Response; @@ -270,9 +265,7 @@ macro_rules! impl_service { self.inner.poll_ready(cx) } - fn call(&mut self, req: Request) -> Self::Future { - let req = req.map(Body::new); - + fn call(&mut self, req: Request) -> Self::Future { let not_ready_inner = self.inner.clone(); let ready_inner = std::mem::replace(&mut self.inner, not_ready_inner); @@ -330,13 +323,14 @@ where } /// The remainder of a middleware stack, including the handler. -pub struct Next { - inner: BoxCloneService, Response, Infallible>, +#[derive(Debug, Clone)] +pub struct Next { + inner: BoxCloneService, } -impl Next { +impl Next { /// Execute the remaining middleware stack. - pub async fn run(mut self, req: Request) -> Response { + pub async fn run(mut self, req: Request) -> Response { match self.inner.call(req).await { Ok(res) => res, Err(err) => match err {}, @@ -344,23 +338,7 @@ impl Next { } } -impl fmt::Debug for Next { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FromFnLayer") - .field("inner", &self.inner) - .finish() - } -} - -impl Clone for Next { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - } - } -} - -impl Service> for Next { +impl Service for Next { type Response = Response; type Error = Infallible; type Future = Pin> + Send>>; @@ -369,7 +347,7 @@ impl Service> for Next { self.inner.poll_ready(cx) } - fn call(&mut self, req: Request) -> Self::Future { + fn call(&mut self, req: Request) -> Self::Future { self.inner.call(req) } } @@ -402,7 +380,7 @@ mod tests { #[crate::test] async fn basic() { - async fn insert_header(mut req: Request, next: Next) -> impl IntoResponse { + async fn insert_header(mut req: Request, next: Next) -> impl IntoResponse { req.headers_mut() .insert("x-axum-test", "ok".parse().unwrap()); diff --git a/axum/src/response/mod.rs b/axum/src/response/mod.rs index 95bcb762e2..919ca78b1c 100644 --- a/axum/src/response/mod.rs +++ b/axum/src/response/mod.rs @@ -1,6 +1,6 @@ #![doc = include_str!("../docs/response.md")] -use crate::body::{Bytes, Full}; +use axum_core::body::Body; use http::{header, HeaderValue}; mod redirect; @@ -44,7 +44,7 @@ pub struct Html(pub T); impl IntoResponse for Html where - T: Into>, + T: Into, { fn into_response(self) -> Response { ( diff --git a/axum/src/response/sse.rs b/axum/src/response/sse.rs index 1b205a93e0..3f849b9278 100644 --- a/axum/src/response/sse.rs +++ b/axum/src/response/sse.rs @@ -32,7 +32,7 @@ use crate::{ BoxError, }; use axum_core::{ - body, + body::Body, response::{IntoResponse, Response}, }; use bytes::{BufMut, BytesMut}; @@ -104,7 +104,7 @@ where (http::header::CONTENT_TYPE, mime::TEXT_EVENT_STREAM.as_ref()), (http::header::CACHE_CONTROL, "no-cache"), ], - body::boxed(Body { + Body::new(SseBody { event_stream: SyncWrapper::new(self.stream), keep_alive: self.keep_alive.map(KeepAliveStream::new), }), @@ -114,7 +114,7 @@ where } pin_project! { - struct Body { + struct SseBody { #[pin] event_stream: SyncWrapper, #[pin] @@ -122,7 +122,7 @@ pin_project! { } } -impl HttpBody for Body +impl HttpBody for SseBody where S: Stream>, { diff --git a/axum/src/routing/into_make_service.rs b/axum/src/routing/into_make_service.rs index fbc57c4acc..36da73a21e 100644 --- a/axum/src/routing/into_make_service.rs +++ b/axum/src/routing/into_make_service.rs @@ -46,12 +46,11 @@ opaque_future! { #[cfg(test)] mod tests { use super::*; - use crate::body::Body; #[test] fn traits() { use crate::test_helpers::*; - assert_send::>(); + assert_send::>(); } } diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 7d982f4232..9040ee30a5 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -8,11 +8,11 @@ use crate::{ boxed::BoxedIntoRoute, error_handling::{HandleError, HandleErrorLayer}, handler::Handler, - http::{Method, Request, StatusCode}, + http::{Method, StatusCode}, response::Response, routing::{future::RouteFuture, Fallback, MethodFilter, Route}, }; -use axum_core::{response::IntoResponse, BoxError}; +use axum_core::{extract::Request, response::IntoResponse, BoxError}; use bytes::BytesMut; use std::{ convert::Infallible, @@ -34,7 +34,7 @@ macro_rules! top_level_service_fn { /// /// ```rust /// use axum::{ - /// http::Request, + /// extract::Request, /// Router, /// routing::get_service, /// body::Body, @@ -42,7 +42,7 @@ macro_rules! top_level_service_fn { /// use http::Response; /// use std::convert::Infallible; /// - /// let service = tower::service_fn(|request: Request| async { + /// let service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) /// }); /// @@ -80,7 +80,7 @@ macro_rules! top_level_service_fn { $(#[$m])+ pub fn $name(svc: T) -> MethodRouter where - T: Service> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, S: Clone, @@ -161,7 +161,7 @@ macro_rules! chained_service_fn { /// /// ```rust /// use axum::{ - /// http::Request, + /// extract::Request, /// Router, /// routing::post_service, /// body::Body, @@ -169,11 +169,11 @@ macro_rules! chained_service_fn { /// use http::Response; /// use std::convert::Infallible; /// - /// let service = tower::service_fn(|request: Request| async { + /// let service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) /// }); /// - /// let other_service = tower::service_fn(|request: Request| async { + /// let other_service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) /// }); /// @@ -213,7 +213,7 @@ macro_rules! chained_service_fn { #[track_caller] pub fn $name(self, svc: T) -> Self where - T: Service, Error = E> + T: Service + Clone + Send + 'static, @@ -301,7 +301,7 @@ top_level_service_fn!(trace_service, TRACE); /// /// ```rust /// use axum::{ -/// http::Request, +/// extract::Request, /// routing::on, /// Router, /// body::Body, @@ -310,7 +310,7 @@ top_level_service_fn!(trace_service, TRACE); /// use http::Response; /// use std::convert::Infallible; /// -/// let service = tower::service_fn(|request: Request| async { +/// let service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) /// }); /// @@ -322,7 +322,7 @@ top_level_service_fn!(trace_service, TRACE); /// ``` pub fn on_service(filter: MethodFilter, svc: T) -> MethodRouter where - T: Service> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, S: Clone, @@ -336,7 +336,7 @@ where /// /// ```rust /// use axum::{ -/// http::Request, +/// extract::Request, /// Router, /// routing::any_service, /// body::Body, @@ -344,7 +344,7 @@ where /// use http::Response; /// use std::convert::Infallible; /// -/// let service = tower::service_fn(|request: Request| async { +/// let service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) /// }); /// @@ -359,7 +359,7 @@ where /// /// ```rust /// use axum::{ -/// http::Request, +/// extract::Request, /// Router, /// routing::any_service, /// body::Body, @@ -367,12 +367,12 @@ where /// use http::Response; /// use std::convert::Infallible; /// -/// let service = tower::service_fn(|request: Request| async { +/// let service = tower::service_fn(|request: Request| async { /// # Ok::<_, Infallible>(Response::new(Body::empty())) /// // ... /// }); /// -/// let other_service = tower::service_fn(|request: Request| async { +/// let other_service = tower::service_fn(|request: Request| async { /// # Ok::<_, Infallible>(Response::new(Body::empty())) /// // ... /// }); @@ -385,7 +385,7 @@ where /// ``` pub fn any_service(svc: T) -> MethodRouter where - T: Service> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, S: Clone, @@ -487,7 +487,7 @@ where /// /// ``` /// use tower::Service; -/// use axum::{routing::get, extract::State, body::Body, http::Request}; +/// use axum::{routing::get, extract::{State, Request}, body::Body}; /// /// // this `MethodRouter` doesn't require any state, i.e. the state is `()`, /// let method_router = get(|| async {}); @@ -504,7 +504,7 @@ where /// // helper to check that a value implements `Service` /// fn assert_service(service: S) /// where -/// S: Service>, +/// S: Service, /// {} /// ``` #[must_use] @@ -703,7 +703,7 @@ where /// Create a default `MethodRouter` that will respond with `405 Method Not Allowed` to all /// requests. pub fn new() -> Self { - let fallback = Route::new(service_fn(|_: Request| async { + let fallback = Route::new(service_fn(|_: Request| async { Ok(StatusCode::METHOD_NOT_ALLOWED.into_response()) })); @@ -744,7 +744,7 @@ where /// /// ```rust /// use axum::{ - /// http::Request, + /// extract::Request, /// Router, /// routing::{MethodFilter, on_service}, /// body::Body, @@ -752,7 +752,7 @@ where /// use http::Response; /// use std::convert::Infallible; /// - /// let service = tower::service_fn(|request: Request| async { + /// let service = tower::service_fn(|request: Request| async { /// Ok::<_, Infallible>(Response::new(Body::empty())) /// }); /// @@ -765,7 +765,7 @@ where #[track_caller] pub fn on_service(self, filter: MethodFilter, svc: T) -> Self where - T: Service, Error = E> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, { @@ -897,7 +897,7 @@ where #[doc = include_str!("../docs/method_routing/fallback.md")] pub fn fallback_service(mut self, svc: T) -> Self where - T: Service, Error = E> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, { @@ -909,10 +909,10 @@ where pub fn layer(self, layer: L) -> MethodRouter where L: Layer> + Clone + Send + 'static, - L::Service: Service> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Error: Into + 'static, - >>::Future: Send + 'static, + L::Service: Service + Clone + Send + 'static, + >::Response: IntoResponse + 'static, + >::Error: Into + 'static, + >::Future: Send + 'static, E: 'static, S: 'static, NewError: 'static, @@ -938,9 +938,9 @@ where pub fn route_layer(mut self, layer: L) -> MethodRouter where L: Layer> + Clone + Send + 'static, - L::Service: Service, Error = E> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Future: Send + 'static, + L::Service: Service + Clone + Send + 'static, + >::Response: IntoResponse + 'static, + >::Future: Send + 'static, E: 'static, S: 'static, { @@ -1036,9 +1036,9 @@ where pub fn handle_error(self, f: F) -> MethodRouter where F: Clone + Send + Sync + 'static, - HandleError, F, T>: Service, Error = Infallible>, - , F, T> as Service>>::Future: Send, - , F, T> as Service>>::Response: IntoResponse + Send, + HandleError, F, T>: Service, + , F, T> as Service>::Future: Send, + , F, T> as Service>::Response: IntoResponse + Send, T: 'static, E: 'static, S: 'static, @@ -1051,7 +1051,7 @@ where self } - pub(crate) fn call_with_state(&mut self, req: Request, state: S) -> RouteFuture { + pub(crate) fn call_with_state(&mut self, req: Request, state: S) -> RouteFuture { macro_rules! call { ( $req:expr, @@ -1256,7 +1256,7 @@ where { type Future = InfallibleRouteFuture; - fn call(mut self, req: Request, state: S) -> Self::Future { + fn call(mut self, req: Request, state: S) -> Self::Future { InfallibleRouteFuture::new(self.call_with_state(req, state)) } } @@ -1284,7 +1284,7 @@ mod tests { #[crate::test] async fn get_service_fn() { - async fn handle(_req: Request) -> Result, Infallible> { + async fn handle(_req: Request) -> Result, Infallible> { Ok(Response::new(Body::from("ok"))) } @@ -1545,7 +1545,7 @@ mod tests { async fn call(method: Method, svc: &mut S) -> (StatusCode, HeaderMap, String) where - S: Service, Error = Infallible>, + S: Service, S::Response: IntoResponse, { let request = Request::builder() diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index e789a56bb0..0a3252bfdb 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -9,8 +9,10 @@ use crate::{ handler::Handler, util::try_downcast, }; -use axum_core::response::{IntoResponse, Response}; -use http::Request; +use axum_core::{ + extract::Request, + response::{IntoResponse, Response}, +}; use matchit::MatchError; use std::{ collections::HashMap, @@ -152,7 +154,7 @@ where #[doc = include_str!("../docs/routing/route_service.md")] pub fn route_service(self, path: &str, service: T) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, { @@ -203,7 +205,7 @@ where #[track_caller] pub fn nest_service(self, path: &str, svc: T) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, { @@ -213,7 +215,7 @@ where #[track_caller] fn nest_endpoint(mut self, mut path: &str, router_or_service: RouterOrService) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, { @@ -299,10 +301,10 @@ where pub fn layer(self, layer: L) -> Router where L: Layer + Clone + Send + 'static, - L::Service: Service> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Error: Into + 'static, - >>::Future: Send + 'static, + L::Service: Service + Clone + Send + 'static, + >::Response: IntoResponse + 'static, + >::Error: Into + 'static, + >::Future: Send + 'static, { let routes = self .routes @@ -328,10 +330,10 @@ where pub fn route_layer(self, layer: L) -> Self where L: Layer + Clone + Send + 'static, - L::Service: Service> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Error: Into + 'static, - >>::Future: Send + 'static, + L::Service: Service + Clone + Send + 'static, + >::Response: IntoResponse + 'static, + >::Error: Into + 'static, + >::Future: Send + 'static, { if self.routes.is_empty() { panic!( @@ -372,7 +374,7 @@ where /// See [`Router::fallback`] for more details. pub fn fallback_service(mut self, svc: T) -> Self where - T: Service, Error = Infallible> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, { @@ -411,7 +413,7 @@ where pub(crate) fn call_with_state( &mut self, - mut req: Request, + mut req: Request, state: S, ) -> RouteFuture { #[cfg(feature = "original-uri")] @@ -821,10 +823,10 @@ where fn layer(self, layer: L) -> Endpoint where L: Layer + Clone + Send + 'static, - L::Service: Service> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Error: Into + 'static, - >>::Future: Send + 'static, + L::Service: Service + Clone + Send + 'static, + >::Response: IntoResponse + 'static, + >::Error: Into + 'static, + >::Future: Send + 'static, { match self { Endpoint::MethodRouter(method_router) => { diff --git a/axum/src/routing/route.rs b/axum/src/routing/route.rs index 335c87df9e..0a2713a8d2 100644 --- a/axum/src/routing/route.rs +++ b/axum/src/routing/route.rs @@ -1,12 +1,12 @@ use crate::{ - body::{boxed, Body, Empty, HttpBody}, + body::{Body, HttpBody}, response::Response, }; -use axum_core::response::IntoResponse; +use axum_core::{extract::Request, response::IntoResponse}; use bytes::Bytes; use http::{ header::{self, CONTENT_LENGTH}, - HeaderMap, HeaderValue, Request, + HeaderMap, HeaderValue, }; use pin_project_lite::pin_project; use std::{ @@ -27,12 +27,12 @@ use tower_service::Service; /// /// You normally shouldn't need to care about this type. It's used in /// [`Router::layer`](super::Router::layer). -pub struct Route(BoxCloneService, Response, E>); +pub struct Route(BoxCloneService); impl Route { pub(crate) fn new(svc: T) -> Self where - T: Service, Error = E> + Clone + Send + 'static, + T: Service + Clone + Send + 'static, T::Response: IntoResponse + 'static, T::Future: Send + 'static, { @@ -43,18 +43,18 @@ impl Route { pub(crate) fn oneshot_inner( &mut self, - req: Request, - ) -> Oneshot, Response, E>, Request> { + req: Request, + ) -> Oneshot, Request> { self.0.clone().oneshot(req) } pub(crate) fn layer(self, layer: L) -> Route where L: Layer> + Clone + Send + 'static, - L::Service: Service> + Clone + Send + 'static, - >>::Response: IntoResponse + 'static, - >>::Error: Into + 'static, - >>::Future: Send + 'static, + L::Service: Service + Clone + Send + 'static, + >::Response: IntoResponse + 'static, + >::Error: Into + 'static, + >::Future: Send + 'static, NewError: 'static, { let layer = ServiceBuilder::new() @@ -117,8 +117,8 @@ pin_project! { Future { #[pin] future: Oneshot< - BoxCloneService, Response, E>, - Request, + BoxCloneService, + Request, >, }, Response { @@ -129,7 +129,7 @@ pin_project! { impl RouteFuture { pub(crate) fn from_future( - future: Oneshot, Response, E>, Request>, + future: Oneshot, Request>, ) -> Self { Self { kind: RouteFutureKind::Future { future }, @@ -173,7 +173,7 @@ impl Future for RouteFuture { set_content_length(res.size_hint(), res.headers_mut()); let res = if *this.strip_body { - res.map(|_| boxed(Empty::new())) + res.map(|_| Body::empty()) } else { res }; diff --git a/axum/src/routing/tests/get_to_head.rs b/axum/src/routing/tests/get_to_head.rs index b46114c60e..cedf84918e 100644 --- a/axum/src/routing/tests/get_to_head.rs +++ b/axum/src/routing/tests/get_to_head.rs @@ -45,7 +45,7 @@ mod for_services { async fn get_handles_head() { let app = Router::new().route( "/", - get_service(service_fn(|_req: Request| async move { + get_service(service_fn(|_req: Request| async move { Ok::<_, Infallible>( ([("x-some-header", "foobar")], "you shouldn't see this").into_response(), ) diff --git a/axum/src/routing/tests/merge.rs b/axum/src/routing/tests/merge.rs index 0344a87939..31b42d8336 100644 --- a/axum/src/routing/tests/merge.rs +++ b/axum/src/routing/tests/merge.rs @@ -195,13 +195,13 @@ async fn services() { let app = Router::new() .route( "/foo", - get_service(service_fn(|_: Request| async { + get_service(service_fn(|_: Request| async { Ok::<_, Infallible>(Response::new(Body::empty())) })), ) .merge(Router::new().route( "/bar", - get_service(service_fn(|_: Request| async { + get_service(service_fn(|_: Request| async { Ok::<_, Infallible>(Response::new(Body::empty())) })), )); @@ -218,7 +218,7 @@ async fn services() { async fn all_the_uris( uri: Uri, OriginalUri(original_uri): OriginalUri, - req: Request, + req: Request, ) -> impl IntoResponse { Json(json!({ "uri": uri.to_string(), diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index c60d8a9df3..989a58717f 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -1,15 +1,16 @@ use crate::{ - body::{Body, Bytes, Empty}, + body::{Body, Bytes}, error_handling::HandleErrorLayer, extract::{self, DefaultBodyLimit, FromRef, Path, State}, handler::{Handler, HandlerWithoutStateExt}, - response::IntoResponse, + response::{IntoResponse, Response}, routing::{delete, get, get_service, on, on_service, patch, patch_service, post, MethodFilter}, test_helpers::*, BoxError, Json, Router, }; +use axum_core::extract::Request; use futures_util::stream::StreamExt; -use http::{header::ALLOW, header::CONTENT_LENGTH, HeaderMap, Request, Response, StatusCode, Uri}; +use http::{header::ALLOW, header::CONTENT_LENGTH, HeaderMap, StatusCode, Uri}; use serde_json::json; use std::{ convert::Infallible, @@ -30,15 +31,15 @@ mod nest; #[crate::test] async fn hello_world() { - async fn root(_: Request) -> &'static str { + async fn root(_: Request) -> &'static str { "Hello, World!" } - async fn foo(_: Request) -> &'static str { + async fn foo(_: Request) -> &'static str { "foo" } - async fn users_create(_: Request) -> &'static str { + async fn users_create(_: Request) -> &'static str { "users#create" } @@ -66,13 +67,12 @@ async fn routing() { let app = Router::new() .route( "/users", - get(|_: Request| async { "users#index" }) - .post(|_: Request| async { "users#create" }), + get(|_: Request| async { "users#index" }).post(|_: Request| async { "users#create" }), ) - .route("/users/:id", get(|_: Request| async { "users#show" })) + .route("/users/:id", get(|_: Request| async { "users#show" })) .route( "/users/:id/action", - get(|_: Request| async { "users#action" }), + get(|_: Request| async { "users#action" }), ); let client = TestClient::new(app); @@ -102,12 +102,8 @@ async fn router_type_doesnt_change() { let app: Router = Router::new() .route( "/", - on(MethodFilter::GET, |_: Request| async { - "hi from GET" - }) - .on(MethodFilter::POST, |_: Request| async { - "hi from POST" - }), + on(MethodFilter::GET, |_: Request| async { "hi from GET" }) + .on(MethodFilter::POST, |_: Request| async { "hi from POST" }), ) .layer(tower_http::compression::CompressionLayer::new()); @@ -127,22 +123,22 @@ async fn routing_between_services() { use std::convert::Infallible; use tower::service_fn; - async fn handle(_: Request) -> &'static str { + async fn handle(_: Request) -> &'static str { "handler" } let app = Router::new() .route( "/one", - get_service(service_fn(|_: Request| async { + get_service(service_fn(|_: Request| async { Ok::<_, Infallible>(Response::new(Body::from("one get"))) })) - .post_service(service_fn(|_: Request| async { + .post_service(service_fn(|_: Request| async { Ok::<_, Infallible>(Response::new(Body::from("one post"))) })) .on_service( MethodFilter::PUT, - service_fn(|_: Request| async { + service_fn(|_: Request| async { Ok::<_, Infallible>(Response::new(Body::from("one put"))) }), ), @@ -173,7 +169,7 @@ async fn middleware_on_single_route() { use tower::ServiceBuilder; use tower_http::{compression::CompressionLayer, trace::TraceLayer}; - async fn handle(_: Request) -> &'static str { + async fn handle(_: Request) -> &'static str { "Hello, World!" } @@ -197,7 +193,7 @@ async fn middleware_on_single_route() { #[crate::test] async fn service_in_bottom() { - async fn handler(_req: Request) -> Result, Infallible> { + async fn handler(_req: Request) -> Result, Infallible> { Ok(Response::new(Body::empty())) } @@ -235,7 +231,7 @@ async fn wrong_method_service() { struct Svc; impl Service for Svc { - type Response = Response>; + type Response = Response; type Error = Infallible; type Future = Ready>; @@ -244,7 +240,7 @@ async fn wrong_method_service() { } fn call(&mut self, _req: R) -> Self::Future { - ready(Ok(Response::new(Empty::new()))) + ready(Ok(().into_response())) } } @@ -271,7 +267,7 @@ async fn wrong_method_service() { #[crate::test] async fn multiple_methods_for_one_handler() { - async fn root(_: Request) -> &'static str { + async fn root(_: Request) -> &'static str { "Hello, World!" } diff --git a/axum/src/routing/tests/nest.rs b/axum/src/routing/tests/nest.rs index 7a3f38033f..7c8d799dd8 100644 --- a/axum/src/routing/tests/nest.rs +++ b/axum/src/routing/tests/nest.rs @@ -1,5 +1,5 @@ use super::*; -use crate::{body::boxed, extract::Extension}; +use crate::extract::Extension; use std::collections::HashMap; use tower_http::services::ServeDir; @@ -141,7 +141,7 @@ async fn nested_url_extractor() { .route("/baz", get(|uri: Uri| async move { uri.to_string() })) .route( "/qux", - get(|req: Request| async move { req.uri().to_string() }), + get(|req: Request| async move { req.uri().to_string() }), ), ), ); @@ -185,8 +185,8 @@ async fn nested_service_sees_stripped_uri() { "/bar", Router::new().route_service( "/baz", - service_fn(|req: Request| async move { - let body = boxed(Body::from(req.uri().to_string())); + service_fn(|req: Request| async move { + let body = Body::from(req.uri().to_string()); Ok::<_, Infallible>(Response::new(body)) }), ), diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index d1d73f6c1d..fa052a004d 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -4,7 +4,7 @@ use http::{ header::{HeaderName, HeaderValue}, Request, StatusCode, }; -use hyper::{Body, Server}; +use hyper::Server; use std::net::{SocketAddr, TcpListener}; use tower::make::Shared; use tower_service::Service; @@ -17,7 +17,10 @@ pub(crate) struct TestClient { impl TestClient { pub(crate) fn new(svc: S) -> Self where - S: Service, Response = http::Response> + Clone + Send + 'static, + S: Service, Response = http::Response> + + Clone + + Send + + 'static, ResBody: HttpBody + Send + 'static, ResBody::Data: Send, ResBody::Error: Into, diff --git a/examples/consume-body-in-extractor-or-middleware/src/main.rs b/examples/consume-body-in-extractor-or-middleware/src/main.rs index 25819a2278..f1b2cbf754 100644 --- a/examples/consume-body-in-extractor-or-middleware/src/main.rs +++ b/examples/consume-body-in-extractor-or-middleware/src/main.rs @@ -7,8 +7,8 @@ use axum::{ async_trait, body::{Body, Bytes}, - extract::FromRequest, - http::{Request, StatusCode}, + extract::{FromRequest, Request}, + http::StatusCode, middleware::{self, Next}, response::{IntoResponse, Response}, routing::post, @@ -41,10 +41,7 @@ async fn main() { } // middleware that shows how to consume the request body upfront -async fn print_request_body( - request: Request, - next: Next, -) -> Result { +async fn print_request_body(request: Request, next: Next) -> Result { let request = buffer_request_body(request).await?; Ok(next.run(request).await) @@ -52,7 +49,7 @@ async fn print_request_body( // the trick is to take the request apart, buffer the body, do what you need to do, then put // the request back together -async fn buffer_request_body(request: Request) -> Result, Response> { +async fn buffer_request_body(request: Request) -> Result { let (parts, body) = request.into_parts(); // this wont work if the body is an long running stream @@ -84,7 +81,7 @@ where { type Rejection = Response; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let body = Bytes::from_request(req, state) .await .map_err(|err| err.into_response())?; diff --git a/examples/customize-extractor-error/src/custom_extractor.rs b/examples/customize-extractor-error/src/custom_extractor.rs index 6a39ffbd22..3611fba796 100644 --- a/examples/customize-extractor-error/src/custom_extractor.rs +++ b/examples/customize-extractor-error/src/custom_extractor.rs @@ -6,9 +6,7 @@ //! - Complexity: Manually implementing `FromRequest` results on more complex code use axum::{ async_trait, - body::Body, - extract::{rejection::JsonRejection, FromRequest, MatchedPath}, - http::Request, + extract::{rejection::JsonRejection, FromRequest, MatchedPath, Request}, http::StatusCode, response::IntoResponse, RequestPartsExt, @@ -30,7 +28,7 @@ where { type Rejection = (StatusCode, axum::Json); - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let (mut parts, body) = req.into_parts(); // We can use other extractors to provide better rejection messages. diff --git a/examples/http-proxy/src/main.rs b/examples/http-proxy/src/main.rs index 73abf78f18..08845ca9e0 100644 --- a/examples/http-proxy/src/main.rs +++ b/examples/http-proxy/src/main.rs @@ -13,8 +13,9 @@ //! Example is based on use axum::{ - body::{self, Body}, - http::{Method, Request, StatusCode}, + body::Body, + extract::Request, + http::{Method, StatusCode}, response::{IntoResponse, Response}, routing::get, Router, @@ -59,7 +60,7 @@ async fn main() { .unwrap(); } -async fn proxy(req: Request) -> Result { +async fn proxy(req: Request) -> Result { tracing::trace!(?req); if let Some(host_addr) = req.uri().authority().map(|auth| auth.to_string()) { @@ -74,7 +75,7 @@ async fn proxy(req: Request) -> Result { } }); - Ok(Response::new(body::boxed(body::Empty::new()))) + Ok(Response::new(Body::empty())) } else { tracing::warn!("CONNECT host is not socket addr: {:?}", req.uri()); Ok(( diff --git a/examples/low-level-rustls/src/main.rs b/examples/low-level-rustls/src/main.rs index afe6a037e9..1e9a951f49 100644 --- a/examples/low-level-rustls/src/main.rs +++ b/examples/low-level-rustls/src/main.rs @@ -4,7 +4,7 @@ //! cargo run -p example-low-level-rustls //! ``` -use axum::{body::Body, extract::ConnectInfo, http::Request, routing::get, Router}; +use axum::{extract::ConnectInfo, extract::Request, routing::get, Router}; use futures_util::future::poll_fn; use hyper::server::{ accept::Accept, @@ -67,7 +67,7 @@ async fn main() { let protocol = protocol.clone(); - let svc = MakeService::<_, Request>::make_service(&mut app, &stream); + let svc = MakeService::<_, Request>::make_service(&mut app, &stream); tokio::spawn(async move { if let Ok(stream) = acceptor.accept(stream).await { diff --git a/examples/parse-body-based-on-content-type/src/main.rs b/examples/parse-body-based-on-content-type/src/main.rs index d66791bcdd..9ee26c6a10 100644 --- a/examples/parse-body-based-on-content-type/src/main.rs +++ b/examples/parse-body-based-on-content-type/src/main.rs @@ -8,9 +8,8 @@ use axum::{ async_trait, - body::Body, - extract::FromRequest, - http::{header::CONTENT_TYPE, Request, StatusCode}, + extract::{FromRequest, Request}, + http::{header::CONTENT_TYPE, StatusCode}, response::{IntoResponse, Response}, routing::post, Form, Json, RequestExt, Router, @@ -61,7 +60,7 @@ where { type Rejection = Response; - async fn from_request(req: Request, _state: &S) -> Result { + async fn from_request(req: Request, _state: &S) -> Result { let content_type_header = req.headers().get(CONTENT_TYPE); let content_type = content_type_header.and_then(|value| value.to_str().ok()); diff --git a/examples/print-request-response/src/main.rs b/examples/print-request-response/src/main.rs index c071ff5495..4703c05845 100644 --- a/examples/print-request-response/src/main.rs +++ b/examples/print-request-response/src/main.rs @@ -6,7 +6,8 @@ use axum::{ body::{Body, Bytes}, - http::{Request, StatusCode}, + extract::Request, + http::StatusCode, middleware::{self, Next}, response::{IntoResponse, Response}, routing::post, @@ -38,8 +39,8 @@ async fn main() { } async fn print_request_response( - req: Request, - next: Next, + req: Request, + next: Next, ) -> Result { let (parts, body) = req.into_parts(); let bytes = buffer_and_print("request", body).await?; diff --git a/examples/prometheus-metrics/src/main.rs b/examples/prometheus-metrics/src/main.rs index cf7db5ac29..675310bede 100644 --- a/examples/prometheus-metrics/src/main.rs +++ b/examples/prometheus-metrics/src/main.rs @@ -8,8 +8,7 @@ //! ``` use axum::{ - extract::MatchedPath, - http::Request, + extract::{MatchedPath, Request}, middleware::{self, Next}, response::IntoResponse, routing::get, @@ -94,7 +93,7 @@ fn setup_metrics_recorder() -> PrometheusHandle { .unwrap() } -async fn track_metrics(req: Request, next: Next) -> impl IntoResponse { +async fn track_metrics(req: Request, next: Next) -> impl IntoResponse { let start = Instant::now(); let path = if let Some(matched_path) = req.extensions().get::() { matched_path.as_str().to_owned() diff --git a/examples/rest-grpc-multiplex/src/multiplex_service.rs b/examples/rest-grpc-multiplex/src/multiplex_service.rs index de6b8b6c20..94e6e9817d 100644 --- a/examples/rest-grpc-multiplex/src/multiplex_service.rs +++ b/examples/rest-grpc-multiplex/src/multiplex_service.rs @@ -1,6 +1,10 @@ -use axum::{body::BoxBody, http::header::CONTENT_TYPE, response::IntoResponse}; +use axum::{ + body::Body, + extract::Request, + http::header::CONTENT_TYPE, + response::{IntoResponse, Response}, +}; use futures::{future::BoxFuture, ready}; -use hyper::{Body, Request, Response}; use std::{ convert::Infallible, task::{Context, Poll}, @@ -41,16 +45,16 @@ where } } -impl Service> for MultiplexService +impl Service> for MultiplexService where - A: Service, Error = Infallible>, + A: Service, Error = Infallible>, A::Response: IntoResponse, A::Future: Send + 'static, - B: Service, Error = Infallible>, + B: Service, Error = Infallible>, B::Response: IntoResponse, B::Future: Send + 'static, { - type Response = Response; + type Response = Response; type Error = Infallible; type Future = BoxFuture<'static, Result>; @@ -73,7 +77,7 @@ where } } - fn call(&mut self, req: Request) -> Self::Future { + fn call(&mut self, req: Request) -> Self::Future { // require users to call `poll_ready` first, if they don't we're allowed to panic // as per the `tower::Service` contract assert!( diff --git a/examples/reverse-proxy/src/main.rs b/examples/reverse-proxy/src/main.rs index 634a6a0440..a01947c652 100644 --- a/examples/reverse-proxy/src/main.rs +++ b/examples/reverse-proxy/src/main.rs @@ -9,8 +9,8 @@ use axum::{ body::Body, - extract::State, - http::{uri::Uri, Request}, + extract::{Request, State}, + http::uri::Uri, response::{IntoResponse, Response}, routing::get, Router, @@ -36,7 +36,7 @@ async fn main() { .unwrap(); } -async fn handler(State(client): State, mut req: Request) -> Response { +async fn handler(State(client): State, mut req: Request) -> Response { let path = req.uri().path(); let path_query = req .uri() diff --git a/examples/static-file-server/src/main.rs b/examples/static-file-server/src/main.rs index 50857d9611..3a3a24149f 100644 --- a/examples/static-file-server/src/main.rs +++ b/examples/static-file-server/src/main.rs @@ -5,11 +5,7 @@ //! ``` use axum::{ - body::Body, - handler::HandlerWithoutStateExt, - http::{Request, StatusCode}, - routing::get, - Router, + extract::Request, handler::HandlerWithoutStateExt, http::StatusCode, routing::get, Router, }; use std::net::SocketAddr; use tower::ServiceExt; @@ -97,7 +93,7 @@ fn calling_serve_dir_from_a_handler() -> Router { // call `ServeDir` yourself from a handler Router::new().nest_service( "/foo", - get(|request: Request| async { + get(|request: Request| async { let service = ServeDir::new("assets"); let result = service.oneshot(request).await; result diff --git a/examples/stream-to-file/src/main.rs b/examples/stream-to-file/src/main.rs index e58fd1497f..2d51416239 100644 --- a/examples/stream-to-file/src/main.rs +++ b/examples/stream-to-file/src/main.rs @@ -5,9 +5,9 @@ //! ``` use axum::{ - body::{Body, Bytes}, - extract::{Multipart, Path}, - http::{Request, StatusCode}, + body::Bytes, + extract::{Multipart, Path, Request}, + http::StatusCode, response::{Html, Redirect}, routing::{get, post}, BoxError, Router, @@ -52,7 +52,7 @@ async fn main() { // POST'ing to `/file/foo.txt` will create a file called `foo.txt`. async fn save_request_body( Path(file_name): Path, - request: Request, + request: Request, ) -> Result<(), (StatusCode, String)> { stream_to_file(&file_name, request.into_body()).await } diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 02079eb856..5f7dbc07f1 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -162,7 +162,7 @@ mod tests { // in multiple request #[tokio::test] async fn multiple_request() { - let mut app = app(); + let mut app = app().into_service(); let request = Request::builder().uri("/").body(Body::empty()).unwrap(); let response = ServiceExt::>::ready(&mut app) @@ -190,20 +190,15 @@ mod tests { // tests. #[tokio::test] async fn with_into_make_service_with_connect_info() { - let mut app = app().layer(MockConnectInfo(SocketAddr::from(([0, 0, 0, 0], 3000)))); + let mut app = app() + .layer(MockConnectInfo(SocketAddr::from(([0, 0, 0, 0], 3000)))) + .into_service(); let request = Request::builder() .uri("/requires-connect-into") .body(Body::empty()) .unwrap(); - let response = app - .as_service() - .ready() - .await - .unwrap() - .call(request) - .await - .unwrap(); + let response = app.ready().await.unwrap().call(request).await.unwrap(); assert_eq!(response.status(), StatusCode::OK); } } diff --git a/examples/validator/src/main.rs b/examples/validator/src/main.rs index 8545a3e9ce..4f4f6239bf 100644 --- a/examples/validator/src/main.rs +++ b/examples/validator/src/main.rs @@ -12,9 +12,8 @@ use async_trait::async_trait; use axum::{ - body::Body, - extract::{rejection::FormRejection, Form, FromRequest}, - http::{Request, StatusCode}, + extract::{rejection::FormRejection, Form, FromRequest, Request}, + http::StatusCode, response::{Html, IntoResponse, Response}, routing::get, Router, @@ -70,7 +69,7 @@ where { type Rejection = ServerError; - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request(req: Request, state: &S) -> Result { let Form(value) = Form::::from_request(req, state).await?; value.validate()?; Ok(ValidatedForm(value)) From d153719bef09110b2b465c5fcc3d81bd191aa6b1 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 22 Mar 2023 23:42:14 +0100 Subject: [PATCH 08/57] Add `serve` function and remove `Server` re-export (#1868) --- axum-extra/src/extract/query.rs | 4 +- axum-extra/src/lib.rs | 2 +- axum-extra/src/protobuf.rs | 8 +- axum-macros/src/lib.rs | 18 +- axum/CHANGELOG.md | 3 + axum/Cargo.toml | 5 + axum/README.md | 9 +- axum/benches/benches.rs | 4 +- axum/src/body/stream_body.rs | 4 +- axum/src/docs/error_handling.md | 12 +- axum/src/docs/extract.md | 32 +-- axum/src/docs/method_routing/layer.md | 4 +- axum/src/docs/method_routing/route_layer.md | 4 +- axum/src/docs/middleware.md | 18 +- .../into_make_service_with_connect_info.md | 22 +- axum/src/docs/routing/nest.md | 12 +- axum/src/docs/routing/route.md | 12 +- axum/src/docs/routing/route_layer.md | 4 +- axum/src/docs/routing/route_service.md | 8 +- axum/src/docs/routing/with_state.md | 40 ++- axum/src/extension.rs | 4 +- axum/src/extract/connect_info.rs | 53 ++-- axum/src/extract/matched_path.rs | 4 +- axum/src/extract/multipart.rs | 4 +- axum/src/extract/path/mod.rs | 16 +- axum/src/extract/query.rs | 4 +- axum/src/extract/raw_form.rs | 4 +- axum/src/extract/raw_query.rs | 4 +- axum/src/extract/request_parts.rs | 8 +- axum/src/extract/state.rs | 10 +- axum/src/extract/ws.rs | 19 +- axum/src/handler/mod.rs | 4 +- axum/src/handler/service.rs | 41 ++- axum/src/json.rs | 8 +- axum/src/lib.rs | 40 ++- axum/src/middleware/from_extractor.rs | 4 +- axum/src/routing/method_routing.rs | 87 +++---- axum/src/routing/mod.rs | 26 +- axum/src/serve.rs | 234 ++++++++++++++++++ axum/src/test_helpers/mod.rs | 2 +- axum/src/test_helpers/test_client.rs | 29 +-- axum/src/typed_header.rs | 4 +- deny.toml | 2 + examples/anyhow-error-response/src/main.rs | 8 +- examples/chat/src/main.rs | 8 +- .../src/main.rs | 8 +- examples/cors/src/main.rs | 6 +- .../customize-extractor-error/src/main.rs | 8 +- examples/customize-path-rejection/src/main.rs | 8 +- .../src/main.rs | 9 +- examples/form/src/main.rs | 10 +- examples/global-404-handler/src/main.rs | 8 +- examples/graceful-shutdown/Cargo.toml | 1 + examples/graceful-shutdown/src/main.rs | 2 +- examples/handle-head-request/src/main.rs | 8 +- examples/hello-world/src/main.rs | 8 +- examples/http-proxy/src/main.rs | 2 +- examples/jwt/src/main.rs | 10 +- examples/key-value-store/src/main.rs | 8 +- examples/listen-multiple-addrs/src/main.rs | 2 +- examples/multipart-form/src/main.rs | 8 +- examples/oauth/src/main.rs | 10 +- .../src/main.rs | 8 +- examples/print-request-response/src/main.rs | 8 +- examples/prometheus-metrics/src/main.rs | 19 +- .../src/main.rs | 5 +- examples/readme/src/main.rs | 9 +- examples/rest-grpc-multiplex/src/main.rs | 2 +- examples/reverse-proxy/src/main.rs | 15 +- .../src/main.rs | 8 +- examples/sessions/src/main.rs | 8 +- examples/sqlx-postgres/src/main.rs | 12 +- examples/sse/src/main.rs | 9 +- examples/static-file-server/src/main.rs | 6 +- examples/stream-to-file/src/main.rs | 9 +- examples/templates/src/main.rs | 8 +- examples/testing-websockets/src/main.rs | 19 +- examples/testing/src/main.rs | 20 +- examples/tls-rustls/src/main.rs | 7 +- examples/todos/src/main.rs | 8 +- examples/tokio-postgres/src/main.rs | 10 +- examples/tracing-aka-logging/src/main.rs | 12 +- examples/unix-domain-socket/src/main.rs | 2 +- examples/validator/src/main.rs | 12 +- examples/versioning/src/main.rs | 9 +- examples/websockets/src/main.rs | 12 +- 86 files changed, 641 insertions(+), 564 deletions(-) create mode 100644 axum/src/serve.rs diff --git a/axum-extra/src/extract/query.rs b/axum-extra/src/extract/query.rs index 7ff8108533..ebfbd768f3 100644 --- a/axum-extra/src/extract/query.rs +++ b/axum-extra/src/extract/query.rs @@ -41,9 +41,7 @@ use std::{fmt, ops::Deref}; /// } /// /// let app = Router::new().route("/list_things", get(list_things)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// If the query string cannot be parsed it will reject the request with a `400 diff --git a/axum-extra/src/lib.rs b/axum-extra/src/lib.rs index 9391fcd1dd..5d0992549e 100644 --- a/axum-extra/src/lib.rs +++ b/axum-extra/src/lib.rs @@ -101,7 +101,7 @@ pub mod __private { pub(crate) mod test_helpers { #![allow(unused_imports)] - use axum::{body::HttpBody, BoxError, Router}; + use axum::{extract::Request, response::Response, serve}; mod test_client { #![allow(dead_code)] diff --git a/axum-extra/src/protobuf.rs b/axum-extra/src/protobuf.rs index 870fdb59ad..6fe00e40be 100644 --- a/axum-extra/src/protobuf.rs +++ b/axum-extra/src/protobuf.rs @@ -46,9 +46,7 @@ use std::ops::{Deref, DerefMut}; /// } /// /// let app = Router::new().route("/users", post(create_user)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// # As response @@ -86,9 +84,7 @@ use std::ops::{Deref, DerefMut}; /// } /// /// let app = Router::new().route("/users/:id", get(get_user)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[derive(Debug, Clone, Copy, Default)] #[cfg_attr(docsrs, doc(cfg(feature = "protobuf")))] diff --git a/axum-macros/src/lib.rs b/axum-macros/src/lib.rs index 3327f0b565..db10c9018e 100644 --- a/axum-macros/src/lib.rs +++ b/axum-macros/src/lib.rs @@ -415,10 +415,8 @@ pub fn derive_from_request_parts(item: TokenStream) -> TokenStream { /// async fn main() { /// let app = Router::new().route("/", get(handler)); /// -/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -/// .serve(app.into_make_service()) -/// .await -/// .unwrap(); +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, app).await.unwrap(); /// } /// /// fn handler() -> &'static str { @@ -437,10 +435,8 @@ pub fn derive_from_request_parts(item: TokenStream) -> TokenStream { /// # async fn main() { /// # let app = Router::new().route("/", get(handler)); /// # -/// # axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -/// # .serve(app.into_make_service()) -/// # .await -/// # .unwrap(); +/// # let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// # axum::serve(listener, app).await.unwrap(); /// # } /// # /// #[debug_handler] @@ -467,10 +463,8 @@ pub fn derive_from_request_parts(item: TokenStream) -> TokenStream { /// # async { /// let app = Router::new().route("/", get(handler)); /// -/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -/// .serve(app.into_make_service()) -/// .await -/// .unwrap(); +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, app).await.unwrap(); /// # }; /// } /// diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index c12a24cfb6..98f9535367 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -40,12 +40,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **added:** Add `axum::extract::Request` type alias where the body is `axum::body::Body` ([#1789]) - **added:** Add `Router::as_service` and `Router::into_service` to workaround type inference issues when calling `ServiceExt` methods on a `Router` ([#1835]) +- **breaking:** Removed `axum::Server` as it was removed in hyper 1.0. Instead + use `axum::serve(listener, service)` or hyper/hyper-util for more configuration options ([#1868]) [#1664]: https://github.com/tokio-rs/axum/pull/1664 [#1751]: https://github.com/tokio-rs/axum/pull/1751 [#1762]: https://github.com/tokio-rs/axum/pull/1762 [#1835]: https://github.com/tokio-rs/axum/pull/1835 [#1789]: https://github.com/tokio-rs/axum/pull/1789 +[#1868]: https://github.com/tokio-rs/axum/pull/1868 # 0.6.12 (22. March, 2023) diff --git a/axum/Cargo.toml b/axum/Cargo.toml index cfb651bf61..43a7f04add 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -50,6 +50,10 @@ tower = { version = "0.4.13", default-features = false, features = ["util"] } tower-layer = "0.3.2" tower-service = "0.3" +# wont need this when axum uses http-body 1.0 +hyper1 = { package = "hyper", version = "1.0.0-rc.3", features = ["server", "http1"] } +tower-hyper-http-body-compat = { version = "0.1.4", features = ["server", "http1"] } + # optional dependencies axum-macros = { path = "../axum-macros", version = "0.3.7", optional = true } base64 = { version = "0.21.0", optional = true } @@ -186,6 +190,7 @@ allowed = [ "http_body", "hyper", "serde", + "tokio", "tower_layer", "tower_service", ] diff --git a/axum/README.md b/axum/README.md index 08487807c5..264ba56dd5 100644 --- a/axum/README.md +++ b/axum/README.md @@ -48,13 +48,8 @@ async fn main() { .route("/users", post(create_user)); // run our app with hyper - // `axum::Server` is a re-export of `hyper::Server` - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + axum::serve(listener, app).await.unwrap(); } // basic handler that responds with a static string diff --git a/axum/benches/benches.rs b/axum/benches/benches.rs index 50a7417b3b..3d4124a46c 100644 --- a/axum/benches/benches.rs +++ b/axum/benches/benches.rs @@ -1,7 +1,7 @@ use axum::{ extract::State, routing::{get, post}, - Extension, Json, Router, Server, + Extension, Json, Router, }; use hyper::server::conn::AddrIncoming; use serde::{Deserialize, Serialize}; @@ -151,7 +151,7 @@ impl BenchmarkBuilder { std::thread::spawn(move || { rt.block_on(async move { let incoming = AddrIncoming::from_listener(listener).unwrap(); - Server::builder(incoming) + hyper::Server::builder(incoming) .serve(app.into_make_service()) .await .unwrap(); diff --git a/axum/src/body/stream_body.rs b/axum/src/body/stream_body.rs index 6331567a74..933d10511d 100644 --- a/axum/src/body/stream_body.rs +++ b/axum/src/body/stream_body.rs @@ -47,9 +47,7 @@ pin_project! { /// } /// /// let app = Router::new().route("/", get(handler)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` /// /// [`Stream`]: futures_util::stream::Stream diff --git a/axum/src/docs/error_handling.md b/axum/src/docs/error_handling.md index 45c768c69a..b9abce6f95 100644 --- a/axum/src/docs/error_handling.md +++ b/axum/src/docs/error_handling.md @@ -85,9 +85,7 @@ async fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) { format!("Something went wrong: {}", err), ) } -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Applying fallible middleware @@ -129,9 +127,7 @@ async fn handle_timeout_error(err: BoxError) -> (StatusCode, String) { ) } } -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Running extractors for error handling @@ -171,9 +167,7 @@ async fn handle_timeout_error( format!("`{} {}` failed with {}", method, uri, err), ) } -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` [`tower::Service`]: `tower::Service` diff --git a/axum/src/docs/extract.md b/axum/src/docs/extract.md index 70bf645bca..71525b878a 100644 --- a/axum/src/docs/extract.md +++ b/axum/src/docs/extract.md @@ -46,9 +46,7 @@ async fn create_user(Json(payload): Json) { } let app = Router::new().route("/users", post(create_user)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Common extractors @@ -110,9 +108,7 @@ let app = Router::new() .route("/json", post(json)) .route("/request", post(request)) .route("/extension", post(extension)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Applying multiple extractors @@ -150,9 +146,7 @@ async fn get_user_things( // ... } -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # The order of extractors @@ -252,9 +246,7 @@ async fn create_user(payload: Option>) { } let app = Router::new().route("/users", post(create_user)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` Wrapping extractors in `Result` makes them optional and gives you the reason @@ -294,9 +286,7 @@ async fn create_user(payload: Result, JsonRejection>) { } let app = Router::new().route("/users", post(create_user)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Customizing extractor responses @@ -451,9 +441,7 @@ async fn handler(ExtractUserAgent(user_agent): ExtractUserAgent) { } let app = Router::new().route("/foo", get(handler)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` ## Implementing `FromRequest` @@ -500,9 +488,7 @@ async fn handler(ValidatedBody(body): ValidatedBody) { } let app = Router::new().route("/foo", get(handler)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` ## Cannot implement both `FromRequest` and `FromRequestParts` @@ -623,9 +609,7 @@ async fn handler(user: AuthenticatedUser) { let state = State { /* ... */ }; let app = Router::new().route("/", get(handler)).layer(Extension(state)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Request body limits diff --git a/axum/src/docs/method_routing/layer.md b/axum/src/docs/method_routing/layer.md index cdf6f93342..7ceadd4dad 100644 --- a/axum/src/docs/method_routing/layer.md +++ b/axum/src/docs/method_routing/layer.md @@ -23,7 +23,5 @@ let app = Router::new().route( // All requests to `GET /` will be sent through `ConcurrencyLimitLayer` get(hander).layer(ConcurrencyLimitLayer::new(64)), ); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` diff --git a/axum/src/docs/method_routing/route_layer.md b/axum/src/docs/method_routing/route_layer.md index f497e8b102..7a7ff36a37 100644 --- a/axum/src/docs/method_routing/route_layer.md +++ b/axum/src/docs/method_routing/route_layer.md @@ -28,7 +28,5 @@ let app = Router::new().route( // `GET /foo` with a valid token will receive `200 OK` // `GET /foo` with a invalid token will receive `401 Unauthorized` // `POST /FOO` with a invalid token will receive `405 Method Not Allowed` -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` diff --git a/axum/src/docs/middleware.md b/axum/src/docs/middleware.md index 6aca7a3ead..70eda706c4 100644 --- a/axum/src/docs/middleware.md +++ b/axum/src/docs/middleware.md @@ -55,9 +55,7 @@ let app = Router::new() .layer(TraceLayer::new_for_http()) .layer(Extension(State {})) ); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Commonly used middleware @@ -319,9 +317,7 @@ let app = Router::new() })) .layer(TimeoutLayer::new(Duration::from_secs(10))) ); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` See [`error_handling`](crate::error_handling) for more details on axum's error @@ -376,9 +372,7 @@ let app = Router::new().route("/", get(handler)); let app = ServiceBuilder::new() .layer(some_backpressure_sensitive_middleware) .service(app); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` However when applying middleware around your whole application in this way @@ -563,10 +557,8 @@ let app = Router::new(); let app_with_middleware = middleware.layer(app); # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(app_with_middleware.into_make_service()) - .await - .unwrap(); +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, app_with_middleware.into_make_service()).await.unwrap(); # }; ``` diff --git a/axum/src/docs/routing/into_make_service_with_connect_info.md b/axum/src/docs/routing/into_make_service_with_connect_info.md index 05ee750c56..67dd652499 100644 --- a/axum/src/docs/routing/into_make_service_with_connect_info.md +++ b/axum/src/docs/routing/into_make_service_with_connect_info.md @@ -21,12 +21,8 @@ async fn handler(ConnectInfo(addr): ConnectInfo) -> String { } # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve( - app.into_make_service_with_connect_info::() - ) - .await - .expect("server failed"); +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, app.into_make_service_with_connect_info::()).await.unwrap(); # }; ``` @@ -36,9 +32,9 @@ You can implement custom a [`Connected`] like so: use axum::{ extract::connect_info::{ConnectInfo, Connected}, routing::get, + serve::IncomingStream, Router, }; -use hyper::server::conn::AddrStream; let app = Router::new().route("/", get(handler)); @@ -53,8 +49,8 @@ struct MyConnectInfo { // ... } -impl Connected<&AddrStream> for MyConnectInfo { - fn connect_info(target: &AddrStream) -> Self { +impl Connected> for MyConnectInfo { + fn connect_info(target: IncomingStream<'_>) -> Self { MyConnectInfo { // ... } @@ -62,12 +58,8 @@ impl Connected<&AddrStream> for MyConnectInfo { } # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve( - app.into_make_service_with_connect_info::() - ) - .await - .expect("server failed"); +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, app.into_make_service_with_connect_info::()).await.unwrap(); # }; ``` diff --git a/axum/src/docs/routing/nest.md b/axum/src/docs/routing/nest.md index b40d0fc951..c3f7308fdb 100644 --- a/axum/src/docs/routing/nest.md +++ b/axum/src/docs/routing/nest.md @@ -24,9 +24,7 @@ let app = Router::new().nest("/api", api_routes); // Our app now accepts // - GET /api/users/:id // - POST /api/teams -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # How the URI changes @@ -59,9 +57,7 @@ async fn users_get(Path(params): Path>) { let users_api = Router::new().route("/users/:id", get(users_get)); let app = Router::new().nest("/:version/api", users_api); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Differences from wildcard routes @@ -83,9 +79,7 @@ let app = Router::new() // `uri` will contain `/foo` })) .nest("/bar", nested_router); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Fallbacks diff --git a/axum/src/docs/routing/route.md b/axum/src/docs/routing/route.md index ae425142fc..394ae59b49 100644 --- a/axum/src/docs/routing/route.md +++ b/axum/src/docs/routing/route.md @@ -77,9 +77,7 @@ async fn get_root() {} async fn post_root() {} async fn delete_root() {} -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # More examples @@ -105,9 +103,7 @@ async fn show_user(Path(id): Path) {} async fn do_users_action(Path((version, id)): Path<(String, u64)>) {} async fn serve_asset(Path(path): Path) {} -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Panics @@ -120,9 +116,7 @@ use axum::{routing::get, Router}; let app = Router::new() .route("/", get(|| async {})) .route("/", get(|| async {})); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` The static route `/foo` and the dynamic route `/:key` are not considered to diff --git a/axum/src/docs/routing/route_layer.md b/axum/src/docs/routing/route_layer.md index fe5b8faa7d..5e60eaf5d3 100644 --- a/axum/src/docs/routing/route_layer.md +++ b/axum/src/docs/routing/route_layer.md @@ -26,7 +26,5 @@ let app = Router::new() // `GET /foo` with a valid token will receive `200 OK` // `GET /foo` with a invalid token will receive `401 Unauthorized` // `GET /not-found` with a invalid token will receive `404 Not Found` -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` diff --git a/axum/src/docs/routing/route_service.md b/axum/src/docs/routing/route_service.md index 190e0183a3..7f016105df 100644 --- a/axum/src/docs/routing/route_service.md +++ b/axum/src/docs/routing/route_service.md @@ -43,9 +43,7 @@ let app = Router::new() "/static/Cargo.toml", ServeFile::new("Cargo.toml"), ); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` Routing to arbitrary services in this way has complications for backpressure @@ -64,9 +62,7 @@ let app = Router::new().route_service( "/", Router::new().route("/foo", get(|| async {})), ); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` Use [`Router::nest`] instead. diff --git a/axum/src/docs/routing/with_state.md b/axum/src/docs/routing/with_state.md index 3f9c815132..bece920fe0 100644 --- a/axum/src/docs/routing/with_state.md +++ b/axum/src/docs/routing/with_state.md @@ -13,9 +13,8 @@ let routes = Router::new() .with_state(AppState {}); # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(routes.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, routes).await.unwrap(); # }; ``` @@ -40,9 +39,8 @@ fn routes() -> Router { let routes = routes().with_state(AppState {}); # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(routes.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, routes).await.unwrap(); # }; ``` @@ -64,9 +62,8 @@ fn routes(state: AppState) -> Router { let routes = routes(AppState {}); # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(routes.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, routes).await.unwrap(); # }; ``` @@ -92,9 +89,8 @@ fn routes(state: AppState) -> Router { let routes = Router::new().nest("/api", routes(AppState {})); # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(routes.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, routes).await.unwrap(); # }; ``` @@ -133,9 +129,8 @@ let router: Router<()> = router.with_state(AppState {}); // You cannot call `into_make_service` on a `Router` // because it is still missing an `AppState`. # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(router.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, router).await.unwrap(); # }; ``` @@ -163,9 +158,8 @@ let final_router: Router<()> = string_router.with_state("foo".to_owned()); // Since we have a `Router<()>` we can run it. # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(final_router.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, final_router).await.unwrap(); # }; ``` @@ -190,9 +184,8 @@ let app = routes(AppState {}); // We can only call `Router::into_make_service` on a `Router<()>` // but `app` is a `Router` # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(app.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, app).await.unwrap(); # }; ``` @@ -214,9 +207,8 @@ let app = routes(AppState {}); // We can now call `Router::into_make_service` # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(app.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, app).await.unwrap(); # }; ``` diff --git a/axum/src/extension.rs b/axum/src/extension.rs index 5f94cd6991..f5768d43c0 100644 --- a/axum/src/extension.rs +++ b/axum/src/extension.rs @@ -41,9 +41,7 @@ use tower_service::Service; /// // Add middleware that inserts the state into all incoming request's /// // extensions. /// .layer(Extension(state)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// If the extension is missing it will reject the request with a `500 Internal diff --git a/axum/src/extract/connect_info.rs b/axum/src/extract/connect_info.rs index 7dd0c47d49..0482ab0cd5 100644 --- a/axum/src/extract/connect_info.rs +++ b/axum/src/extract/connect_info.rs @@ -5,7 +5,7 @@ //! [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info use super::{Extension, FromRequestParts}; -use crate::middleware::AddExtension; +use crate::{middleware::AddExtension, serve::IncomingStream}; use async_trait::async_trait; use http::request::Parts; use hyper::server::conn::AddrStream; @@ -89,6 +89,12 @@ impl Connected<&AddrStream> for SocketAddr { } } +impl Connected> for SocketAddr { + fn connect_info(target: IncomingStream<'_>) -> Self { + target.remote_addr() + } +} + impl Service for IntoMakeServiceWithConnectInfo where S: Clone, @@ -211,8 +217,9 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{routing::get, test_helpers::TestClient, Router, Server}; - use std::net::{SocketAddr, TcpListener}; + use crate::{routing::get, test_helpers::TestClient, Router}; + use std::net::SocketAddr; + use tokio::net::TcpListener; #[crate::test] async fn socket_addr() { @@ -220,17 +227,19 @@ mod tests { format!("{addr}") } - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let addr = listener.local_addr().unwrap(); let (tx, rx) = tokio::sync::oneshot::channel(); tokio::spawn(async move { let app = Router::new().route("/", get(handler)); - let server = Server::from_tcp(listener) - .unwrap() - .serve(app.into_make_service_with_connect_info::()); tx.send(()).unwrap(); - server.await.expect("server error"); + crate::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await + .unwrap(); }); rx.await.unwrap(); @@ -248,8 +257,8 @@ mod tests { value: &'static str, } - impl Connected<&AddrStream> for MyConnectInfo { - fn connect_info(_target: &AddrStream) -> Self { + impl Connected> for MyConnectInfo { + fn connect_info(_target: IncomingStream<'_>) -> Self { Self { value: "it worked!", } @@ -260,17 +269,19 @@ mod tests { addr.value } - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let addr = listener.local_addr().unwrap(); let (tx, rx) = tokio::sync::oneshot::channel(); tokio::spawn(async move { let app = Router::new().route("/", get(handler)); - let server = Server::from_tcp(listener) - .unwrap() - .serve(app.into_make_service_with_connect_info::()); tx.send(()).unwrap(); - server.await.expect("server error"); + crate::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await + .unwrap(); }); rx.await.unwrap(); @@ -304,7 +315,7 @@ mod tests { format!("{addr}") } - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let addr = listener.local_addr().unwrap(); tokio::spawn(async move { @@ -312,10 +323,12 @@ mod tests { .route("/", get(handler)) .layer(MockConnectInfo(SocketAddr::from(([0, 0, 0, 0], 1337)))); - let server = Server::from_tcp(listener) - .unwrap() - .serve(app.into_make_service_with_connect_info::()); - server.await.expect("server error"); + crate::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await + .unwrap(); }); let client = reqwest::Client::new(); diff --git a/axum/src/extract/matched_path.rs b/axum/src/extract/matched_path.rs index ff109e67a1..0d6121b13e 100644 --- a/axum/src/extract/matched_path.rs +++ b/axum/src/extract/matched_path.rs @@ -20,9 +20,7 @@ use std::{collections::HashMap, sync::Arc}; /// // `path` will be "/users/:id" /// }) /// ); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// # Accessing `MatchedPath` via extensions diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 83499f4319..de6207bab5 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -46,9 +46,7 @@ use std::{ /// } /// /// let app = Router::new().route("/upload", post(upload)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))] #[derive(Debug)] diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 088ed06803..f56e3d17aa 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -46,9 +46,7 @@ use std::{ /// } /// /// let app = Router::new().route("/users/:user_id/team/:team_id", get(users_teams_show)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// If the path contains only one parameter, then you can omit the tuple. @@ -66,9 +64,7 @@ use std::{ /// } /// /// let app = Router::new().route("/users/:user_id", get(user_info)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// Path segments also can be deserialized into any type that implements @@ -107,9 +103,7 @@ use std::{ /// "/users/:user_id/team/:team_id", /// get(users_teams_show).post(users_teams_create), /// ); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// If you wish to capture all path parameters you can use `HashMap` or `Vec`: @@ -136,9 +130,7 @@ use std::{ /// /// let app = Router::new() /// .route("/users/:user_id/team/:team_id", get(params_map).post(params_vec)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// # Providing detailed rejection output diff --git a/axum/src/extract/query.rs b/axum/src/extract/query.rs index c8e25e702f..b4beae8522 100644 --- a/axum/src/extract/query.rs +++ b/axum/src/extract/query.rs @@ -33,9 +33,7 @@ use std::ops::Deref; /// } /// /// let app = Router::new().route("/list_things", get(list_things)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// If the query string cannot be parsed it will reject the request with a `400 diff --git a/axum/src/extract/raw_form.rs b/axum/src/extract/raw_form.rs index b1a6f97336..f9c7d3572c 100644 --- a/axum/src/extract/raw_form.rs +++ b/axum/src/extract/raw_form.rs @@ -25,9 +25,7 @@ use super::{ /// async fn handler(RawForm(form): RawForm) {} /// /// let app = Router::new().route("/", get(handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[derive(Debug)] pub struct RawForm(pub Bytes); diff --git a/axum/src/extract/raw_query.rs b/axum/src/extract/raw_query.rs index 98a60b0930..d8c56f84a4 100644 --- a/axum/src/extract/raw_query.rs +++ b/axum/src/extract/raw_query.rs @@ -20,9 +20,7 @@ use std::convert::Infallible; /// } /// /// let app = Router::new().route("/users", get(handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[derive(Debug)] pub struct RawQuery(pub Option); diff --git a/axum/src/extract/request_parts.rs b/axum/src/extract/request_parts.rs index c5e5aa9b8b..2c00e7824f 100644 --- a/axum/src/extract/request_parts.rs +++ b/axum/src/extract/request_parts.rs @@ -28,9 +28,7 @@ use std::convert::Infallible; /// ); /// /// let app = Router::new().nest("/api", api_routes); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// # Extracting via request extensions @@ -65,9 +63,7 @@ use std::convert::Infallible; /// ); /// /// let app = Router::new().nest("/api", api_routes); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[cfg(feature = "original-uri")] #[derive(Debug, Clone)] diff --git a/axum/src/extract/state.rs b/axum/src/extract/state.rs index e2307d391c..1592a813df 100644 --- a/axum/src/extract/state.rs +++ b/axum/src/extract/state.rs @@ -131,13 +131,11 @@ use std::{ /// let method_router_with_state = get(handler) /// // provide the state so the handler can access it /// .with_state(state); +/// # let _: axum::routing::MethodRouter = method_router_with_state; /// /// async fn handler(State(state): State) { /// // use `state`... /// } -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(method_router_with_state.into_make_service()).await.unwrap(); -/// # }; /// ``` /// /// # With `Handler` @@ -158,10 +156,8 @@ use std::{ /// let handler_with_state = handler.with_state(state); /// /// # async { -/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -/// .serve(handler_with_state.into_make_service()) -/// .await -/// .expect("server failed"); +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, handler_with_state.into_make_service()).await.unwrap(); /// # }; /// ``` /// diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 18616ca966..feda02506f 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -31,9 +31,7 @@ //! } //! } //! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! # Passing data and/or state to an `on_upgrade` callback @@ -62,9 +60,7 @@ //! let app = Router::new() //! .route("/ws", get(handler)) //! .with_state(AppState { /* ... */ }); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! # Read and write concurrently @@ -108,7 +104,6 @@ use http::{ request::Parts, Method, StatusCode, }; -use hyper::upgrade::{OnUpgrade, Upgraded}; use sha1::{Digest, Sha1}; use std::{ borrow::Cow, @@ -137,7 +132,7 @@ pub struct WebSocketUpgrade { /// The chosen protocol sent in the `Sec-WebSocket-Protocol` header of the response. protocol: Option, sec_websocket_key: HeaderValue, - on_upgrade: OnUpgrade, + on_upgrade: hyper1::upgrade::OnUpgrade, on_failed_upgrade: F, sec_websocket_protocol: Option, } @@ -206,9 +201,7 @@ impl WebSocketUpgrade { /// // ... /// }) /// } - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` pub fn protocols(mut self, protocols: I) -> Self where @@ -393,7 +386,7 @@ where let on_upgrade = parts .extensions - .remove::() + .remove::() .ok_or(ConnectionNotUpgradable)?; let sec_websocket_protocol = parts.headers.get(header::SEC_WEBSOCKET_PROTOCOL).cloned(); @@ -436,7 +429,7 @@ fn header_contains(headers: &HeaderMap, key: HeaderName, value: &'static str) -> /// See [the module level documentation](self) for more details. #[derive(Debug)] pub struct WebSocket { - inner: WebSocketStream, + inner: WebSocketStream, protocol: Option, } diff --git a/axum/src/handler/mod.rs b/axum/src/handler/mod.rs index 69e7879c1a..d13701454d 100644 --- a/axum/src/handler/mod.rs +++ b/axum/src/handler/mod.rs @@ -135,9 +135,7 @@ pub trait Handler: Clone + Send + Sized + 'static { /// /// let layered_handler = handler.layer(ConcurrencyLimitLayer::new(64)); /// let app = Router::new().route("/", get(layered_handler)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` fn layer(self, layer: L) -> Layered where diff --git a/axum/src/handler/service.rs b/axum/src/handler/service.rs index 86b93afeb3..35974a4cda 100644 --- a/axum/src/handler/service.rs +++ b/axum/src/handler/service.rs @@ -37,7 +37,6 @@ impl HandlerService { /// /// ```rust /// use axum::{ - /// Server, /// handler::Handler, /// extract::State, /// http::{Uri, Method}, @@ -55,10 +54,8 @@ impl HandlerService { /// let app = handler.with_state(AppState {}); /// /// # async { - /// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))) - /// .serve(app.into_make_service()) - /// .await?; - /// # Ok::<_, hyper::Error>(()) + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, app.into_make_service()).await.unwrap(); /// # }; /// ``` /// @@ -74,7 +71,6 @@ impl HandlerService { /// /// ```rust /// use axum::{ - /// Server, /// handler::Handler, /// response::IntoResponse, /// extract::{ConnectInfo, State}, @@ -94,10 +90,11 @@ impl HandlerService { /// let app = handler.with_state(AppState {}); /// /// # async { - /// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))) - /// .serve(app.into_make_service_with_connect_info::()) - /// .await?; - /// # Ok::<_, hyper::Error>(()) + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve( + /// listener, + /// app.into_make_service_with_connect_info::(), + /// ).await.unwrap(); /// # }; /// ``` /// @@ -179,3 +176,27 @@ where super::future::IntoServiceFuture::new(future) } } + +// for `axum::serve(listener, handler)` +#[cfg(feature = "tokio")] +const _: () = { + use crate::serve::IncomingStream; + + impl Service> for HandlerService + where + H: Clone, + S: Clone, + { + type Response = Self; + type Error = Infallible; + type Future = std::future::Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: IncomingStream<'_>) -> Self::Future { + std::future::ready(Ok(self.clone())) + } + } +}; diff --git a/axum/src/json.rs b/axum/src/json.rs index 5d97537bb6..69c1a30158 100644 --- a/axum/src/json.rs +++ b/axum/src/json.rs @@ -51,9 +51,7 @@ use std::ops::{Deref, DerefMut}; /// } /// /// let app = Router::new().route("/users", post(create_user)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// When used as a response, it can serialize any type that implements [`serde::Serialize`] to @@ -88,9 +86,7 @@ use std::ops::{Deref, DerefMut}; /// } /// /// let app = Router::new().route("/users/:id", get(get_user)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[derive(Debug, Clone, Copy, Default)] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] diff --git a/axum/src/lib.rs b/axum/src/lib.rs index f40dc71a81..9dca510fc1 100644 --- a/axum/src/lib.rs +++ b/axum/src/lib.rs @@ -53,11 +53,9 @@ //! // build our application with a single route //! let app = Router::new().route("/", get(|| async { "Hello, World!" })); //! -//! // run it with hyper on localhost:3000 -//! axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -//! .serve(app.into_make_service()) -//! .await -//! .unwrap(); +//! // run it on localhost:3000 +//! let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +//! axum::serve(listener, app).await.unwrap(); //! } //! ``` //! @@ -82,9 +80,7 @@ //! async fn get_foo() {} //! async fn post_foo() {} //! async fn foo_bar() {} -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! See [`Router`] for more details on routing. @@ -145,9 +141,7 @@ //! let app = Router::new() //! .route("/plain_text", get(plain_text)) //! .route("/json", get(json)); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! See [`response`](crate::response) for more details on building responses. @@ -202,9 +196,7 @@ //! ) { //! // ... //! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! You should prefer using [`State`] if possible since it's more type safe. The downside is that @@ -240,9 +232,7 @@ //! ) { //! // ... //! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! The downside to this approach is that you'll get runtime errors @@ -298,9 +288,7 @@ //! struct CreateUserPayload { //! // ... //! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! The downside to this approach is that it's a little more verbose than using @@ -356,7 +344,7 @@ //! `matched-path` | Enables capturing of every request's router path and the [`MatchedPath`] extractor | Yes //! `multipart` | Enables parsing `multipart/form-data` requests with [`Multipart`] | No //! `original-uri` | Enables capturing of every request's original URI and the [`OriginalUri`] extractor | Yes -//! `tokio` | Enables `tokio` as a dependency and `axum::Server`, `SSE` and `extract::connect_info` types. | Yes +//! `tokio` | Enables `tokio` as a dependency and `axum::serve`, `SSE` and `extract::connect_info` types. | Yes //! `tower-log` | Enables `tower`'s `log` feature | Yes //! `ws` | Enables WebSockets support via [`extract::ws`] | No //! `form` | Enables the `Form` extractor | Yes @@ -376,7 +364,6 @@ //! [`Timeout`]: tower::timeout::Timeout //! [examples]: https://github.com/tokio-rs/axum/tree/main/examples //! [`Router::merge`]: crate::routing::Router::merge -//! [`axum::Server`]: hyper::server::Server //! [`Service`]: tower::Service //! [`Service::poll_ready`]: tower::Service::poll_ready //! [`Service`'s]: tower::Service @@ -458,6 +445,8 @@ pub mod handler; pub mod middleware; pub mod response; pub mod routing; +#[cfg(feature = "tokio")] +pub mod serve; #[cfg(test)] mod test_helpers; @@ -469,9 +458,6 @@ pub use async_trait::async_trait; pub use headers; #[doc(no_inline)] pub use http; -#[cfg(feature = "tokio")] -#[doc(no_inline)] -pub use hyper::Server; #[doc(inline)] pub use self::extension::Extension; @@ -495,6 +481,10 @@ pub use axum_core::{BoxError, Error, RequestExt, RequestPartsExt}; #[cfg(feature = "macros")] pub use axum_macros::debug_handler; +#[cfg(feature = "tokio")] +#[doc(inline)] +pub use self::serve::serve; + pub use self::service_ext::ServiceExt; #[cfg(test)] diff --git a/axum/src/middleware/from_extractor.rs b/axum/src/middleware/from_extractor.rs index 8c9a24833f..e120ffc1fc 100644 --- a/axum/src/middleware/from_extractor.rs +++ b/axum/src/middleware/from_extractor.rs @@ -84,9 +84,7 @@ use tower_service::Service; /// .route("/foo", post(other_handler)) /// // The extractor will run before all routes /// .route_layer(from_extractor::()); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// [`Bytes`]: bytes::Bytes diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 9040ee30a5..dc8f9ec019 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -48,9 +48,7 @@ macro_rules! top_level_service_fn { /// /// // Requests to `GET /` will go to `service`. /// let app = Router::new().route("/", get_service(service)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` /// /// Note that `get` routes will also be called for `HEAD` requests but will have @@ -109,9 +107,7 @@ macro_rules! top_level_handler_fn { /// /// // Requests to `GET /` will go to `handler`. /// let app = Router::new().route("/", get(handler)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` /// /// Note that `get` routes will also be called for `HEAD` requests but will have @@ -180,9 +176,7 @@ macro_rules! chained_service_fn { /// // Requests to `POST /` will go to `service` and `GET /` will go to /// // `other_service`. /// let app = Router::new().route("/", post_service(service).get_service(other_service)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` /// /// Note that `get` routes will also be called for `HEAD` requests but will have @@ -244,9 +238,7 @@ macro_rules! chained_handler_fn { /// // Requests to `POST /` will go to `handler` and `GET /` will go to /// // `other_handler`. /// let app = Router::new().route("/", post(handler).get(other_handler)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` /// /// Note that `get` routes will also be called for `HEAD` requests but will have @@ -316,9 +308,7 @@ top_level_service_fn!(trace_service, TRACE); /// /// // Requests to `POST /` will go to `service`. /// let app = Router::new().route("/", on_service(MethodFilter::POST, service)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` pub fn on_service(filter: MethodFilter, svc: T) -> MethodRouter where @@ -350,9 +340,7 @@ where /// /// // All requests to `/` will go to `service`. /// let app = Router::new().route("/", any_service(service)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// Additional methods can still be chained: @@ -379,9 +367,7 @@ where /// /// // `POST /` goes to `other_service`. All other requests go to `service` /// let app = Router::new().route("/", any_service(service).post_service(other_service)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` pub fn any_service(svc: T) -> MethodRouter where @@ -419,9 +405,7 @@ top_level_handler_fn!(trace, TRACE); /// /// // Requests to `POST /` will go to `handler`. /// let app = Router::new().route("/", on(MethodFilter::POST, handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` pub fn on(filter: MethodFilter, handler: H) -> MethodRouter where @@ -446,9 +430,7 @@ where /// /// // All requests to `/` will go to `handler`. /// let app = Router::new().route("/", any(handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// Additional methods can still be chained: @@ -465,9 +447,7 @@ where /// /// // `POST /` goes to `other_handler`. All other requests go to `handler` /// let app = Router::new().route("/", any(handler).post(other_handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` pub fn any(handler: H) -> MethodRouter where @@ -587,9 +567,7 @@ where /// // Requests to `GET /` will go to `handler` and `DELETE /` will go to /// // `other_handler` /// let app = Router::new().route("/", get(handler).on(MethodFilter::DELETE, other_handler)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` #[track_caller] pub fn on(self, filter: MethodFilter, handler: H) -> Self @@ -632,7 +610,6 @@ impl MethodRouter<(), Infallible> { /// /// ```rust /// use axum::{ - /// Server, /// handler::Handler, /// http::{Uri, Method}, /// response::IntoResponse, @@ -647,10 +624,8 @@ impl MethodRouter<(), Infallible> { /// let router = get(handler).post(handler); /// /// # async { - /// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))) - /// .serve(router.into_make_service()) - /// .await?; - /// # Ok::<_, hyper::Error>(()) + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, router.into_make_service()).await.unwrap(); /// # }; /// ``` /// @@ -666,7 +641,6 @@ impl MethodRouter<(), Infallible> { /// /// ```rust /// use axum::{ - /// Server, /// handler::Handler, /// response::IntoResponse, /// extract::ConnectInfo, @@ -681,10 +655,8 @@ impl MethodRouter<(), Infallible> { /// let router = get(handler).post(handler); /// /// # async { - /// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))) - /// .serve(router.into_make_service_with_connect_info::()) - /// .await?; - /// # Ok::<_, hyper::Error>(()) + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, router.into_make_service()).await.unwrap(); /// # }; /// ``` /// @@ -758,9 +730,7 @@ where /// /// // Requests to `DELETE /` will go to `service` /// let app = Router::new().route("/", on_service(MethodFilter::DELETE, service)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` #[track_caller] pub fn on_service(self, filter: MethodFilter, svc: T) -> Self @@ -1261,6 +1231,26 @@ where } } +// for `axum::serve(listener, router)` +#[cfg(feature = "tokio")] +const _: () = { + use crate::serve::IncomingStream; + + impl Service> for MethodRouter<()> { + type Response = Self; + type Error = Infallible; + type Future = std::future::Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: IncomingStream<'_>) -> Self::Future { + std::future::ready(Ok(self.clone())) + } + } +}; + #[cfg(test)] mod tests { use super::*; @@ -1361,7 +1351,7 @@ mod tests { } #[allow(dead_code)] - fn buiding_complex_router() { + async fn buiding_complex_router() { let app = crate::Router::new().route( "/", // use the all the things :bomb: @@ -1380,7 +1370,8 @@ mod tests { ), ); - crate::Server::bind(&"0.0.0.0:0".parse().unwrap()).serve(app.into_make_service()); + let listener = tokio::net::TcpListener::bind("0.0.0.0:0").await.unwrap(); + crate::serve(listener, app).await.unwrap(); } #[crate::test] diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 0a3252bfdb..44c257d4c4 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -585,10 +585,8 @@ impl Router { /// let app = Router::new().route("/", get(|| async { "Hi!" })); /// /// # async { - /// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - /// .serve(app.into_make_service()) - /// .await - /// .expect("server failed"); + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, app).await.unwrap(); /// # }; /// ``` /// @@ -608,6 +606,26 @@ impl Router { } } +// for `axum::serve(listener, router)` +#[cfg(feature = "tokio")] +const _: () = { + use crate::serve::IncomingStream; + + impl Service> for Router<()> { + type Response = Self; + type Error = Infallible; + type Future = std::future::Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: IncomingStream<'_>) -> Self::Future { + std::future::ready(Ok(self.clone())) + } + } +}; + impl Service> for Router<()> where B: HttpBody + Send + 'static, diff --git a/axum/src/serve.rs b/axum/src/serve.rs new file mode 100644 index 0000000000..83f529c8c3 --- /dev/null +++ b/axum/src/serve.rs @@ -0,0 +1,234 @@ +//! Serve services. + +use std::{convert::Infallible, io, net::SocketAddr}; + +use axum_core::{body::Body, extract::Request, response::Response}; +use futures_util::{future::poll_fn, FutureExt}; +use hyper1::server::conn::http1; +use tokio::net::{TcpListener, TcpStream}; +use tower_hyper_http_body_compat::{HttpBody04ToHttpBody1, HttpBody1ToHttpBody04}; +use tower_service::Service; + +/// Serve the service with the supplied listener. +/// +/// This method of running a service is intentionally simple and doesn't support any configuration. +/// Use hyper or hyper-util if you need configuration. +/// +/// It only supports HTTP/1. +/// +/// # Examples +/// +/// Serving a [`Router`]: +/// +/// ``` +/// use axum::{Router, routing::get}; +/// +/// # async { +/// let router = Router::new().route("/", get(|| async { "Hello, World!" })); +/// +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, router).await.unwrap(); +/// # }; +/// ``` +/// +/// See also [`Router::into_make_service_with_connect_info`]. +/// +/// Serving a [`MethodRouter`]: +/// +/// ``` +/// use axum::routing::get; +/// +/// # async { +/// let router = get(|| async { "Hello, World!" }); +/// +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, router).await.unwrap(); +/// # }; +/// ``` +/// +/// See also [`MethodRouter::into_make_service_with_connect_info`]. +/// +/// Serving a [`Handler`]: +/// +/// ``` +/// use axum::handler::HandlerWithoutStateExt; +/// +/// # async { +/// async fn handler() -> &'static str { +/// "Hello, World!" +/// } +/// +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, handler.into_make_service()).await.unwrap(); +/// # }; +/// ``` +/// +/// See also [`HandlerWithoutStateExt::into_make_service_with_connect_info`] and +/// [`HandlerService::into_make_service_with_connect_info`]. +/// +/// [`Router`]: crate::Router +/// [`Router::into_make_service_with_connect_info`]: crate::Router::into_make_service_with_connect_info +/// [`MethodRouter`]: crate::routing::MethodRouter +/// [`MethodRouter::into_make_service_with_connect_info`]: crate::routing::MethodRouter::into_make_service_with_connect_info +/// [`Handler`]: crate::handler::Handler +/// [`HandlerWithoutStateExt::into_make_service_with_connect_info`]: crate::handler::HandlerWithoutStateExt::into_make_service_with_connect_info +/// [`HandlerService::into_make_service_with_connect_info`]: crate::handler::HandlerService::into_make_service_with_connect_info +#[cfg(feature = "tokio")] +pub async fn serve(tcp_listener: TcpListener, mut make_service: M) -> io::Result<()> +where + M: for<'a> Service, Error = Infallible, Response = S>, + S: Service + Clone + Send + 'static, + S::Future: Send, +{ + loop { + let (tcp_stream, remote_addr) = tcp_listener.accept().await?; + + poll_fn(|cx| make_service.poll_ready(cx)) + .await + .unwrap_or_else(|err| match err {}); + + let mut service = make_service + .call(IncomingStream { + tcp_stream: &tcp_stream, + remote_addr, + }) + .await + .unwrap_or_else(|err| match err {}); + + let service = hyper1::service::service_fn(move |req: Request| { + let req = req.map(|body| { + // wont need this when axum uses http-body 1.0 + let http_body_04 = HttpBody1ToHttpBody04::new(body); + Body::new(http_body_04) + }); + + // doing this saves cloning the service just to await the service being ready + // + // services like `Router` are always ready, so assume the service + // we're running here is also always ready... + match futures_util::future::poll_fn(|cx| service.poll_ready(cx)).now_or_never() { + Some(Ok(())) => {} + Some(Err(err)) => match err {}, + None => { + // ...otherwise load shed + let mut res = Response::new(HttpBody04ToHttpBody1::new(Body::empty())); + *res.status_mut() = http::StatusCode::SERVICE_UNAVAILABLE; + return std::future::ready(Ok(res)).left_future(); + } + } + + let future = service.call(req); + + async move { + let response = future + .await + .unwrap_or_else(|err| match err {}) + // wont need this when axum uses http-body 1.0 + .map(HttpBody04ToHttpBody1::new); + + Ok::<_, Infallible>(response) + } + .right_future() + }); + + tokio::task::spawn(async move { + match http1::Builder::new() + .serve_connection(tcp_stream, service) + // for websockets + .with_upgrades() + .await + { + Ok(()) => {} + Err(_err) => { + // This error only appears when the client doesn't send a request and + // terminate the connection. + // + // If client sends one request then terminate connection whenever, it doesn't + // appear. + } + } + }); + } +} + +/// An incoming stream. +/// +/// Used with [`serve`] and [`IntoMakeServiceWithConnectInfo`]. +/// +/// [`IntoMakeServiceWithConnectInfo`]: crate::extract::connect_info::IntoMakeServiceWithConnectInfo +#[derive(Debug)] +pub struct IncomingStream<'a> { + tcp_stream: &'a TcpStream, + remote_addr: SocketAddr, +} + +impl IncomingStream<'_> { + /// Returns the local address that this stream is bound to. + pub fn local_addr(&self) -> std::io::Result { + self.tcp_stream.local_addr() + } + + /// Returns the remote address that this stream is bound to. + pub fn remote_addr(&self) -> SocketAddr { + self.remote_addr + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + handler::{Handler, HandlerWithoutStateExt}, + routing::get, + Router, + }; + + #[allow(dead_code, unused_must_use)] + async fn if_it_compiles_it_works() { + let router: Router = Router::new(); + + let addr = "0.0.0.0:0"; + + // router + serve(TcpListener::bind(addr).await.unwrap(), router.clone()); + serve( + TcpListener::bind(addr).await.unwrap(), + router.clone().into_make_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + router.into_make_service_with_connect_info::(), + ); + + // method router + serve(TcpListener::bind(addr).await.unwrap(), get(handler)); + serve( + TcpListener::bind(addr).await.unwrap(), + get(handler).into_make_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + get(handler).into_make_service_with_connect_info::(), + ); + + // handler + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + handler.with_state(()), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_make_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_make_service_with_connect_info::(), + ); + } + + async fn handler() {} +} diff --git a/axum/src/test_helpers/mod.rs b/axum/src/test_helpers/mod.rs index 7c48916f20..8c6504ea08 100644 --- a/axum/src/test_helpers/mod.rs +++ b/axum/src/test_helpers/mod.rs @@ -1,6 +1,6 @@ #![allow(clippy::disallowed_names)] -use crate::{body::HttpBody, BoxError}; +use crate::{extract::Request, response::Response, serve}; mod test_client; pub(crate) use self::test_client::*; diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index fa052a004d..9e1858cb97 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -1,11 +1,11 @@ -use super::{BoxError, HttpBody}; +use super::{serve, Request, Response}; use bytes::Bytes; use http::{ header::{HeaderName, HeaderValue}, - Request, StatusCode, + StatusCode, }; -use hyper::Server; -use std::net::{SocketAddr, TcpListener}; +use std::{convert::Infallible, net::SocketAddr}; +use tokio::net::TcpListener; use tower::make::Shared; use tower_service::Service; @@ -15,25 +15,22 @@ pub(crate) struct TestClient { } impl TestClient { - pub(crate) fn new(svc: S) -> Self + pub(crate) fn new(svc: S) -> Self where - S: Service, Response = http::Response> - + Clone - + Send - + 'static, - ResBody: HttpBody + Send + 'static, - ResBody::Data: Send, - ResBody::Error: Into, + S: Service + Clone + Send + 'static, S::Future: Send, - S::Error: Into, { - let listener = TcpListener::bind("127.0.0.1:0").expect("Could not bind ephemeral socket"); + let std_listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + std_listener.set_nonblocking(true).unwrap(); + let listener = TcpListener::from_std(std_listener).unwrap(); + let addr = listener.local_addr().unwrap(); println!("Listening on {addr}"); tokio::spawn(async move { - let server = Server::from_tcp(listener).unwrap().serve(Shared::new(svc)); - server.await.expect("server error"); + serve(listener, Shared::new(svc)) + .await + .expect("server error") }); let client = reqwest::Client::builder() diff --git a/axum/src/typed_header.rs b/axum/src/typed_header.rs index 717bc2409a..8cc3720c48 100644 --- a/axum/src/typed_header.rs +++ b/axum/src/typed_header.rs @@ -27,9 +27,7 @@ use std::{convert::Infallible, ops::Deref}; /// } /// /// let app = Router::new().route("/users/:user_id/team/:team_id", get(users_teams_show)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// # As response diff --git a/deny.toml b/deny.toml index c543455c59..9a3fd95570 100644 --- a/deny.toml +++ b/deny.toml @@ -25,6 +25,8 @@ skip-tree = [ { name = "spin" }, # lots still pulls in syn 1.x { name = "syn" }, + # until 1.0 is out we're pulling in both 0.14 and 1.0-rc.x + { name = "hyper" }, ] [sources] diff --git a/examples/anyhow-error-response/src/main.rs b/examples/anyhow-error-response/src/main.rs index 6854831340..b7a2416a4a 100644 --- a/examples/anyhow-error-response/src/main.rs +++ b/examples/anyhow-error-response/src/main.rs @@ -10,18 +10,16 @@ use axum::{ routing::get, Router, }; -use std::net::SocketAddr; #[tokio::main] async fn main() { let app = Router::new().route("/", get(handler)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler() -> Result<(), AppError> { diff --git a/examples/chat/src/main.rs b/examples/chat/src/main.rs index 3b49c3e81a..f7298c3436 100644 --- a/examples/chat/src/main.rs +++ b/examples/chat/src/main.rs @@ -18,7 +18,6 @@ use axum::{ use futures::{sink::SinkExt, stream::StreamExt}; use std::{ collections::HashSet, - net::SocketAddr, sync::{Arc, Mutex}, }; use tokio::sync::broadcast; @@ -53,12 +52,11 @@ async fn main() { .route("/websocket", get(websocket_handler)) .with_state(app_state); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn websocket_handler( diff --git a/examples/consume-body-in-extractor-or-middleware/src/main.rs b/examples/consume-body-in-extractor-or-middleware/src/main.rs index f1b2cbf754..fd63fcb1ff 100644 --- a/examples/consume-body-in-extractor-or-middleware/src/main.rs +++ b/examples/consume-body-in-extractor-or-middleware/src/main.rs @@ -14,7 +14,6 @@ use axum::{ routing::post, Router, }; -use std::net::SocketAddr; use tower::ServiceBuilder; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -32,12 +31,11 @@ async fn main() { .route("/", post(handler)) .layer(ServiceBuilder::new().layer(middleware::from_fn(print_request_body))); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } // middleware that shows how to consume the request body upfront diff --git a/examples/cors/src/main.rs b/examples/cors/src/main.rs index a0a5e8d8db..bbd192065b 100644 --- a/examples/cors/src/main.rs +++ b/examples/cors/src/main.rs @@ -40,10 +40,8 @@ async fn main() { async fn serve(app: Router, port: u16) { let addr = SocketAddr::from(([127, 0, 0, 1], port)); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app).await.unwrap(); } async fn html() -> impl IntoResponse { diff --git a/examples/customize-extractor-error/src/main.rs b/examples/customize-extractor-error/src/main.rs index 893fbd148a..e8820326f9 100644 --- a/examples/customize-extractor-error/src/main.rs +++ b/examples/customize-extractor-error/src/main.rs @@ -9,7 +9,6 @@ mod derive_from_request; mod with_rejection; use axum::{routing::post, Router}; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -29,10 +28,9 @@ async fn main() { .route("/derive-from-request", post(derive_from_request::handler)); // Run our application - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } diff --git a/examples/customize-path-rejection/src/main.rs b/examples/customize-path-rejection/src/main.rs index 3bcd91d33d..fa382e4bd6 100644 --- a/examples/customize-path-rejection/src/main.rs +++ b/examples/customize-path-rejection/src/main.rs @@ -13,7 +13,6 @@ use axum::{ Router, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -30,12 +29,11 @@ async fn main() { let app = Router::new().route("/users/:user_id/teams/:team_id", get(handler)); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler(Path(params): Path) -> impl IntoResponse { diff --git a/examples/error-handling-and-dependency-injection/src/main.rs b/examples/error-handling-and-dependency-injection/src/main.rs index f28ed4ff54..a5bdad9e6f 100644 --- a/examples/error-handling-and-dependency-injection/src/main.rs +++ b/examples/error-handling-and-dependency-injection/src/main.rs @@ -17,7 +17,7 @@ use axum::{ }; use serde::{Deserialize, Serialize}; use serde_json::json; -use std::{net::SocketAddr, sync::Arc}; +use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use uuid::Uuid; @@ -42,12 +42,11 @@ async fn main() { .with_state(user_repo); // Run our application - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } /// Handler for `GET /users/:id`. diff --git a/examples/form/src/main.rs b/examples/form/src/main.rs index 93a5ae1375..3f9ed09560 100644 --- a/examples/form/src/main.rs +++ b/examples/form/src/main.rs @@ -6,7 +6,6 @@ use axum::{extract::Form, response::Html, routing::get, Router}; use serde::Deserialize; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -22,13 +21,12 @@ async fn main() { // build our application with some routes let app = Router::new().route("/", get(show_form).post(accept_form)); - // run it with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + // run it + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn show_form() -> Html<&'static str> { diff --git a/examples/global-404-handler/src/main.rs b/examples/global-404-handler/src/main.rs index 8eb1469162..38b029439b 100644 --- a/examples/global-404-handler/src/main.rs +++ b/examples/global-404-handler/src/main.rs @@ -10,7 +10,6 @@ use axum::{ routing::get, Router, }; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -30,12 +29,11 @@ async fn main() { let app = app.fallback(handler_404); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler() -> Html<&'static str> { diff --git a/examples/graceful-shutdown/Cargo.toml b/examples/graceful-shutdown/Cargo.toml index 19af31136c..f287feeb9c 100644 --- a/examples/graceful-shutdown/Cargo.toml +++ b/examples/graceful-shutdown/Cargo.toml @@ -6,4 +6,5 @@ publish = false [dependencies] axum = { path = "../../axum" } +hyper = { version = "0.14", features = ["full"] } tokio = { version = "1.0", features = ["full"] } diff --git a/examples/graceful-shutdown/src/main.rs b/examples/graceful-shutdown/src/main.rs index 3704889a85..dabfee1697 100644 --- a/examples/graceful-shutdown/src/main.rs +++ b/examples/graceful-shutdown/src/main.rs @@ -17,7 +17,7 @@ async fn main() { // run it let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); println!("listening on {}", addr); - axum::Server::bind(&addr) + hyper::Server::bind(&addr) .serve(app.into_make_service()) .with_graceful_shutdown(shutdown_signal()) .await diff --git a/examples/handle-head-request/src/main.rs b/examples/handle-head-request/src/main.rs index 5b0db2640b..d49d88ef26 100644 --- a/examples/handle-head-request/src/main.rs +++ b/examples/handle-head-request/src/main.rs @@ -6,7 +6,6 @@ use axum::response::{IntoResponse, Response}; use axum::{http, routing::get, Router}; -use std::net::SocketAddr; fn app() -> Router { Router::new().route("/get-head", get(get_head_handler)) @@ -14,12 +13,11 @@ fn app() -> Router { #[tokio::main] async fn main() { - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - - axum::Server::bind(&addr) - .serve(app().into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app()).await.unwrap(); } // GET routes will also be called for HEAD requests but will have the response body removed. diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index 466cacebcc..de018335d1 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -5,7 +5,6 @@ //! ``` use axum::{response::Html, routing::get, Router}; -use std::net::SocketAddr; #[tokio::main] async fn main() { @@ -13,12 +12,11 @@ async fn main() { let app = Router::new().route("/", get(handler)); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler() -> Html<&'static str> { diff --git a/examples/http-proxy/src/main.rs b/examples/http-proxy/src/main.rs index 08845ca9e0..1abf3bbf29 100644 --- a/examples/http-proxy/src/main.rs +++ b/examples/http-proxy/src/main.rs @@ -52,7 +52,7 @@ async fn main() { let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) + hyper::Server::bind(&addr) .http1_preserve_header_case(true) .http1_title_case_headers(true) .serve(Shared::new(service)) diff --git a/examples/jwt/src/main.rs b/examples/jwt/src/main.rs index 4ee20d4bf7..a6f8bffa4c 100644 --- a/examples/jwt/src/main.rs +++ b/examples/jwt/src/main.rs @@ -19,7 +19,7 @@ use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation} use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use serde_json::json; -use std::{fmt::Display, net::SocketAddr}; +use std::fmt::Display; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; // Quick instructions @@ -67,13 +67,11 @@ async fn main() { .route("/protected", get(protected)) .route("/authorize", post(authorize)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn protected(claims: Claims) -> Result { diff --git a/examples/key-value-store/src/main.rs b/examples/key-value-store/src/main.rs index ccf21825db..b22802cb4b 100644 --- a/examples/key-value-store/src/main.rs +++ b/examples/key-value-store/src/main.rs @@ -19,7 +19,6 @@ use axum::{ use std::{ borrow::Cow, collections::HashMap, - net::SocketAddr, sync::{Arc, RwLock}, time::Duration, }; @@ -74,12 +73,11 @@ async fn main() { .with_state(Arc::clone(&shared_state)); // Run our app with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } type SharedState = Arc>; diff --git a/examples/listen-multiple-addrs/src/main.rs b/examples/listen-multiple-addrs/src/main.rs index f292c1d693..fed70daa1d 100644 --- a/examples/listen-multiple-addrs/src/main.rs +++ b/examples/listen-multiple-addrs/src/main.rs @@ -28,7 +28,7 @@ async fn main() { b: incoming_v6, }; - axum::Server::builder(combined) + hyper::Server::builder(combined) .serve(app.into_make_service()) .await .unwrap(); diff --git a/examples/multipart-form/src/main.rs b/examples/multipart-form/src/main.rs index 31f2887e6d..f8c0d96b1c 100644 --- a/examples/multipart-form/src/main.rs +++ b/examples/multipart-form/src/main.rs @@ -10,7 +10,6 @@ use axum::{ routing::get, Router, }; -use std::net::SocketAddr; use tower_http::limit::RequestBodyLimitLayer; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -34,12 +33,11 @@ async fn main() { .layer(tower_http::trace::TraceLayer::new_for_http()); // run it with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn show_form() -> Html<&'static str> { diff --git a/examples/oauth/src/main.rs b/examples/oauth/src/main.rs index 1a01570f69..40a039bcd8 100644 --- a/examples/oauth/src/main.rs +++ b/examples/oauth/src/main.rs @@ -25,7 +25,7 @@ use oauth2::{ ClientSecret, CsrfToken, RedirectUrl, Scope, TokenResponse, TokenUrl, }; use serde::{Deserialize, Serialize}; -use std::{env, net::SocketAddr}; +use std::env; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; static COOKIE_NAME: &str = "SESSION"; @@ -56,13 +56,11 @@ async fn main() { .route("/logout", get(logout)) .with_state(app_state); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } #[derive(Clone)] diff --git a/examples/parse-body-based-on-content-type/src/main.rs b/examples/parse-body-based-on-content-type/src/main.rs index 9ee26c6a10..bae4ec1d29 100644 --- a/examples/parse-body-based-on-content-type/src/main.rs +++ b/examples/parse-body-based-on-content-type/src/main.rs @@ -15,7 +15,6 @@ use axum::{ Form, Json, RequestExt, Router, }; use serde::{Deserialize, Serialize}; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -31,12 +30,11 @@ async fn main() { let app = Router::new().route("/", post(handler)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } #[derive(Debug, Serialize, Deserialize)] diff --git a/examples/print-request-response/src/main.rs b/examples/print-request-response/src/main.rs index 4703c05845..1348d026d5 100644 --- a/examples/print-request-response/src/main.rs +++ b/examples/print-request-response/src/main.rs @@ -13,7 +13,6 @@ use axum::{ routing::post, Router, }; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -30,12 +29,11 @@ async fn main() { .route("/", post(|| async move { "Hello from `POST /`" })) .layer(middleware::from_fn(print_request_response)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn print_request_response( diff --git a/examples/prometheus-metrics/src/main.rs b/examples/prometheus-metrics/src/main.rs index 675310bede..b90c384f5b 100644 --- a/examples/prometheus-metrics/src/main.rs +++ b/examples/prometheus-metrics/src/main.rs @@ -17,7 +17,6 @@ use axum::{ use metrics_exporter_prometheus::{Matcher, PrometheusBuilder, PrometheusHandle}; use std::{ future::ready, - net::SocketAddr, time::{Duration, Instant}, }; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -42,24 +41,22 @@ fn main_app() -> Router { async fn start_main_server() { let app = main_app(); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await - .unwrap() + .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn start_metrics_server() { let app = metrics_app(); // NOTE: expose metrics enpoint on a different port - let addr = SocketAddr::from(([127, 0, 0, 1], 3001)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3001") .await - .unwrap() + .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } #[tokio::main] diff --git a/examples/query-params-with-empty-strings/src/main.rs b/examples/query-params-with-empty-strings/src/main.rs index aa42464384..19117c4eaa 100644 --- a/examples/query-params-with-empty-strings/src/main.rs +++ b/examples/query-params-with-empty-strings/src/main.rs @@ -10,10 +10,11 @@ use std::{fmt, str::FromStr}; #[tokio::main] async fn main() { - axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(app().into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app()).await.unwrap(); } fn app() -> Router { diff --git a/examples/readme/src/main.rs b/examples/readme/src/main.rs index bdcb894557..cd2120cb77 100644 --- a/examples/readme/src/main.rs +++ b/examples/readme/src/main.rs @@ -11,7 +11,6 @@ use axum::{ Json, Router, }; use serde::{Deserialize, Serialize}; -use std::net::SocketAddr; #[tokio::main] async fn main() { @@ -26,13 +25,11 @@ async fn main() { .route("/users", post(create_user)); // run our app with hyper - // `axum::Server` is a re-export of `hyper::Server` - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } // basic handler that responds with a static string diff --git a/examples/rest-grpc-multiplex/src/main.rs b/examples/rest-grpc-multiplex/src/main.rs index bd4ce0d94b..b0550d80d2 100644 --- a/examples/rest-grpc-multiplex/src/main.rs +++ b/examples/rest-grpc-multiplex/src/main.rs @@ -65,7 +65,7 @@ async fn main() { let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) + hyper::Server::bind(&addr) .serve(tower::make::Shared::new(service)) .await .unwrap(); diff --git a/examples/reverse-proxy/src/main.rs b/examples/reverse-proxy/src/main.rs index a01947c652..875a3a5a6e 100644 --- a/examples/reverse-proxy/src/main.rs +++ b/examples/reverse-proxy/src/main.rs @@ -16,7 +16,6 @@ use axum::{ Router, }; use hyper::client::HttpConnector; -use std::net::SocketAddr; type Client = hyper::client::Client; @@ -28,12 +27,11 @@ async fn main() { let app = Router::new().route("/", get(handler)).with_state(client); - let addr = SocketAddr::from(([127, 0, 0, 1], 4000)); - println!("reverse proxy listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:4000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler(State(client): State, mut req: Request) -> Response { @@ -54,10 +52,9 @@ async fn handler(State(client): State, mut req: Request) -> Response { async fn server() { let app = Router::new().route("/", get(|| async { "Hello, world!" })); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("server listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } diff --git a/examples/routes-and-handlers-close-together/src/main.rs b/examples/routes-and-handlers-close-together/src/main.rs index 75320d095c..50721e700a 100644 --- a/examples/routes-and-handlers-close-together/src/main.rs +++ b/examples/routes-and-handlers-close-together/src/main.rs @@ -8,7 +8,6 @@ use axum::{ routing::{get, post, MethodRouter}, Router, }; -use std::net::SocketAddr; #[tokio::main] async fn main() { @@ -17,12 +16,11 @@ async fn main() { .merge(get_foo()) .merge(post_foo()); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } fn root() -> Router { diff --git a/examples/sessions/src/main.rs b/examples/sessions/src/main.rs index 9bea9c1b06..fcac6c5670 100644 --- a/examples/sessions/src/main.rs +++ b/examples/sessions/src/main.rs @@ -21,7 +21,6 @@ use axum::{ }; use serde::{Deserialize, Serialize}; use std::fmt::Debug; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use uuid::Uuid; @@ -42,12 +41,11 @@ async fn main() { let app = Router::new().route("/", get(handler)).with_state(store); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler(user_id: UserIdFromSession) -> impl IntoResponse { diff --git a/examples/sqlx-postgres/src/main.rs b/examples/sqlx-postgres/src/main.rs index 8e3353b9a1..e955303798 100644 --- a/examples/sqlx-postgres/src/main.rs +++ b/examples/sqlx-postgres/src/main.rs @@ -21,9 +21,10 @@ use axum::{ Router, }; use sqlx::postgres::{PgPool, PgPoolOptions}; +use tokio::net::TcpListener; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; -use std::{net::SocketAddr, time::Duration}; +use std::time::Duration; #[tokio::main] async fn main() { @@ -55,12 +56,9 @@ async fn main() { .with_state(pool); // run it with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } // we can extract the connection pool with `State` diff --git a/examples/sse/src/main.rs b/examples/sse/src/main.rs index dab5a565cb..528881337c 100644 --- a/examples/sse/src/main.rs +++ b/examples/sse/src/main.rs @@ -11,7 +11,7 @@ use axum::{ Router, }; use futures::stream::{self, Stream}; -use std::{convert::Infallible, net::SocketAddr, path::PathBuf, time::Duration}; +use std::{convert::Infallible, path::PathBuf, time::Duration}; use tokio_stream::StreamExt as _; use tower_http::{services::ServeDir, trace::TraceLayer}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -37,12 +37,11 @@ async fn main() { .layer(TraceLayer::new_for_http()); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn sse_handler( diff --git a/examples/static-file-server/src/main.rs b/examples/static-file-server/src/main.rs index 3a3a24149f..3aa7a9a8fe 100644 --- a/examples/static-file-server/src/main.rs +++ b/examples/static-file-server/src/main.rs @@ -103,9 +103,9 @@ fn calling_serve_dir_from_a_handler() -> Router { async fn serve(app: Router, port: u16) { let addr = SocketAddr::from(([127, 0, 0, 1], port)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.layer(TraceLayer::new_for_http()).into_make_service()) + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app.layer(TraceLayer::new_for_http())) .await .unwrap(); } diff --git a/examples/stream-to-file/src/main.rs b/examples/stream-to-file/src/main.rs index 2d51416239..02164d4e50 100644 --- a/examples/stream-to-file/src/main.rs +++ b/examples/stream-to-file/src/main.rs @@ -13,7 +13,7 @@ use axum::{ BoxError, Router, }; use futures::{Stream, TryStreamExt}; -use std::{io, net::SocketAddr}; +use std::io; use tokio::{fs::File, io::BufWriter}; use tokio_util::io::StreamReader; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -39,12 +39,11 @@ async fn main() { .route("/", get(show_form).post(accept_form)) .route("/file/:file_name", post(save_request_body)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } // Handler that streams the request body to a file. diff --git a/examples/templates/src/main.rs b/examples/templates/src/main.rs index d7e01a944b..1abdb33eb3 100644 --- a/examples/templates/src/main.rs +++ b/examples/templates/src/main.rs @@ -12,7 +12,6 @@ use axum::{ routing::get, Router, }; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -29,12 +28,11 @@ async fn main() { let app = Router::new().route("/greet/:name", get(greet)); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn greet(extract::Path(name): extract::Path) -> impl IntoResponse { diff --git a/examples/testing-websockets/src/main.rs b/examples/testing-websockets/src/main.rs index b778115c9f..954168b170 100644 --- a/examples/testing-websockets/src/main.rs +++ b/examples/testing-websockets/src/main.rs @@ -14,16 +14,14 @@ use axum::{ Router, }; use futures::{Sink, SinkExt, Stream, StreamExt}; -use std::net::SocketAddr; #[tokio::main] async fn main() { - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {addr}"); - axum::Server::bind(&addr) - .serve(app().into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app()).await.unwrap(); } fn app() -> Router { @@ -94,17 +92,18 @@ where #[cfg(test)] mod tests { use super::*; - use std::net::Ipv4Addr; + use std::net::{Ipv4Addr, SocketAddr}; use tokio_tungstenite::tungstenite; // We can integration test one handler by running the server in a background task and // connecting to it like any other client would. #[tokio::test] async fn integration_test() { - let server = axum::Server::bind(&SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0))) - .serve(app().into_make_service()); - let addr = server.local_addr(); - tokio::spawn(server); + let listener = tokio::net::TcpListener::bind(SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0))) + .await + .unwrap(); + let addr = listener.local_addr().unwrap(); + tokio::spawn(axum::serve(listener, app())); let (mut socket, _response) = tokio_tungstenite::connect_async(format!("ws://{addr}/integration-testable")) diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 5f7dbc07f1..99cc0402cf 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -24,14 +24,11 @@ async fn main() { .with(tracing_subscriber::fmt::layer()) .init(); - let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000)); - - tracing::debug!("listening on {}", addr); - - axum::Server::bind(&addr) - .serve(app().into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app()).await.unwrap(); } /// Having a function that produces our app makes it easy to call it from tests @@ -63,7 +60,8 @@ mod tests { http::{self, Request, StatusCode}, }; use serde_json::{json, Value}; - use std::net::{SocketAddr, TcpListener}; + use std::net::SocketAddr; + use tokio::net::TcpListener; use tower::Service; // for `call` use tower::ServiceExt; // for `oneshot` and `ready` @@ -131,15 +129,11 @@ mod tests { // You can also spawn a server and talk to it like any other HTTP server: #[tokio::test] async fn the_real_deal() { - let listener = TcpListener::bind("0.0.0.0:0".parse::().unwrap()).unwrap(); + let listener = TcpListener::bind("0.0.0.0:0").await.unwrap(); let addr = listener.local_addr().unwrap(); tokio::spawn(async move { - axum::Server::from_tcp(listener) - .unwrap() - .serve(app().into_make_service()) - .await - .unwrap(); + axum::serve(listener, app()).await.unwrap(); }); let client = hyper::Client::new(); diff --git a/examples/tls-rustls/src/main.rs b/examples/tls-rustls/src/main.rs index 5034cf31e7..860f56b5ee 100644 --- a/examples/tls-rustls/src/main.rs +++ b/examples/tls-rustls/src/main.rs @@ -93,10 +93,9 @@ async fn redirect_http_to_https(ports: Ports) { }; let addr = SocketAddr::from(([127, 0, 0, 1], ports.http)); - tracing::debug!("http redirect listening on {}", addr); - - axum::Server::bind(&addr) - .serve(redirect.into_make_service()) + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, redirect.into_make_service()) .await .unwrap(); } diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 4463bfa44a..9e08876d8c 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -24,7 +24,6 @@ use axum::{ use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, - net::SocketAddr, sync::{Arc, RwLock}, time::Duration, }; @@ -68,12 +67,11 @@ async fn main() { ) .with_state(db); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } // The query parameters for todos index diff --git a/examples/tokio-postgres/src/main.rs b/examples/tokio-postgres/src/main.rs index 77c4c112b5..effc032089 100644 --- a/examples/tokio-postgres/src/main.rs +++ b/examples/tokio-postgres/src/main.rs @@ -13,7 +13,6 @@ use axum::{ }; use bb8::{Pool, PooledConnection}; use bb8_postgres::PostgresConnectionManager; -use std::net::SocketAddr; use tokio_postgres::NoTls; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -41,13 +40,12 @@ async fn main() { ) .with_state(pool); - // run it with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + // run it + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } type ConnectionPool = Pool>; diff --git a/examples/tracing-aka-logging/src/main.rs b/examples/tracing-aka-logging/src/main.rs index 58ece2b568..50787fca34 100644 --- a/examples/tracing-aka-logging/src/main.rs +++ b/examples/tracing-aka-logging/src/main.rs @@ -12,7 +12,8 @@ use axum::{ routing::get, Router, }; -use std::{net::SocketAddr, time::Duration}; +use std::time::Duration; +use tokio::net::TcpListener; use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer}; use tracing::{info_span, Span}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -77,12 +78,9 @@ async fn main() { ); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler() -> Html<&'static str> { diff --git a/examples/unix-domain-socket/src/main.rs b/examples/unix-domain-socket/src/main.rs index 0a3d3f0d47..ce350720c0 100644 --- a/examples/unix-domain-socket/src/main.rs +++ b/examples/unix-domain-socket/src/main.rs @@ -63,7 +63,7 @@ mod unix { tokio::spawn(async { let app = Router::new().route("/", get(handler)); - axum::Server::builder(ServerAccept { uds }) + hyper::Server::builder(ServerAccept { uds }) .serve(app.into_make_service_with_connect_info::()) .await .unwrap(); diff --git a/examples/validator/src/main.rs b/examples/validator/src/main.rs index 4f4f6239bf..a6a25b8566 100644 --- a/examples/validator/src/main.rs +++ b/examples/validator/src/main.rs @@ -19,8 +19,8 @@ use axum::{ Router, }; use serde::{de::DeserializeOwned, Deserialize}; -use std::net::SocketAddr; use thiserror::Error; +use tokio::net::TcpListener; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use validator::Validate; @@ -38,13 +38,9 @@ async fn main() { let app = Router::new().route("/", get(handler)); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } #[derive(Debug, Deserialize, Validate)] diff --git a/examples/versioning/src/main.rs b/examples/versioning/src/main.rs index fd3f66682a..b5324d69f4 100644 --- a/examples/versioning/src/main.rs +++ b/examples/versioning/src/main.rs @@ -12,7 +12,7 @@ use axum::{ routing::get, RequestPartsExt, Router, }; -use std::{collections::HashMap, net::SocketAddr}; +use std::collections::HashMap; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -29,12 +29,11 @@ async fn main() { let app = Router::new().route("/:version/foo", get(handler)); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler(version: Version) { diff --git a/examples/websockets/src/main.rs b/examples/websockets/src/main.rs index 73993c15f3..e13182bbb9 100644 --- a/examples/websockets/src/main.rs +++ b/examples/websockets/src/main.rs @@ -66,12 +66,16 @@ async fn main() { ); // run it with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service_with_connect_info::()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await + .unwrap(); } /// The handler for the HTTP request (this gets called when the HTTP GET lands at the start From 49183e404ed3f29a442b3fc9e59b7afcccb55528 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 22 Mar 2023 23:52:15 +0100 Subject: [PATCH 09/57] Remove `IntoResponse` for http-body types (#1877) --- axum-core/src/response/into_response.rs | 65 ++----------------------- axum/CHANGELOG.md | 7 +++ 2 files changed, 12 insertions(+), 60 deletions(-) diff --git a/axum-core/src/response/into_response.rs b/axum-core/src/response/into_response.rs index 8a40641a04..ab684e2c36 100644 --- a/axum-core/src/response/into_response.rs +++ b/axum-core/src/response/into_response.rs @@ -5,10 +5,7 @@ use http::{ header::{self, HeaderMap, HeaderName, HeaderValue}, Extensions, StatusCode, }; -use http_body::{ - combinators::{MapData, MapErr}, - Empty, Full, SizeHint, -}; +use http_body::SizeHint; use std::{ borrow::Cow, convert::Infallible, @@ -136,7 +133,7 @@ impl IntoResponse for StatusCode { impl IntoResponse for () { fn into_response(self) -> Response { - Empty::new().into_response() + Body::empty().into_response() } } @@ -175,64 +172,12 @@ impl IntoResponse for http::response::Parts { } } -impl IntoResponse for Full { - fn into_response(self) -> Response { - Response::new(Body::new(self)) - } -} - -impl IntoResponse for Empty { - fn into_response(self) -> Response { - Response::new(Body::new(self)) - } -} - impl IntoResponse for Body { fn into_response(self) -> Response { Response::new(self) } } -impl IntoResponse for http_body::combinators::BoxBody -where - E: Into + 'static, -{ - fn into_response(self) -> Response { - Response::new(Body::new(self)) - } -} - -impl IntoResponse for http_body::combinators::UnsyncBoxBody -where - E: Into + 'static, -{ - fn into_response(self) -> Response { - Response::new(Body::new(self)) - } -} - -impl IntoResponse for MapData -where - B: http_body::Body + Send + 'static, - F: FnMut(B::Data) -> Bytes + Send + 'static, - B::Error: Into, -{ - fn into_response(self) -> Response { - Response::new(Body::new(self)) - } -} - -impl IntoResponse for MapErr -where - B: http_body::Body + Send + 'static, - F: FnMut(B::Error) -> E + Send + 'static, - E: Into, -{ - fn into_response(self) -> Response { - Response::new(Body::new(self)) - } -} - impl IntoResponse for &'static str { fn into_response(self) -> Response { Cow::Borrowed(self).into_response() @@ -247,7 +192,7 @@ impl IntoResponse for String { impl IntoResponse for Cow<'static, str> { fn into_response(self) -> Response { - let mut res = Full::from(self).into_response(); + let mut res = Body::from(self).into_response(); res.headers_mut().insert( header::CONTENT_TYPE, HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()), @@ -258,7 +203,7 @@ impl IntoResponse for Cow<'static, str> { impl IntoResponse for Bytes { fn into_response(self) -> Response { - let mut res = Full::from(self).into_response(); + let mut res = Body::from(self).into_response(); res.headers_mut().insert( header::CONTENT_TYPE, HeaderValue::from_static(mime::APPLICATION_OCTET_STREAM.as_ref()), @@ -372,7 +317,7 @@ impl IntoResponse for Vec { impl IntoResponse for Cow<'static, [u8]> { fn into_response(self) -> Response { - let mut res = Full::from(self).into_response(); + let mut res = Body::from(self).into_response(); res.headers_mut().insert( header::CONTENT_TYPE, HeaderValue::from_static(mime::APPLICATION_OCTET_STREAM.as_ref()), diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 98f9535367..c7f33b397f 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -37,6 +37,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **breaking:** Removed the `BoxBody` type alias and its `box_body` constructor. Use `axum::body::Body::new` instead ([#1789]) - **breaking:** Remove `RawBody` extractor. `axum::body::Body` implements `FromRequest` directly ([#1789]) +- **breaking:** The following types from `http-body` no longer implement `IntoResponse`: + - `Full`, use `Body::from` instead + - `Empty`, use `Body::empty` instead + - `BoxBody`, use `Body::new` instead + - `UnsyncBoxBody`, use `Body::new` instead + - `MapData`, use `Body::new` instead + - `MapErr`, use `Body::new` instead - **added:** Add `axum::extract::Request` type alias where the body is `axum::body::Body` ([#1789]) - **added:** Add `Router::as_service` and `Router::into_service` to workaround type inference issues when calling `ServiceExt` methods on a `Router` ([#1835]) From 44bb38dfa2a9cd748963aa6bbfa39ac91767983b Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 23 Mar 2023 22:38:37 +0100 Subject: [PATCH 10/57] update deps --- axum-core/Cargo.toml | 4 +- axum-extra/Cargo.toml | 6 +-- axum/Cargo.toml | 10 ++-- .../Cargo.toml | 2 +- examples/graceful-shutdown/Cargo.toml | 2 +- examples/handle-head-request/Cargo.toml | 2 +- examples/http-proxy/Cargo.toml | 2 +- examples/hyper-1-0/Cargo.toml | 14 ----- examples/hyper-1-0/src/main.rs | 52 ------------------- examples/listen-multiple-addrs/Cargo.toml | 2 +- examples/low-level-openssl/Cargo.toml | 2 +- examples/low-level-rustls/Cargo.toml | 2 +- examples/print-request-response/Cargo.toml | 2 +- .../Cargo.toml | 2 +- examples/rest-grpc-multiplex/Cargo.toml | 2 +- examples/reverse-proxy/Cargo.toml | 2 +- examples/testing-websockets/Cargo.toml | 2 +- examples/testing/Cargo.toml | 2 +- examples/unix-domain-socket/Cargo.toml | 2 +- examples/validator/Cargo.toml | 2 +- 20 files changed, 23 insertions(+), 93 deletions(-) delete mode 100644 examples/hyper-1-0/Cargo.toml delete mode 100644 examples/hyper-1-0/src/main.rs diff --git a/axum-core/Cargo.toml b/axum-core/Cargo.toml index 129d212ce1..2cf7ef5113 100644 --- a/axum-core/Cargo.toml +++ b/axum-core/Cargo.toml @@ -19,7 +19,7 @@ async-trait = "0.1.67" bytes = "1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } http = "0.2.7" -http-body = "0.4.5" +http-body = "1.0.0-rc.2" mime = "0.3.16" pin-project-lite = "0.2.7" sync_wrapper = "0.1.1" @@ -35,7 +35,7 @@ rustversion = "1.0.9" [dev-dependencies] axum = { path = "../axum", version = "0.6.0", features = ["headers"] } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -hyper = "0.14.24" +hyper = "1.0.0-rc.3" tokio = { version = "1.25.0", features = ["macros"] } tower-http = { version = "0.4", features = ["limit"] } diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index 984ebb3f26..cbc449941e 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -39,7 +39,7 @@ axum = { path = "../axum", version = "0.6.9", default-features = false } bytes = "1.1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } http = "0.2" -http-body = "0.4.4" +http-body = "1.0.0-rc.2" mime = "0.3" pin-project-lite = "0.2" tokio = "1.19" @@ -63,8 +63,8 @@ tokio-util = { version = "0.7", optional = true } [dev-dependencies] axum = { path = "../axum", version = "0.6.0", features = ["headers"] } -http-body = "0.4.4" -hyper = "0.14" +http-body = "1.0.0-rc.2" +hyper = "1.0.0-rc.3" reqwest = { version = "0.11", default-features = false, features = ["json", "stream", "multipart"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.71" diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 43a7f04add..802082cbe1 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -22,7 +22,7 @@ matched-path = [] multipart = ["dep:multer"] original-uri = [] query = ["dep:serde_urlencoded"] -tokio = ["dep:tokio", "hyper/server", "hyper/tcp", "hyper/runtime", "tower/make"] +tokio = ["dep:tokio", "hyper/server", "tower/make"] tower-log = ["tower/log"] ws = ["tokio", "dep:tokio-tungstenite", "dep:sha1", "dep:base64"] @@ -36,8 +36,8 @@ bitflags = "1.0" bytes = "1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } http = "0.2.9" -http-body = "0.4.4" -hyper = { version = "0.14.24", features = ["stream"] } +http-body = "1.0.0-rc.2" +hyper = "1.0.0-rc.3" itoa = "1.0.5" matchit = "0.7" memchr = "2.4.1" @@ -50,10 +50,6 @@ tower = { version = "0.4.13", default-features = false, features = ["util"] } tower-layer = "0.3.2" tower-service = "0.3" -# wont need this when axum uses http-body 1.0 -hyper1 = { package = "hyper", version = "1.0.0-rc.3", features = ["server", "http1"] } -tower-hyper-http-body-compat = { version = "0.1.4", features = ["server", "http1"] } - # optional dependencies axum-macros = { path = "../axum-macros", version = "0.3.7", optional = true } base64 = { version = "0.21.0", optional = true } diff --git a/examples/consume-body-in-extractor-or-middleware/Cargo.toml b/examples/consume-body-in-extractor-or-middleware/Cargo.toml index 655ffae1b7..29ee2a0712 100644 --- a/examples/consume-body-in-extractor-or-middleware/Cargo.toml +++ b/examples/consume-body-in-extractor-or-middleware/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = "0.14" +hyper = "1.0.0-rc.3" tokio = { version = "1.0", features = ["full"] } tower = "0.4" tower-http = { version = "0.4.0", features = ["map-request-body", "util"] } diff --git a/examples/graceful-shutdown/Cargo.toml b/examples/graceful-shutdown/Cargo.toml index f287feeb9c..e6909bf5ef 100644 --- a/examples/graceful-shutdown/Cargo.toml +++ b/examples/graceful-shutdown/Cargo.toml @@ -6,5 +6,5 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } tokio = { version = "1.0", features = ["full"] } diff --git a/examples/handle-head-request/Cargo.toml b/examples/handle-head-request/Cargo.toml index 0672b238d6..c32a726dec 100644 --- a/examples/handle-head-request/Cargo.toml +++ b/examples/handle-head-request/Cargo.toml @@ -9,5 +9,5 @@ axum = { path = "../../axum" } tokio = { version = "1.0", features = ["full"] } [dev-dependencies] -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } tower = { version = "0.4", features = ["util"] } diff --git a/examples/http-proxy/Cargo.toml b/examples/http-proxy/Cargo.toml index 90ea85c5c5..64ba31f86b 100644 --- a/examples/http-proxy/Cargo.toml +++ b/examples/http-proxy/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["make"] } tracing = "0.1" diff --git a/examples/hyper-1-0/Cargo.toml b/examples/hyper-1-0/Cargo.toml deleted file mode 100644 index 772b268d0a..0000000000 --- a/examples/hyper-1-0/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "example-hyper-1-0" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -axum = { path = "../../axum" } -hyper = { version = "1.0.0-rc.3", features = ["full"] } -tokio = { version = "1.0", features = ["full"] } -tower-http = { version = "0.4", features = ["trace"] } -tower-hyper-http-body-compat = { version = "0.1", features = ["http1", "server"] } -tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/hyper-1-0/src/main.rs b/examples/hyper-1-0/src/main.rs deleted file mode 100644 index 51493d4450..0000000000 --- a/examples/hyper-1-0/src/main.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! Run with -//! -//! ```not_rust -//! cargo run -p example-hyper-1-0 -//! ``` - -use axum::{routing::get, Router}; -use std::net::SocketAddr; -use tokio::net::TcpListener; -use tower_http::trace::TraceLayer; -use tower_hyper_http_body_compat::TowerService03HttpServiceAsHyper1HttpService; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - -// this is hyper 1.0 -use hyper::server::conn::http1; - -#[tokio::main] -async fn main() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "example_hyper_1_0=debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); - - let app = Router::new() - .route("/", get(|| async { "Hello, World!" })) - // we can still add regular tower middleware - .layer(TraceLayer::new_for_http()); - - // `Router` implements tower-service 0.3's `Service` trait. Convert that to something - // that implements hyper 1.0's `Service` trait. - let service = TowerService03HttpServiceAsHyper1HttpService::new(app); - - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - let tcp_listener = TcpListener::bind(addr).await.unwrap(); - tracing::debug!("listening on {addr}"); - loop { - let (tcp_stream, _) = tcp_listener.accept().await.unwrap(); - let service = service.clone(); - tokio::task::spawn(async move { - if let Err(http_err) = http1::Builder::new() - .keep_alive(true) - .serve_connection(tcp_stream, service) - .await - { - eprintln!("Error while serving HTTP connection: {http_err}"); - } - }); - } -} diff --git a/examples/listen-multiple-addrs/Cargo.toml b/examples/listen-multiple-addrs/Cargo.toml index 5aeebc9e36..3f66cc469b 100644 --- a/examples/listen-multiple-addrs/Cargo.toml +++ b/examples/listen-multiple-addrs/Cargo.toml @@ -6,5 +6,5 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } tokio = { version = "1", features = ["full"] } diff --git a/examples/low-level-openssl/Cargo.toml b/examples/low-level-openssl/Cargo.toml index d0e1a46db1..4e1266c7fe 100644 --- a/examples/low-level-openssl/Cargo.toml +++ b/examples/low-level-openssl/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } openssl = "0.10" tokio = { version = "1", features = ["full"] } tokio-openssl = "0.6" diff --git a/examples/low-level-rustls/Cargo.toml b/examples/low-level-rustls/Cargo.toml index 910691284e..ae2944b4f0 100644 --- a/examples/low-level-rustls/Cargo.toml +++ b/examples/low-level-rustls/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } rustls-pemfile = "0.3" tokio = { version = "1", features = ["full"] } tokio-rustls = "0.23" diff --git a/examples/print-request-response/Cargo.toml b/examples/print-request-response/Cargo.toml index e59f7eae99..1b95f35f9e 100644 --- a/examples/print-request-response/Cargo.toml +++ b/examples/print-request-response/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util", "filter"] } tracing = "0.1" diff --git a/examples/query-params-with-empty-strings/Cargo.toml b/examples/query-params-with-empty-strings/Cargo.toml index b64c1244be..30a6115551 100644 --- a/examples/query-params-with-empty-strings/Cargo.toml +++ b/examples/query-params-with-empty-strings/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = "0.14" +hyper = "1.0.0-rc.3" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util"] } diff --git a/examples/rest-grpc-multiplex/Cargo.toml b/examples/rest-grpc-multiplex/Cargo.toml index 61afe01ab1..57a106a493 100644 --- a/examples/rest-grpc-multiplex/Cargo.toml +++ b/examples/rest-grpc-multiplex/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures = "0.3" -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } prost = "0.11" tokio = { version = "1", features = ["full"] } tonic = { version = "0.8" } diff --git a/examples/reverse-proxy/Cargo.toml b/examples/reverse-proxy/Cargo.toml index 81ddb519ca..9f97c9df4d 100644 --- a/examples/reverse-proxy/Cargo.toml +++ b/examples/reverse-proxy/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] axum = { path = "../../axum" } -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } tokio = { version = "1", features = ["full"] } diff --git a/examples/testing-websockets/Cargo.toml b/examples/testing-websockets/Cargo.toml index 26c4f8d40b..2806648b85 100644 --- a/examples/testing-websockets/Cargo.toml +++ b/examples/testing-websockets/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] axum = { path = "../../axum", features = ["ws"] } futures = "0.3" -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tokio-tungstenite = "0.17" diff --git a/examples/testing/Cargo.toml b/examples/testing/Cargo.toml index fbfb0544f8..a100516be8 100644 --- a/examples/testing/Cargo.toml +++ b/examples/testing/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } mime = "0.3" serde_json = "1.0" tokio = { version = "1.0", features = ["full"] } diff --git a/examples/unix-domain-socket/Cargo.toml b/examples/unix-domain-socket/Cargo.toml index e3e018463b..7702caca84 100644 --- a/examples/unix-domain-socket/Cargo.toml +++ b/examples/unix-domain-socket/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures = "0.3" -hyper = { version = "0.14", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util"] } tracing = "0.1" diff --git a/examples/validator/Cargo.toml b/examples/validator/Cargo.toml index b164a30acf..bb7125a743 100644 --- a/examples/validator/Cargo.toml +++ b/examples/validator/Cargo.toml @@ -7,7 +7,7 @@ version = "0.1.0" [dependencies] async-trait = "0.1.67" axum = { path = "../../axum" } -http-body = "0.4.3" +http-body = "1.0.0-rc.2" serde = { version = "1.0", features = ["derive"] } thiserror = "1.0.29" tokio = { version = "1.0", features = ["full"] } From c73b6a99698e13be0212222f83ce1cb4a2523ea0 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 23 Mar 2023 23:23:39 +0100 Subject: [PATCH 11/57] Update to hyper 1.0-rc.3 and http-body-util 0.1.0-rc.2 --- axum-core/Cargo.toml | 1 + axum-core/src/body.rs | 103 ++---- axum-core/src/ext_traits/request.rs | 6 +- axum-core/src/extract/rejection.rs | 2 +- axum-core/src/extract/request_parts.rs | 13 +- axum-core/src/response/into_response.rs | 17 +- axum-extra/Cargo.toml | 1 + axum-extra/src/body/async_read_body.rs | 21 +- axum-extra/src/extract/cookie/mod.rs | 3 +- axum-extra/src/extract/multipart.rs | 2 +- axum-extra/src/routing/resource.rs | 3 +- axum/Cargo.toml | 1 + axum/benches/benches.rs | 9 +- axum/src/extract/connect_info.rs | 7 - axum/src/extract/multipart.rs | 2 +- axum/src/extract/ws.rs | 6 +- axum/src/middleware/from_fn.rs | 3 +- axum/src/response/sse.rs | 16 +- axum/src/routing/method_routing.rs | 4 +- axum/src/routing/tests/get_to_head.rs | 4 +- axum/src/routing/tests/mod.rs | 1 + axum/src/serve.rs | 21 +- .../Cargo.toml | 1 + .../src/main.rs | 7 +- examples/graceful-shutdown/src/main.rs | 97 ++--- examples/handle-head-request/Cargo.toml | 1 + examples/handle-head-request/src/main.rs | 5 +- examples/http-proxy/src/main.rs | 159 ++++---- examples/listen-multiple-addrs/src/main.rs | 99 ++--- examples/low-level-openssl/src/main.rs | 174 ++++----- examples/low-level-rustls/src/main.rs | 195 +++++----- examples/print-request-response/Cargo.toml | 1 + examples/print-request-response/src/main.rs | 5 +- .../Cargo.toml | 1 + .../src/main.rs | 3 +- examples/rest-grpc-multiplex/src/main.rs | 113 +++--- examples/reverse-proxy/src/main.rs | 93 ++--- examples/testing/Cargo.toml | 1 + examples/testing/src/main.rs | 45 +-- examples/tls-rustls/src/main.rs | 79 ++-- examples/unix-domain-socket/src/main.rs | 349 +++++++++--------- 41 files changed, 824 insertions(+), 850 deletions(-) diff --git a/axum-core/Cargo.toml b/axum-core/Cargo.toml index 2cf7ef5113..a558307183 100644 --- a/axum-core/Cargo.toml +++ b/axum-core/Cargo.toml @@ -20,6 +20,7 @@ bytes = "1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } http = "0.2.7" http-body = "1.0.0-rc.2" +http-body-util = "0.1.0-rc.2" mime = "0.3.16" pin-project-lite = "0.2.7" sync_wrapper = "0.1.1" diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index 89e69218bd..00a1e98e54 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -2,17 +2,16 @@ use crate::{BoxError, Error}; use bytes::Bytes; -use bytes::{Buf, BufMut}; use futures_util::stream::Stream; use futures_util::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 std::pin::Pin; use std::task::{Context, Poll}; use sync_wrapper::SyncWrapper; -type BoxBody = http_body::combinators::UnsyncBoxBody; +type BoxBody = http_body_util::combinators::UnsyncBoxBody; fn boxed(body: B) -> BoxBody where @@ -35,58 +34,6 @@ where } } -// copied from hyper under the following license: -// Copyright (c) 2014-2021 Sean McArthur - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -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()) -} - /// The body type used in axum requests and responses. #[derive(Debug)] pub struct Body(BoxBody); @@ -103,7 +50,7 @@ impl Body { /// Create an empty body. pub fn empty() -> Self { - Self::new(http_body::Empty::new()) + Self::new(http_body_util::Empty::new()) } /// Create a new `Body` from a [`Stream`]. @@ -131,7 +78,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)) } } }; @@ -152,19 +99,11 @@ impl http_body::Body for Body { type Error = Error; #[inline] - fn poll_data( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> std::task::Poll>> { - Pin::new(&mut self.0).poll_data(cx) - } - - #[inline] - fn poll_trailers( + fn poll_frame( 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) } #[inline] @@ -182,8 +121,16 @@ impl Stream for Body { type Item = Result; #[inline] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_data(cx) + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match futures_util::ready!(self.as_mut().poll_frame(cx)?) { + Some(frame) => match frame.into_data() { + Ok(data) => return Poll::Ready(Some(Ok(data))), + Err(_frame) => {} + }, + None => return Poll::Ready(None), + } + } } } @@ -203,25 +150,17 @@ where type Data = Bytes; type Error = Error; - 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(Error::new(err)))), None => Poll::Ready(None), } } - - #[inline] - fn poll_trailers( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - Poll::Ready(Ok(None)) - } } #[test] diff --git a/axum-core/src/ext_traits/request.rs b/axum-core/src/ext_traits/request.rs index df41a3c943..0e69f26110 100644 --- a/axum-core/src/ext_traits/request.rs +++ b/axum-core/src/ext_traits/request.rs @@ -1,7 +1,7 @@ use crate::body::Body; use crate::extract::{DefaultBodyLimitKind, FromRequest, FromRequestParts, Request}; use futures_util::future::BoxFuture; -use http_body::Limited; +use http_body_util::Limited; mod sealed { pub trait Sealed {} @@ -326,9 +326,9 @@ impl RequestExt for Request { match self.extensions().get::().copied() { Some(DefaultBodyLimitKind::Disable) => Err(self), Some(DefaultBodyLimitKind::Limit(limit)) => { - Ok(self.map(|b| http_body::Limited::new(b, limit))) + Ok(self.map(|b| http_body_util::Limited::new(b, limit))) } - None => Ok(self.map(|b| http_body::Limited::new(b, DEFAULT_LIMIT))), + None => Ok(self.map(|b| http_body_util::Limited::new(b, DEFAULT_LIMIT))), } } diff --git a/axum-core/src/extract/rejection.rs b/axum-core/src/extract/rejection.rs index 037f4217a3..e5531c619a 100644 --- a/axum-core/src/extract/rejection.rs +++ b/axum-core/src/extract/rejection.rs @@ -20,7 +20,7 @@ impl FailedToBufferBody { Ok(err) => err.into_inner(), Err(err) => err, }; - match box_error.downcast::() { + match box_error.downcast::() { Ok(err) => Self::LengthLimitError(LengthLimitError::from_err(err)), Err(err) => Self::UnknownBodyError(UnknownBodyError::from_err(err)), } diff --git a/axum-core/src/extract/request_parts.rs b/axum-core/src/extract/request_parts.rs index 98943bea35..1f1f7d9d7a 100644 --- a/axum-core/src/extract/request_parts.rs +++ b/axum-core/src/extract/request_parts.rs @@ -3,6 +3,7 @@ use crate::{body::Body, RequestExt}; use async_trait::async_trait; use bytes::Bytes; use http::{request::Parts, HeaderMap, Method, Uri, Version}; +use http_body_util::BodyExt; use std::convert::Infallible; #[async_trait] @@ -79,12 +80,16 @@ where async fn from_request(req: Request, _: &S) -> Result { let bytes = match req.into_limited_body() { - Ok(limited_body) => crate::body::to_bytes(limited_body) + Ok(limited_body) => limited_body + .collect() .await - .map_err(FailedToBufferBody::from_err)?, - Err(unlimited_body) => crate::body::to_bytes(unlimited_body) + .map_err(FailedToBufferBody::from_err)? + .to_bytes(), + Err(unlimited_body) => unlimited_body + .collect() .await - .map_err(FailedToBufferBody::from_err)?, + .map_err(FailedToBufferBody::from_err)? + .to_bytes(), }; Ok(bytes) diff --git a/axum-core/src/response/into_response.rs b/axum-core/src/response/into_response.rs index ab684e2c36..b4bf8f6f12 100644 --- a/axum-core/src/response/into_response.rs +++ b/axum-core/src/response/into_response.rs @@ -5,7 +5,7 @@ use http::{ header::{self, HeaderMap, HeaderName, HeaderValue}, Extensions, StatusCode, }; -use http_body::SizeHint; +use http_body::{Frame, SizeHint}; use std::{ borrow::Cow, convert::Infallible, @@ -250,30 +250,23 @@ where type Data = Bytes; type Error = Infallible; - fn poll_data( + fn poll_frame( mut self: Pin<&mut Self>, _cx: &mut Context<'_>, - ) -> Poll>> { + ) -> Poll, Self::Error>>> { if let Some(mut buf) = self.first.take() { let bytes = buf.copy_to_bytes(buf.remaining()); - return Poll::Ready(Some(Ok(bytes))); + return Poll::Ready(Some(Ok(Frame::data(bytes)))); } if let Some(mut buf) = self.second.take() { let bytes = buf.copy_to_bytes(buf.remaining()); - return Poll::Ready(Some(Ok(bytes))); + return Poll::Ready(Some(Ok(Frame::data(bytes)))); } Poll::Ready(None) } - fn poll_trailers( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - Poll::Ready(Ok(None)) - } - fn is_end_stream(&self) -> bool { self.first.is_none() && self.second.is_none() } diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index cbc449941e..4a9616f9dd 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -40,6 +40,7 @@ bytes = "1.1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } http = "0.2" http-body = "1.0.0-rc.2" +http-body-util = "0.1.0-rc.2" mime = "0.3" pin-project-lite = "0.2" tokio = "1.19" diff --git a/axum-extra/src/body/async_read_body.rs b/axum-extra/src/body/async_read_body.rs index ce87e43693..adac1962c0 100644 --- a/axum-extra/src/body/async_read_body.rs +++ b/axum-extra/src/body/async_read_body.rs @@ -1,6 +1,5 @@ use axum::{ body::{Body, Bytes, HttpBody}, - http::HeaderMap, response::{IntoResponse, Response}, Error, }; @@ -69,18 +68,22 @@ impl HttpBody for AsyncReadBody { type Data = Bytes; type Error = Error; - fn poll_data( + #[inline] + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { - self.project().body.poll_data(cx) + ) -> Poll, Self::Error>>> { + self.project().body.poll_frame(cx) } - fn poll_trailers( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - self.project().body.poll_trailers(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() } } diff --git a/axum-extra/src/extract/cookie/mod.rs b/axum-extra/src/extract/cookie/mod.rs index 5cd021fa70..b370aee83e 100644 --- a/axum-extra/src/extract/cookie/mod.rs +++ b/axum-extra/src/extract/cookie/mod.rs @@ -232,6 +232,7 @@ fn set_cookies(jar: cookie::CookieJar, headers: &mut HeaderMap) { mod tests { use super::*; use axum::{body::Body, extract::FromRef, http::Request, routing::get, Router}; + use http_body_util::BodyExt; use tower::ServiceExt; macro_rules! cookie_test { @@ -376,7 +377,7 @@ mod tests { B: axum::body::HttpBody, B::Error: std::fmt::Debug, { - let bytes = hyper::body::to_bytes(body).await.unwrap(); + let bytes = body.collect().await.unwrap().to_bytes(); String::from_utf8(bytes.to_vec()).unwrap() } } diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 403e183041..bf47be256b 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -278,7 +278,7 @@ fn status_code_from_multer_error(err: &multer::Error) -> StatusCode { if err .downcast_ref::() .and_then(|err| err.source()) - .and_then(|err| err.downcast_ref::()) + .and_then(|err| err.downcast_ref::()) .is_some() { return StatusCode::PAYLOAD_TOO_LARGE; diff --git a/axum-extra/src/routing/resource.rs b/axum-extra/src/routing/resource.rs index 2ab3b9d9e8..362281e03a 100644 --- a/axum-extra/src/routing/resource.rs +++ b/axum-extra/src/routing/resource.rs @@ -148,6 +148,7 @@ mod tests { use super::*; use axum::{body::Body, extract::Path, http::Method, Router}; use http::Request; + use http_body_util::BodyExt; use tower::ServiceExt; #[tokio::test] @@ -216,7 +217,7 @@ mod tests { ) .await .unwrap(); - let bytes = hyper::body::to_bytes(res).await.unwrap(); + let bytes = res.collect().await.unwrap().to_bytes(); String::from_utf8(bytes.to_vec()).unwrap() } } diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 802082cbe1..5f5c8ed961 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -37,6 +37,7 @@ bytes = "1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } http = "0.2.9" http-body = "1.0.0-rc.2" +http-body-util = "0.1.0-rc.2" hyper = "1.0.0-rc.3" itoa = "1.0.5" matchit = "0.7" diff --git a/axum/benches/benches.rs b/axum/benches/benches.rs index 3d4124a46c..b969070bbe 100644 --- a/axum/benches/benches.rs +++ b/axum/benches/benches.rs @@ -3,7 +3,6 @@ use axum::{ routing::{get, post}, Extension, Json, Router, }; -use hyper::server::conn::AddrIncoming; use serde::{Deserialize, Serialize}; use std::{ io::BufRead, @@ -149,13 +148,7 @@ impl BenchmarkBuilder { let addr = listener.local_addr().unwrap(); std::thread::spawn(move || { - rt.block_on(async move { - let incoming = AddrIncoming::from_listener(listener).unwrap(); - hyper::Server::builder(incoming) - .serve(app.into_make_service()) - .await - .unwrap(); - }); + rt.block_on(axum::serve(listener, app)); }); let mut cmd = Command::new("rewrk"); diff --git a/axum/src/extract/connect_info.rs b/axum/src/extract/connect_info.rs index 0482ab0cd5..3b64e5f6f1 100644 --- a/axum/src/extract/connect_info.rs +++ b/axum/src/extract/connect_info.rs @@ -8,7 +8,6 @@ use super::{Extension, FromRequestParts}; use crate::{middleware::AddExtension, serve::IncomingStream}; use async_trait::async_trait; use http::request::Parts; -use hyper::server::conn::AddrStream; use std::{ convert::Infallible, fmt, @@ -83,12 +82,6 @@ pub trait Connected: Clone + Send + Sync + 'static { fn connect_info(target: T) -> Self; } -impl Connected<&AddrStream> for SocketAddr { - fn connect_info(target: &AddrStream) -> Self { - target.remote_addr() - } -} - impl Connected> for SocketAddr { fn connect_info(target: IncomingStream<'_>) -> Self { target.remote_addr() diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index de6207bab5..7f6332d1bb 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -240,7 +240,7 @@ fn status_code_from_multer_error(err: &multer::Error) -> StatusCode { if err .downcast_ref::() .and_then(|err| err.source()) - .and_then(|err| err.downcast_ref::()) + .and_then(|err| err.downcast_ref::()) .is_some() { return StatusCode::PAYLOAD_TOO_LARGE; diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index feda02506f..b7d221978f 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -132,7 +132,7 @@ pub struct WebSocketUpgrade { /// The chosen protocol sent in the `Sec-WebSocket-Protocol` header of the response. protocol: Option, sec_websocket_key: HeaderValue, - on_upgrade: hyper1::upgrade::OnUpgrade, + on_upgrade: hyper::upgrade::OnUpgrade, on_failed_upgrade: F, sec_websocket_protocol: Option, } @@ -386,7 +386,7 @@ where let on_upgrade = parts .extensions - .remove::() + .remove::() .ok_or(ConnectionNotUpgradable)?; let sec_websocket_protocol = parts.headers.get(header::SEC_WEBSOCKET_PROTOCOL).cloned(); @@ -429,7 +429,7 @@ fn header_contains(headers: &HeaderMap, key: HeaderName, value: &'static str) -> /// See [the module level documentation](self) for more details. #[derive(Debug)] pub struct WebSocket { - inner: WebSocketStream, + inner: WebSocketStream, protocol: Option, } diff --git a/axum/src/middleware/from_fn.rs b/axum/src/middleware/from_fn.rs index c2cf5fe659..0cfc4e6496 100644 --- a/axum/src/middleware/from_fn.rs +++ b/axum/src/middleware/from_fn.rs @@ -376,6 +376,7 @@ mod tests { use super::*; use crate::{body::Body, routing::get, Router}; use http::{HeaderMap, StatusCode}; + use http_body_util::BodyExt; use tower::ServiceExt; #[crate::test] @@ -400,7 +401,7 @@ mod tests { .await .unwrap(); assert_eq!(res.status(), StatusCode::OK); - let body = hyper::body::to_bytes(res).await.unwrap(); + let body = res.collect().await.unwrap().to_bytes(); assert_eq!(&body[..], b"ok"); } } diff --git a/axum/src/response/sse.rs b/axum/src/response/sse.rs index 3f849b9278..80403607a3 100644 --- a/axum/src/response/sse.rs +++ b/axum/src/response/sse.rs @@ -40,6 +40,7 @@ use futures_util::{ ready, stream::{Stream, TryStream}, }; +use http_body::Frame; use pin_project_lite::pin_project; use std::{ fmt, @@ -129,16 +130,16 @@ where type Data = Bytes; type Error = E; - fn poll_data( + fn poll_frame( self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll>> { + ) -> Poll, Self::Error>>> { let this = self.project(); match this.event_stream.get_pin_mut().poll_next(cx) { Poll::Pending => { if let Some(keep_alive) = this.keep_alive.as_pin_mut() { - keep_alive.poll_event(cx).map(|e| Some(Ok(e))) + keep_alive.poll_event(cx).map(|e| Some(Ok(Frame::data(e)))) } else { Poll::Pending } @@ -147,19 +148,12 @@ where if let Some(keep_alive) = this.keep_alive.as_pin_mut() { keep_alive.reset(); } - Poll::Ready(Some(Ok(event.finalize()))) + Poll::Ready(Some(Ok(Frame::data(event.finalize())))) } Poll::Ready(Some(Err(error))) => Poll::Ready(Some(Err(error))), Poll::Ready(None) => Poll::Ready(None), } } - - fn poll_trailers( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - Poll::Ready(Ok(None)) - } } /// Server-sent event diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index dc8f9ec019..8a83d8aaa7 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -1260,6 +1260,7 @@ mod tests { }; use axum_core::response::IntoResponse; use http::{header::ALLOW, HeaderMap}; + use http_body_util::BodyExt; use std::time::Duration; use tower::{timeout::TimeoutLayer, Service, ServiceBuilder, ServiceExt}; use tower_http::{services::fs::ServeDir, validate_request::ValidateRequestHeaderLayer}; @@ -1553,7 +1554,8 @@ mod tests { .unwrap() .into_response(); let (parts, body) = response.into_parts(); - let body = String::from_utf8(hyper::body::to_bytes(body).await.unwrap().to_vec()).unwrap(); + let body = + String::from_utf8(BodyExt::collect(body).await.unwrap().to_bytes().to_vec()).unwrap(); (parts.status, parts.headers, body) } diff --git a/axum/src/routing/tests/get_to_head.rs b/axum/src/routing/tests/get_to_head.rs index cedf84918e..811e5390c7 100644 --- a/axum/src/routing/tests/get_to_head.rs +++ b/axum/src/routing/tests/get_to_head.rs @@ -32,7 +32,7 @@ mod for_handlers { assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.headers()["x-some-header"], "foobar"); - let body = hyper::body::to_bytes(res.into_body()).await.unwrap(); + let body = BodyExt::collect(res.into_body()).await.unwrap().to_bytes(); assert_eq!(body.len(), 0); } } @@ -67,7 +67,7 @@ mod for_services { assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.headers()["x-some-header"], "foobar"); - let body = hyper::body::to_bytes(res.into_body()).await.unwrap(); + let body = BodyExt::collect(res.into_body()).await.unwrap().to_bytes(); assert_eq!(body.len(), 0); } } diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index 989a58717f..6f49101a7e 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -11,6 +11,7 @@ use crate::{ use axum_core::extract::Request; use futures_util::stream::StreamExt; use http::{header::ALLOW, header::CONTENT_LENGTH, HeaderMap, StatusCode, Uri}; +use http_body_util::BodyExt; use serde_json::json; use std::{ convert::Infallible, diff --git a/axum/src/serve.rs b/axum/src/serve.rs index 83f529c8c3..d4094c4f25 100644 --- a/axum/src/serve.rs +++ b/axum/src/serve.rs @@ -4,9 +4,8 @@ use std::{convert::Infallible, io, net::SocketAddr}; use axum_core::{body::Body, extract::Request, response::Response}; use futures_util::{future::poll_fn, FutureExt}; -use hyper1::server::conn::http1; +use hyper::server::conn::http1; use tokio::net::{TcpListener, TcpStream}; -use tower_hyper_http_body_compat::{HttpBody04ToHttpBody1, HttpBody1ToHttpBody04}; use tower_service::Service; /// Serve the service with the supplied listener. @@ -95,13 +94,7 @@ where .await .unwrap_or_else(|err| match err {}); - let service = hyper1::service::service_fn(move |req: Request| { - let req = req.map(|body| { - // wont need this when axum uses http-body 1.0 - let http_body_04 = HttpBody1ToHttpBody04::new(body); - Body::new(http_body_04) - }); - + let service = hyper::service::service_fn(move |req: Request| { // doing this saves cloning the service just to await the service being ready // // services like `Router` are always ready, so assume the service @@ -111,20 +104,16 @@ where Some(Err(err)) => match err {}, None => { // ...otherwise load shed - let mut res = Response::new(HttpBody04ToHttpBody1::new(Body::empty())); + let mut res = Response::new(Body::empty()); *res.status_mut() = http::StatusCode::SERVICE_UNAVAILABLE; return std::future::ready(Ok(res)).left_future(); } } - let future = service.call(req); + let future = service.call(req.map(Body::new)); async move { - let response = future - .await - .unwrap_or_else(|err| match err {}) - // wont need this when axum uses http-body 1.0 - .map(HttpBody04ToHttpBody1::new); + let response = future.await.unwrap_or_else(|err| match err {}); Ok::<_, Infallible>(response) } diff --git a/examples/consume-body-in-extractor-or-middleware/Cargo.toml b/examples/consume-body-in-extractor-or-middleware/Cargo.toml index 29ee2a0712..8afdbfa077 100644 --- a/examples/consume-body-in-extractor-or-middleware/Cargo.toml +++ b/examples/consume-body-in-extractor-or-middleware/Cargo.toml @@ -6,6 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } +http-body-util = "0.1.0-rc.2" hyper = "1.0.0-rc.3" tokio = { version = "1.0", features = ["full"] } tower = "0.4" diff --git a/examples/consume-body-in-extractor-or-middleware/src/main.rs b/examples/consume-body-in-extractor-or-middleware/src/main.rs index fd63fcb1ff..e8564f210c 100644 --- a/examples/consume-body-in-extractor-or-middleware/src/main.rs +++ b/examples/consume-body-in-extractor-or-middleware/src/main.rs @@ -14,6 +14,7 @@ use axum::{ routing::post, Router, }; +use http_body_util::BodyExt; use tower::ServiceBuilder; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -51,9 +52,11 @@ async fn buffer_request_body(request: Request) -> Result { let (parts, body) = request.into_parts(); // this wont work if the body is an long running stream - let bytes = hyper::body::to_bytes(body) + let bytes = body + .collect() .await - .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response())?; + .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response())? + .to_bytes(); do_thing_with_request_body(bytes.clone()); diff --git a/examples/graceful-shutdown/src/main.rs b/examples/graceful-shutdown/src/main.rs index dabfee1697..a0e45b7c7b 100644 --- a/examples/graceful-shutdown/src/main.rs +++ b/examples/graceful-shutdown/src/main.rs @@ -5,51 +5,56 @@ //! kill or ctrl-c //! ``` -use axum::{response::Html, routing::get, Router}; -use std::net::SocketAddr; -use tokio::signal; - -#[tokio::main] -async fn main() { - // build our application with a route - let app = Router::new().route("/", get(handler)); - - // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {}", addr); - hyper::Server::bind(&addr) - .serve(app.into_make_service()) - .with_graceful_shutdown(shutdown_signal()) - .await - .unwrap(); +// TODO +fn main() { + eprint!("this example has not yet been updated to hyper 1.0"); } -async fn handler() -> Html<&'static str> { - Html("

Hello, World!

") -} - -async fn shutdown_signal() { - let ctrl_c = async { - signal::ctrl_c() - .await - .expect("failed to install Ctrl+C handler"); - }; - - #[cfg(unix)] - let terminate = async { - signal::unix::signal(signal::unix::SignalKind::terminate()) - .expect("failed to install signal handler") - .recv() - .await; - }; - - #[cfg(not(unix))] - let terminate = std::future::pending::<()>(); - - tokio::select! { - _ = ctrl_c => {}, - _ = terminate => {}, - } - - println!("signal received, starting graceful shutdown"); -} +// use axum::{response::Html, routing::get, Router}; +// use std::net::SocketAddr; +// use tokio::signal; + +// #[tokio::main] +// async fn main() { +// // build our application with a route +// let app = Router::new().route("/", get(handler)); + +// // run it +// let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); +// println!("listening on {}", addr); +// hyper::Server::bind(&addr) +// .serve(app.into_make_service()) +// .with_graceful_shutdown(shutdown_signal()) +// .await +// .unwrap(); +// } + +// async fn handler() -> Html<&'static str> { +// Html("

Hello, World!

") +// } + +// async fn shutdown_signal() { +// let ctrl_c = async { +// signal::ctrl_c() +// .await +// .expect("failed to install Ctrl+C handler"); +// }; + +// #[cfg(unix)] +// let terminate = async { +// signal::unix::signal(signal::unix::SignalKind::terminate()) +// .expect("failed to install signal handler") +// .recv() +// .await; +// }; + +// #[cfg(not(unix))] +// let terminate = std::future::pending::<()>(); + +// tokio::select! { +// _ = ctrl_c => {}, +// _ = terminate => {}, +// } + +// println!("signal received, starting graceful shutdown"); +// } diff --git a/examples/handle-head-request/Cargo.toml b/examples/handle-head-request/Cargo.toml index c32a726dec..5856a7f10b 100644 --- a/examples/handle-head-request/Cargo.toml +++ b/examples/handle-head-request/Cargo.toml @@ -9,5 +9,6 @@ axum = { path = "../../axum" } tokio = { version = "1.0", features = ["full"] } [dev-dependencies] +http-body-util = "0.1.0-rc.2" hyper = { version = "1.0.0-rc.3", features = ["full"] } tower = { version = "0.4", features = ["util"] } diff --git a/examples/handle-head-request/src/main.rs b/examples/handle-head-request/src/main.rs index d49d88ef26..6cb71f5fb4 100644 --- a/examples/handle-head-request/src/main.rs +++ b/examples/handle-head-request/src/main.rs @@ -44,6 +44,7 @@ mod tests { use super::*; use axum::body::Body; use axum::http::{Request, StatusCode}; + use http_body_util::BodyExt; use tower::ServiceExt; #[tokio::test] @@ -58,7 +59,7 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); assert_eq!(response.headers()["x-some-header"], "header from GET"); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + let body = response.collect().await.unwrap().to_bytes(); assert_eq!(&body[..], b"body from GET"); } @@ -74,7 +75,7 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); assert_eq!(response.headers()["x-some-header"], "header from HEAD"); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + let body = response.collect().await.unwrap().to_bytes(); assert!(body.is_empty()); } } diff --git a/examples/http-proxy/src/main.rs b/examples/http-proxy/src/main.rs index 1abf3bbf29..04ef3e5181 100644 --- a/examples/http-proxy/src/main.rs +++ b/examples/http-proxy/src/main.rs @@ -12,91 +12,96 @@ //! //! Example is based on -use axum::{ - body::Body, - extract::Request, - http::{Method, StatusCode}, - response::{IntoResponse, Response}, - routing::get, - Router, -}; -use hyper::upgrade::Upgraded; -use std::net::SocketAddr; -use tokio::net::TcpStream; -use tower::{make::Shared, ServiceExt}; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +// TODO +fn main() { + eprint!("this example has not yet been updated to hyper 1.0"); +} -#[tokio::main] -async fn main() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "example_http_proxy=trace,tower_http=debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); +// use axum::{ +// body::Body, +// extract::Request, +// http::{Method, StatusCode}, +// response::{IntoResponse, Response}, +// routing::get, +// Router, +// }; +// use hyper::upgrade::Upgraded; +// use std::net::SocketAddr; +// use tokio::net::TcpStream; +// use tower::{make::Shared, ServiceExt}; +// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - let router_svc = Router::new().route("/", get(|| async { "Hello, World!" })); +// #[tokio::main] +// async fn main() { +// tracing_subscriber::registry() +// .with( +// tracing_subscriber::EnvFilter::try_from_default_env() +// .unwrap_or_else(|_| "example_http_proxy=trace,tower_http=debug".into()), +// ) +// .with(tracing_subscriber::fmt::layer()) +// .init(); - let service = tower::service_fn(move |req: Request<_>| { - let router_svc = router_svc.clone(); - let req = req.map(Body::new); - async move { - if req.method() == Method::CONNECT { - proxy(req).await - } else { - router_svc.oneshot(req).await.map_err(|err| match err {}) - } - } - }); +// let router_svc = Router::new().route("/", get(|| async { "Hello, World!" })); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - hyper::Server::bind(&addr) - .http1_preserve_header_case(true) - .http1_title_case_headers(true) - .serve(Shared::new(service)) - .await - .unwrap(); -} +// let service = tower::service_fn(move |req: Request<_>| { +// let router_svc = router_svc.clone(); +// let req = req.map(Body::new); +// async move { +// if req.method() == Method::CONNECT { +// proxy(req).await +// } else { +// router_svc.oneshot(req).await.map_err(|err| match err {}) +// } +// } +// }); -async fn proxy(req: Request) -> Result { - tracing::trace!(?req); +// let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); +// tracing::debug!("listening on {}", addr); +// hyper::Server::bind(&addr) +// .http1_preserve_header_case(true) +// .http1_title_case_headers(true) +// .serve(Shared::new(service)) +// .await +// .unwrap(); +// } - if let Some(host_addr) = req.uri().authority().map(|auth| auth.to_string()) { - tokio::task::spawn(async move { - match hyper::upgrade::on(req).await { - Ok(upgraded) => { - if let Err(e) = tunnel(upgraded, host_addr).await { - tracing::warn!("server io error: {}", e); - }; - } - Err(e) => tracing::warn!("upgrade error: {}", e), - } - }); +// async fn proxy(req: Request) -> Result { +// tracing::trace!(?req); - Ok(Response::new(Body::empty())) - } else { - tracing::warn!("CONNECT host is not socket addr: {:?}", req.uri()); - Ok(( - StatusCode::BAD_REQUEST, - "CONNECT must be to a socket address", - ) - .into_response()) - } -} +// if let Some(host_addr) = req.uri().authority().map(|auth| auth.to_string()) { +// tokio::task::spawn(async move { +// match hyper::upgrade::on(req).await { +// Ok(upgraded) => { +// if let Err(e) = tunnel(upgraded, host_addr).await { +// tracing::warn!("server io error: {}", e); +// }; +// } +// Err(e) => tracing::warn!("upgrade error: {}", e), +// } +// }); -async fn tunnel(mut upgraded: Upgraded, addr: String) -> std::io::Result<()> { - let mut server = TcpStream::connect(addr).await?; +// Ok(Response::new(Body::empty())) +// } else { +// tracing::warn!("CONNECT host is not socket addr: {:?}", req.uri()); +// Ok(( +// StatusCode::BAD_REQUEST, +// "CONNECT must be to a socket address", +// ) +// .into_response()) +// } +// } - let (from_client, from_server) = - tokio::io::copy_bidirectional(&mut upgraded, &mut server).await?; +// async fn tunnel(mut upgraded: Upgraded, addr: String) -> std::io::Result<()> { +// let mut server = TcpStream::connect(addr).await?; - tracing::debug!( - "client wrote {} bytes and received {} bytes", - from_client, - from_server - ); +// let (from_client, from_server) = +// tokio::io::copy_bidirectional(&mut upgraded, &mut server).await?; - Ok(()) -} +// tracing::debug!( +// "client wrote {} bytes and received {} bytes", +// from_client, +// from_server +// ); + +// Ok(()) +// } diff --git a/examples/listen-multiple-addrs/src/main.rs b/examples/listen-multiple-addrs/src/main.rs index fed70daa1d..ba21856603 100644 --- a/examples/listen-multiple-addrs/src/main.rs +++ b/examples/listen-multiple-addrs/src/main.rs @@ -5,56 +5,61 @@ //! listen on both IPv4 and IPv6 when the IPv6 catch-all listener is used (`::`), //! [like older versions of Windows.](https://docs.microsoft.com/en-us/windows/win32/winsock/dual-stack-sockets) -use axum::{routing::get, Router}; -use hyper::server::{accept::Accept, conn::AddrIncoming}; -use std::{ - net::{Ipv4Addr, Ipv6Addr, SocketAddr}, - pin::Pin, - task::{Context, Poll}, -}; - -#[tokio::main] -async fn main() { - let app = Router::new().route("/", get(|| async { "Hello, World!" })); - - let localhost_v4 = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 8080); - let incoming_v4 = AddrIncoming::bind(&localhost_v4).unwrap(); - - let localhost_v6 = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 8080); - let incoming_v6 = AddrIncoming::bind(&localhost_v6).unwrap(); - - let combined = CombinedIncoming { - a: incoming_v4, - b: incoming_v6, - }; - - hyper::Server::builder(combined) - .serve(app.into_make_service()) - .await - .unwrap(); +// TODO +fn main() { + eprint!("this example has not yet been updated to hyper 1.0"); } -struct CombinedIncoming { - a: AddrIncoming, - b: AddrIncoming, -} +// use axum::{routing::get, Router}; +// use hyper::server::{accept::Accept, conn::AddrIncoming}; +// use std::{ +// net::{Ipv4Addr, Ipv6Addr, SocketAddr}, +// pin::Pin, +// task::{Context, Poll}, +// }; -impl Accept for CombinedIncoming { - type Conn = ::Conn; - type Error = ::Error; +// #[tokio::main] +// async fn main() { +// let app = Router::new().route("/", get(|| async { "Hello, World!" })); - fn poll_accept( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>> { - if let Poll::Ready(Some(value)) = Pin::new(&mut self.a).poll_accept(cx) { - return Poll::Ready(Some(value)); - } +// let localhost_v4 = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 8080); +// let incoming_v4 = AddrIncoming::bind(&localhost_v4).unwrap(); - if let Poll::Ready(Some(value)) = Pin::new(&mut self.b).poll_accept(cx) { - return Poll::Ready(Some(value)); - } +// let localhost_v6 = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 8080); +// let incoming_v6 = AddrIncoming::bind(&localhost_v6).unwrap(); - Poll::Pending - } -} +// let combined = CombinedIncoming { +// a: incoming_v4, +// b: incoming_v6, +// }; + +// hyper::Server::builder(combined) +// .serve(app.into_make_service()) +// .await +// .unwrap(); +// } + +// struct CombinedIncoming { +// a: AddrIncoming, +// b: AddrIncoming, +// } + +// impl Accept for CombinedIncoming { +// type Conn = ::Conn; +// type Error = ::Error; + +// fn poll_accept( +// mut self: Pin<&mut Self>, +// cx: &mut Context<'_>, +// ) -> Poll>> { +// if let Poll::Ready(Some(value)) = Pin::new(&mut self.a).poll_accept(cx) { +// return Poll::Ready(Some(value)); +// } + +// if let Poll::Ready(Some(value)) = Pin::new(&mut self.b).poll_accept(cx) { +// return Poll::Ready(Some(value)); +// } + +// Poll::Pending +// } +// } diff --git a/examples/low-level-openssl/src/main.rs b/examples/low-level-openssl/src/main.rs index c40e24997c..c32becd3f3 100644 --- a/examples/low-level-openssl/src/main.rs +++ b/examples/low-level-openssl/src/main.rs @@ -1,88 +1,90 @@ -use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod}; -use tokio_openssl::SslStream; - -use axum::{body::Body, extract::ConnectInfo, http::Request, routing::get, Router}; -use futures_util::future::poll_fn; -use hyper::server::{ - accept::Accept, - conn::{AddrIncoming, Http}, -}; -use std::{net::SocketAddr, path::PathBuf, pin::Pin, sync::Arc}; -use tokio::net::TcpListener; -use tower::MakeService; - -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - -#[tokio::main] -async fn main() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "example_low_level_openssl=debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); - - let mut tls_builder = SslAcceptor::mozilla_modern_v5(SslMethod::tls()).unwrap(); - - tls_builder - .set_certificate_file( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("self_signed_certs") - .join("cert.pem"), - SslFiletype::PEM, - ) - .unwrap(); - - tls_builder - .set_private_key_file( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("self_signed_certs") - .join("key.pem"), - SslFiletype::PEM, - ) - .unwrap(); - - tls_builder.check_private_key().unwrap(); - - let acceptor = tls_builder.build(); - - let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); - let mut listener = AddrIncoming::from_listener(listener).unwrap(); - - let protocol = Arc::new(Http::new()); - - let mut app = Router::new() - .route("/", get(handler)) - .into_make_service_with_connect_info::(); - - tracing::info!("listening on https://localhost:3000"); - - loop { - let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx)) - .await - .unwrap() - .unwrap(); - - let acceptor = acceptor.clone(); - - let protocol = protocol.clone(); - - let svc = MakeService::<_, Request>::make_service(&mut app, &stream); - - tokio::spawn(async move { - let ssl = Ssl::new(acceptor.context()).unwrap(); - let mut tls_stream = SslStream::new(ssl, stream).unwrap(); - - SslStream::accept(Pin::new(&mut tls_stream)).await.unwrap(); - - let _ = protocol - .serve_connection(tls_stream, svc.await.unwrap()) - .await; - }); - } +// TODO +fn main() { + eprint!("this example has not yet been updated to hyper 1.0"); } -async fn handler(ConnectInfo(addr): ConnectInfo) -> String { - addr.to_string() -} +// use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod}; +// use tokio_openssl::SslStream; + +// use axum::{body::Body, extract::ConnectInfo, http::Request, routing::get, Router}; +// use futures_util::future::poll_fn; +// use std::{net::SocketAddr, path::PathBuf, pin::Pin, sync::Arc}; +// use tokio::net::TcpListener; +// use tower::MakeService; + +// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +// #[tokio::main] +// async fn main() { +// tracing_subscriber::registry() +// .with( +// tracing_subscriber::EnvFilter::try_from_default_env() +// .unwrap_or_else(|_| "example_low_level_openssl=debug".into()), +// ) +// .with(tracing_subscriber::fmt::layer()) +// .init(); + +// let mut tls_builder = SslAcceptor::mozilla_modern_v5(SslMethod::tls()).unwrap(); + +// tls_builder +// .set_certificate_file( +// PathBuf::from(env!("CARGO_MANIFEST_DIR")) +// .join("self_signed_certs") +// .join("cert.pem"), +// SslFiletype::PEM, +// ) +// .unwrap(); + +// tls_builder +// .set_private_key_file( +// PathBuf::from(env!("CARGO_MANIFEST_DIR")) +// .join("self_signed_certs") +// .join("key.pem"), +// SslFiletype::PEM, +// ) +// .unwrap(); + +// tls_builder.check_private_key().unwrap(); + +// let acceptor = tls_builder.build(); + +// let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); +// let mut listener = AddrIncoming::from_listener(listener).unwrap(); + +// let protocol = Arc::new(Http::new()); + +// let mut app = Router::new() +// .route("/", get(handler)) +// .into_make_service_with_connect_info::(); + +// tracing::info!("listening on https://localhost:3000"); + +// loop { +// let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx)) +// .await +// .unwrap() +// .unwrap(); + +// let acceptor = acceptor.clone(); + +// let protocol = protocol.clone(); + +// let svc = MakeService::<_, Request>::make_service(&mut app, &stream); + +// tokio::spawn(async move { +// let ssl = Ssl::new(acceptor.context()).unwrap(); +// let mut tls_stream = SslStream::new(ssl, stream).unwrap(); + +// SslStream::accept(Pin::new(&mut tls_stream)).await.unwrap(); + +// let _ = protocol +// .serve_connection(tls_stream, svc.await.unwrap()) +// .await; +// }); +// } +// } + +// #[allow(dead_code)] +// async fn handler(ConnectInfo(addr): ConnectInfo) -> String { +// addr.to_string() +// } diff --git a/examples/low-level-rustls/src/main.rs b/examples/low-level-rustls/src/main.rs index 1e9a951f49..1f961a06f7 100644 --- a/examples/low-level-rustls/src/main.rs +++ b/examples/low-level-rustls/src/main.rs @@ -4,101 +4,104 @@ //! cargo run -p example-low-level-rustls //! ``` -use axum::{extract::ConnectInfo, extract::Request, routing::get, Router}; -use futures_util::future::poll_fn; -use hyper::server::{ - accept::Accept, - conn::{AddrIncoming, Http}, -}; -use rustls_pemfile::{certs, pkcs8_private_keys}; -use std::{ - fs::File, - io::BufReader, - net::SocketAddr, - path::{Path, PathBuf}, - pin::Pin, - sync::Arc, -}; -use tokio::net::TcpListener; -use tokio_rustls::{ - rustls::{Certificate, PrivateKey, ServerConfig}, - TlsAcceptor, -}; -use tower::make::MakeService; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - -#[tokio::main] -async fn main() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "example_tls_rustls=debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); - - let rustls_config = rustls_server_config( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("self_signed_certs") - .join("key.pem"), - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("self_signed_certs") - .join("cert.pem"), - ); - - let acceptor = TlsAcceptor::from(rustls_config); - - let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); - let mut listener = AddrIncoming::from_listener(listener).unwrap(); - - let protocol = Arc::new(Http::new()); - - let mut app = Router::<()>::new() - .route("/", get(handler)) - .into_make_service_with_connect_info::(); - - loop { - let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx)) - .await - .unwrap() - .unwrap(); - - let acceptor = acceptor.clone(); - - let protocol = protocol.clone(); - - let svc = MakeService::<_, Request>::make_service(&mut app, &stream); - - tokio::spawn(async move { - if let Ok(stream) = acceptor.accept(stream).await { - let _ = protocol.serve_connection(stream, svc.await.unwrap()).await; - } - }); - } +// TODO +fn main() { + eprint!("this example has not yet been updated to hyper 1.0"); } -async fn handler(ConnectInfo(addr): ConnectInfo) -> String { - addr.to_string() -} - -fn rustls_server_config(key: impl AsRef, cert: impl AsRef) -> Arc { - let mut key_reader = BufReader::new(File::open(key).unwrap()); - let mut cert_reader = BufReader::new(File::open(cert).unwrap()); - - let key = PrivateKey(pkcs8_private_keys(&mut key_reader).unwrap().remove(0)); - let certs = certs(&mut cert_reader) - .unwrap() - .into_iter() - .map(Certificate) - .collect(); - - let mut config = ServerConfig::builder() - .with_safe_defaults() - .with_no_client_auth() - .with_single_cert(certs, key) - .expect("bad certificate/key"); - - config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - - Arc::new(config) -} +// use axum::{extract::ConnectInfo, extract::Request, routing::get, Router}; +// use futures_util::future::poll_fn; +// use rustls_pemfile::{certs, pkcs8_private_keys}; +// use std::{ +// fs::File, +// io::BufReader, +// net::SocketAddr, +// path::{Path, PathBuf}, +// pin::Pin, +// sync::Arc, +// }; +// use tokio::net::TcpListener; +// use tokio_rustls::{ +// rustls::{Certificate, PrivateKey, ServerConfig}, +// TlsAcceptor, +// }; +// use tower::make::MakeService; +// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +// #[tokio::main] +// async fn main() { +// tracing_subscriber::registry() +// .with( +// tracing_subscriber::EnvFilter::try_from_default_env() +// .unwrap_or_else(|_| "example_tls_rustls=debug".into()), +// ) +// .with(tracing_subscriber::fmt::layer()) +// .init(); + +// let rustls_config = rustls_server_config( +// PathBuf::from(env!("CARGO_MANIFEST_DIR")) +// .join("self_signed_certs") +// .join("key.pem"), +// PathBuf::from(env!("CARGO_MANIFEST_DIR")) +// .join("self_signed_certs") +// .join("cert.pem"), +// ); + +// let acceptor = TlsAcceptor::from(rustls_config); + +// let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); +// let mut listener = AddrIncoming::from_listener(listener).unwrap(); + +// let protocol = Arc::new(Http::new()); + +// let mut app = Router::<()>::new() +// .route("/", get(handler)) +// .into_make_service_with_connect_info::(); + +// loop { +// let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx)) +// .await +// .unwrap() +// .unwrap(); + +// let acceptor = acceptor.clone(); + +// let protocol = protocol.clone(); + +// let svc = MakeService::<_, Request>::make_service(&mut app, &stream); + +// tokio::spawn(async move { +// if let Ok(stream) = acceptor.accept(stream).await { +// let _ = protocol.serve_connection(stream, svc.await.unwrap()).await; +// } +// }); +// } +// } + +// #[allow(dead_code)] +// async fn handler(ConnectInfo(addr): ConnectInfo) -> String { +// addr.to_string() +// } + +// #[allow(dead_code)] +// fn rustls_server_config(key: impl AsRef, cert: impl AsRef) -> Arc { +// let mut key_reader = BufReader::new(File::open(key).unwrap()); +// let mut cert_reader = BufReader::new(File::open(cert).unwrap()); + +// let key = PrivateKey(pkcs8_private_keys(&mut key_reader).unwrap().remove(0)); +// let certs = certs(&mut cert_reader) +// .unwrap() +// .into_iter() +// .map(Certificate) +// .collect(); + +// let mut config = ServerConfig::builder() +// .with_safe_defaults() +// .with_no_client_auth() +// .with_single_cert(certs, key) +// .expect("bad certificate/key"); + +// config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; + +// Arc::new(config) +// } diff --git a/examples/print-request-response/Cargo.toml b/examples/print-request-response/Cargo.toml index 1b95f35f9e..d709b455bf 100644 --- a/examples/print-request-response/Cargo.toml +++ b/examples/print-request-response/Cargo.toml @@ -6,6 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } +http-body-util = "0.1.0-rc.2" hyper = { version = "1.0.0-rc.3", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util", "filter"] } diff --git a/examples/print-request-response/src/main.rs b/examples/print-request-response/src/main.rs index 1348d026d5..40bf7e142c 100644 --- a/examples/print-request-response/src/main.rs +++ b/examples/print-request-response/src/main.rs @@ -13,6 +13,7 @@ use axum::{ routing::post, Router, }; +use http_body_util::BodyExt; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -58,8 +59,8 @@ where B: axum::body::HttpBody, B::Error: std::fmt::Display, { - let bytes = match hyper::body::to_bytes(body).await { - Ok(bytes) => bytes, + let bytes = match body.collect().await { + Ok(collected) => collected.to_bytes(), Err(err) => { return Err(( StatusCode::BAD_REQUEST, diff --git a/examples/query-params-with-empty-strings/Cargo.toml b/examples/query-params-with-empty-strings/Cargo.toml index 30a6115551..8ff7add921 100644 --- a/examples/query-params-with-empty-strings/Cargo.toml +++ b/examples/query-params-with-empty-strings/Cargo.toml @@ -6,6 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } +http-body-util = "0.1.0-rc.2" hyper = "1.0.0-rc.3" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] } diff --git a/examples/query-params-with-empty-strings/src/main.rs b/examples/query-params-with-empty-strings/src/main.rs index 19117c4eaa..f72b3bf9a0 100644 --- a/examples/query-params-with-empty-strings/src/main.rs +++ b/examples/query-params-with-empty-strings/src/main.rs @@ -58,6 +58,7 @@ where mod tests { use super::*; use axum::{body::Body, http::Request}; + use http_body_util::BodyExt; use tower::ServiceExt; #[tokio::test] @@ -114,7 +115,7 @@ mod tests { .await .unwrap() .into_body(); - let bytes = hyper::body::to_bytes(body).await.unwrap(); + let bytes = body.collect().await.unwrap().to_bytes(); String::from_utf8(bytes.to_vec()).unwrap() } } diff --git a/examples/rest-grpc-multiplex/src/main.rs b/examples/rest-grpc-multiplex/src/main.rs index b0550d80d2..af5318496e 100644 --- a/examples/rest-grpc-multiplex/src/main.rs +++ b/examples/rest-grpc-multiplex/src/main.rs @@ -4,69 +4,74 @@ //! cargo run -p example-rest-grpc-multiplex //! ``` -use self::multiplex_service::MultiplexService; -use axum::{routing::get, Router}; -use proto::{ - greeter_server::{Greeter, GreeterServer}, - HelloReply, HelloRequest, -}; -use std::net::SocketAddr; -use tonic::{Response as TonicResponse, Status}; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +// TODO: updating this example requires updating tonic +fn main() { + eprint!("this example has not yet been updated to hyper 1.0"); +} -mod multiplex_service; +// use self::multiplex_service::MultiplexService; +// use axum::{routing::get, Router}; +// use proto::{ +// greeter_server::{Greeter, GreeterServer}, +// HelloReply, HelloRequest, +// }; +// use std::net::SocketAddr; +// use tonic::{Response as TonicResponse, Status}; +// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; -mod proto { - tonic::include_proto!("helloworld"); -} +// mod multiplex_service; -#[derive(Default)] -struct GrpcServiceImpl {} +// mod proto { +// tonic::include_proto!("helloworld"); +// } -#[tonic::async_trait] -impl Greeter for GrpcServiceImpl { - async fn say_hello( - &self, - request: tonic::Request, - ) -> Result, Status> { - tracing::info!("Got a request from {:?}", request.remote_addr()); +// #[derive(Default)] +// struct GrpcServiceImpl {} - let reply = HelloReply { - message: format!("Hello {}!", request.into_inner().name), - }; +// #[tonic::async_trait] +// impl Greeter for GrpcServiceImpl { +// async fn say_hello( +// &self, +// request: tonic::Request, +// ) -> Result, Status> { +// tracing::info!("Got a request from {:?}", request.remote_addr()); - Ok(TonicResponse::new(reply)) - } -} +// let reply = HelloReply { +// message: format!("Hello {}!", request.into_inner().name), +// }; -async fn web_root() -> &'static str { - "Hello, World!" -} +// Ok(TonicResponse::new(reply)) +// } +// } -#[tokio::main] -async fn main() { - // initialize tracing - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "example_rest_grpc_multiplex=debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); +// async fn web_root() -> &'static str { +// "Hello, World!" +// } - // build the rest service - let rest = Router::new().route("/", get(web_root)); +// #[tokio::main] +// async fn main() { +// // initialize tracing +// tracing_subscriber::registry() +// .with( +// tracing_subscriber::EnvFilter::try_from_default_env() +// .unwrap_or_else(|_| "example_rest_grpc_multiplex=debug".into()), +// ) +// .with(tracing_subscriber::fmt::layer()) +// .init(); - // build the grpc service - let grpc = GreeterServer::new(GrpcServiceImpl::default()); +// // build the rest service +// let rest = Router::new().route("/", get(web_root)); - // combine them into one service - let service = MultiplexService::new(rest, grpc); +// // build the grpc service +// let grpc = GreeterServer::new(GrpcServiceImpl::default()); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - hyper::Server::bind(&addr) - .serve(tower::make::Shared::new(service)) - .await - .unwrap(); -} +// // combine them into one service +// let service = MultiplexService::new(rest, grpc); + +// let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); +// tracing::debug!("listening on {}", addr); +// hyper::Server::bind(&addr) +// .serve(tower::make::Shared::new(service)) +// .await +// .unwrap(); +// } diff --git a/examples/reverse-proxy/src/main.rs b/examples/reverse-proxy/src/main.rs index 875a3a5a6e..3ebc116e7a 100644 --- a/examples/reverse-proxy/src/main.rs +++ b/examples/reverse-proxy/src/main.rs @@ -7,54 +7,59 @@ //! cargo run -p example-reverse-proxy //! ``` -use axum::{ - body::Body, - extract::{Request, State}, - http::uri::Uri, - response::{IntoResponse, Response}, - routing::get, - Router, -}; -use hyper::client::HttpConnector; - -type Client = hyper::client::Client; - -#[tokio::main] -async fn main() { - tokio::spawn(server()); - - let client: Client = hyper::Client::builder().build(HttpConnector::new()); - - let app = Router::new().route("/", get(handler)).with_state(client); - - let listener = tokio::net::TcpListener::bind("127.0.0.1:4000") - .await - .unwrap(); - println!("listening on {}", listener.local_addr().unwrap()); - axum::serve(listener, app).await.unwrap(); +// TODO +fn main() { + eprint!("this example has not yet been updated to hyper 1.0"); } -async fn handler(State(client): State, mut req: Request) -> Response { - let path = req.uri().path(); - let path_query = req - .uri() - .path_and_query() - .map(|v| v.as_str()) - .unwrap_or(path); +// use axum::{ +// body::Body, +// extract::{Request, State}, +// http::uri::Uri, +// response::{IntoResponse, Response}, +// routing::get, +// Router, +// }; +// // use hyper::client::HttpConnector; - let uri = format!("http://127.0.0.1:3000{}", path_query); +// // type Client = hyper::client::Client; - *req.uri_mut() = Uri::try_from(uri).unwrap(); +// #[tokio::main] +// async fn main() { +// tokio::spawn(server()); - client.request(req).await.unwrap().into_response() -} +// let client: Client = hyper::Client::builder().build(HttpConnector::new()); -async fn server() { - let app = Router::new().route("/", get(|| async { "Hello, world!" })); +// let app = Router::new().route("/", get(handler)).with_state(client); - let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") - .await - .unwrap(); - println!("listening on {}", listener.local_addr().unwrap()); - axum::serve(listener, app).await.unwrap(); -} +// let listener = tokio::net::TcpListener::bind("127.0.0.1:4000") +// .await +// .unwrap(); +// println!("listening on {}", listener.local_addr().unwrap()); +// axum::serve(listener, app).await.unwrap(); +// } + +// async fn handler(State(client): State, mut req: Request) -> Response { +// let path = req.uri().path(); +// let path_query = req +// .uri() +// .path_and_query() +// .map(|v| v.as_str()) +// .unwrap_or(path); + +// let uri = format!("http://127.0.0.1:3000{}", path_query); + +// *req.uri_mut() = Uri::try_from(uri).unwrap(); + +// client.request(req).await.unwrap().into_response() +// } + +// async fn server() { +// let app = Router::new().route("/", get(|| async { "Hello, world!" })); + +// let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") +// .await +// .unwrap(); +// println!("listening on {}", listener.local_addr().unwrap()); +// axum::serve(listener, app).await.unwrap(); +// } diff --git a/examples/testing/Cargo.toml b/examples/testing/Cargo.toml index a100516be8..c958e4bffc 100644 --- a/examples/testing/Cargo.toml +++ b/examples/testing/Cargo.toml @@ -6,6 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } +http-body-util = "0.1.0-rc.2" hyper = { version = "1.0.0-rc.3", features = ["full"] } mime = "0.3" serde_json = "1.0" diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 99cc0402cf..9b62a23f6a 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -59,11 +59,12 @@ mod tests { extract::connect_info::MockConnectInfo, http::{self, Request, StatusCode}, }; + use http_body_util::BodyExt; use serde_json::{json, Value}; use std::net::SocketAddr; use tokio::net::TcpListener; use tower::Service; // for `call` - use tower::ServiceExt; // for `oneshot` and `ready` + use tower::ServiceExt; // for `oneshot` and `ready` // for `collect` #[tokio::test] async fn hello_world() { @@ -78,7 +79,7 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + let body = response.into_body().collect().await.unwrap().to_bytes(); assert_eq!(&body[..], b"Hello, World!"); } @@ -102,7 +103,7 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + let body = response.into_body().collect().await.unwrap().to_bytes(); let body: Value = serde_json::from_slice(&body).unwrap(); assert_eq!(body, json!({ "data": [1, 2, 3, 4] })); } @@ -122,34 +123,36 @@ mod tests { .unwrap(); assert_eq!(response.status(), StatusCode::NOT_FOUND); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + let body = response.into_body().collect().await.unwrap().to_bytes(); assert!(body.is_empty()); } // You can also spawn a server and talk to it like any other HTTP server: #[tokio::test] async fn the_real_deal() { - let listener = TcpListener::bind("0.0.0.0:0").await.unwrap(); - let addr = listener.local_addr().unwrap(); + todo!(); - tokio::spawn(async move { - axum::serve(listener, app()).await.unwrap(); - }); + // let listener = TcpListener::bind("0.0.0.0:0").await.unwrap(); + // let addr = listener.local_addr().unwrap(); - let client = hyper::Client::new(); + // tokio::spawn(async move { + // axum::serve(listener, app()).await.unwrap(); + // }); - let response = client - .request( - Request::builder() - .uri(format!("http://{}", addr)) - .body(hyper::Body::empty()) - .unwrap(), - ) - .await - .unwrap(); + // let client = hyper::Client::new(); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - assert_eq!(&body[..], b"Hello, World!"); + // let response = client + // .request( + // Request::builder() + // .uri(format!("http://{}", addr)) + // .body(axum::Body::empty()) + // .unwrap(), + // ) + // .await + // .unwrap(); + + // let body = response.into_body().collect().await.unwrap().to_bytes(); + // assert_eq!(&body[..], b"Hello, World!"); } // You can use `ready()` and `call()` to avoid using `clone()` diff --git a/examples/tls-rustls/src/main.rs b/examples/tls-rustls/src/main.rs index 860f56b5ee..441eefda16 100644 --- a/examples/tls-rustls/src/main.rs +++ b/examples/tls-rustls/src/main.rs @@ -4,6 +4,8 @@ //! cargo run -p example-tls-rustls //! ``` +#![allow(unused_imports)] + use axum::{ extract::Host, handler::HandlerWithoutStateExt, @@ -16,6 +18,7 @@ use axum_server::tls_rustls::RustlsConfig; use std::{net::SocketAddr, path::PathBuf}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +#[allow(dead_code)] #[derive(Clone, Copy)] struct Ports { http: u16, @@ -24,48 +27,52 @@ struct Ports { #[tokio::main] async fn main() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "example_tls_rustls=debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); - - let ports = Ports { - http: 7878, - https: 3000, - }; - // optional: spawn a second server to redirect http requests to this server - tokio::spawn(redirect_http_to_https(ports)); - - // configure certificate and private key used by https - let config = RustlsConfig::from_pem_file( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("self_signed_certs") - .join("cert.pem"), - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("self_signed_certs") - .join("key.pem"), - ) - .await - .unwrap(); - - let app = Router::new().route("/", get(handler)); - - // run https server - let addr = SocketAddr::from(([127, 0, 0, 1], ports.https)); - tracing::debug!("listening on {}", addr); - axum_server::bind_rustls(addr, config) - .serve(app.into_make_service()) - .await - .unwrap(); + // Updating this example to hyper 1.0 requires axum_server to update first + + // tracing_subscriber::registry() + // .with( + // tracing_subscriber::EnvFilter::try_from_default_env() + // .unwrap_or_else(|_| "example_tls_rustls=debug".into()), + // ) + // .with(tracing_subscriber::fmt::layer()) + // .init(); + + // let ports = Ports { + // http: 7878, + // https: 3000, + // }; + // // optional: spawn a second server to redirect http requests to this server + // tokio::spawn(redirect_http_to_https(ports)); + + // // configure certificate and private key used by https + // let config = RustlsConfig::from_pem_file( + // PathBuf::from(env!("CARGO_MANIFEST_DIR")) + // .join("self_signed_certs") + // .join("cert.pem"), + // PathBuf::from(env!("CARGO_MANIFEST_DIR")) + // .join("self_signed_certs") + // .join("key.pem"), + // ) + // .await + // .unwrap(); + + // let app = Router::new().route("/", get(handler)); + + // // run https server + // let addr = SocketAddr::from(([127, 0, 0, 1], ports.https)); + // tracing::debug!("listening on {}", addr); + // axum_server::bind_rustls(addr, config) + + // .await + // .unwrap(); } +#[allow(dead_code)] async fn handler() -> &'static str { "Hello, World!" } +#[allow(dead_code)] async fn redirect_http_to_https(ports: Ports) { fn make_https(host: String, uri: Uri, ports: Ports) -> Result { let mut parts = uri.into_parts(); diff --git a/examples/unix-domain-socket/src/main.rs b/examples/unix-domain-socket/src/main.rs index ce350720c0..188b4b8234 100644 --- a/examples/unix-domain-socket/src/main.rs +++ b/examples/unix-domain-socket/src/main.rs @@ -4,178 +4,183 @@ //! cargo run -p example-unix-domain-socket //! ``` -#[cfg(unix)] -#[tokio::main] -async fn main() { - unix::server().await; -} - -#[cfg(not(unix))] +// TODO fn main() { - println!("This example requires unix") + eprint!("this example has not yet been updated to hyper 1.0"); } -#[cfg(unix)] -mod unix { - use axum::{ - body::Body, - extract::connect_info::{self, ConnectInfo}, - http::{Method, Request, StatusCode, Uri}, - routing::get, - Router, - }; - use futures::ready; - use hyper::{ - client::connect::{Connected, Connection}, - server::accept::Accept, - }; - use std::{ - io, - path::PathBuf, - pin::Pin, - sync::Arc, - task::{Context, Poll}, - }; - use tokio::{ - io::{AsyncRead, AsyncWrite}, - net::{unix::UCred, UnixListener, UnixStream}, - }; - use tower::BoxError; - use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - - pub async fn server() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); - - let path = PathBuf::from("/tmp/axum/helloworld"); - - let _ = tokio::fs::remove_file(&path).await; - tokio::fs::create_dir_all(path.parent().unwrap()) - .await - .unwrap(); - - let uds = UnixListener::bind(path.clone()).unwrap(); - tokio::spawn(async { - let app = Router::new().route("/", get(handler)); - - hyper::Server::builder(ServerAccept { uds }) - .serve(app.into_make_service_with_connect_info::()) - .await - .unwrap(); - }); - - let connector = tower::service_fn(move |_: Uri| { - let path = path.clone(); - Box::pin(async move { - let stream = UnixStream::connect(path).await?; - Ok::<_, io::Error>(ClientConnection { stream }) - }) - }); - let client = hyper::Client::builder().build(connector); - - let request = Request::builder() - .method(Method::GET) - .uri("http://uri-doesnt-matter.com") - .body(Body::empty()) - .unwrap(); - - let response = client.request(request).await.unwrap(); - - assert_eq!(response.status(), StatusCode::OK); - - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let body = String::from_utf8(body.to_vec()).unwrap(); - assert_eq!(body, "Hello, World!"); - } - - async fn handler(ConnectInfo(info): ConnectInfo) -> &'static str { - println!("new connection from `{:?}`", info); - - "Hello, World!" - } - - struct ServerAccept { - uds: UnixListener, - } - - impl Accept for ServerAccept { - type Conn = UnixStream; - type Error = BoxError; - - fn poll_accept( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>> { - let (stream, _addr) = ready!(self.uds.poll_accept(cx))?; - Poll::Ready(Some(Ok(stream))) - } - } - - struct ClientConnection { - stream: UnixStream, - } - - impl AsyncWrite for ClientConnection { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Pin::new(&mut self.stream).poll_write(cx, buf) - } - - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - Pin::new(&mut self.stream).poll_flush(cx) - } - - fn poll_shutdown( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - Pin::new(&mut self.stream).poll_shutdown(cx) - } - } - - impl AsyncRead for ClientConnection { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut tokio::io::ReadBuf<'_>, - ) -> Poll> { - Pin::new(&mut self.stream).poll_read(cx, buf) - } - } - - impl Connection for ClientConnection { - fn connected(&self) -> Connected { - Connected::new() - } - } - - #[derive(Clone, Debug)] - #[allow(dead_code)] - struct UdsConnectInfo { - peer_addr: Arc, - peer_cred: UCred, - } - - impl connect_info::Connected<&UnixStream> for UdsConnectInfo { - fn connect_info(target: &UnixStream) -> Self { - let peer_addr = target.peer_addr().unwrap(); - let peer_cred = target.peer_cred().unwrap(); - - Self { - peer_addr: Arc::new(peer_addr), - peer_cred, - } - } - } -} +// #[cfg(unix)] +// #[tokio::main] +// async fn main() { +// unix::server().await; +// } + +// #[cfg(not(unix))] +// fn main() { +// println!("This example requires unix") +// } + +// #[cfg(unix)] +// mod unix { +// use axum::{ +// body::Body, +// extract::connect_info::{self, ConnectInfo}, +// http::{Method, Request, StatusCode, Uri}, +// routing::get, +// Router, +// }; +// use futures::ready; +// use hyper::{ +// client::connect::{Connected, Connection}, +// server::accept::Accept, +// }; +// use std::{ +// io, +// path::PathBuf, +// pin::Pin, +// sync::Arc, +// task::{Context, Poll}, +// }; +// use tokio::{ +// io::{AsyncRead, AsyncWrite}, +// net::{unix::UCred, UnixListener, UnixStream}, +// }; +// use tower::BoxError; +// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +// pub async fn server() { +// tracing_subscriber::registry() +// .with( +// tracing_subscriber::EnvFilter::try_from_default_env() +// .unwrap_or_else(|_| "debug".into()), +// ) +// .with(tracing_subscriber::fmt::layer()) +// .init(); + +// let path = PathBuf::from("/tmp/axum/helloworld"); + +// let _ = tokio::fs::remove_file(&path).await; +// tokio::fs::create_dir_all(path.parent().unwrap()) +// .await +// .unwrap(); + +// let uds = UnixListener::bind(path.clone()).unwrap(); +// tokio::spawn(async { +// let app = Router::new().route("/", get(handler)); + +// hyper::Server::builder(ServerAccept { uds }) +// .serve(app.into_make_service_with_connect_info::()) +// .await +// .unwrap(); +// }); + +// let connector = tower::service_fn(move |_: Uri| { +// let path = path.clone(); +// Box::pin(async move { +// let stream = UnixStream::connect(path).await?; +// Ok::<_, io::Error>(ClientConnection { stream }) +// }) +// }); +// let client = hyper::Client::builder().build(connector); + +// let request = Request::builder() +// .method(Method::GET) +// .uri("http://uri-doesnt-matter.com") +// .body(Body::empty()) +// .unwrap(); + +// let response = client.request(request).await.unwrap(); + +// assert_eq!(response.status(), StatusCode::OK); + +// let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); +// let body = String::from_utf8(body.to_vec()).unwrap(); +// assert_eq!(body, "Hello, World!"); +// } + +// async fn handler(ConnectInfo(info): ConnectInfo) -> &'static str { +// println!("new connection from `{:?}`", info); + +// "Hello, World!" +// } + +// struct ServerAccept { +// uds: UnixListener, +// } + +// impl Accept for ServerAccept { +// type Conn = UnixStream; +// type Error = BoxError; + +// fn poll_accept( +// self: Pin<&mut Self>, +// cx: &mut Context<'_>, +// ) -> Poll>> { +// let (stream, _addr) = ready!(self.uds.poll_accept(cx))?; +// Poll::Ready(Some(Ok(stream))) +// } +// } + +// struct ClientConnection { +// stream: UnixStream, +// } + +// impl AsyncWrite for ClientConnection { +// fn poll_write( +// mut self: Pin<&mut Self>, +// cx: &mut Context<'_>, +// buf: &[u8], +// ) -> Poll> { +// Pin::new(&mut self.stream).poll_write(cx, buf) +// } + +// fn poll_flush( +// mut self: Pin<&mut Self>, +// cx: &mut Context<'_>, +// ) -> Poll> { +// Pin::new(&mut self.stream).poll_flush(cx) +// } + +// fn poll_shutdown( +// mut self: Pin<&mut Self>, +// cx: &mut Context<'_>, +// ) -> Poll> { +// Pin::new(&mut self.stream).poll_shutdown(cx) +// } +// } + +// impl AsyncRead for ClientConnection { +// fn poll_read( +// mut self: Pin<&mut Self>, +// cx: &mut Context<'_>, +// buf: &mut tokio::io::ReadBuf<'_>, +// ) -> Poll> { +// Pin::new(&mut self.stream).poll_read(cx, buf) +// } +// } + +// impl Connection for ClientConnection { +// fn connected(&self) -> Connected { +// Connected::new() +// } +// } + +// #[derive(Clone, Debug)] +// #[allow(dead_code)] +// struct UdsConnectInfo { +// peer_addr: Arc, +// peer_cred: UCred, +// } + +// impl connect_info::Connected<&UnixStream> for UdsConnectInfo { +// fn connect_info(target: &UnixStream) -> Self { +// let peer_addr = target.peer_addr().unwrap(); +// let peer_cred = target.peer_cred().unwrap(); + +// Self { +// peer_addr: Arc::new(peer_addr), +// peer_cred, +// } +// } +// } +// } From 11a543a4092929e119ee99d3863a8dc07a74a648 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 15:11:30 +0100 Subject: [PATCH 12/57] use tower-http patch --- Cargo.toml | 7 +++++++ axum-core/src/extract/default_body_limit.rs | 6 +++--- axum-core/src/response/into_response.rs | 19 +++++-------------- axum/Cargo.toml | 2 +- axum/src/docs/method_routing/fallback.md | 9 ++------- axum/src/docs/method_routing/merge.md | 4 +--- axum/src/docs/routing/fallback.md | 4 +--- axum/src/docs/routing/merge.md | 4 +--- axum/src/lib.rs | 8 ++------ axum/src/response/redirect.rs | 4 +--- axum/src/response/sse.rs | 4 +--- 11 files changed, 25 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a68aaab16a..f94cfd92b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,10 @@ default-members = ["axum", "axum-*"] # Example has been deleted, but README.md remains exclude = ["examples/async-graphql"] resolver = "2" + +[patch.crates-io] +# https://github.com/tower-rs/tower-http/pull/348 +tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "8734fc4438828" } + +# for `Frame::map_data` +http-body = { git = "https://github.com/hyperium/http-body", rev = "7bf321acbb422" } diff --git a/axum-core/src/extract/default_body_limit.rs b/axum-core/src/extract/default_body_limit.rs index 5a2cd971b5..7ca5446381 100644 --- a/axum-core/src/extract/default_body_limit.rs +++ b/axum-core/src/extract/default_body_limit.rs @@ -46,7 +46,7 @@ use tower_layer::Layer; /// ``` /// use axum::{Router, routing::post, body::Body, extract::Request}; /// use tower_http::limit::RequestBodyLimitLayer; -/// use http_body::Limited; +/// use http_body_util::Limited; /// /// let app = Router::new() /// .route( @@ -102,7 +102,7 @@ impl DefaultBodyLimit { /// extract::DefaultBodyLimit, /// }; /// use tower_http::limit::RequestBodyLimitLayer; - /// use http_body::Limited; + /// use http_body_util::Limited; /// /// let app: Router<()> = Router::new() /// .route("/", get(|body: Bytes| async {})) @@ -137,7 +137,7 @@ impl DefaultBodyLimit { /// extract::DefaultBodyLimit, /// }; /// use tower_http::limit::RequestBodyLimitLayer; - /// use http_body::Limited; + /// use http_body_util::Limited; /// /// let app: Router<()> = Router::new() /// .route("/", get(|body: Bytes| async {})) diff --git a/axum-core/src/response/into_response.rs b/axum-core/src/response/into_response.rs index b4bf8f6f12..6d597068a7 100644 --- a/axum-core/src/response/into_response.rs +++ b/axum-core/src/response/into_response.rs @@ -58,9 +58,7 @@ use std::{ /// async fn handler() -> Result<(), MyError> { /// Err(MyError::SomethingWentWrong) /// } -/// # async { -/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// Or if you have a custom body type you'll also need to implement @@ -76,6 +74,7 @@ use std::{ /// }; /// use http::HeaderMap; /// use bytes::Bytes; +/// use http_body::Frame; /// use std::{ /// convert::Infallible, /// task::{Poll, Context}, @@ -90,18 +89,10 @@ use std::{ /// type Data = Bytes; /// type Error = Infallible; /// -/// fn poll_data( -/// self: Pin<&mut Self>, -/// cx: &mut Context<'_> -/// ) -> Poll>> { -/// # unimplemented!() -/// // ... -/// } -/// -/// fn poll_trailers( +/// fn poll_frame( /// self: Pin<&mut Self>, -/// cx: &mut Context<'_> -/// ) -> Poll, Self::Error>> { +/// cx: &mut Context<'_>, +/// ) -> Poll, Self::Error>>> { /// # unimplemented!() /// // ... /// } diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 5f5c8ed961..1fe66989fd 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -22,7 +22,7 @@ matched-path = [] multipart = ["dep:multer"] original-uri = [] query = ["dep:serde_urlencoded"] -tokio = ["dep:tokio", "hyper/server", "tower/make"] +tokio = ["dep:tokio", "tokio/rt", "tokio/net", "hyper/server", "tower/make"] tower-log = ["tower/log"] ws = ["tokio", "dep:tokio-tungstenite", "dep:sha1", "dep:base64"] diff --git a/axum/src/docs/method_routing/fallback.md b/axum/src/docs/method_routing/fallback.md index 906cbb3b5d..48be18d32c 100644 --- a/axum/src/docs/method_routing/fallback.md +++ b/axum/src/docs/method_routing/fallback.md @@ -18,9 +18,7 @@ let app = Router::new().route("/", handler); async fn fallback(method: Method, uri: Uri) -> (StatusCode, String) { (StatusCode::NOT_FOUND, format!("`{}` not allowed for {}", method, uri)) } -# async { -# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` ## When used with `MethodRouter::merge` @@ -44,10 +42,7 @@ let method_route = one.merge(two); async fn fallback_one() -> impl IntoResponse { /* ... */ } async fn fallback_two() -> impl IntoResponse { /* ... */ } -# let app = axum::Router::new().route("/", method_route); -# async { -# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let app: axum::Router = axum::Router::new().route("/", method_route); ``` ## Setting the `Allow` header diff --git a/axum/src/docs/method_routing/merge.md b/axum/src/docs/method_routing/merge.md index 39d74d048a..a88ee2d7ae 100644 --- a/axum/src/docs/method_routing/merge.md +++ b/axum/src/docs/method_routing/merge.md @@ -19,7 +19,5 @@ let app = Router::new().route("/", merged); // Our app now accepts // - GET / // - POST / -# async { -# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` diff --git a/axum/src/docs/routing/fallback.md b/axum/src/docs/routing/fallback.md index f2b5d3331e..e81d25c19a 100644 --- a/axum/src/docs/routing/fallback.md +++ b/axum/src/docs/routing/fallback.md @@ -18,9 +18,7 @@ let app = Router::new() async fn fallback(uri: Uri) -> (StatusCode, String) { (StatusCode::NOT_FOUND, format!("No route for {}", uri)) } -# async { -# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` Fallbacks only apply to routes that aren't matched by anything in the diff --git a/axum/src/docs/routing/merge.md b/axum/src/docs/routing/merge.md index 0e103c83ca..08136f87ef 100644 --- a/axum/src/docs/routing/merge.md +++ b/axum/src/docs/routing/merge.md @@ -32,9 +32,7 @@ let app = Router::new() // - GET /users // - GET /users/:id // - POST /teams -# async { -# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Merging routers with state diff --git a/axum/src/lib.rs b/axum/src/lib.rs index 9dca510fc1..c64c710739 100644 --- a/axum/src/lib.rs +++ b/axum/src/lib.rs @@ -308,16 +308,12 @@ //! ```toml //! [dependencies] //! axum = "" -//! hyper = { version = "", features = ["full"] } //! tokio = { version = "", features = ["full"] } //! tower = "" //! ``` //! -//! The `"full"` feature for hyper and tokio isn't strictly necessary but it's -//! the easiest way to get started. -//! -//! Note that [`hyper::Server`] is re-exported by axum so if that's all you need -//! then you don't have to explicitly depend on hyper. +//! The `"full"` feature for tokio isn't strictly necessary but it's the easiest way to get +//! started. //! //! Tower isn't strictly necessary either but helpful for testing. See the //! testing example in the repo to learn more about testing axum apps. diff --git a/axum/src/response/redirect.rs b/axum/src/response/redirect.rs index 4dee5b5c82..8bc6eb5e15 100644 --- a/axum/src/response/redirect.rs +++ b/axum/src/response/redirect.rs @@ -15,9 +15,7 @@ use http::{header::LOCATION, HeaderValue, StatusCode}; /// let app = Router::new() /// .route("/old", get(|| async { Redirect::permanent("/new") })) /// .route("/new", get(|| async { "Hello!" })); -/// # async { -/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[must_use = "needs to be returned from a handler or otherwise turned into a Response to be useful"] #[derive(Debug, Clone)] diff --git a/axum/src/response/sse.rs b/axum/src/response/sse.rs index 80403607a3..2fdb8d9ccf 100644 --- a/axum/src/response/sse.rs +++ b/axum/src/response/sse.rs @@ -22,9 +22,7 @@ //! //! Sse::new(stream).keep_alive(KeepAlive::default()) //! } -//! # async { -//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` use crate::{ From 1fd6dbb4e6e8cffd04022100edee7539152f1bb1 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 15:15:21 +0100 Subject: [PATCH 13/57] fix docs --- axum-core/src/extract/default_body_limit.rs | 4 ++-- axum-core/src/extract/rejection.rs | 2 +- axum/src/routing/mod.rs | 3 --- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/axum-core/src/extract/default_body_limit.rs b/axum-core/src/extract/default_body_limit.rs index 7ca5446381..997bcd88b3 100644 --- a/axum-core/src/extract/default_body_limit.rs +++ b/axum-core/src/extract/default_body_limit.rs @@ -8,7 +8,7 @@ use tower_layer::Layer; /// /// This middleware provides ways to configure that. /// -/// Note that if an extractor consumes the body directly with [`Body::data`], or similar, the +/// Note that if an extractor consumes the body directly with [`Body::poll_frame`], or similar, the /// default limit is _not_ applied. /// /// # Difference between `DefaultBodyLimit` and [`RequestBodyLimit`] @@ -63,7 +63,7 @@ use tower_layer::Layer; /// extractors and want to sure a limit is also applied there then [`RequestBodyLimit`] should be /// used. /// -/// [`Body::data`]: http_body::Body::data +/// [`Body::poll_frame`]: http_body::Body::poll_frame /// [`Bytes`]: bytes::Bytes /// [`Json`]: https://docs.rs/axum/0.6.0/axum/struct.Json.html /// [`Form`]: https://docs.rs/axum/0.6.0/axum/struct.Form.html diff --git a/axum-core/src/extract/rejection.rs b/axum-core/src/extract/rejection.rs index e5531c619a..e656fe7124 100644 --- a/axum-core/src/extract/rejection.rs +++ b/axum-core/src/extract/rejection.rs @@ -33,7 +33,7 @@ define_rejection! { /// Encountered some other error when buffering the body. /// /// This can _only_ happen when you're using [`tower_http::limit::RequestBodyLimitLayer`] or - /// otherwise wrapping request bodies in [`http_body::Limited`]. + /// otherwise wrapping request bodies in [`http_body_util::Limited`]. pub struct LengthLimitError(Error); } diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 44c257d4c4..f3d92c1c90 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -573,9 +573,6 @@ impl Router { /// Convert this router into a [`MakeService`], that is a [`Service`] whose /// response is another service. /// - /// This is useful when running your application with hyper's - /// [`Server`](hyper::server::Server): - /// /// ``` /// use axum::{ /// routing::get, From f1c0b118ea8ad660aa147e710789d717585c08fc Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 15:19:17 +0100 Subject: [PATCH 14/57] remove `Limited` from public API --- axum-core/src/ext_traits/request.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/axum-core/src/ext_traits/request.rs b/axum-core/src/ext_traits/request.rs index 0e69f26110..36c34723b2 100644 --- a/axum-core/src/ext_traits/request.rs +++ b/axum-core/src/ext_traits/request.rs @@ -1,7 +1,6 @@ use crate::body::Body; use crate::extract::{DefaultBodyLimitKind, FromRequest, FromRequestParts, Request}; use futures_util::future::BoxFuture; -use http_body_util::Limited; mod sealed { pub trait Sealed {} @@ -257,12 +256,18 @@ pub trait RequestExt: sealed::Sealed + Sized { /// Apply the [default body limit](crate::extract::DefaultBodyLimit). /// /// If it is disabled, return the request as-is in `Err`. - fn with_limited_body(self) -> Result>, Request>; + /// + /// Note that while the `Ok` and `Err` types are the same, [`http_body_util::Limited`] will have + /// been applied in the `Ok` case. + fn with_limited_body(self) -> Result; - /// Consumes the request, returning the body wrapped in [`Limited`] if a + /// Consumes the request, returning the body wrapped in [`http_body_util::Limited`] if a /// [default limit](crate::extract::DefaultBodyLimit) is in place, or not wrapped if the /// default limit is disabled. - fn into_limited_body(self) -> Result, Body>; + /// + /// Note that while the `Ok` and `Err` types are the same, [`http_body_util::Limited`] will have + /// been applied in the `Ok` case. + fn into_limited_body(self) -> Result; } impl RequestExt for Request { @@ -318,7 +323,7 @@ impl RequestExt for Request { }) } - fn with_limited_body(self) -> Result>, Request> { + fn with_limited_body(self) -> Result { // update docs in `axum-core/src/extract/default_body_limit.rs` and // `axum/src/docs/extract.md` if this changes const DEFAULT_LIMIT: usize = 2_097_152; // 2 mb @@ -326,13 +331,13 @@ impl RequestExt for Request { match self.extensions().get::().copied() { Some(DefaultBodyLimitKind::Disable) => Err(self), Some(DefaultBodyLimitKind::Limit(limit)) => { - Ok(self.map(|b| http_body_util::Limited::new(b, limit))) + Ok(self.map(|b| Body::new(http_body_util::Limited::new(b, limit)))) } - None => Ok(self.map(|b| http_body_util::Limited::new(b, DEFAULT_LIMIT))), + None => Ok(self.map(|b| Body::new(http_body_util::Limited::new(b, DEFAULT_LIMIT)))), } } - fn into_limited_body(self) -> Result, Body> { + fn into_limited_body(self) -> Result { self.with_limited_body() .map(Request::into_body) .map_err(Request::into_body) From b1e9930eb689322e0c1dbec2154cc7526387439b Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 15:25:29 +0100 Subject: [PATCH 15/57] serve also requires hyper/http1 --- axum/benches/benches.rs | 2 +- axum/src/lib.rs | 4 ++-- examples/testing/src/main.rs | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/axum/benches/benches.rs b/axum/benches/benches.rs index b969070bbe..4b51a69dce 100644 --- a/axum/benches/benches.rs +++ b/axum/benches/benches.rs @@ -148,7 +148,7 @@ impl BenchmarkBuilder { let addr = listener.local_addr().unwrap(); std::thread::spawn(move || { - rt.block_on(axum::serve(listener, app)); + rt.block_on(axum::serve(listener, app)).unwrap(); }); let mut cmd = Command::new("rewrk"); diff --git a/axum/src/lib.rs b/axum/src/lib.rs index c64c710739..3d732c1900 100644 --- a/axum/src/lib.rs +++ b/axum/src/lib.rs @@ -441,7 +441,7 @@ pub mod handler; pub mod middleware; pub mod response; pub mod routing; -#[cfg(feature = "tokio")] +#[cfg(all(feature = "tokio", feature = "http1"))] pub mod serve; #[cfg(test)] @@ -477,7 +477,7 @@ pub use axum_core::{BoxError, Error, RequestExt, RequestPartsExt}; #[cfg(feature = "macros")] pub use axum_macros::debug_handler; -#[cfg(feature = "tokio")] +#[cfg(all(feature = "tokio", feature = "http1"))] #[doc(inline)] pub use self::serve::serve; diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 9b62a23f6a..d95940cb55 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -62,7 +62,6 @@ mod tests { use http_body_util::BodyExt; use serde_json::{json, Value}; use std::net::SocketAddr; - use tokio::net::TcpListener; use tower::Service; // for `call` use tower::ServiceExt; // for `oneshot` and `ready` // for `collect` From 91bebd8da90cdd982058e5166283fb6b2c874cc8 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 15:31:52 +0100 Subject: [PATCH 16/57] more cfgs --- axum/src/extract/connect_info.rs | 18 ++++++++++++------ axum/src/handler/service.rs | 2 +- axum/src/routing/method_routing.rs | 2 +- axum/src/routing/mod.rs | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/axum/src/extract/connect_info.rs b/axum/src/extract/connect_info.rs index 3b64e5f6f1..d3f735a898 100644 --- a/axum/src/extract/connect_info.rs +++ b/axum/src/extract/connect_info.rs @@ -4,8 +4,9 @@ //! //! [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info +use crate::extension::AddExtension; + use super::{Extension, FromRequestParts}; -use crate::{middleware::AddExtension, serve::IncomingStream}; use async_trait::async_trait; use http::request::Parts; use std::{ @@ -82,11 +83,16 @@ pub trait Connected: Clone + Send + Sync + 'static { fn connect_info(target: T) -> Self; } -impl Connected> for SocketAddr { - fn connect_info(target: IncomingStream<'_>) -> Self { - target.remote_addr() +#[cfg(all(feature = "tokio", feature = "http1"))] +const _: () = { + use crate::serve::IncomingStream; + + impl Connected> for SocketAddr { + fn connect_info(target: IncomingStream<'_>) -> Self { + target.remote_addr() + } } -} +}; impl Service for IntoMakeServiceWithConnectInfo where @@ -210,7 +216,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{routing::get, test_helpers::TestClient, Router}; + use crate::{routing::get, test_helpers::TestClient, Router, serve::IncomingStream}; use std::net::SocketAddr; use tokio::net::TcpListener; diff --git a/axum/src/handler/service.rs b/axum/src/handler/service.rs index 35974a4cda..fd8b8e68bf 100644 --- a/axum/src/handler/service.rs +++ b/axum/src/handler/service.rs @@ -178,7 +178,7 @@ where } // for `axum::serve(listener, handler)` -#[cfg(feature = "tokio")] +#[cfg(all(feature = "tokio", feature = "http1"))] const _: () = { use crate::serve::IncomingStream; diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 8a83d8aaa7..c73fd4963d 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -1232,7 +1232,7 @@ where } // for `axum::serve(listener, router)` -#[cfg(feature = "tokio")] +#[cfg(all(feature = "tokio", feature = "http1"))] const _: () = { use crate::serve::IncomingStream; diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index f3d92c1c90..75d1cefc9e 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -604,7 +604,7 @@ impl Router { } // for `axum::serve(listener, router)` -#[cfg(feature = "tokio")] +#[cfg(all(feature = "tokio", feature = "http1"))] const _: () = { use crate::serve::IncomingStream; From 7dece69cc61d2728b63b1c6b18168750f07a1738 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 15:36:36 +0100 Subject: [PATCH 17/57] handle additional error wrapping --- axum-core/src/extract/rejection.rs | 7 +++++++ axum-extra/src/extract/multipart.rs | 2 +- axum/src/extract/multipart.rs | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/axum-core/src/extract/rejection.rs b/axum-core/src/extract/rejection.rs index e656fe7124..7c52a44967 100644 --- a/axum-core/src/extract/rejection.rs +++ b/axum-core/src/extract/rejection.rs @@ -16,10 +16,17 @@ impl FailedToBufferBody { where E: Into, { + // two layers of boxes where because `with_limited_body` + // wraps the `http_body_util::Limited` in a `axum_core::Body` + // which also wraps the error type let box_error = match err.into().downcast::() { Ok(err) => err.into_inner(), Err(err) => err, }; + let box_error = match box_error.downcast::() { + Ok(err) => err.into_inner(), + Err(err) => err, + }; match box_error.downcast::() { Ok(err) => Self::LengthLimitError(LengthLimitError::from_err(err)), Err(err) => Self::UnknownBodyError(UnknownBodyError::from_err(err)), diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index bf47be256b..fbe63f5ecd 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -100,7 +100,7 @@ where async fn from_request(req: Request, _state: &S) -> Result { let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?; let stream = match req.with_limited_body() { - Ok(limited) => Body::new(limited), + Ok(limited) => limited.into_body(), Err(unlimited) => unlimited.into_body(), }; let multipart = multer::Multipart::new(stream, boundary); diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 7f6332d1bb..ed4312dd9f 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -64,7 +64,7 @@ where async fn from_request(req: Request, _state: &S) -> Result { let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?; let stream = match req.with_limited_body() { - Ok(limited) => Body::new(limited), + Ok(limited) => limited.into_body(), Err(unlimited) => unlimited.into_body(), }; let multipart = multer::Multipart::new(stream, boundary); From 69a413c010f0ec2e3e31dd7121e75c757ed6ffbd Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 15:36:49 +0100 Subject: [PATCH 18/57] don't need this import --- axum/src/extract/multipart.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index ed4312dd9f..09280cf2fc 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -5,7 +5,6 @@ use super::{FromRequest, Request}; use crate::body::Bytes; use async_trait::async_trait; -use axum_core::body::Body; use axum_core::response::{IntoResponse, Response}; use axum_core::RequestExt; use futures_util::stream::Stream; From aa6fe6d91c1c86e8fd922d9d8c82a1bcaa2dd098 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 15:36:56 +0100 Subject: [PATCH 19/57] tower-http's compression middleware which haven't been upgraded yet --- axum/src/routing/tests/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index 6f49101a7e..fb27affcbc 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -593,6 +593,7 @@ async fn head_content_length_through_hyper_server_that_hits_fallback() { } #[crate::test] +#[ignore] async fn head_with_middleware_applied() { use tower_http::compression::{predicate::SizeAbove, CompressionLayer}; From f0348be1cae55a88601ad1a76ef1e362d1380b44 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 15:42:01 +0100 Subject: [PATCH 20/57] format --- axum/src/extract/connect_info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum/src/extract/connect_info.rs b/axum/src/extract/connect_info.rs index d3f735a898..87e2062ffd 100644 --- a/axum/src/extract/connect_info.rs +++ b/axum/src/extract/connect_info.rs @@ -216,7 +216,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{routing::get, test_helpers::TestClient, Router, serve::IncomingStream}; + use crate::{routing::get, serve::IncomingStream, test_helpers::TestClient, Router}; use std::net::SocketAddr; use tokio::net::TcpListener; From 82350cc25082c13f766c522e5d8ca38f0b677dfb Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 24 Mar 2023 15:53:30 +0100 Subject: [PATCH 21/57] convert test to use hyper's low level client --- examples/testing/src/main.rs | 44 +++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index d95940cb55..ecd0d62a17 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -62,6 +62,7 @@ mod tests { use http_body_util::BodyExt; use serde_json::{json, Value}; use std::net::SocketAddr; + use tokio::net::{TcpListener, TcpStream}; use tower::Service; // for `call` use tower::ServiceExt; // for `oneshot` and `ready` // for `collect` @@ -129,29 +130,36 @@ mod tests { // You can also spawn a server and talk to it like any other HTTP server: #[tokio::test] async fn the_real_deal() { - todo!(); + // TODO(david): convert this to hyper-util when thats published - // let listener = TcpListener::bind("0.0.0.0:0").await.unwrap(); - // let addr = listener.local_addr().unwrap(); + use hyper::client::conn; - // tokio::spawn(async move { - // axum::serve(listener, app()).await.unwrap(); - // }); + let listener = TcpListener::bind("0.0.0.0:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); - // let client = hyper::Client::new(); + tokio::spawn(async move { + axum::serve(listener, app()).await.unwrap(); + }); - // let response = client - // .request( - // Request::builder() - // .uri(format!("http://{}", addr)) - // .body(axum::Body::empty()) - // .unwrap(), - // ) - // .await - // .unwrap(); + let target_stream = TcpStream::connect(addr).await.unwrap(); - // let body = response.into_body().collect().await.unwrap().to_bytes(); - // assert_eq!(&body[..], b"Hello, World!"); + let (mut request_sender, connection) = conn::http1::handshake(target_stream).await.unwrap(); + + tokio::spawn(async move { connection.await.unwrap() }); + + let response = request_sender + .send_request( + Request::builder() + .uri(format!("http://{}", addr)) + .header("Host", "localhost") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + let body = response.into_body().collect().await.unwrap().to_bytes(); + assert_eq!(&body[..], b"Hello, World!"); } // You can use `ready()` and `call()` to avoid using `clone()` From a7cbb386aad31c634732d1c998157e7750f3b586 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 15:23:49 +0100 Subject: [PATCH 22/57] update tower-http --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f94cfd92b7..8e91d124d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ resolver = "2" [patch.crates-io] # https://github.com/tower-rs/tower-http/pull/348 -tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "8734fc4438828" } +tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "b7b33bbc40175" } # for `Frame::map_data` http-body = { git = "https://github.com/hyperium/http-body", rev = "7bf321acbb422" } From 2873facfc57d0a404cc8ae527a32b4bcb6871668 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 15:40:48 +0100 Subject: [PATCH 23/57] fixes --- axum-extra/Cargo.toml | 2 +- axum/Cargo.toml | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index 9592109636..a69efbbb12 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -65,7 +65,7 @@ tokio-stream = { version = "0.1.9", optional = true } tokio-util = { version = "0.7", optional = true } [dev-dependencies] -axum = { path = "../axum", version = "0.6.0", features = ["headers"] } +axum = { path = "../axum", version = "0.6.0" } http-body = "1.0.0-rc.2" hyper = "1.0.0-rc.4" reqwest = { version = "0.11", default-features = false, features = ["json", "stream", "multipart"] } diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 4576ad5f5a..de9cd225a9 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -22,7 +22,7 @@ matched-path = [] multipart = ["dep:multer"] original-uri = [] query = ["dep:serde_urlencoded"] -tokio = ["dep:hyper-util", "dep:tokio", "hyper/runtime", "hyper/server", "hyper/tcp", "tokio/net", "tokio/rt", "tower/make"] +tokio = ["dep:hyper-util", "dep:tokio", "tokio/net", "tokio/rt", "tower/make"] tower-log = ["tower/log"] tracing = ["dep:tracing", "axum-core/tracing"] ws = ["tokio", "dep:tokio-tungstenite", "dep:sha1", "dep:base64"] @@ -38,7 +38,7 @@ futures-util = { version = "0.3", default-features = false, features = ["alloc"] http = "0.2.9" http-body = "1.0.0-rc.2" http-body-util = "0.1.0-rc.2" -hyper = "1.0.0-rc.4" +hyper = { package = "hyper", version = "=1.0.0-rc.4", features = ["server", "http1"] } itoa = "1.0.5" matchit = "0.7" memchr = "2.4.1" @@ -52,7 +52,6 @@ tower-layer = "0.3.2" tower-service = "0.3" # wont need this when axum uses http-body 1.0 -hyper1 = { package = "hyper", version = "=1.0.0-rc.4", features = ["server", "http1", "http2"] } tower-hyper-http-body-compat = { version = "0.2", features = ["server", "http1"] } # optional dependencies From 783408992bafdff88a5a19dafc2b4925a76bc897 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 16:10:07 +0100 Subject: [PATCH 24/57] fixes --- Cargo.toml | 2 +- axum-core/Cargo.toml | 6 +- axum-core/src/body.rs | 137 ------------------ axum-core/src/ext_traits/request.rs | 2 +- axum-extra/Cargo.toml | 7 +- axum/Cargo.toml | 7 +- axum/src/extract/connect_info.rs | 1 - axum/src/routing/mod.rs | 72 --------- axum/src/routing/tests/mod.rs | 23 ++- axum/src/serve.rs | 89 ++++++++---- .../Cargo.toml | 4 +- examples/graceful-shutdown/Cargo.toml | 2 +- examples/handle-head-request/Cargo.toml | 4 +- examples/http-proxy/Cargo.toml | 2 +- examples/hyper-1-0/Cargo.toml | 15 -- examples/hyper-1-0/src/main.rs | 53 ------- examples/listen-multiple-addrs/Cargo.toml | 2 +- examples/low-level-openssl/Cargo.toml | 2 +- examples/low-level-rustls/Cargo.toml | 2 +- examples/print-request-response/Cargo.toml | 4 +- .../Cargo.toml | 4 +- examples/rest-grpc-multiplex/Cargo.toml | 2 +- examples/reverse-proxy/Cargo.toml | 2 +- examples/testing-websockets/Cargo.toml | 2 +- examples/testing/Cargo.toml | 4 +- examples/unix-domain-socket/Cargo.toml | 2 +- examples/validator/Cargo.toml | 2 +- 27 files changed, 107 insertions(+), 347 deletions(-) delete mode 100644 examples/hyper-1-0/Cargo.toml delete mode 100644 examples/hyper-1-0/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 8e91d124d6..7bcb2d710c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,4 @@ resolver = "2" tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "b7b33bbc40175" } # for `Frame::map_data` -http-body = { git = "https://github.com/hyperium/http-body", rev = "7bf321acbb422" } +http-body = { git = "https://github.com/hyperium/http-body", rev = "7bf321acbb422214c89933c103417bcfe3892aed" } diff --git a/axum-core/Cargo.toml b/axum-core/Cargo.toml index b43c4e8e73..a750a7c985 100644 --- a/axum-core/Cargo.toml +++ b/axum-core/Cargo.toml @@ -22,8 +22,8 @@ async-trait = "0.1.67" bytes = "1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } http = "0.2.7" -http-body = "1.0.0-rc.2" -http-body-util = "0.1.0-rc.2" +http-body = "=1.0.0-rc.2" +http-body-util = "=0.1.0-rc.3" mime = "0.3.16" pin-project-lite = "0.2.7" sync_wrapper = "0.1.1" @@ -41,7 +41,7 @@ rustversion = "1.0.9" axum = { path = "../axum", version = "0.6.0" } axum-extra = { path = "../axum-extra", features = ["typed-header"] } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -hyper = "1.0.0-rc.4" +hyper = "=1.0.0-rc.4" tokio = { version = "1.25.0", features = ["macros"] } tower-http = { version = "0.4", features = ["limit"] } diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index 2f33b2d355..00a1e98e54 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -163,143 +163,6 @@ where } } -/// The body type used in axum requests and responses. -#[derive(Debug)] -pub struct Body(BoxBody); - -impl Body { - /// Create a new `Body` that wraps another [`http_body::Body`]. - pub fn new(body: B) -> Self - where - B: http_body::Body + Send + 'static, - B::Error: Into, - { - try_downcast(body).unwrap_or_else(|body| Self(boxed(body))) - } - - /// Create an empty body. - pub fn empty() -> Self { - Self::new(http_body::Empty::new()) - } - - /// Create a new `Body` from a [`Stream`]. - /// - /// [`Stream`]: futures_util::stream::Stream - pub 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 = Error; - - #[inline] - fn poll_data( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> std::task::Poll>> { - Pin::new(&mut self.0).poll_data(cx) - } - - #[inline] - fn poll_trailers( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> std::task::Poll, Self::Error>> { - Pin::new(&mut self.0).poll_trailers(cx) - } - - #[inline] - fn size_hint(&self) -> http_body::SizeHint { - self.0.size_hint() - } - - #[inline] - fn is_end_stream(&self) -> bool { - self.0.is_end_stream() - } -} - -impl Stream for Body { - type Item = Result; - - #[inline] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_data(cx) - } -} - -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 = Error; - - 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(Error::new(err)))), - None => Poll::Ready(None), - } - } - - #[inline] - fn poll_trailers( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll, Self::Error>> { - Poll::Ready(Ok(None)) - } -} - #[test] fn test_try_downcast() { assert_eq!(try_downcast::(5_u32), Err(5_u32)); diff --git a/axum-core/src/ext_traits/request.rs b/axum-core/src/ext_traits/request.rs index b1f918ae2c..467267d08d 100644 --- a/axum-core/src/ext_traits/request.rs +++ b/axum-core/src/ext_traits/request.rs @@ -1,7 +1,7 @@ use crate::body::Body; use crate::extract::{DefaultBodyLimitKind, FromRequest, FromRequestParts, Request}; use futures_util::future::BoxFuture; -use http_body::Limited; +use http_body_util::Limited; mod sealed { pub trait Sealed {} diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index a69efbbb12..ec8491d5ed 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -41,8 +41,8 @@ axum-core = { path = "../axum-core", version = "0.3.4" } bytes = "1.1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } http = "0.2" -http-body = "1.0.0-rc.2" -http-body-util = "0.1.0-rc.2" +http-body = "=1.0.0-rc.2" +http-body-util = "=0.1.0-rc.3" mime = "0.3" pin-project-lite = "0.2" serde = "1.0" @@ -66,8 +66,7 @@ tokio-util = { version = "0.7", optional = true } [dev-dependencies] axum = { path = "../axum", version = "0.6.0" } -http-body = "1.0.0-rc.2" -hyper = "1.0.0-rc.4" +hyper = "=1.0.0-rc.4" reqwest = { version = "0.11", default-features = false, features = ["json", "stream", "multipart"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.71" diff --git a/axum/Cargo.toml b/axum/Cargo.toml index de9cd225a9..21635ac1db 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -36,8 +36,8 @@ axum-core = { path = "../axum-core", version = "0.3.4" } bytes = "1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } http = "0.2.9" -http-body = "1.0.0-rc.2" -http-body-util = "0.1.0-rc.2" +http-body = "=1.0.0-rc.2" +http-body-util = "=0.1.0-rc.3" hyper = { package = "hyper", version = "=1.0.0-rc.4", features = ["server", "http1"] } itoa = "1.0.5" matchit = "0.7" @@ -51,9 +51,6 @@ tower = { version = "0.4.13", default-features = false, features = ["util"] } tower-layer = "0.3.2" tower-service = "0.3" -# wont need this when axum uses http-body 1.0 -tower-hyper-http-body-compat = { version = "0.2", features = ["server", "http1"] } - # optional dependencies axum-macros = { path = "../axum-macros", version = "0.3.7", optional = true } base64 = { version = "0.21.0", optional = true } diff --git a/axum/src/extract/connect_info.rs b/axum/src/extract/connect_info.rs index 76dfa82151..be8e78e0e5 100644 --- a/axum/src/extract/connect_info.rs +++ b/axum/src/extract/connect_info.rs @@ -7,7 +7,6 @@ use crate::extension::AddExtension; use super::{Extension, FromRequestParts}; -use crate::{middleware::AddExtension, serve::IncomingStream}; use async_trait::async_trait; use http::request::Parts; use std::{ diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index d84bda134c..3d83de2423 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -393,78 +393,6 @@ where _marker: PhantomData, } } - - /// Convert the router into a borrowed [`Service`] with a fixed request body type, to aid type - /// inference. - /// - /// In some cases when calling methods from [`tower::ServiceExt`] on a [`Router`] you might get - /// type inference errors along the lines of - /// - /// ```not_rust - /// let response = router.ready().await?.call(request).await?; - /// ^^^^^ cannot infer type for type parameter `B` - /// ``` - /// - /// This happens because `Router` implements [`Service`] with `impl Service> for Router<()>`. - /// - /// For example: - /// - /// ```compile_fail - /// use axum::{ - /// Router, - /// routing::get, - /// http::Request, - /// body::Body, - /// }; - /// use tower::{Service, ServiceExt}; - /// - /// # async fn async_main() -> Result<(), Box> { - /// let mut router = Router::new().route("/", get(|| async {})); - /// let request = Request::new(Body::empty()); - /// let response = router.ready().await?.call(request).await?; - /// # Ok(()) - /// # } - /// ``` - /// - /// Calling `Router::as_service` fixes that: - /// - /// ``` - /// use axum::{ - /// Router, - /// routing::get, - /// http::Request, - /// body::Body, - /// }; - /// use tower::{Service, ServiceExt}; - /// - /// # async fn async_main() -> Result<(), Box> { - /// let mut router = Router::new().route("/", get(|| async {})); - /// let request = Request::new(Body::empty()); - /// let response = router.as_service().ready().await?.call(request).await?; - /// # Ok(()) - /// # } - /// ``` - /// - /// This is mainly used when calling `Router` in tests. It shouldn't be necessary when running - /// the `Router` normally via [`Router::into_make_service`]. - pub fn as_service(&mut self) -> RouterAsService<'_, B, S> { - RouterAsService { - router: self, - _marker: PhantomData, - } - } - - /// Convert the router into an owned [`Service`] with a fixed request body type, to aid type - /// inference. - /// - /// This is the same as [`Router::as_service`] instead it returns an owned [`Service`]. See - /// that method for more details. - pub fn into_service(self) -> RouterIntoService { - RouterIntoService { - router: self, - _marker: PhantomData, - } - } } impl Router { diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index f59db5dcf4..7b3f6d0117 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -4,14 +4,25 @@ use crate::{ extract::{self, DefaultBodyLimit, FromRef, Path, State}, handler::{Handler, HandlerWithoutStateExt}, response::{IntoResponse, Response}, - routing::{delete, get, get_service, on, on_service, patch, patch_service, post, MethodFilter}, - test_helpers::*, - BoxError, Json, Router, + routing::{ + delete, get, get_service, on, on_service, patch, patch_service, + path_router::path_for_nested_route, post, MethodFilter, + }, + test_helpers::{ + tracing_helpers::{capture_tracing, TracingEvent}, + *, + }, + BoxError, Extension, Json, Router, }; use axum_core::extract::Request; use futures_util::stream::StreamExt; -use http::{header::ALLOW, header::CONTENT_LENGTH, HeaderMap, StatusCode, Uri}; +use http::{ + header::ALLOW, + header::{CONTENT_LENGTH, HOST}, + HeaderMap, Method, StatusCode, Uri, +}; use http_body_util::BodyExt; +use serde::Deserialize; use serde_json::json; use std::{ convert::Infallible, @@ -1049,7 +1060,7 @@ async fn connect_going_to_custom_fallback() { let res = app.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::NOT_FOUND); - let text = String::from_utf8(hyper::body::to_bytes(res).await.unwrap().to_vec()).unwrap(); + let text = String::from_utf8(res.collect().await.unwrap().to_bytes().to_vec()).unwrap(); assert_eq!(text, "custom fallback"); } @@ -1067,7 +1078,7 @@ async fn connect_going_to_default_fallback() { let res = app.oneshot(req).await.unwrap(); assert_eq!(res.status(), StatusCode::NOT_FOUND); - let body = hyper::body::to_bytes(res).await.unwrap(); + let body = res.collect().await.unwrap().to_bytes(); assert!(body.is_empty()); } diff --git a/axum/src/serve.rs b/axum/src/serve.rs index 034ed2e716..d82fd0843d 100644 --- a/axum/src/serve.rs +++ b/axum/src/serve.rs @@ -1,14 +1,24 @@ //! Serve services. -use std::{convert::Infallible, io, net::SocketAddr}; +use std::{ + convert::Infallible, + future::Future, + io, + net::SocketAddr, + pin::Pin, + task::{Context, Poll}, +}; use axum_core::{body::Body, extract::Request, response::Response}; -use futures_util::{future::poll_fn, FutureExt}; +use futures_util::future::poll_fn; +use hyper::body::Incoming; use hyper_util::{ rt::{TokioExecutor, TokioIo}, server::conn::auto::Builder, }; +use pin_project_lite::pin_project; use tokio::net::{TcpListener, TcpStream}; +use tower::util::{Oneshot, ServiceExt}; use tower_service::Service; /// Serve the service with the supplied listener. @@ -90,7 +100,7 @@ where .await .unwrap_or_else(|err| match err {}); - let service = make_service + let tower_service = make_service .call(IncomingStream { tcp_stream: &tcp_stream, remote_addr, @@ -98,36 +108,14 @@ where .await .unwrap_or_else(|err| match err {}); - let service = hyper::service::service_fn(move |req: Request| { - // doing this saves cloning the service just to await the service being ready - // - // services like `Router` are always ready, so assume the service - // we're running here is also always ready... - match poll_fn(|cx| service.poll_ready(cx)).now_or_never() { - Some(Ok(())) => {} - Some(Err(err)) => match err {}, - None => { - // ...otherwise load shed - let mut res = Response::new(Body::empty()); - *res.status_mut() = http::StatusCode::SERVICE_UNAVAILABLE; - return std::future::ready(Ok(res)).left_future(); - } - } - - let future = service.call(req.map(Body::new)); - - async move { - let response = future.await.unwrap_or_else(|err| match err {}); - - Ok::<_, Infallible>(response) - } - .right_future() - }); + let hyper_service = TowerToHyperService { + service: tower_service, + }; tokio::task::spawn(async move { match Builder::new(TokioExecutor::new()) // upgrades needed for websockets - .serve_connection_with_upgrades(tcp_stream.into_inner(), service) + .serve_connection_with_upgrades(tcp_stream.into_inner(), hyper_service) .await { Ok(()) => {} @@ -143,6 +131,49 @@ where } } +#[derive(Debug, Copy, Clone)] +struct TowerToHyperService { + service: S, +} + +impl hyper::service::Service> for TowerToHyperService +where + S: tower_service::Service + Clone, +{ + type Response = S::Response; + type Error = S::Error; + type Future = TowerToHyperServiceFuture; + + fn call(&self, req: Request) -> Self::Future { + let req = req.map(Body::new); + TowerToHyperServiceFuture { + future: self.service.clone().oneshot(req), + } + } +} + +pin_project! { + struct TowerToHyperServiceFuture + where + S: tower_service::Service, + { + #[pin] + future: Oneshot, + } +} + +impl Future for TowerToHyperServiceFuture +where + S: tower_service::Service, +{ + type Output = Result; + + #[inline] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.project().future.poll(cx) + } +} + /// An incoming stream. /// /// Used with [`serve`] and [`IntoMakeServiceWithConnectInfo`]. diff --git a/examples/consume-body-in-extractor-or-middleware/Cargo.toml b/examples/consume-body-in-extractor-or-middleware/Cargo.toml index 1b277312a6..6a79882e41 100644 --- a/examples/consume-body-in-extractor-or-middleware/Cargo.toml +++ b/examples/consume-body-in-extractor-or-middleware/Cargo.toml @@ -6,8 +6,8 @@ publish = false [dependencies] axum = { path = "../../axum" } -http-body-util = "0.1.0-rc.2" -hyper = "1.0.0-rc.4" +http-body-util = "=0.1.0-rc.3" +hyper = "=1.0.0-rc.4" tokio = { version = "1.0", features = ["full"] } tower = "0.4" tower-http = { version = "0.4.0", features = ["map-request-body", "util"] } diff --git a/examples/graceful-shutdown/Cargo.toml b/examples/graceful-shutdown/Cargo.toml index 0481cd6c71..0e29710ddf 100644 --- a/examples/graceful-shutdown/Cargo.toml +++ b/examples/graceful-shutdown/Cargo.toml @@ -6,5 +6,5 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "1.0.0-rc.4", features = ["full"] } +hyper = { version = "=1.0.0-rc.4", features = ["full"] } tokio = { version = "1.0", features = ["full"] } diff --git a/examples/handle-head-request/Cargo.toml b/examples/handle-head-request/Cargo.toml index c52cb35e86..4740908b36 100644 --- a/examples/handle-head-request/Cargo.toml +++ b/examples/handle-head-request/Cargo.toml @@ -9,6 +9,6 @@ axum = { path = "../../axum" } tokio = { version = "1.0", features = ["full"] } [dev-dependencies] -http-body-util = "0.1.0-rc.2" -hyper = { version = "1.0.0-rc.4", features = ["full"] } +http-body-util = "=0.1.0-rc.3" +hyper = { version = "=1.0.0-rc.4", features = ["full"] } tower = { version = "0.4", features = ["util"] } diff --git a/examples/http-proxy/Cargo.toml b/examples/http-proxy/Cargo.toml index e7ec6ea2c9..90db640d55 100644 --- a/examples/http-proxy/Cargo.toml +++ b/examples/http-proxy/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "1.0.0-rc.4", features = ["full"] } +hyper = { version = "=1.0.0-rc.4", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["make"] } tracing = "0.1" diff --git a/examples/hyper-1-0/Cargo.toml b/examples/hyper-1-0/Cargo.toml deleted file mode 100644 index f55e532a60..0000000000 --- a/examples/hyper-1-0/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "example-hyper-1-0" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -axum = { path = "../../axum" } -hyper = { version = "=1.0.0-rc.4", features = ["full"] } -hyper-util = { git = "https://github.com/hyperium/hyper-util", rev = "f898015", features = ["full"] } -tokio = { version = "1.0", features = ["full"] } -tower-http = { version = "0.4", features = ["trace"] } -tower-hyper-http-body-compat = { version = "0.2", features = ["http1", "server"] } -tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/hyper-1-0/src/main.rs b/examples/hyper-1-0/src/main.rs deleted file mode 100644 index 06216b5765..0000000000 --- a/examples/hyper-1-0/src/main.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! Run with -//! -//! ```not_rust -//! cargo run -p example-hyper-1-0 -//! ``` - -use axum::{routing::get, Router}; -use std::net::SocketAddr; -use tokio::net::TcpListener; -use tower_http::trace::TraceLayer; -use tower_hyper_http_body_compat::TowerService03HttpServiceAsHyper1HttpService; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - -// this is hyper 1.0 -use hyper::server::conn::http1; - -#[tokio::main] -async fn main() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "example_hyper_1_0=debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); - - let app = Router::new() - .route("/", get(|| async { "Hello, World!" })) - // we can still add regular tower middleware - .layer(TraceLayer::new_for_http()); - - // `Router` implements tower-service 0.3's `Service` trait. Convert that to something - // that implements hyper 1.0's `Service` trait. - let service = TowerService03HttpServiceAsHyper1HttpService::new(app); - - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - let tcp_listener = TcpListener::bind(addr).await.unwrap(); - tracing::debug!("listening on {addr}"); - loop { - let (tcp_stream, _) = tcp_listener.accept().await.unwrap(); - let tcp_stream = hyper_util::rt::TokioIo::new(tcp_stream); - let service = service.clone(); - tokio::task::spawn(async move { - if let Err(http_err) = http1::Builder::new() - .keep_alive(true) - .serve_connection(tcp_stream, service) - .await - { - eprintln!("Error while serving HTTP connection: {http_err}"); - } - }); - } -} diff --git a/examples/listen-multiple-addrs/Cargo.toml b/examples/listen-multiple-addrs/Cargo.toml index 5c2d2bd059..2009ad1463 100644 --- a/examples/listen-multiple-addrs/Cargo.toml +++ b/examples/listen-multiple-addrs/Cargo.toml @@ -6,5 +6,5 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "1.0.0-rc.4", features = ["full"] } +hyper = { version = "=1.0.0-rc.4", features = ["full"] } tokio = { version = "1", features = ["full"] } diff --git a/examples/low-level-openssl/Cargo.toml b/examples/low-level-openssl/Cargo.toml index 495cc0b879..f217c90833 100644 --- a/examples/low-level-openssl/Cargo.toml +++ b/examples/low-level-openssl/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -hyper = { version = "1.0.0-rc.4", features = ["full"] } +hyper = { version = "=1.0.0-rc.4", features = ["full"] } openssl = "0.10" tokio = { version = "1", features = ["full"] } tokio-openssl = "0.6" diff --git a/examples/low-level-rustls/Cargo.toml b/examples/low-level-rustls/Cargo.toml index 8c6087df9a..cde06d8c6f 100644 --- a/examples/low-level-rustls/Cargo.toml +++ b/examples/low-level-rustls/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -hyper = { version = "1.0.0-rc.4", features = ["full"] } +hyper = { version = "=1.0.0-rc.4", features = ["full"] } rustls-pemfile = "0.3" tokio = { version = "1", features = ["full"] } tokio-rustls = "0.23" diff --git a/examples/print-request-response/Cargo.toml b/examples/print-request-response/Cargo.toml index 1b0037357c..40b460bb40 100644 --- a/examples/print-request-response/Cargo.toml +++ b/examples/print-request-response/Cargo.toml @@ -6,8 +6,8 @@ publish = false [dependencies] axum = { path = "../../axum" } -http-body-util = "0.1.0-rc.2" -hyper = { version = "1.0.0-rc.4", features = ["full"] } +http-body-util = "=0.1.0-rc.3" +hyper = { version = "=1.0.0-rc.4", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util", "filter"] } tracing = "0.1" diff --git a/examples/query-params-with-empty-strings/Cargo.toml b/examples/query-params-with-empty-strings/Cargo.toml index 9641ab78b3..34955d5c76 100644 --- a/examples/query-params-with-empty-strings/Cargo.toml +++ b/examples/query-params-with-empty-strings/Cargo.toml @@ -6,8 +6,8 @@ publish = false [dependencies] axum = { path = "../../axum" } -http-body-util = "0.1.0-rc.2" -hyper = "1.0.0-rc.4" +http-body-util = "=0.1.0-rc.3" +hyper = "=1.0.0-rc.4" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util"] } diff --git a/examples/rest-grpc-multiplex/Cargo.toml b/examples/rest-grpc-multiplex/Cargo.toml index 04ee51f5c6..7165005e9e 100644 --- a/examples/rest-grpc-multiplex/Cargo.toml +++ b/examples/rest-grpc-multiplex/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures = "0.3" -hyper = { version = "1.0.0-rc.4", features = ["full"] } +hyper = { version = "=1.0.0-rc.4", features = ["full"] } prost = "0.11" tokio = { version = "1", features = ["full"] } tonic = { version = "0.9" } diff --git a/examples/reverse-proxy/Cargo.toml b/examples/reverse-proxy/Cargo.toml index 1e4a3ede67..ddd421a8d6 100644 --- a/examples/reverse-proxy/Cargo.toml +++ b/examples/reverse-proxy/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] axum = { path = "../../axum" } -hyper = { version = "1.0.0-rc.4", features = ["full"] } +hyper = { version = "=1.0.0-rc.4", features = ["full"] } tokio = { version = "1", features = ["full"] } diff --git a/examples/testing-websockets/Cargo.toml b/examples/testing-websockets/Cargo.toml index 0973322269..20787cf373 100644 --- a/examples/testing-websockets/Cargo.toml +++ b/examples/testing-websockets/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] axum = { path = "../../axum", features = ["ws"] } futures = "0.3" -hyper = { version = "1.0.0-rc.4", features = ["full"] } +hyper = { version = "=1.0.0-rc.4", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tokio-tungstenite = "0.20" diff --git a/examples/testing/Cargo.toml b/examples/testing/Cargo.toml index 6a824685fe..209ca248b7 100644 --- a/examples/testing/Cargo.toml +++ b/examples/testing/Cargo.toml @@ -6,8 +6,8 @@ publish = false [dependencies] axum = { path = "../../axum" } -http-body-util = "0.1.0-rc.2" -hyper = { version = "1.0.0-rc.4", features = ["full"] } +http-body-util = "=0.1.0-rc.3" +hyper = { version = "=1.0.0-rc.4", features = ["full"] } mime = "0.3" serde_json = "1.0" tokio = { version = "1.0", features = ["full"] } diff --git a/examples/unix-domain-socket/Cargo.toml b/examples/unix-domain-socket/Cargo.toml index e287871dfa..52300a2252 100644 --- a/examples/unix-domain-socket/Cargo.toml +++ b/examples/unix-domain-socket/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures = "0.3" -hyper = { version = "1.0.0-rc.4", features = ["full"] } +hyper = { version = "=1.0.0-rc.4", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util"] } tracing = "0.1" diff --git a/examples/validator/Cargo.toml b/examples/validator/Cargo.toml index bb7125a743..804c408775 100644 --- a/examples/validator/Cargo.toml +++ b/examples/validator/Cargo.toml @@ -7,7 +7,7 @@ version = "0.1.0" [dependencies] async-trait = "0.1.67" axum = { path = "../../axum" } -http-body = "1.0.0-rc.2" +http-body = "=1.0.0-rc.2" serde = { version = "1.0", features = ["derive"] } thiserror = "1.0.29" tokio = { version = "1.0", features = ["full"] } From a5ff5d8c4a455522b444126dcdcaba77e16b4347 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 16:15:36 +0100 Subject: [PATCH 25/57] use latest version of tower-http --- Cargo.toml | 2 +- axum-core/Cargo.toml | 4 ++-- axum-extra/Cargo.toml | 2 +- axum/Cargo.toml | 4 ++-- examples/consume-body-in-extractor-or-middleware/Cargo.toml | 2 +- examples/cors/Cargo.toml | 2 +- examples/key-value-store/Cargo.toml | 2 +- examples/multipart-form/Cargo.toml | 2 +- examples/reqwest-response/Cargo.toml | 2 +- examples/sse/Cargo.toml | 2 +- examples/static-file-server/Cargo.toml | 2 +- examples/testing/Cargo.toml | 2 +- examples/todos/Cargo.toml | 2 +- examples/tracing-aka-logging/Cargo.toml | 2 +- examples/websockets/Cargo.toml | 2 +- 15 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7bcb2d710c..64eaf1df23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ resolver = "2" [patch.crates-io] # https://github.com/tower-rs/tower-http/pull/348 -tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "b7b33bbc40175" } +tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "58070136720b6" } # for `Frame::map_data` http-body = { git = "https://github.com/hyperium/http-body", rev = "7bf321acbb422214c89933c103417bcfe3892aed" } diff --git a/axum-core/Cargo.toml b/axum-core/Cargo.toml index a750a7c985..b29395f201 100644 --- a/axum-core/Cargo.toml +++ b/axum-core/Cargo.toml @@ -31,7 +31,7 @@ tower-layer = "0.3" tower-service = "0.3" # optional dependencies -tower-http = { version = "0.4", optional = true, features = ["limit"] } +tower-http = { version = "0.4.4", optional = true, features = ["limit"] } tracing = { version = "0.1.37", default-features = false, optional = true } [build-dependencies] @@ -43,7 +43,7 @@ axum-extra = { path = "../axum-extra", features = ["typed-header"] } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } hyper = "=1.0.0-rc.4" tokio = { version = "1.25.0", features = ["macros"] } -tower-http = { version = "0.4", features = ["limit"] } +tower-http = { version = "0.4.4", features = ["limit"] } [package.metadata.cargo-public-api-crates] allowed = [ diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index ec8491d5ed..44c9ab1043 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -72,7 +72,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.71" tokio = { version = "1.14", features = ["full"] } tower = { version = "0.4", features = ["util"] } -tower-http = { version = "0.4", features = ["map-response-body", "timeout"] } +tower-http = { version = "0.4.4", features = ["map-response-body", "timeout"] } [package.metadata.docs.rs] all-features = true diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 21635ac1db..7afeccc691 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -65,7 +65,7 @@ tokio-tungstenite = { version = "0.20", optional = true } tracing = { version = "0.1", default-features = false, optional = true } [dependencies.tower-http] -version = "0.4" +version = "0.4.4" optional = true features = [ # all tower-http features except (de)?compression-zstd which doesn't @@ -135,7 +135,7 @@ features = [ ] [dev-dependencies.tower-http] -version = "0.4" +version = "0.4.4" features = [ # all tower-http features except (de)?compression-zstd which doesn't # build on `--target armv5te-unknown-linux-musleabi` diff --git a/examples/consume-body-in-extractor-or-middleware/Cargo.toml b/examples/consume-body-in-extractor-or-middleware/Cargo.toml index 6a79882e41..3ba84e366a 100644 --- a/examples/consume-body-in-extractor-or-middleware/Cargo.toml +++ b/examples/consume-body-in-extractor-or-middleware/Cargo.toml @@ -10,6 +10,6 @@ http-body-util = "=0.1.0-rc.3" hyper = "=1.0.0-rc.4" tokio = { version = "1.0", features = ["full"] } tower = "0.4" -tower-http = { version = "0.4.0", features = ["map-request-body", "util"] } +tower-http = { version = "0.4.4", features = ["map-request-body", "util"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/cors/Cargo.toml b/examples/cors/Cargo.toml index fa44a53cf1..b56f3a9410 100644 --- a/examples/cors/Cargo.toml +++ b/examples/cors/Cargo.toml @@ -7,4 +7,4 @@ publish = false [dependencies] axum = { path = "../../axum" } tokio = { version = "1.0", features = ["full"] } -tower-http = { version = "0.4.0", features = ["cors"] } +tower-http = { version = "0.4.4", features = ["cors"] } diff --git a/examples/key-value-store/Cargo.toml b/examples/key-value-store/Cargo.toml index e6d21cb754..4366e3c2fc 100644 --- a/examples/key-value-store/Cargo.toml +++ b/examples/key-value-store/Cargo.toml @@ -8,7 +8,7 @@ publish = false axum = { path = "../../axum" } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util", "timeout", "load-shed", "limit"] } -tower-http = { version = "0.4.0", features = [ +tower-http = { version = "0.4.4", features = [ "add-extension", "auth", "compression-full", diff --git a/examples/multipart-form/Cargo.toml b/examples/multipart-form/Cargo.toml index a81574a0e9..80d9519663 100644 --- a/examples/multipart-form/Cargo.toml +++ b/examples/multipart-form/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] axum = { path = "../../axum", features = ["multipart"] } tokio = { version = "1.0", features = ["full"] } -tower-http = { version = "0.4.0", features = ["limit", "trace"] } +tower-http = { version = "0.4.4", features = ["limit", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/reqwest-response/Cargo.toml b/examples/reqwest-response/Cargo.toml index 9f47fd63b0..8be5ce0ea2 100644 --- a/examples/reqwest-response/Cargo.toml +++ b/examples/reqwest-response/Cargo.toml @@ -9,6 +9,6 @@ axum = { path = "../../axum" } reqwest = { version = "0.11", features = ["stream"] } tokio = { version = "1.0", features = ["full"] } tokio-stream = "0.1" -tower-http = { version = "0.4", features = ["trace"] } +tower-http = { version = "0.4.4", features = ["trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/sse/Cargo.toml b/examples/sse/Cargo.toml index 0131991d8a..3976ecd45b 100644 --- a/examples/sse/Cargo.toml +++ b/examples/sse/Cargo.toml @@ -11,6 +11,6 @@ futures = "0.3" headers = "0.3" tokio = { version = "1.0", features = ["full"] } tokio-stream = "0.1" -tower-http = { version = "0.4.0", features = ["fs", "trace"] } +tower-http = { version = "0.4.4", features = ["fs", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/static-file-server/Cargo.toml b/examples/static-file-server/Cargo.toml index 7762350aa6..b9e31b7de8 100644 --- a/examples/static-file-server/Cargo.toml +++ b/examples/static-file-server/Cargo.toml @@ -9,6 +9,6 @@ axum = { path = "../../axum" } axum-extra = { path = "../../axum-extra" } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util"] } -tower-http = { version = "0.4.0", features = ["fs", "trace"] } +tower-http = { version = "0.4.4", features = ["fs", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/testing/Cargo.toml b/examples/testing/Cargo.toml index 209ca248b7..5bb642b5fc 100644 --- a/examples/testing/Cargo.toml +++ b/examples/testing/Cargo.toml @@ -11,7 +11,7 @@ hyper = { version = "=1.0.0-rc.4", features = ["full"] } mime = "0.3" serde_json = "1.0" tokio = { version = "1.0", features = ["full"] } -tower-http = { version = "0.4.0", features = ["trace"] } +tower-http = { version = "0.4.4", features = ["trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml index 970ccfe107..af3cc56452 100644 --- a/examples/todos/Cargo.toml +++ b/examples/todos/Cargo.toml @@ -9,7 +9,7 @@ axum = { path = "../../axum" } serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util", "timeout"] } -tower-http = { version = "0.4.0", features = ["add-extension", "trace"] } +tower-http = { version = "0.4.4", features = ["add-extension", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } uuid = { version = "1.0", features = ["serde", "v4"] } diff --git a/examples/tracing-aka-logging/Cargo.toml b/examples/tracing-aka-logging/Cargo.toml index db5e2517cd..fb50adafeb 100644 --- a/examples/tracing-aka-logging/Cargo.toml +++ b/examples/tracing-aka-logging/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] axum = { path = "../../axum", features = ["tracing"] } tokio = { version = "1.0", features = ["full"] } -tower-http = { version = "0.4.0", features = ["trace"] } +tower-http = { version = "0.4.4", features = ["trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/websockets/Cargo.toml b/examples/websockets/Cargo.toml index 3fe2d355c0..24f343dc3a 100644 --- a/examples/websockets/Cargo.toml +++ b/examples/websockets/Cargo.toml @@ -13,7 +13,7 @@ headers = "0.3" tokio = { version = "1.0", features = ["full"] } tokio-tungstenite = "0.20" tower = { version = "0.4", features = ["util"] } -tower-http = { version = "0.4.0", features = ["fs", "trace"] } +tower-http = { version = "0.4.4", features = ["fs", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } From 1c2cfe727ce38e2b90bf319f3ccdc9312b25a35a Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 10 Nov 2023 16:35:38 +0100 Subject: [PATCH 26/57] fix more things --- axum-core/src/ext_traits/request.rs | 1 - axum-extra/src/body/async_read_body.rs | 1 - axum/Cargo.toml | 26 +- axum/src/boxed.rs | 14 - axum/src/extract/multipart.rs | 1 - axum/src/handler/mod.rs | 6 +- axum/src/routing/mod.rs | 2 - axum/src/routing/tests/mod.rs | 82 ++-- .../src/main.rs | 1 - examples/key-value-store/Cargo.toml | 2 +- examples/key-value-store/src/main.rs | 22 +- examples/oauth/src/main.rs | 5 - examples/testing/src/main.rs | 402 +++++++++--------- examples/tls-graceful-shutdown/src/main.rs | 266 ++++++------ 14 files changed, 404 insertions(+), 427 deletions(-) diff --git a/axum-core/src/ext_traits/request.rs b/axum-core/src/ext_traits/request.rs index 467267d08d..f664f0c3ec 100644 --- a/axum-core/src/ext_traits/request.rs +++ b/axum-core/src/ext_traits/request.rs @@ -1,7 +1,6 @@ use crate::body::Body; use crate::extract::{DefaultBodyLimitKind, FromRequest, FromRequestParts, Request}; use futures_util::future::BoxFuture; -use http_body_util::Limited; mod sealed { pub trait Sealed {} diff --git a/axum-extra/src/body/async_read_body.rs b/axum-extra/src/body/async_read_body.rs index 1ef9360ce4..ec273b0549 100644 --- a/axum-extra/src/body/async_read_body.rs +++ b/axum-extra/src/body/async_read_body.rs @@ -1,6 +1,5 @@ use axum::{ body::{Body, Bytes, HttpBody}, - http::HeaderMap, response::{IntoResponse, Response}, Error, }; diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 7afeccc691..dba33ac289 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -73,13 +73,14 @@ features = [ "add-extension", "auth", "catch-panic", - "compression-br", - "compression-deflate", - "compression-gzip", + # we can bring these features back when they're implemented on https://github.com/tower-rs/tower-http/pull/348 + # "compression-br", + # "compression-deflate", + # "compression-gzip", "cors", - "decompression-br", - "decompression-deflate", - "decompression-gzip", + # "decompression-br", + # "decompression-deflate", + # "decompression-gzip", "follow-redirect", "fs", "limit", @@ -142,13 +143,14 @@ features = [ "add-extension", "auth", "catch-panic", - "compression-br", - "compression-deflate", - "compression-gzip", + # we can bring these features back when they're implemented on https://github.com/tower-rs/tower-http/pull/348 + # "compression-br", + # "compression-deflate", + # "compression-gzip", "cors", - "decompression-br", - "decompression-deflate", - "decompression-gzip", + # "decompression-br", + # "decompression-deflate", + # "decompression-gzip", "follow-redirect", "fs", "limit", diff --git a/axum/src/boxed.rs b/axum/src/boxed.rs index 92ab1c54c6..5473b2491b 100644 --- a/axum/src/boxed.rs +++ b/axum/src/boxed.rs @@ -25,20 +25,6 @@ where into_route: |handler, state| Route::new(Handler::with_state(handler, state)), })) } - - pub(crate) fn from_router(router: Router) -> Self - where - S: Clone + Send + Sync + 'static, - { - Self(Box::new(MakeErasedRouter { - router, - into_route: |router, state| Route::new(router.with_state(state)), - })) - } - - pub(crate) fn call_with_state(self, request: Request, state: S) -> RouteFuture { - self.0.call_with_state(request, state) - } } impl BoxedIntoRoute { diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 758f4cbe47..54dee8f02a 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -7,7 +7,6 @@ use crate::body::Bytes; use async_trait::async_trait; use axum_core::__composite_rejection as composite_rejection; use axum_core::__define_rejection as define_rejection; -use axum_core::body::Body; use axum_core::response::{IntoResponse, Response}; use axum_core::RequestExt; use futures_util::stream::Stream; diff --git a/axum/src/handler/mod.rs b/axum/src/handler/mod.rs index a75c204641..41596dd001 100644 --- a/axum/src/handler/mod.rs +++ b/axum/src/handler/mod.rs @@ -391,9 +391,8 @@ mod tests { use http::StatusCode; use std::time::Duration; use tower_http::{ - compression::CompressionLayer, limit::RequestBodyLimitLayer, - map_request_body::MapRequestBodyLayer, map_response_body::MapResponseBodyLayer, - timeout::TimeoutLayer, + limit::RequestBodyLimitLayer, map_request_body::MapRequestBodyLayer, + map_response_body::MapResponseBodyLayer, timeout::TimeoutLayer, }; #[crate::test] @@ -420,7 +419,6 @@ mod tests { RequestBodyLimitLayer::new(1024), TimeoutLayer::new(Duration::from_secs(10)), MapResponseBodyLayer::new(Body::new), - CompressionLayer::new(), )) .layer(MapRequestBodyLayer::new(Body::new)) .with_state("foo"); diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 3d83de2423..6dc6c36688 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -13,12 +13,10 @@ use axum_core::{ extract::Request, response::{IntoResponse, Response}, }; -use matchit::MatchError; use std::{ convert::Infallible, fmt, marker::PhantomData, - sync::Arc, task::{Context, Poll}, }; use tower_layer::Layer; diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index 7b3f6d0117..1e7dcf6bf6 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -120,7 +120,7 @@ async fn router_type_doesnt_change() { on(MethodFilter::GET, |_: Request| async { "hi from GET" }) .on(MethodFilter::POST, |_: Request| async { "hi from POST" }), ) - .layer(tower_http::compression::CompressionLayer::new()); + .layer(tower_http::trace::TraceLayer::new_for_http()); let client = TestClient::new(app); @@ -181,16 +181,13 @@ async fn routing_between_services() { #[crate::test] async fn middleware_on_single_route() { - use tower_http::{compression::CompressionLayer, trace::TraceLayer}; + use tower_http::trace::TraceLayer; async fn handle(_: Request) -> &'static str { "Hello, World!" } - let app = Router::new().route( - "/", - get(handle.layer((TraceLayer::new_for_http(), CompressionLayer::new()))), - ); + let app = Router::new().route("/", get(handle.layer(TraceLayer::new_for_http()))); let client = TestClient::new(app); @@ -594,42 +591,43 @@ async fn head_content_length_through_hyper_server_that_hits_fallback() { assert_eq!(res.headers()["content-length"], "3"); } -#[crate::test] -#[ignore] -async fn head_with_middleware_applied() { - use tower_http::compression::{predicate::SizeAbove, CompressionLayer}; - - let app = Router::new() - .nest( - "/", - Router::new().route("/", get(|| async { "Hello, World!" })), - ) - .layer(CompressionLayer::new().compress_when(SizeAbove::new(0))); - - let client = TestClient::new(app); - - // send GET request - let res = client - .get("/") - .header("accept-encoding", "gzip") - .send() - .await; - assert_eq!(res.headers()["transfer-encoding"], "chunked"); - // cannot have `transfer-encoding: chunked` and `content-length` - assert!(!res.headers().contains_key("content-length")); - - // send HEAD request - let res = client - .head("/") - .header("accept-encoding", "gzip") - .send() - .await; - // no response body so no `transfer-encoding` - assert!(!res.headers().contains_key("transfer-encoding")); - // no content-length since we cannot know it since the response - // is compressed - assert!(!res.headers().contains_key("content-length")); -} +// TODO(david): bring this back when https://github.com/tower-rs/tower-http/pull/348 supports compression +// #[crate::test] +// #[ignore] +// async fn head_with_middleware_applied() { +// use tower_http::compression::{predicate::SizeAbove, CompressionLayer}; + +// let app = Router::new() +// .nest( +// "/", +// Router::new().route("/", get(|| async { "Hello, World!" })), +// ) +// .layer(CompressionLayer::new().compress_when(SizeAbove::new(0))); + +// let client = TestClient::new(app); + +// // send GET request +// let res = client +// .get("/") +// .header("accept-encoding", "gzip") +// .send() +// .await; +// assert_eq!(res.headers()["transfer-encoding"], "chunked"); +// // cannot have `transfer-encoding: chunked` and `content-length` +// assert!(!res.headers().contains_key("content-length")); + +// // send HEAD request +// let res = client +// .head("/") +// .header("accept-encoding", "gzip") +// .send() +// .await; +// // no response body so no `transfer-encoding` +// assert!(!res.headers().contains_key("transfer-encoding")); +// // no content-length since we cannot know it since the response +// // is compressed +// assert!(!res.headers().contains_key("content-length")); +// } #[crate::test] #[should_panic(expected = "Paths must start with a `/`")] diff --git a/examples/consume-body-in-extractor-or-middleware/src/main.rs b/examples/consume-body-in-extractor-or-middleware/src/main.rs index bc0681cb8a..107edb6f1b 100644 --- a/examples/consume-body-in-extractor-or-middleware/src/main.rs +++ b/examples/consume-body-in-extractor-or-middleware/src/main.rs @@ -15,7 +15,6 @@ use axum::{ Router, }; use http_body_util::BodyExt; -use tower::ServiceBuilder; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] diff --git a/examples/key-value-store/Cargo.toml b/examples/key-value-store/Cargo.toml index 4366e3c2fc..afa2d6cc9b 100644 --- a/examples/key-value-store/Cargo.toml +++ b/examples/key-value-store/Cargo.toml @@ -11,7 +11,7 @@ tower = { version = "0.4", features = ["util", "timeout", "load-shed", "limit"] tower-http = { version = "0.4.4", features = [ "add-extension", "auth", - "compression-full", + # "compression-full", "limit", "trace", ] } diff --git a/examples/key-value-store/src/main.rs b/examples/key-value-store/src/main.rs index 1e2a5e748c..f06e731d57 100644 --- a/examples/key-value-store/src/main.rs +++ b/examples/key-value-store/src/main.rs @@ -24,8 +24,7 @@ use std::{ }; use tower::{BoxError, ServiceBuilder}; use tower_http::{ - compression::CompressionLayer, limit::RequestBodyLimitLayer, trace::TraceLayer, - validate_request::ValidateRequestHeaderLayer, + limit::RequestBodyLimitLayer, trace::TraceLayer, validate_request::ValidateRequestHeaderLayer, }; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -45,17 +44,14 @@ async fn main() { let app = Router::new() .route( "/:key", - // Add compression to `kv_get` - get(kv_get.layer(CompressionLayer::new())) - // But don't compress `kv_set` - .post_service( - kv_set - .layer(( - DefaultBodyLimit::disable(), - RequestBodyLimitLayer::new(1024 * 5_000 /* ~5mb */), - )) - .with_state(Arc::clone(&shared_state)), - ), + get(kv_get).post_service( + kv_set + .layer(( + DefaultBodyLimit::disable(), + RequestBodyLimitLayer::new(1024 * 5_000 /* ~5mb */), + )) + .with_state(Arc::clone(&shared_state)), + ), ) .route("/keys", get(list_keys)) // Nest our admin routes under `/admin` diff --git a/examples/oauth/src/main.rs b/examples/oauth/src/main.rs index 6949caf126..6ceffe8f84 100644 --- a/examples/oauth/src/main.rs +++ b/examples/oauth/src/main.rs @@ -69,11 +69,6 @@ async fn main() { .unwrap() ); - axum::serve(listener, app) - .await - .context("failed to serve service") - .unwrap(); - tracing::debug!("listening on {}", listener.local_addr().unwrap()); axum::serve(listener, app).await.unwrap(); } diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 6481dbfa66..59cd8f6d08 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -4,204 +4,208 @@ //! cargo test -p example-testing //! ``` -use std::net::SocketAddr; - -use axum::{ - extract::ConnectInfo, - routing::{get, post}, - Json, Router, -}; -use tower_http::trace::TraceLayer; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - -#[tokio::main] -async fn main() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "example_testing=debug,tower_http=debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); - - let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") - .await - .unwrap(); - tracing::debug!("listening on {}", listener.local_addr().unwrap()); - axum::serve(listener, app()).await.unwrap(); +fn main() { + // This example has not yet been updated to Hyper 1.0 } -/// Having a function that produces our app makes it easy to call it from tests -/// without having to create an HTTP server. -fn app() -> Router { - Router::new() - .route("/", get(|| async { "Hello, World!" })) - .route( - "/json", - post(|payload: Json| async move { - Json(serde_json::json!({ "data": payload.0 })) - }), - ) - .route( - "/requires-connect-into", - get(|ConnectInfo(addr): ConnectInfo| async move { format!("Hi {addr}") }), - ) - // We can still add middleware - .layer(TraceLayer::new_for_http()) -} - -#[cfg(test)] -mod tests { - use super::*; - use axum::{ - body::Body, - extract::connect_info::MockConnectInfo, - http::{self, Request, StatusCode}, - }; - use http_body_util::BodyExt; - use serde_json::{json, Value}; - use std::net::SocketAddr; - use tokio::net::{TcpListener, TcpStream}; - use tower::Service; // for `call` - use tower::ServiceExt; // for `oneshot` and `ready` // for `collect` - - #[tokio::test] - async fn hello_world() { - let app = app(); - - // `Router` implements `tower::Service>` so we can - // call it like any tower service, no need to run an HTTP server. - let response = app - .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) - .await - .unwrap(); - - assert_eq!(response.status(), StatusCode::OK); - - let body = response.into_body().collect().await.unwrap().to_bytes(); - assert_eq!(&body[..], b"Hello, World!"); - } - - #[tokio::test] - async fn json() { - let app = app(); - - let response = app - .oneshot( - Request::builder() - .method(http::Method::POST) - .uri("/json") - .header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) - .body(Body::from( - serde_json::to_vec(&json!([1, 2, 3, 4])).unwrap(), - )) - .unwrap(), - ) - .await - .unwrap(); - - assert_eq!(response.status(), StatusCode::OK); - - let body = response.into_body().collect().await.unwrap().to_bytes(); - let body: Value = serde_json::from_slice(&body).unwrap(); - assert_eq!(body, json!({ "data": [1, 2, 3, 4] })); - } - - #[tokio::test] - async fn not_found() { - let app = app(); - - let response = app - .oneshot( - Request::builder() - .uri("/does-not-exist") - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - - assert_eq!(response.status(), StatusCode::NOT_FOUND); - let body = response.into_body().collect().await.unwrap().to_bytes(); - assert!(body.is_empty()); - } - - // You can also spawn a server and talk to it like any other HTTP server: - #[tokio::test] - async fn the_real_deal() { - // TODO(david): convert this to hyper-util when thats published - - use hyper::client::conn; - - let listener = TcpListener::bind("0.0.0.0:0").await.unwrap(); - let addr = listener.local_addr().unwrap(); - - tokio::spawn(async move { - axum::serve(listener, app()).await.unwrap(); - }); - - let target_stream = TcpStream::connect(addr).await.unwrap(); - - let (mut request_sender, connection) = conn::http1::handshake(target_stream).await.unwrap(); - - tokio::spawn(async move { connection.await.unwrap() }); - - let response = request_sender - .send_request( - Request::builder() - .uri(format!("http://{addr}")) - .header("Host", "localhost") - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - - let body = response.into_body().collect().await.unwrap().to_bytes(); - assert_eq!(&body[..], b"Hello, World!"); - } - - // You can use `ready()` and `call()` to avoid using `clone()` - // in multiple request - #[tokio::test] - async fn multiple_request() { - let mut app = app().into_service(); - - let request = Request::builder().uri("/").body(Body::empty()).unwrap(); - let response = ServiceExt::>::ready(&mut app) - .await - .unwrap() - .call(request) - .await - .unwrap(); - assert_eq!(response.status(), StatusCode::OK); - - let request = Request::builder().uri("/").body(Body::empty()).unwrap(); - let response = ServiceExt::>::ready(&mut app) - .await - .unwrap() - .call(request) - .await - .unwrap(); - assert_eq!(response.status(), StatusCode::OK); - } - - // Here we're calling `/requires-connect-into` which requires `ConnectInfo` - // - // That is normally set with `Router::into_make_service_with_connect_info` but we can't easily - // use that during tests. The solution is instead to set the `MockConnectInfo` layer during - // tests. - #[tokio::test] - async fn with_into_make_service_with_connect_info() { - let mut app = app() - .layer(MockConnectInfo(SocketAddr::from(([0, 0, 0, 0], 3000)))) - .into_service(); - - let request = Request::builder() - .uri("/requires-connect-into") - .body(Body::empty()) - .unwrap(); - let response = app.ready().await.unwrap().call(request).await.unwrap(); - assert_eq!(response.status(), StatusCode::OK); - } -} +//use std::net::SocketAddr; + +//use axum::{ +// extract::ConnectInfo, +// routing::{get, post}, +// Json, Router, +//}; +//use tower_http::trace::TraceLayer; +//use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +//#[tokio::main] +//async fn main() { +// tracing_subscriber::registry() +// .with( +// tracing_subscriber::EnvFilter::try_from_default_env() +// .unwrap_or_else(|_| "example_testing=debug,tower_http=debug".into()), +// ) +// .with(tracing_subscriber::fmt::layer()) +// .init(); + +// let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") +// .await +// .unwrap(); +// tracing::debug!("listening on {}", listener.local_addr().unwrap()); +// axum::serve(listener, app()).await.unwrap(); +//} + +///// Having a function that produces our app makes it easy to call it from tests +///// without having to create an HTTP server. +//fn app() -> Router { +// Router::new() +// .route("/", get(|| async { "Hello, World!" })) +// .route( +// "/json", +// post(|payload: Json| async move { +// Json(serde_json::json!({ "data": payload.0 })) +// }), +// ) +// .route( +// "/requires-connect-into", +// get(|ConnectInfo(addr): ConnectInfo| async move { format!("Hi {addr}") }), +// ) +// // We can still add middleware +// .layer(TraceLayer::new_for_http()) +//} + +//#[cfg(test)] +//mod tests { +// use super::*; +// use axum::{ +// body::Body, +// extract::connect_info::MockConnectInfo, +// http::{self, Request, StatusCode}, +// }; +// use http_body_util::BodyExt; +// use serde_json::{json, Value}; +// use std::net::SocketAddr; +// use tokio::net::{TcpListener, TcpStream}; +// use tower::Service; // for `call` +// use tower::ServiceExt; // for `oneshot` and `ready` // for `collect` + +// #[tokio::test] +// async fn hello_world() { +// let app = app(); + +// // `Router` implements `tower::Service>` so we can +// // call it like any tower service, no need to run an HTTP server. +// let response = app +// .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) +// .await +// .unwrap(); + +// assert_eq!(response.status(), StatusCode::OK); + +// let body = response.into_body().collect().await.unwrap().to_bytes(); +// assert_eq!(&body[..], b"Hello, World!"); +// } + +// #[tokio::test] +// async fn json() { +// let app = app(); + +// let response = app +// .oneshot( +// Request::builder() +// .method(http::Method::POST) +// .uri("/json") +// .header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) +// .body(Body::from( +// serde_json::to_vec(&json!([1, 2, 3, 4])).unwrap(), +// )) +// .unwrap(), +// ) +// .await +// .unwrap(); + +// assert_eq!(response.status(), StatusCode::OK); + +// let body = response.into_body().collect().await.unwrap().to_bytes(); +// let body: Value = serde_json::from_slice(&body).unwrap(); +// assert_eq!(body, json!({ "data": [1, 2, 3, 4] })); +// } + +// #[tokio::test] +// async fn not_found() { +// let app = app(); + +// let response = app +// .oneshot( +// Request::builder() +// .uri("/does-not-exist") +// .body(Body::empty()) +// .unwrap(), +// ) +// .await +// .unwrap(); + +// assert_eq!(response.status(), StatusCode::NOT_FOUND); +// let body = response.into_body().collect().await.unwrap().to_bytes(); +// assert!(body.is_empty()); +// } + +// // You can also spawn a server and talk to it like any other HTTP server: +// #[tokio::test] +// async fn the_real_deal() { +// // TODO(david): convert this to hyper-util when thats published + +// use hyper::client::conn; + +// let listener = TcpListener::bind("0.0.0.0:0").await.unwrap(); +// let addr = listener.local_addr().unwrap(); + +// tokio::spawn(async move { +// axum::serve(listener, app()).await.unwrap(); +// }); + +// let target_stream = TcpStream::connect(addr).await.unwrap(); + +// let (mut request_sender, connection) = conn::http1::handshake(target_stream).await.unwrap(); + +// tokio::spawn(async move { connection.await.unwrap() }); + +// let response = request_sender +// .send_request( +// Request::builder() +// .uri(format!("http://{addr}")) +// .header("Host", "localhost") +// .body(Body::empty()) +// .unwrap(), +// ) +// .await +// .unwrap(); + +// let body = response.into_body().collect().await.unwrap().to_bytes(); +// assert_eq!(&body[..], b"Hello, World!"); +// } + +// // You can use `ready()` and `call()` to avoid using `clone()` +// // in multiple request +// #[tokio::test] +// async fn multiple_request() { +// let mut app = app().into_service(); + +// let request = Request::builder().uri("/").body(Body::empty()).unwrap(); +// let response = ServiceExt::>::ready(&mut app) +// .await +// .unwrap() +// .call(request) +// .await +// .unwrap(); +// assert_eq!(response.status(), StatusCode::OK); + +// let request = Request::builder().uri("/").body(Body::empty()).unwrap(); +// let response = ServiceExt::>::ready(&mut app) +// .await +// .unwrap() +// .call(request) +// .await +// .unwrap(); +// assert_eq!(response.status(), StatusCode::OK); +// } + +// // Here we're calling `/requires-connect-into` which requires `ConnectInfo` +// // +// // That is normally set with `Router::into_make_service_with_connect_info` but we can't easily +// // use that during tests. The solution is instead to set the `MockConnectInfo` layer during +// // tests. +// #[tokio::test] +// async fn with_into_make_service_with_connect_info() { +// let mut app = app() +// .layer(MockConnectInfo(SocketAddr::from(([0, 0, 0, 0], 3000)))) +// .into_service(); + +// let request = Request::builder() +// .uri("/requires-connect-into") +// .body(Body::empty()) +// .unwrap(); +// let response = app.ready().await.unwrap().call(request).await.unwrap(); +// assert_eq!(response.status(), StatusCode::OK); +// } +//} diff --git a/examples/tls-graceful-shutdown/src/main.rs b/examples/tls-graceful-shutdown/src/main.rs index 048eb70e88..13251846de 100644 --- a/examples/tls-graceful-shutdown/src/main.rs +++ b/examples/tls-graceful-shutdown/src/main.rs @@ -4,136 +4,140 @@ //! cargo run -p example-tls-graceful-shutdown //! ``` -use axum::{ - extract::Host, - handler::HandlerWithoutStateExt, - http::{StatusCode, Uri}, - response::Redirect, - routing::get, - BoxError, Router, -}; -use axum_server::tls_rustls::RustlsConfig; -use std::{future::Future, net::SocketAddr, path::PathBuf, time::Duration}; -use tokio::signal; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - -#[derive(Clone, Copy)] -struct Ports { - http: u16, - https: u16, +fn main() { + // This example has not yet been updated to Hyper 1.0 } -#[tokio::main] -async fn main() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "example_tls_graceful_shutdown=debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); - - let ports = Ports { - http: 7878, - https: 3000, - }; - - //Create a handle for our TLS server so the shutdown signal can all shutdown - let handle = axum_server::Handle::new(); - //save the future for easy shutting down of redirect server - let shutdown_future = shutdown_signal(handle.clone()); - - // optional: spawn a second server to redirect http requests to this server - tokio::spawn(redirect_http_to_https(ports, shutdown_future)); - - // configure certificate and private key used by https - let config = RustlsConfig::from_pem_file( - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("self_signed_certs") - .join("cert.pem"), - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("self_signed_certs") - .join("key.pem"), - ) - .await - .unwrap(); - - let app = Router::new().route("/", get(handler)); - - // run https server - let addr = SocketAddr::from(([127, 0, 0, 1], ports.https)); - tracing::debug!("listening on {addr}"); - axum_server::bind_rustls(addr, config) - .handle(handle) - .serve(app.into_make_service()) - .await - .unwrap(); -} - -async fn shutdown_signal(handle: axum_server::Handle) { - let ctrl_c = async { - signal::ctrl_c() - .await - .expect("failed to install Ctrl+C handler"); - }; - - #[cfg(unix)] - let terminate = async { - signal::unix::signal(signal::unix::SignalKind::terminate()) - .expect("failed to install signal handler") - .recv() - .await; - }; - - #[cfg(not(unix))] - let terminate = std::future::pending::<()>(); - - tokio::select! { - _ = ctrl_c => {}, - _ = terminate => {}, - } - - tracing::info!("Received termination signal shutting down"); - handle.graceful_shutdown(Some(Duration::from_secs(10))); // 10 secs is how long docker will wait - // to force shutdown -} - -async fn handler() -> &'static str { - "Hello, World!" -} - -async fn redirect_http_to_https(ports: Ports, signal: impl Future) { - fn make_https(host: String, uri: Uri, ports: Ports) -> Result { - let mut parts = uri.into_parts(); - - parts.scheme = Some(axum::http::uri::Scheme::HTTPS); - - if parts.path_and_query.is_none() { - parts.path_and_query = Some("/".parse().unwrap()); - } - - let https_host = host.replace(&ports.http.to_string(), &ports.https.to_string()); - parts.authority = Some(https_host.parse()?); - - Ok(Uri::from_parts(parts)?) - } - - let redirect = move |Host(host): Host, uri: Uri| async move { - match make_https(host, uri, ports) { - Ok(uri) => Ok(Redirect::permanent(&uri.to_string())), - Err(error) => { - tracing::warn!(%error, "failed to convert URI to HTTPS"); - Err(StatusCode::BAD_REQUEST) - } - } - }; - - let addr = SocketAddr::from(([127, 0, 0, 1], ports.http)); - //let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); - tracing::debug!("listening on {addr}"); - hyper::Server::bind(&addr) - .serve(redirect.into_make_service()) - .with_graceful_shutdown(signal) - .await - .unwrap(); -} +//use axum::{ +// extract::Host, +// handler::HandlerWithoutStateExt, +// http::{StatusCode, Uri}, +// response::Redirect, +// routing::get, +// BoxError, Router, +//}; +//use axum_server::tls_rustls::RustlsConfig; +//use std::{future::Future, net::SocketAddr, path::PathBuf, time::Duration}; +//use tokio::signal; +//use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +//#[derive(Clone, Copy)] +//struct Ports { +// http: u16, +// https: u16, +//} + +//#[tokio::main] +//async fn main() { +// tracing_subscriber::registry() +// .with( +// tracing_subscriber::EnvFilter::try_from_default_env() +// .unwrap_or_else(|_| "example_tls_graceful_shutdown=debug".into()), +// ) +// .with(tracing_subscriber::fmt::layer()) +// .init(); + +// let ports = Ports { +// http: 7878, +// https: 3000, +// }; + +// //Create a handle for our TLS server so the shutdown signal can all shutdown +// let handle = axum_server::Handle::new(); +// //save the future for easy shutting down of redirect server +// let shutdown_future = shutdown_signal(handle.clone()); + +// // optional: spawn a second server to redirect http requests to this server +// tokio::spawn(redirect_http_to_https(ports, shutdown_future)); + +// // configure certificate and private key used by https +// let config = RustlsConfig::from_pem_file( +// PathBuf::from(env!("CARGO_MANIFEST_DIR")) +// .join("self_signed_certs") +// .join("cert.pem"), +// PathBuf::from(env!("CARGO_MANIFEST_DIR")) +// .join("self_signed_certs") +// .join("key.pem"), +// ) +// .await +// .unwrap(); + +// let app = Router::new().route("/", get(handler)); + +// // run https server +// let addr = SocketAddr::from(([127, 0, 0, 1], ports.https)); +// tracing::debug!("listening on {addr}"); +// axum_server::bind_rustls(addr, config) +// .handle(handle) +// .serve(app.into_make_service()) +// .await +// .unwrap(); +//} + +//async fn shutdown_signal(handle: axum_server::Handle) { +// let ctrl_c = async { +// signal::ctrl_c() +// .await +// .expect("failed to install Ctrl+C handler"); +// }; + +// #[cfg(unix)] +// let terminate = async { +// signal::unix::signal(signal::unix::SignalKind::terminate()) +// .expect("failed to install signal handler") +// .recv() +// .await; +// }; + +// #[cfg(not(unix))] +// let terminate = std::future::pending::<()>(); + +// tokio::select! { +// _ = ctrl_c => {}, +// _ = terminate => {}, +// } + +// tracing::info!("Received termination signal shutting down"); +// handle.graceful_shutdown(Some(Duration::from_secs(10))); // 10 secs is how long docker will wait +// // to force shutdown +//} + +//async fn handler() -> &'static str { +// "Hello, World!" +//} + +//async fn redirect_http_to_https(ports: Ports, signal: impl Future) { +// fn make_https(host: String, uri: Uri, ports: Ports) -> Result { +// let mut parts = uri.into_parts(); + +// parts.scheme = Some(axum::http::uri::Scheme::HTTPS); + +// if parts.path_and_query.is_none() { +// parts.path_and_query = Some("/".parse().unwrap()); +// } + +// let https_host = host.replace(&ports.http.to_string(), &ports.https.to_string()); +// parts.authority = Some(https_host.parse()?); + +// Ok(Uri::from_parts(parts)?) +// } + +// let redirect = move |Host(host): Host, uri: Uri| async move { +// match make_https(host, uri, ports) { +// Ok(uri) => Ok(Redirect::permanent(&uri.to_string())), +// Err(error) => { +// tracing::warn!(%error, "failed to convert URI to HTTPS"); +// Err(StatusCode::BAD_REQUEST) +// } +// } +// }; + +// let addr = SocketAddr::from(([127, 0, 0, 1], ports.http)); +// //let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); +// tracing::debug!("listening on {addr}"); +// hyper::Server::bind(&addr) +// .serve(redirect.into_make_service()) +// .with_graceful_shutdown(signal) +// .await +// .unwrap(); +//} From ad8029b83eeba37699fedbb64f21833533da093c Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 11 Nov 2023 13:06:44 +0100 Subject: [PATCH 27/57] fix docs --- axum/src/docs/routing/layer.md | 9 +++++---- axum/src/docs/routing/merge.md | 4 +--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/axum/src/docs/routing/layer.md b/axum/src/docs/routing/layer.md index 1c029c7ff4..98b27be5bc 100644 --- a/axum/src/docs/routing/layer.md +++ b/axum/src/docs/routing/layer.md @@ -32,20 +32,21 @@ If you only want middleware on some routes you can use [`Router::merge`]: ```rust use axum::{routing::get, Router}; -use tower_http::{trace::TraceLayer, compression::CompressionLayer}; +use tower_http::{trace::TraceLayer, timeout::TimeoutLayer}; +use std::time::Duration; let with_tracing = Router::new() .route("/foo", get(|| async {})) .layer(TraceLayer::new_for_http()); -let with_compression = Router::new() +let with_timeout = Router::new() .route("/bar", get(|| async {})) - .layer(CompressionLayer::new()); + .layer(TimeoutLayer::new(Duration::from_secs(10))); // Merge everything into one `Router` let app = Router::new() .merge(with_tracing) - .merge(with_compression); + .merge(with_timeout); # let _: Router = app; ``` diff --git a/axum/src/docs/routing/merge.md b/axum/src/docs/routing/merge.md index b88175130b..f166b600d3 100644 --- a/axum/src/docs/routing/merge.md +++ b/axum/src/docs/routing/merge.md @@ -32,9 +32,7 @@ let app = Router::new() // - GET /users // - GET /users/:id // - GET /teams -# async { -# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Merging routers with state From b598d31102960806f29c9756d6aa1d89d145a99f Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 11 Nov 2023 13:12:07 +0100 Subject: [PATCH 28/57] fix macro tests --- .../fail/argument_not_extractor.stderr | 15 ------- .../fail/generic_without_via.stderr | 8 ---- .../fail/generic_without_via_rejection.stderr | 8 ---- ...rride_rejection_on_enum_without_via.stderr | 42 ------------------- 4 files changed, 73 deletions(-) diff --git a/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr b/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr index 9706683f10..0eb6e72c9a 100644 --- a/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr +++ b/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr @@ -7,19 +7,6 @@ error[E0277]: the trait bound `bool: FromRequestParts<()>` is not satisfied = note: Function argument is not a valid axum extractor. See `https://docs.rs/axum/latest/axum/extract/index.html` for details = help: the following other types implement trait `FromRequestParts`: -<<<<<<< HEAD - <() as FromRequestParts> - <(T1, T2) as FromRequestParts> - <(T1, T2, T3) as FromRequestParts> - <(T1, T2, T3, T4) as FromRequestParts> - <(T1, T2, T3, T4, T5) as FromRequestParts> - <(T1, T2, T3, T4, T5, T6) as FromRequestParts> - <(T1, T2, T3, T4, T5, T6, T7) as FromRequestParts> - <(T1, T2, T3, T4, T5, T6, T7, T8) as FromRequestParts> -<<<<<<< HEAD - and 26 others -======= -======= > as FromRequestParts> > @@ -28,9 +15,7 @@ error[E0277]: the trait bound `bool: FromRequestParts<()>` is not satisfied as FromRequestParts> as FromRequestParts> > ->>>>>>> main and $N others ->>>>>>> main = note: required for `bool` to implement `FromRequest<(), axum_core::extract::private::ViaParts>` note: required by a bound in `__axum_macros_check_handler_0_from_request_check` --> tests/debug_handler/fail/argument_not_extractor.rs:5:24 diff --git a/axum-macros/tests/from_request/fail/generic_without_via.stderr b/axum-macros/tests/from_request/fail/generic_without_via.stderr index 2a833ebdde..9620e64373 100644 --- a/axum-macros/tests/from_request/fail/generic_without_via.stderr +++ b/axum-macros/tests/from_request/fail/generic_without_via.stderr @@ -20,16 +20,8 @@ note: required by a bound in `axum::routing::get` --> $WORKSPACE/axum/src/routing/method_routing.rs | | top_level_handler_fn!(get, GET); -<<<<<<< HEAD -<<<<<<< HEAD - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get` -======= - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `get` ->>>>>>> main -======= | ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^ | | | | | required by a bound in this function | required by this bound in `get` ->>>>>>> main = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr b/axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr index b5b6e19086..ee6739af63 100644 --- a/axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr +++ b/axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr @@ -20,16 +20,8 @@ note: required by a bound in `axum::routing::get` --> $WORKSPACE/axum/src/routing/method_routing.rs | | top_level_handler_fn!(get, GET); -<<<<<<< HEAD -<<<<<<< HEAD - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get` -======= - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `get` ->>>>>>> main -======= | ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^ | | | | | required by a bound in this function | required by this bound in `get` ->>>>>>> main = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/axum-macros/tests/from_request/fail/override_rejection_on_enum_without_via.stderr b/axum-macros/tests/from_request/fail/override_rejection_on_enum_without_via.stderr index 2fbc5f4c04..41252bc3e7 100644 --- a/axum-macros/tests/from_request/fail/override_rejection_on_enum_without_via.stderr +++ b/axum-macros/tests/from_request/fail/override_rejection_on_enum_without_via.stderr @@ -5,24 +5,7 @@ error: cannot use `rejection` without `via` | ^^^^^^^^^ error[E0277]: the trait bound `fn(MyExtractor) -> impl Future {handler}: Handler<_, _>` is not satisfied -<<<<<<< HEAD -<<<<<<< HEAD - --> tests/from_request/fail/override_rejection_on_enum_without_via.rs:10:50 - | -10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); - | --- ^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(MyExtractor) -> impl Future {handler}` - | | - | required by a bound introduced by this call - | - = note: Consider using `#[axum::debug_handler]` to improve the error message - = help: the following other types implement trait `Handler`: - as Handler> - as Handler<(), S>> -======= - --> tests/from_request/fail/override_rejection_on_enum_without_via.rs:10:50 -======= --> tests/from_request/fail/override_rejection_on_enum_without_via.rs:11:50 ->>>>>>> main | 11 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); | --- ^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(MyExtractor) -> impl Future {handler}` @@ -33,7 +16,6 @@ error[E0277]: the trait bound `fn(MyExtractor) -> impl Future {hand = help: the following other types implement trait `Handler`: as Handler> as Handler<(), S>> ->>>>>>> main note: required by a bound in `axum::routing::get` --> $WORKSPACE/axum/src/routing/method_routing.rs | @@ -45,30 +27,7 @@ note: required by a bound in `axum::routing::get` = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `fn(Result) -> impl Future {handler_result}: Handler<_, _>` is not satisfied -<<<<<<< HEAD -<<<<<<< HEAD - --> tests/from_request/fail/override_rejection_on_enum_without_via.rs:10:64 - | -10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); - | ---- ^^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Result) -> impl Future {handler_result}` - | | - | required by a bound introduced by this call - | - = note: Consider using `#[axum::debug_handler]` to improve the error message - = help: the following other types implement trait `Handler`: - as Handler> - as Handler<(), S>> -note: required by a bound in `MethodRouter::::post` - --> $WORKSPACE/axum/src/routing/method_routing.rs - | - | chained_handler_fn!(post, POST); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `MethodRouter::::post` - = note: this error originates in the macro `chained_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) -======= - --> tests/from_request/fail/override_rejection_on_enum_without_via.rs:10:64 -======= --> tests/from_request/fail/override_rejection_on_enum_without_via.rs:11:64 ->>>>>>> main | 11 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); | ---- ^^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Result) -> impl Future {handler_result}` @@ -88,4 +47,3 @@ note: required by a bound in `MethodRouter::::post` | | required by a bound in this associated function | required by this bound in `MethodRouter::::post` = note: this error originates in the macro `chained_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) ->>>>>>> main From 760b460e1833d8c4d1547e5052046e6d26c236f6 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 11 Nov 2023 13:16:50 +0100 Subject: [PATCH 29/57] fix docs link --- axum/src/docs/middleware.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/axum/src/docs/middleware.md b/axum/src/docs/middleware.md index 15bfffb7d7..9141e16849 100644 --- a/axum/src/docs/middleware.md +++ b/axum/src/docs/middleware.md @@ -64,14 +64,11 @@ Some commonly used middleware are: - [`TraceLayer`](tower_http::trace) for high level tracing/logging. - [`CorsLayer`](tower_http::cors) for handling CORS. -- [`CompressionLayer`](tower_http::compression) for automatic compression of - responses. +- `CompressionLayer` for automatic compression of responses. - [`RequestIdLayer`](tower_http::request_id) and [`PropagateRequestIdLayer`](tower_http::request_id) set and propagate request ids. -- [`TimeoutLayer`](tower::timeout::TimeoutLayer) for timeouts. Note this - requires using [`HandleErrorLayer`](crate::error_handling::HandleErrorLayer) - to convert timeouts to responses. +- [`TimeoutLayer`](tower_http::timeout::TimeoutLayer) for timeouts. # Ordering From a0d04ea4014870c8d6a54d504dd3bdda0c4cb681 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 11 Nov 2023 13:21:52 +0100 Subject: [PATCH 30/57] do we still need this? --- .github/workflows/CI.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 79ce211291..288260790c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -112,8 +112,6 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Select minimal version run: cargo +nightly update -Z minimal-versions - - name: Fix up Cargo.lock - run: cargo +nightly update -p crc32fast --precise 1.1.1 - name: Run tests run: > cargo +${{ env.MSRV }} From a663a1df0376539a52000e9371d9d42dfcffa689 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 11 Nov 2023 13:37:51 +0100 Subject: [PATCH 31/57] bump msrv --- .github/workflows/CI.yml | 2 +- axum-extra/Cargo.toml | 2 +- axum-extra/README.md | 2 +- axum-macros/Cargo.toml | 2 +- axum-macros/README.md | 2 +- axum/CHANGELOG.md | 3 ++- axum/Cargo.toml | 2 +- axum/README.md | 2 +- axum/src/routing/tests/mod.rs | 4 ++-- 9 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 288260790c..71e61b95f2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -2,7 +2,7 @@ name: CI env: CARGO_TERM_COLOR: always - MSRV: '1.65' + MSRV: '1.66' on: push: diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index 44c9ab1043..f85964573f 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -2,7 +2,7 @@ categories = ["asynchronous", "network-programming", "web-programming"] description = "Extra utilities for axum" edition = "2021" -rust-version = "1.65" +rust-version = "1.66" homepage = "https://github.com/tokio-rs/axum" keywords = ["http", "web", "framework"] license = "MIT" diff --git a/axum-extra/README.md b/axum-extra/README.md index d94414cebc..16b96cc8c9 100644 --- a/axum-extra/README.md +++ b/axum-extra/README.md @@ -14,7 +14,7 @@ This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in ## Minimum supported Rust version -axum-extra's MSRV is 1.65. +axum-extra's MSRV is 1.66. ## Getting Help diff --git a/axum-macros/Cargo.toml b/axum-macros/Cargo.toml index dc74f519a9..45ef16203a 100644 --- a/axum-macros/Cargo.toml +++ b/axum-macros/Cargo.toml @@ -2,7 +2,7 @@ categories = ["asynchronous", "network-programming", "web-programming"] description = "Macros for axum" edition = "2021" -rust-version = "1.65" +rust-version = "1.66" homepage = "https://github.com/tokio-rs/axum" keywords = ["axum"] license = "MIT" diff --git a/axum-macros/README.md b/axum-macros/README.md index 79a8752d7e..8fcde01ed2 100644 --- a/axum-macros/README.md +++ b/axum-macros/README.md @@ -14,7 +14,7 @@ This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in ## Minimum supported Rust version -axum-macros's MSRV is 1.65. +axum-macros's MSRV is 1.66. ## Getting Help diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 7294549cf3..aadd3aed3c 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -61,7 +61,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **fixed:** Fix `.source()` of composite rejections ([#2030]) - **fixed:** Allow unreachable code in `#[debug_handler]` ([#2014]) - **change:** Update tokio-tungstenite to 0.19 ([#2021]) -- **change:** axum's MSRV is now 1.65 ([#2021]) +- **change:** axum's MSRV is now 1.66 ([#1882]) - **added:** Implement `Handler` for `T: IntoResponse` ([#2140]) - **added:** Implement `IntoResponse` for `(R,) where R: IntoResponse` ([#2143]) - **changed:** For SSE, add space between field and value for compatibility ([#2149]) @@ -74,6 +74,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#1835]: https://github.com/tokio-rs/axum/pull/1835 [#1850]: https://github.com/tokio-rs/axum/pull/1850 [#1868]: https://github.com/tokio-rs/axum/pull/1868 +[#1882]: https://github.com/tokio-rs/axum/pull/1882 [#1924]: https://github.com/tokio-rs/axum/pull/1924 [#1956]: https://github.com/tokio-rs/axum/pull/1956 [#1972]: https://github.com/tokio-rs/axum/pull/1972 diff --git a/axum/Cargo.toml b/axum/Cargo.toml index dba33ac289..a12a38cff6 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -4,7 +4,7 @@ version = "0.6.16" categories = ["asynchronous", "network-programming", "web-programming::http-server"] description = "Web framework that focuses on ergonomics and modularity" edition = "2021" -rust-version = "1.65" +rust-version = "1.66" homepage = "https://github.com/tokio-rs/axum" keywords = ["http", "web", "framework"] license = "MIT" diff --git a/axum/README.md b/axum/README.md index 0b55d6abb3..73b5af554b 100644 --- a/axum/README.md +++ b/axum/README.md @@ -112,7 +112,7 @@ This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in ## Minimum supported Rust version -axum's MSRV is 1.65. +axum's MSRV is 1.66. ## Examples diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index 1e7dcf6bf6..8060021bfa 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -934,7 +934,7 @@ async fn state_isnt_cloned_too_much() { impl Clone for AppState { fn clone(&self) -> Self { - #[rustversion::since(1.65)] + #[rustversion::since(1.66)] #[track_caller] fn count() { if SETUP_DONE.load(Ordering::SeqCst) { @@ -950,7 +950,7 @@ async fn state_isnt_cloned_too_much() { } } - #[rustversion::not(since(1.65))] + #[rustversion::not(since(1.66))] fn count() { if SETUP_DONE.load(Ordering::SeqCst) { COUNT.fetch_add(1, Ordering::SeqCst); From c33b537ce207816b316b03e295761d1b168ecf52 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 15 Nov 2023 19:47:41 +0100 Subject: [PATCH 32/57] remove patches --- Cargo.toml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 64eaf1df23..a68aaab16a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,10 +5,3 @@ default-members = ["axum", "axum-*"] # Example has been deleted, but README.md remains exclude = ["examples/async-graphql"] resolver = "2" - -[patch.crates-io] -# https://github.com/tower-rs/tower-http/pull/348 -tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "58070136720b6" } - -# for `Frame::map_data` -http-body = { git = "https://github.com/hyperium/http-body", rev = "7bf321acbb422214c89933c103417bcfe3892aed" } From 8dcb7bf1229e26b228eb51e39170d80eb25c7b95 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 15 Nov 2023 19:53:18 +0100 Subject: [PATCH 33/57] update deps --- Cargo.toml | 4 ++++ axum-core/Cargo.toml | 8 ++++---- axum-extra/Cargo.toml | 8 ++++---- axum/Cargo.toml | 10 +++++----- .../consume-body-in-extractor-or-middleware/Cargo.toml | 4 ++-- examples/graceful-shutdown/Cargo.toml | 2 +- examples/handle-head-request/Cargo.toml | 4 ++-- examples/http-proxy/Cargo.toml | 2 +- examples/listen-multiple-addrs/Cargo.toml | 2 +- examples/low-level-openssl/Cargo.toml | 2 +- examples/low-level-rustls/Cargo.toml | 2 +- examples/oauth/Cargo.toml | 2 +- examples/print-request-response/Cargo.toml | 4 ++-- examples/query-params-with-empty-strings/Cargo.toml | 4 ++-- examples/rest-grpc-multiplex/Cargo.toml | 2 +- examples/reverse-proxy/Cargo.toml | 2 +- examples/simple-router-wasm/Cargo.toml | 2 +- examples/testing-websockets/Cargo.toml | 2 +- examples/testing/Cargo.toml | 4 ++-- examples/unix-domain-socket/Cargo.toml | 2 +- examples/validator/Cargo.toml | 2 +- 21 files changed, 39 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a68aaab16a..0eb61ca14b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,7 @@ default-members = ["axum", "axum-*"] # Example has been deleted, but README.md remains exclude = ["examples/async-graphql"] resolver = "2" + +[patch.crates-io] +# https://github.com/tower-rs/tower-http/pull/348 +tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "602f06f248211" } diff --git a/axum-core/Cargo.toml b/axum-core/Cargo.toml index b29395f201..55f820a383 100644 --- a/axum-core/Cargo.toml +++ b/axum-core/Cargo.toml @@ -21,9 +21,9 @@ __private_docs = ["dep:tower-http"] async-trait = "0.1.67" bytes = "1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -http = "0.2.7" -http-body = "=1.0.0-rc.2" -http-body-util = "=0.1.0-rc.3" +http = "1.0.0" +http-body = "1.0.0" +http-body-util = "0.1.0" mime = "0.3.16" pin-project-lite = "0.2.7" sync_wrapper = "0.1.1" @@ -41,7 +41,7 @@ rustversion = "1.0.9" axum = { path = "../axum", version = "0.6.0" } axum-extra = { path = "../axum-extra", features = ["typed-header"] } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -hyper = "=1.0.0-rc.4" +hyper = "1.0.0" tokio = { version = "1.25.0", features = ["macros"] } tower-http = { version = "0.4.4", features = ["limit"] } diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index f85964573f..8929e95f73 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -40,9 +40,9 @@ axum = { path = "../axum", version = "0.6.13", default-features = false } axum-core = { path = "../axum-core", version = "0.3.4" } bytes = "1.1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -http = "0.2" -http-body = "=1.0.0-rc.2" -http-body-util = "=0.1.0-rc.3" +http = "1.0.0" +http-body = "1.0.0" +http-body-util = "0.1.0" mime = "0.3" pin-project-lite = "0.2" serde = "1.0" @@ -66,7 +66,7 @@ tokio-util = { version = "0.7", optional = true } [dev-dependencies] axum = { path = "../axum", version = "0.6.0" } -hyper = "=1.0.0-rc.4" +hyper = "1.0.0" reqwest = { version = "0.11", default-features = false, features = ["json", "stream", "multipart"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.71" diff --git a/axum/Cargo.toml b/axum/Cargo.toml index a12a38cff6..434d5f1ab7 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -35,10 +35,10 @@ async-trait = "0.1.67" axum-core = { path = "../axum-core", version = "0.3.4" } bytes = "1.0" futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -http = "0.2.9" -http-body = "=1.0.0-rc.2" -http-body-util = "=0.1.0-rc.3" -hyper = { package = "hyper", version = "=1.0.0-rc.4", features = ["server", "http1"] } +http = "1.0.0" +http-body = "1.0.0" +http-body-util = "0.1.0" +hyper = { package = "hyper", version = "1.0.0", features = ["server", "http1"] } itoa = "1.0.5" matchit = "0.7" memchr = "2.4.1" @@ -54,7 +54,7 @@ tower-service = "0.3" # optional dependencies axum-macros = { path = "../axum-macros", version = "0.3.7", optional = true } base64 = { version = "0.21.0", optional = true } -hyper-util = { git = "https://github.com/hyperium/hyper-util", rev = "d97181a", features = ["auto"], optional = true } +hyper-util = { git = "https://github.com/hyperium/hyper-util", rev = "4069dda", features = ["auto"], optional = true } multer = { version = "2.0.0", optional = true } serde_json = { version = "1.0", features = ["raw_value"], optional = true } serde_path_to_error = { version = "0.1.8", optional = true } diff --git a/examples/consume-body-in-extractor-or-middleware/Cargo.toml b/examples/consume-body-in-extractor-or-middleware/Cargo.toml index 3ba84e366a..50d5d4d2b5 100644 --- a/examples/consume-body-in-extractor-or-middleware/Cargo.toml +++ b/examples/consume-body-in-extractor-or-middleware/Cargo.toml @@ -6,8 +6,8 @@ publish = false [dependencies] axum = { path = "../../axum" } -http-body-util = "=0.1.0-rc.3" -hyper = "=1.0.0-rc.4" +http-body-util = "0.1.0" +hyper = "1.0.0" tokio = { version = "1.0", features = ["full"] } tower = "0.4" tower-http = { version = "0.4.4", features = ["map-request-body", "util"] } diff --git a/examples/graceful-shutdown/Cargo.toml b/examples/graceful-shutdown/Cargo.toml index 0e29710ddf..0644ae2ccf 100644 --- a/examples/graceful-shutdown/Cargo.toml +++ b/examples/graceful-shutdown/Cargo.toml @@ -6,5 +6,5 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +hyper = { version = "1.0.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] } diff --git a/examples/handle-head-request/Cargo.toml b/examples/handle-head-request/Cargo.toml index 4740908b36..83a8a66e25 100644 --- a/examples/handle-head-request/Cargo.toml +++ b/examples/handle-head-request/Cargo.toml @@ -9,6 +9,6 @@ axum = { path = "../../axum" } tokio = { version = "1.0", features = ["full"] } [dev-dependencies] -http-body-util = "=0.1.0-rc.3" -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +http-body-util = "0.1.0" +hyper = { version = "1.0.0", features = ["full"] } tower = { version = "0.4", features = ["util"] } diff --git a/examples/http-proxy/Cargo.toml b/examples/http-proxy/Cargo.toml index 90db640d55..c44f309462 100644 --- a/examples/http-proxy/Cargo.toml +++ b/examples/http-proxy/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +hyper = { version = "1.0.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["make"] } tracing = "0.1" diff --git a/examples/listen-multiple-addrs/Cargo.toml b/examples/listen-multiple-addrs/Cargo.toml index 2009ad1463..f77fc9d97b 100644 --- a/examples/listen-multiple-addrs/Cargo.toml +++ b/examples/listen-multiple-addrs/Cargo.toml @@ -6,5 +6,5 @@ publish = false [dependencies] axum = { path = "../../axum" } -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +hyper = { version = "1.0.0", features = ["full"] } tokio = { version = "1", features = ["full"] } diff --git a/examples/low-level-openssl/Cargo.toml b/examples/low-level-openssl/Cargo.toml index f217c90833..6e8a756c2f 100644 --- a/examples/low-level-openssl/Cargo.toml +++ b/examples/low-level-openssl/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +hyper = { version = "1.0.0", features = ["full"] } openssl = "0.10" tokio = { version = "1", features = ["full"] } tokio-openssl = "0.6" diff --git a/examples/low-level-rustls/Cargo.toml b/examples/low-level-rustls/Cargo.toml index cde06d8c6f..08e28230c3 100644 --- a/examples/low-level-rustls/Cargo.toml +++ b/examples/low-level-rustls/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +hyper = { version = "1.0.0", features = ["full"] } rustls-pemfile = "0.3" tokio = { version = "1", features = ["full"] } tokio-rustls = "0.23" diff --git a/examples/oauth/Cargo.toml b/examples/oauth/Cargo.toml index fb0c2f6df1..0186b95bfe 100644 --- a/examples/oauth/Cargo.toml +++ b/examples/oauth/Cargo.toml @@ -9,7 +9,7 @@ anyhow = "1" async-session = "3.0.0" axum = { path = "../../axum" } axum-extra = { path = "../../axum-extra", features = ["typed-header"] } -http = "0.2" +http = "1.0.0" oauth2 = "4.1" # Use Rustls because it makes it easier to cross-compile on CI reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "json"] } diff --git a/examples/print-request-response/Cargo.toml b/examples/print-request-response/Cargo.toml index 40b460bb40..a314b5b7fe 100644 --- a/examples/print-request-response/Cargo.toml +++ b/examples/print-request-response/Cargo.toml @@ -6,8 +6,8 @@ publish = false [dependencies] axum = { path = "../../axum" } -http-body-util = "=0.1.0-rc.3" -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +http-body-util = "0.1.0" +hyper = { version = "1.0.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util", "filter"] } tracing = "0.1" diff --git a/examples/query-params-with-empty-strings/Cargo.toml b/examples/query-params-with-empty-strings/Cargo.toml index 34955d5c76..7a52e98d1e 100644 --- a/examples/query-params-with-empty-strings/Cargo.toml +++ b/examples/query-params-with-empty-strings/Cargo.toml @@ -6,8 +6,8 @@ publish = false [dependencies] axum = { path = "../../axum" } -http-body-util = "=0.1.0-rc.3" -hyper = "=1.0.0-rc.4" +http-body-util = "0.1.0" +hyper = "1.0.0" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util"] } diff --git a/examples/rest-grpc-multiplex/Cargo.toml b/examples/rest-grpc-multiplex/Cargo.toml index 7165005e9e..11a6a3a2b4 100644 --- a/examples/rest-grpc-multiplex/Cargo.toml +++ b/examples/rest-grpc-multiplex/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures = "0.3" -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +hyper = { version = "1.0.0", features = ["full"] } prost = "0.11" tokio = { version = "1", features = ["full"] } tonic = { version = "0.9" } diff --git a/examples/reverse-proxy/Cargo.toml b/examples/reverse-proxy/Cargo.toml index ddd421a8d6..39e8d76612 100644 --- a/examples/reverse-proxy/Cargo.toml +++ b/examples/reverse-proxy/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] axum = { path = "../../axum" } -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +hyper = { version = "1.0.0", features = ["full"] } tokio = { version = "1", features = ["full"] } diff --git a/examples/simple-router-wasm/Cargo.toml b/examples/simple-router-wasm/Cargo.toml index da14b580ab..c3041e4a58 100644 --- a/examples/simple-router-wasm/Cargo.toml +++ b/examples/simple-router-wasm/Cargo.toml @@ -12,5 +12,5 @@ axum = { path = "../../axum", default-features = false } # works in wasm as well axum-extra = { path = "../../axum-extra", default-features = false } futures-executor = "0.3.21" -http = "0.2.7" +http = "1.0.0" tower-service = "0.3.1" diff --git a/examples/testing-websockets/Cargo.toml b/examples/testing-websockets/Cargo.toml index 20787cf373..3c3513ade2 100644 --- a/examples/testing-websockets/Cargo.toml +++ b/examples/testing-websockets/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] axum = { path = "../../axum", features = ["ws"] } futures = "0.3" -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +hyper = { version = "1.0.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tokio-tungstenite = "0.20" diff --git a/examples/testing/Cargo.toml b/examples/testing/Cargo.toml index 5bb642b5fc..af6ea5acd4 100644 --- a/examples/testing/Cargo.toml +++ b/examples/testing/Cargo.toml @@ -6,8 +6,8 @@ publish = false [dependencies] axum = { path = "../../axum" } -http-body-util = "=0.1.0-rc.3" -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +http-body-util = "0.1.0" +hyper = { version = "1.0.0", features = ["full"] } mime = "0.3" serde_json = "1.0" tokio = { version = "1.0", features = ["full"] } diff --git a/examples/unix-domain-socket/Cargo.toml b/examples/unix-domain-socket/Cargo.toml index 52300a2252..41bd4a1e6d 100644 --- a/examples/unix-domain-socket/Cargo.toml +++ b/examples/unix-domain-socket/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] axum = { path = "../../axum" } futures = "0.3" -hyper = { version = "=1.0.0-rc.4", features = ["full"] } +hyper = { version = "1.0.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util"] } tracing = "0.1" diff --git a/examples/validator/Cargo.toml b/examples/validator/Cargo.toml index 804c408775..a1adc075a8 100644 --- a/examples/validator/Cargo.toml +++ b/examples/validator/Cargo.toml @@ -7,7 +7,7 @@ version = "0.1.0" [dependencies] async-trait = "0.1.67" axum = { path = "../../axum" } -http-body = "=1.0.0-rc.2" +http-body = "1.0.0" serde = { version = "1.0", features = ["derive"] } thiserror = "1.0.29" tokio = { version = "1.0", features = ["full"] } From dafadf17e24fef42260f44e52572a21dc8ef65f0 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 16 Nov 2023 07:57:39 +0100 Subject: [PATCH 34/57] start updating to http 1.0 --- axum/CHANGELOG.md | 2 ++ axum/src/extension.rs | 4 ++-- axum/src/extract/multipart.rs | 1 + axum/src/routing/url_params.rs | 1 + axum/src/test_helpers/test_client.rs | 15 +++++++++++---- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index aadd3aed3c..536c5e4184 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -66,6 +66,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **added:** Implement `IntoResponse` for `(R,) where R: IntoResponse` ([#2143]) - **changed:** For SSE, add space between field and value for compatibility ([#2149]) - **added:** Add `NestedPath` extractor ([#1924]) +- **breaking:** `impl IntoResponse(Parts) for Extension` now requires `T: + Clone`. As that is required by the http crate ([#1882]) [#1664]: https://github.com/tokio-rs/axum/pull/1664 [#1751]: https://github.com/tokio-rs/axum/pull/1751 diff --git a/axum/src/extension.rs b/axum/src/extension.rs index 42f208c4d8..a72568ac6c 100644 --- a/axum/src/extension.rs +++ b/axum/src/extension.rs @@ -98,7 +98,7 @@ axum_core::__impl_deref!(Extension); impl IntoResponseParts for Extension where - T: Send + Sync + 'static, + T: Clone + Send + Sync + 'static, { type Error = Infallible; @@ -110,7 +110,7 @@ where impl IntoResponse for Extension where - T: Send + Sync + 'static, + T: Clone + Send + Sync + 'static, { fn into_response(self) -> Response { let mut res = ().into_response(); diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 54dee8f02a..47a0a3b1fc 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -13,6 +13,7 @@ use futures_util::stream::Stream; use http::header::{HeaderMap, CONTENT_TYPE}; use http::StatusCode; use std::error::Error; +use std::str::FromStr; use std::{ fmt, pin::Pin, diff --git a/axum/src/routing/url_params.rs b/axum/src/routing/url_params.rs index 6243d379c0..eb5a08a330 100644 --- a/axum/src/routing/url_params.rs +++ b/axum/src/routing/url_params.rs @@ -3,6 +3,7 @@ use http::Extensions; use matchit::Params; use std::sync::Arc; +#[derive(Clone)] pub(crate) enum UrlParams { Params(Vec<(Arc, PercentDecodedStr)>), InvalidUtf8InPathParam { key: Arc }, diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index 9e1858cb97..5f78f38d64 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -4,7 +4,7 @@ use http::{ header::{HeaderName, HeaderValue}, StatusCode, }; -use std::{convert::Infallible, net::SocketAddr}; +use std::{convert::Infallible, net::SocketAddr, str::FromStr}; use tokio::net::TcpListener; use tower::make::Shared; use tower_service::Service; @@ -140,11 +140,18 @@ impl TestResponse { } pub(crate) fn status(&self) -> StatusCode { - self.response.status() + StatusCode::from_u16(self.response.status().as_u16()).unwrap() } - pub(crate) fn headers(&self) -> &http::HeaderMap { - self.response.headers() + pub(crate) fn headers(&self) -> http::HeaderMap { + // reqwest still uses http 0.2 so have to convert into http 1.0 + let mut headers = http::HeaderMap::new(); + for (key, value) in self.response.headers() { + let key = http::HeaderName::from_str(key.as_str()).unwrap(); + let value = http::HeaderValue::from_maybe_shared(value.as_bytes()).unwrap(); + headers.insert(key, value); + } + headers } pub(crate) async fn chunk(&mut self) -> Option { From 2a33bd93cb6885d1df684f81a3a9cf58718fa758 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 16 Nov 2023 08:54:20 +0100 Subject: [PATCH 35/57] make it build by adding crates-io patches --- Cargo.toml | 8 +- axum/src/docs/response.md | 1 + axum/src/extract/multipart.rs | 1 - axum/src/test_helpers/test_client.rs | 10 +- examples/reqwest-response/src/main.rs | 130 +++++++++++++------------- 5 files changed, 84 insertions(+), 66 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0eb61ca14b..996a642708 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,11 @@ exclude = ["examples/async-graphql"] resolver = "2" [patch.crates-io] -# https://github.com/tower-rs/tower-http/pull/348 +# PR=https://github.com/tower-rs/tower-http/pull/348 tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "602f06f248211" } + +# for http 1.0. PR to update is merged but not published +headers = { git = "https://github.com/hyperium/headers", rev = "4400aa90c47a7" } + +# for http 1.0. PR=https://github.com/rousan/multer-rs/pull/59 +multer = { git = "https://github.com/davidpdrsn/multer-rs", rev = "abe0e3a42a1fc" } diff --git a/axum/src/docs/response.md b/axum/src/docs/response.md index 4e1d50b5e2..a5761c34ed 100644 --- a/axum/src/docs/response.md +++ b/axum/src/docs/response.md @@ -127,6 +127,7 @@ async fn with_status_extensions() -> impl IntoResponse { ) } +#[derive(Clone)] struct Foo(&'static str); // Or mix and match all the things diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 47a0a3b1fc..54dee8f02a 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -13,7 +13,6 @@ use futures_util::stream::Stream; use http::header::{HeaderMap, CONTENT_TYPE}; use http::StatusCode; use std::error::Error; -use std::str::FromStr; use std::{ fmt, pin::Pin, diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index 5f78f38d64..31e7074059 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -105,7 +105,15 @@ impl RequestBuilder { HeaderValue: TryFrom, >::Error: Into, { + // reqwest still uses http 0.2 + let key: HeaderName = key.try_into().map_err(Into::into).unwrap(); + let key = reqwest::header::HeaderName::from_bytes(key.as_ref()).unwrap(); + + let value: HeaderValue = value.try_into().map_err(Into::into).unwrap(); + let value = reqwest::header::HeaderValue::from_bytes(value.as_bytes()).unwrap(); + self.builder = self.builder.header(key, value); + self } @@ -148,7 +156,7 @@ impl TestResponse { let mut headers = http::HeaderMap::new(); for (key, value) in self.response.headers() { let key = http::HeaderName::from_str(key.as_str()).unwrap(); - let value = http::HeaderValue::from_maybe_shared(value.as_bytes()).unwrap(); + let value = http::HeaderValue::from_bytes(value.as_bytes()).unwrap(); headers.insert(key, value); } headers diff --git a/examples/reqwest-response/src/main.rs b/examples/reqwest-response/src/main.rs index db703ee54e..3de362f3a0 100644 --- a/examples/reqwest-response/src/main.rs +++ b/examples/reqwest-response/src/main.rs @@ -4,76 +4,80 @@ //! cargo run -p example-reqwest-response //! ``` -use std::{convert::Infallible, time::Duration}; +fn main() { + // this examples requires reqwest to be updated to hyper and http 1.0 +} -use axum::{ - body::{Body, Bytes}, - extract::State, - response::{IntoResponse, Response}, - routing::get, - Router, -}; -use reqwest::{Client, StatusCode}; -use tokio_stream::StreamExt; -use tower_http::trace::TraceLayer; -use tracing::Span; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +// use std::{convert::Infallible, time::Duration}; -#[tokio::main] -async fn main() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "example_reqwest_response=debug,tower_http=debug".into()), - ) - .with(tracing_subscriber::fmt::layer()) - .init(); +// use axum::{ +// body::{Body, Bytes}, +// extract::State, +// response::{IntoResponse, Response}, +// routing::get, +// Router, +// }; +// use reqwest::{Client, StatusCode}; +// use tokio_stream::StreamExt; +// use tower_http::trace::TraceLayer; +// use tracing::Span; +// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - let client = Client::new(); +// #[tokio::main] +// async fn main() { +// tracing_subscriber::registry() +// .with( +// tracing_subscriber::EnvFilter::try_from_default_env() +// .unwrap_or_else(|_| "example_reqwest_response=debug,tower_http=debug".into()), +// ) +// .with(tracing_subscriber::fmt::layer()) +// .init(); - let app = Router::new() - .route("/", get(proxy_via_reqwest)) - .route("/stream", get(stream_some_data)) - // Add some logging so we can see the streams going through - .layer(TraceLayer::new_for_http().on_body_chunk( - |chunk: &Bytes, _latency: Duration, _span: &Span| { - tracing::debug!("streaming {} bytes", chunk.len()); - }, - )) - .with_state(client); +// let client = Client::new(); - let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") - .await - .unwrap(); - tracing::debug!("listening on {}", listener.local_addr().unwrap()); - axum::serve(listener, app).await.unwrap(); -} +// let app = Router::new() +// .route("/", get(proxy_via_reqwest)) +// .route("/stream", get(stream_some_data)) +// // Add some logging so we can see the streams going through +// .layer(TraceLayer::new_for_http().on_body_chunk( +// |chunk: &Bytes, _latency: Duration, _span: &Span| { +// tracing::debug!("streaming {} bytes", chunk.len()); +// }, +// )) +// .with_state(client); -async fn proxy_via_reqwest(State(client): State) -> Response { - let reqwest_response = match client.get("http://127.0.0.1:3000/stream").send().await { - Ok(res) => res, - Err(err) => { - tracing::error!(%err, "request failed"); - return StatusCode::BAD_GATEWAY.into_response(); - } - }; +// let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") +// .await +// .unwrap(); +// tracing::debug!("listening on {}", listener.local_addr().unwrap()); +// axum::serve(listener, app).await.unwrap(); +// } - let mut response_builder = Response::builder().status(reqwest_response.status()); +// async fn proxy_via_reqwest(State(client): State) -> Response { +// let reqwest_response = match client.get("http://127.0.0.1:3000/stream").send().await { +// Ok(res) => res, +// Err(err) => { +// tracing::error!(%err, "request failed"); +// return StatusCode::BAD_GATEWAY.into_response(); +// } +// }; - // This unwrap is fine because we haven't insert any headers yet so there can't be any invalid - // headers - *response_builder.headers_mut().unwrap() = reqwest_response.headers().clone(); +// let mut response_builder = Response::builder().status(reqwest_response.status()); - response_builder - .body(Body::from_stream(reqwest_response.bytes_stream())) - // Same goes for this unwrap - .unwrap() -} +// // This unwrap is fine because we haven't insert any headers yet so there can't be any invalid +// // headers +// *response_builder.headers_mut().unwrap() = reqwest_response.headers().clone(); -async fn stream_some_data() -> Body { - let stream = tokio_stream::iter(0..5) - .throttle(Duration::from_secs(1)) - .map(|n| n.to_string()) - .map(Ok::<_, Infallible>); - Body::from_stream(stream) -} +// response_builder +// .body(Body::from_stream(reqwest_response.bytes_stream())) +// // Same goes for this unwrap +// .unwrap() +// } + +// async fn stream_some_data() -> Body { +// let stream = tokio_stream::iter(0..5) +// .throttle(Duration::from_secs(1)) +// .map(|n| n.to_string()) +// .map(Ok::<_, Infallible>); +// Body::from_stream(stream) +// } From 6e2b97d9c50dea547b7a43b44efcedb2a8fd086a Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 16 Nov 2023 09:12:59 +0100 Subject: [PATCH 36/57] Update docs for DefaultBodyLimit, it doesn't change the body type --- axum-core/src/extract/default_body_limit.rs | 26 +++------------------ 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/axum-core/src/extract/default_body_limit.rs b/axum-core/src/extract/default_body_limit.rs index 2ccb11fe15..01ebdedd07 100644 --- a/axum-core/src/extract/default_body_limit.rs +++ b/axum-core/src/extract/default_body_limit.rs @@ -22,8 +22,7 @@ use tower_layer::Layer; /// [`RequestBodyLimit`] is applied globally to all requests, regardless of which extractors are /// used or how the body is consumed. /// -/// `DefaultBodyLimit` is also easier to integrate into an existing setup since it doesn't change -/// the request body type: +/// # Example /// /// ``` /// use axum::{ @@ -34,31 +33,12 @@ use tower_layer::Layer; /// }; /// /// let app = Router::new() -/// .route( -/// "/", -/// // even with `DefaultBodyLimit` the request body is still just `Body` -/// post(|request: Request| async {}), -/// ) +/// .route("/", post(|request: Request| async {})) +/// // change the default limit /// .layer(DefaultBodyLimit::max(1024)); /// # let _: Router = app; /// ``` /// -/// ``` -/// use axum::{Router, routing::post, body::Body, extract::Request}; -/// use tower_http::limit::RequestBodyLimitLayer; -/// use http_body_util::Limited; -/// -/// let app = Router::new() -/// .route( -/// "/", -/// // `RequestBodyLimitLayer` changes the request body type to `Limited` -/// // extracting a different body type wont work -/// post(|request: Request| async {}), -/// ) -/// .layer(RequestBodyLimitLayer::new(1024)); -/// # let _: Router = app; -/// ``` -/// /// In general using `DefaultBodyLimit` is recommended but if you need to use third party /// extractors and want to sure a limit is also applied there then [`RequestBodyLimit`] should be /// used. From 50b5ea7a3b2b1e6c3e0e42e109aabe9a5f8e4972 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 18 Nov 2023 13:41:18 +0100 Subject: [PATCH 37/57] move to published hyper-util --- axum/Cargo.toml | 2 +- axum/src/serve.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 434d5f1ab7..f83f1c894d 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -54,7 +54,7 @@ tower-service = "0.3" # optional dependencies axum-macros = { path = "../axum-macros", version = "0.3.7", optional = true } base64 = { version = "0.21.0", optional = true } -hyper-util = { git = "https://github.com/hyperium/hyper-util", rev = "4069dda", features = ["auto"], optional = true } +hyper-util = { version = "0.1.1", features = ["tokio", "server", "server-auto"], optional = true } multer = { version = "2.0.0", optional = true } serde_json = { version = "1.0", features = ["raw_value"], optional = true } serde_path_to_error = { version = "0.1.8", optional = true } diff --git a/axum/src/serve.rs b/axum/src/serve.rs index d82fd0843d..70efee2be4 100644 --- a/axum/src/serve.rs +++ b/axum/src/serve.rs @@ -115,7 +115,7 @@ where tokio::task::spawn(async move { match Builder::new(TokioExecutor::new()) // upgrades needed for websockets - .serve_connection_with_upgrades(tcp_stream.into_inner(), hyper_service) + .serve_connection_with_upgrades(tcp_stream, hyper_service) .await { Ok(()) => {} From c0c12f470057ff4aa51c8422c1064040c1762134 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 18 Nov 2023 13:50:58 +0100 Subject: [PATCH 38/57] simplify RequestExt::{with_limited_body, into_limited_body} --- axum-core/src/ext_traits/request.rs | 26 +++++++++----------------- axum-core/src/extract/request_parts.rs | 18 ++++++------------ axum/src/extract/multipart.rs | 5 +---- 3 files changed, 16 insertions(+), 33 deletions(-) diff --git a/axum-core/src/ext_traits/request.rs b/axum-core/src/ext_traits/request.rs index f664f0c3ec..ed32475757 100644 --- a/axum-core/src/ext_traits/request.rs +++ b/axum-core/src/ext_traits/request.rs @@ -257,19 +257,13 @@ pub trait RequestExt: sealed::Sealed + Sized { /// Apply the [default body limit](crate::extract::DefaultBodyLimit). /// - /// If it is disabled, return the request as-is in `Err`. - /// - /// Note that while the `Ok` and `Err` types are the same, [`http_body_util::Limited`] will have - /// been applied in the `Ok` case. - fn with_limited_body(self) -> Result; + /// If it is disabled, the request is return as-is. + fn with_limited_body(self) -> Request; /// Consumes the request, returning the body wrapped in [`http_body_util::Limited`] if a /// [default limit](crate::extract::DefaultBodyLimit) is in place, or not wrapped if the /// default limit is disabled. - /// - /// Note that while the `Ok` and `Err` types are the same, [`http_body_util::Limited`] will have - /// been applied in the `Ok` case. - fn into_limited_body(self) -> Result; + fn into_limited_body(self) -> Body; } impl RequestExt for Request { @@ -325,24 +319,22 @@ impl RequestExt for Request { }) } - fn with_limited_body(self) -> Result { + fn with_limited_body(self) -> Request { // update docs in `axum-core/src/extract/default_body_limit.rs` and // `axum/src/docs/extract.md` if this changes const DEFAULT_LIMIT: usize = 2_097_152; // 2 mb match self.extensions().get::().copied() { - Some(DefaultBodyLimitKind::Disable) => Err(self), + Some(DefaultBodyLimitKind::Disable) => self, Some(DefaultBodyLimitKind::Limit(limit)) => { - Ok(self.map(|b| Body::new(http_body_util::Limited::new(b, limit)))) + self.map(|b| Body::new(http_body_util::Limited::new(b, limit))) } - None => Ok(self.map(|b| Body::new(http_body_util::Limited::new(b, DEFAULT_LIMIT)))), + None => self.map(|b| Body::new(http_body_util::Limited::new(b, DEFAULT_LIMIT))), } } - fn into_limited_body(self) -> Result { - self.with_limited_body() - .map(Request::into_body) - .map_err(Request::into_body) + fn into_limited_body(self) -> Body { + self.with_limited_body().into_body() } } diff --git a/axum-core/src/extract/request_parts.rs b/axum-core/src/extract/request_parts.rs index 1f1f7d9d7a..1950861513 100644 --- a/axum-core/src/extract/request_parts.rs +++ b/axum-core/src/extract/request_parts.rs @@ -79,18 +79,12 @@ where type Rejection = BytesRejection; async fn from_request(req: Request, _: &S) -> Result { - let bytes = match req.into_limited_body() { - Ok(limited_body) => limited_body - .collect() - .await - .map_err(FailedToBufferBody::from_err)? - .to_bytes(), - Err(unlimited_body) => unlimited_body - .collect() - .await - .map_err(FailedToBufferBody::from_err)? - .to_bytes(), - }; + let bytes = req + .into_limited_body() + .collect() + .await + .map_err(FailedToBufferBody::from_err)? + .to_bytes(); Ok(bytes) } diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 54dee8f02a..249c0aae2d 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -71,10 +71,7 @@ where async fn from_request(req: Request, _state: &S) -> Result { let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?; - let stream = match req.with_limited_body() { - Ok(limited) => limited.into_body(), - Err(unlimited) => unlimited.into_body(), - }; + let stream = req.with_limited_body().into_body(); let multipart = multer::Multipart::new(stream, boundary); Ok(Self { inner: multipart }) } From 49b88f96dc4ef124fb5401b66b04997d2dd22723 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 18 Nov 2023 13:51:58 +0100 Subject: [PATCH 39/57] Update axum-core/src/extract/rejection.rs Co-authored-by: Jonas Platte --- axum-core/src/extract/rejection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum-core/src/extract/rejection.rs b/axum-core/src/extract/rejection.rs index 511bdaa6c8..34b8115bd4 100644 --- a/axum-core/src/extract/rejection.rs +++ b/axum-core/src/extract/rejection.rs @@ -19,7 +19,7 @@ impl FailedToBufferBody { where E: Into, { - // two layers of boxes where because `with_limited_body` + // two layers of boxes here because `with_limited_body` // wraps the `http_body_util::Limited` in a `axum_core::Body` // which also wraps the error type let box_error = match err.into().downcast::() { From eb1777a1ec5b44edfd74948c95bb8b1ff8caa337 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 18 Nov 2023 13:52:29 +0100 Subject: [PATCH 40/57] Update axum/CHANGELOG.md Co-authored-by: Jonas Platte --- axum/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 536c5e4184..90d7ff3e2b 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -67,7 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **changed:** For SSE, add space between field and value for compatibility ([#2149]) - **added:** Add `NestedPath` extractor ([#1924]) - **breaking:** `impl IntoResponse(Parts) for Extension` now requires `T: - Clone`. As that is required by the http crate ([#1882]) + Clone`, as that is required by the http crate ([#1882]) [#1664]: https://github.com/tokio-rs/axum/pull/1664 [#1751]: https://github.com/tokio-rs/axum/pull/1751 From 4f639a2efa0d99cde8c3e07ff7e32277e0fa0e28 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sat, 18 Nov 2023 13:52:54 +0100 Subject: [PATCH 41/57] clean up imports --- axum/src/routing/tests/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index 8060021bfa..c6f9a68152 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -17,8 +17,7 @@ use crate::{ use axum_core::extract::Request; use futures_util::stream::StreamExt; use http::{ - header::ALLOW, - header::{CONTENT_LENGTH, HOST}, + header::{ALLOW, CONTENT_LENGTH, HOST}, HeaderMap, Method, StatusCode, Uri, }; use http_body_util::BodyExt; From 90b713d04aa405137f75bc85d01107828a5ca617 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 11:43:01 +0100 Subject: [PATCH 42/57] compression is back --- Cargo.toml | 2 +- axum/Cargo.toml | 26 +++++----- axum/src/docs/middleware.md | 2 +- axum/src/routing/tests/mod.rs | 72 ++++++++++++++-------------- examples/key-value-store/Cargo.toml | 2 +- examples/key-value-store/src/main.rs | 22 +++++---- 6 files changed, 63 insertions(+), 63 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 996a642708..bbd5dfded7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ resolver = "2" [patch.crates-io] # PR=https://github.com/tower-rs/tower-http/pull/348 -tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "602f06f248211" } +tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "30a8cadece58f" } # for http 1.0. PR to update is merged but not published headers = { git = "https://github.com/hyperium/headers", rev = "4400aa90c47a7" } diff --git a/axum/Cargo.toml b/axum/Cargo.toml index f83f1c894d..451ddd38dd 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -73,14 +73,13 @@ features = [ "add-extension", "auth", "catch-panic", - # we can bring these features back when they're implemented on https://github.com/tower-rs/tower-http/pull/348 - # "compression-br", - # "compression-deflate", - # "compression-gzip", + "compression-br", + "compression-deflate", + "compression-gzip", "cors", - # "decompression-br", - # "decompression-deflate", - # "decompression-gzip", + "decompression-br", + "decompression-deflate", + "decompression-gzip", "follow-redirect", "fs", "limit", @@ -143,14 +142,13 @@ features = [ "add-extension", "auth", "catch-panic", - # we can bring these features back when they're implemented on https://github.com/tower-rs/tower-http/pull/348 - # "compression-br", - # "compression-deflate", - # "compression-gzip", + "compression-br", + "compression-deflate", + "compression-gzip", "cors", - # "decompression-br", - # "decompression-deflate", - # "decompression-gzip", + "decompression-br", + "decompression-deflate", + "decompression-gzip", "follow-redirect", "fs", "limit", diff --git a/axum/src/docs/middleware.md b/axum/src/docs/middleware.md index 9141e16849..4496b72194 100644 --- a/axum/src/docs/middleware.md +++ b/axum/src/docs/middleware.md @@ -64,7 +64,7 @@ Some commonly used middleware are: - [`TraceLayer`](tower_http::trace) for high level tracing/logging. - [`CorsLayer`](tower_http::cors) for handling CORS. -- `CompressionLayer` for automatic compression of responses. +- [`CompressionLayer`](tower_http::compression) for automatic compression of responses. - [`RequestIdLayer`](tower_http::request_id) and [`PropagateRequestIdLayer`](tower_http::request_id) set and propagate request ids. diff --git a/axum/src/routing/tests/mod.rs b/axum/src/routing/tests/mod.rs index c6f9a68152..f53e1baa4b 100644 --- a/axum/src/routing/tests/mod.rs +++ b/axum/src/routing/tests/mod.rs @@ -590,43 +590,41 @@ async fn head_content_length_through_hyper_server_that_hits_fallback() { assert_eq!(res.headers()["content-length"], "3"); } -// TODO(david): bring this back when https://github.com/tower-rs/tower-http/pull/348 supports compression -// #[crate::test] -// #[ignore] -// async fn head_with_middleware_applied() { -// use tower_http::compression::{predicate::SizeAbove, CompressionLayer}; - -// let app = Router::new() -// .nest( -// "/", -// Router::new().route("/", get(|| async { "Hello, World!" })), -// ) -// .layer(CompressionLayer::new().compress_when(SizeAbove::new(0))); - -// let client = TestClient::new(app); - -// // send GET request -// let res = client -// .get("/") -// .header("accept-encoding", "gzip") -// .send() -// .await; -// assert_eq!(res.headers()["transfer-encoding"], "chunked"); -// // cannot have `transfer-encoding: chunked` and `content-length` -// assert!(!res.headers().contains_key("content-length")); - -// // send HEAD request -// let res = client -// .head("/") -// .header("accept-encoding", "gzip") -// .send() -// .await; -// // no response body so no `transfer-encoding` -// assert!(!res.headers().contains_key("transfer-encoding")); -// // no content-length since we cannot know it since the response -// // is compressed -// assert!(!res.headers().contains_key("content-length")); -// } +#[crate::test] +async fn head_with_middleware_applied() { + use tower_http::compression::{predicate::SizeAbove, CompressionLayer}; + + let app = Router::new() + .nest( + "/", + Router::new().route("/", get(|| async { "Hello, World!" })), + ) + .layer(CompressionLayer::new().compress_when(SizeAbove::new(0))); + + let client = TestClient::new(app); + + // send GET request + let res = client + .get("/") + .header("accept-encoding", "gzip") + .send() + .await; + assert_eq!(res.headers()["transfer-encoding"], "chunked"); + // cannot have `transfer-encoding: chunked` and `content-length` + assert!(!res.headers().contains_key("content-length")); + + // send HEAD request + let res = client + .head("/") + .header("accept-encoding", "gzip") + .send() + .await; + // no response body so no `transfer-encoding` + assert!(!res.headers().contains_key("transfer-encoding")); + // no content-length since we cannot know it since the response + // is compressed + assert!(!res.headers().contains_key("content-length")); +} #[crate::test] #[should_panic(expected = "Paths must start with a `/`")] diff --git a/examples/key-value-store/Cargo.toml b/examples/key-value-store/Cargo.toml index afa2d6cc9b..4366e3c2fc 100644 --- a/examples/key-value-store/Cargo.toml +++ b/examples/key-value-store/Cargo.toml @@ -11,7 +11,7 @@ tower = { version = "0.4", features = ["util", "timeout", "load-shed", "limit"] tower-http = { version = "0.4.4", features = [ "add-extension", "auth", - # "compression-full", + "compression-full", "limit", "trace", ] } diff --git a/examples/key-value-store/src/main.rs b/examples/key-value-store/src/main.rs index f06e731d57..1e2a5e748c 100644 --- a/examples/key-value-store/src/main.rs +++ b/examples/key-value-store/src/main.rs @@ -24,7 +24,8 @@ use std::{ }; use tower::{BoxError, ServiceBuilder}; use tower_http::{ - limit::RequestBodyLimitLayer, trace::TraceLayer, validate_request::ValidateRequestHeaderLayer, + compression::CompressionLayer, limit::RequestBodyLimitLayer, trace::TraceLayer, + validate_request::ValidateRequestHeaderLayer, }; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -44,14 +45,17 @@ async fn main() { let app = Router::new() .route( "/:key", - get(kv_get).post_service( - kv_set - .layer(( - DefaultBodyLimit::disable(), - RequestBodyLimitLayer::new(1024 * 5_000 /* ~5mb */), - )) - .with_state(Arc::clone(&shared_state)), - ), + // Add compression to `kv_get` + get(kv_get.layer(CompressionLayer::new())) + // But don't compress `kv_set` + .post_service( + kv_set + .layer(( + DefaultBodyLimit::disable(), + RequestBodyLimitLayer::new(1024 * 5_000 /* ~5mb */), + )) + .with_state(Arc::clone(&shared_state)), + ), ) .route("/keys", get(list_keys)) // Nest our admin routes under `/admin` From ae0b8904cc46c4cf73c9494c371b6e34099e9cd5 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 11:43:35 +0100 Subject: [PATCH 43/57] fix error I missed before --- axum-extra/src/extract/multipart.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 3adc8baf0c..2c86f6f085 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -99,10 +99,7 @@ where async fn from_request(req: Request, _state: &S) -> Result { let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?; - let stream = match req.with_limited_body() { - Ok(limited) => limited.into_body(), - Err(unlimited) => unlimited.into_body(), - }; + let stream = req.with_limited_body().into_body(); let multipart = multer::Multipart::new(stream, boundary); Ok(Self { inner: multipart }) } From 820fb5ad6f786977db046181d3c99c73a6fb79cf Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 12:48:20 +0100 Subject: [PATCH 44/57] update tower-http patch --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bbd5dfded7..6cd8078ee1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ resolver = "2" [patch.crates-io] # PR=https://github.com/tower-rs/tower-http/pull/348 -tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "30a8cadece58f" } +tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "77ba9a8d03f23" } # for http 1.0. PR to update is merged but not published headers = { git = "https://github.com/hyperium/headers", rev = "4400aa90c47a7" } From 5b3f2b69c8d128be964780feb3fbac77a8745ca2 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 12:49:53 +0100 Subject: [PATCH 45/57] does using `-Z direct-minimal-versions` fix msrv tests? --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 71e61b95f2..100c23cac9 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -111,7 +111,7 @@ jobs: uses: dtolnay/rust-toolchain@nightly - uses: Swatinem/rust-cache@v2 - name: Select minimal version - run: cargo +nightly update -Z minimal-versions + run: cargo +nightly update -Z direct-minimal-versions - name: Run tests run: > cargo +${{ env.MSRV }} From b49ba58a6dccc1d84d59b20f89bb4187efeec7df Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 12:56:01 +0100 Subject: [PATCH 46/57] add typos-cli config to allow some false positives --- _typos.toml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 _typos.toml diff --git a/_typos.toml b/_typos.toml new file mode 100644 index 0000000000..d411429d72 --- /dev/null +++ b/_typos.toml @@ -0,0 +1,6 @@ +[files] +extend-exclude = ["Cargo.toml"] + +[default.extend-identifiers] +DefaultOnFailedUpdgrade = "DefaultOnFailedUpdgrade" +OnFailedUpdgrade = "OnFailedUpdgrade" From 6d4fd57d6b8878b5b63b94008c3f7ed56239bc27 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 13:15:17 +0100 Subject: [PATCH 47/57] Revert "does using `-Z direct-minimal-versions` fix msrv tests?" This reverts commit 5b3f2b69c8d128be964780feb3fbac77a8745ca2. --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 100c23cac9..71e61b95f2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -111,7 +111,7 @@ jobs: uses: dtolnay/rust-toolchain@nightly - uses: Swatinem/rust-cache@v2 - name: Select minimal version - run: cargo +nightly update -Z direct-minimal-versions + run: cargo +nightly update -Z minimal-versions - name: Run tests run: > cargo +${{ env.MSRV }} From 770ec7d7b1bf6627ed1d4a16da3b7a63315d23e1 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Nov 2023 13:20:57 +0100 Subject: [PATCH 48/57] fix crc32fast --- .github/workflows/CI.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 71e61b95f2..f0fec560a8 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -112,6 +112,8 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Select minimal version run: cargo +nightly update -Z minimal-versions + - name: Fix up Cargo.lock + run: cargo +nightly update -p crc32fast --precise 1.1.1 - name: Run tests run: > cargo +${{ env.MSRV }} From 207f3467c07c8c2de706109b2e880773d93e1dfe Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 20 Nov 2023 15:29:40 +0100 Subject: [PATCH 49/57] Add [`Body::into_data_stream`] --- axum-core/src/body.rs | 45 +++++++++++++++++++++++++++-- axum-extra/src/extract/multipart.rs | 2 +- axum-extra/src/json_lines.rs | 4 +-- axum/src/extract/multipart.rs | 2 +- examples/stream-to-file/src/main.rs | 2 +- 5 files changed, 48 insertions(+), 7 deletions(-) diff --git a/axum-core/src/body.rs b/axum-core/src/body.rs index 00a1e98e54..adc79e87e3 100644 --- a/axum-core/src/body.rs +++ b/axum-core/src/body.rs @@ -66,6 +66,16 @@ impl Body { stream: SyncWrapper::new(stream), }) } + + /// Convert the body into a [`Stream`] of data frames. + /// + /// Non-data frames (such as trailers) will be discarded. Use [`http_body_util::BodyStream`] if + /// you need a [`Stream`] of all frame types. + /// + /// [`http_body_util::BodyStream`]: https://docs.rs/http-body-util/latest/http_body_util/struct.BodyStream.html + pub fn into_data_stream(self) -> BodyDataStream { + BodyDataStream { inner: self } + } } impl Default for Body { @@ -117,13 +127,21 @@ impl http_body::Body for Body { } } -impl Stream for Body { +/// A stream of data frames. +/// +/// Created with [`Body::into_data_stream`]. +#[derive(Debug)] +pub struct BodyDataStream { + inner: Body, +} + +impl Stream for BodyDataStream { type Item = Result; #[inline] fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { - match futures_util::ready!(self.as_mut().poll_frame(cx)?) { + match futures_util::ready!(Pin::new(&mut self.inner).poll_frame(cx)?) { Some(frame) => match frame.into_data() { Ok(data) => return Poll::Ready(Some(Ok(data))), Err(_frame) => {} @@ -134,6 +152,29 @@ impl Stream for Body { } } +impl http_body::Body for BodyDataStream { + type Data = Bytes; + type Error = Error; + + #[inline] + fn poll_frame( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + Pin::new(&mut self.inner).poll_frame(cx) + } + + #[inline] + fn is_end_stream(&self) -> bool { + self.inner.is_end_stream() + } + + #[inline] + fn size_hint(&self) -> http_body::SizeHint { + self.inner.size_hint() + } +} + pin_project! { struct StreamBody { #[pin] diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 2c86f6f085..8c78a77722 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -100,7 +100,7 @@ where async fn from_request(req: Request, _state: &S) -> Result { let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?; let stream = req.with_limited_body().into_body(); - let multipart = multer::Multipart::new(stream, boundary); + let multipart = multer::Multipart::new(stream.into_data_stream(), boundary); Ok(Self { inner: multipart }) } } diff --git a/axum-extra/src/json_lines.rs b/axum-extra/src/json_lines.rs index f7bc506d1b..d72c23b6c6 100644 --- a/axum-extra/src/json_lines.rs +++ b/axum-extra/src/json_lines.rs @@ -111,8 +111,8 @@ where // `Stream::lines` isn't a thing so we have to convert it into an `AsyncRead` // so we can call `AsyncRead::lines` and then convert it back to a `Stream` let body = req.into_body(); - - let stream = TryStreamExt::map_err(body, |err| io::Error::new(io::ErrorKind::Other, err)); + let stream = body.into_data_stream(); + let stream = stream.map_err(|err| io::Error::new(io::ErrorKind::Other, err)); let read = StreamReader::new(stream); let lines_stream = LinesStream::new(read.lines()); diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 249c0aae2d..227e983a4b 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -72,7 +72,7 @@ where async fn from_request(req: Request, _state: &S) -> Result { let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?; let stream = req.with_limited_body().into_body(); - let multipart = multer::Multipart::new(stream, boundary); + let multipart = multer::Multipart::new(stream.into_data_stream(), boundary); Ok(Self { inner: multipart }) } } diff --git a/examples/stream-to-file/src/main.rs b/examples/stream-to-file/src/main.rs index f3263c4deb..a595d0d834 100644 --- a/examples/stream-to-file/src/main.rs +++ b/examples/stream-to-file/src/main.rs @@ -53,7 +53,7 @@ async fn save_request_body( Path(file_name): Path, request: Request, ) -> Result<(), (StatusCode, String)> { - stream_to_file(&file_name, request.into_body()).await + stream_to_file(&file_name, request.into_body().into_data_stream()).await } // Handler that returns HTML for a multipart form. From f93a24277ccf76afb53ec7cc907dc54db0e870d3 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Tue, 21 Nov 2023 15:58:44 +0100 Subject: [PATCH 50/57] use published tower-http 0.5 --- Cargo.toml | 3 --- axum-core/Cargo.toml | 4 ++-- axum-extra/Cargo.toml | 2 +- axum/Cargo.toml | 4 ++-- examples/consume-body-in-extractor-or-middleware/Cargo.toml | 2 +- examples/cors/Cargo.toml | 2 +- examples/key-value-store/Cargo.toml | 2 +- examples/multipart-form/Cargo.toml | 2 +- examples/reqwest-response/Cargo.toml | 2 +- examples/sse/Cargo.toml | 2 +- examples/static-file-server/Cargo.toml | 2 +- examples/testing/Cargo.toml | 2 +- examples/todos/Cargo.toml | 2 +- examples/tracing-aka-logging/Cargo.toml | 2 +- examples/websockets/Cargo.toml | 2 +- 15 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6cd8078ee1..a6e3812efd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,6 @@ exclude = ["examples/async-graphql"] resolver = "2" [patch.crates-io] -# PR=https://github.com/tower-rs/tower-http/pull/348 -tower-http = { git = "https://github.com/tower-rs/tower-http", rev = "77ba9a8d03f23" } - # for http 1.0. PR to update is merged but not published headers = { git = "https://github.com/hyperium/headers", rev = "4400aa90c47a7" } diff --git a/axum-core/Cargo.toml b/axum-core/Cargo.toml index 55f820a383..5295f61720 100644 --- a/axum-core/Cargo.toml +++ b/axum-core/Cargo.toml @@ -31,7 +31,7 @@ tower-layer = "0.3" tower-service = "0.3" # optional dependencies -tower-http = { version = "0.4.4", optional = true, features = ["limit"] } +tower-http = { version = "0.5.0", optional = true, features = ["limit"] } tracing = { version = "0.1.37", default-features = false, optional = true } [build-dependencies] @@ -43,7 +43,7 @@ axum-extra = { path = "../axum-extra", features = ["typed-header"] } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } hyper = "1.0.0" tokio = { version = "1.25.0", features = ["macros"] } -tower-http = { version = "0.4.4", features = ["limit"] } +tower-http = { version = "0.5.0", features = ["limit"] } [package.metadata.cargo-public-api-crates] allowed = [ diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index 8929e95f73..68ef579715 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -72,7 +72,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.71" tokio = { version = "1.14", features = ["full"] } tower = { version = "0.4", features = ["util"] } -tower-http = { version = "0.4.4", features = ["map-response-body", "timeout"] } +tower-http = { version = "0.5.0", features = ["map-response-body", "timeout"] } [package.metadata.docs.rs] all-features = true diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 451ddd38dd..b4b671547a 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -65,7 +65,7 @@ tokio-tungstenite = { version = "0.20", optional = true } tracing = { version = "0.1", default-features = false, optional = true } [dependencies.tower-http] -version = "0.4.4" +version = "0.5.0" optional = true features = [ # all tower-http features except (de)?compression-zstd which doesn't @@ -135,7 +135,7 @@ features = [ ] [dev-dependencies.tower-http] -version = "0.4.4" +version = "0.5.0" features = [ # all tower-http features except (de)?compression-zstd which doesn't # build on `--target armv5te-unknown-linux-musleabi` diff --git a/examples/consume-body-in-extractor-or-middleware/Cargo.toml b/examples/consume-body-in-extractor-or-middleware/Cargo.toml index 50d5d4d2b5..9aeb864d61 100644 --- a/examples/consume-body-in-extractor-or-middleware/Cargo.toml +++ b/examples/consume-body-in-extractor-or-middleware/Cargo.toml @@ -10,6 +10,6 @@ http-body-util = "0.1.0" hyper = "1.0.0" tokio = { version = "1.0", features = ["full"] } tower = "0.4" -tower-http = { version = "0.4.4", features = ["map-request-body", "util"] } +tower-http = { version = "0.5.0", features = ["map-request-body", "util"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/cors/Cargo.toml b/examples/cors/Cargo.toml index b56f3a9410..5d5d2edae5 100644 --- a/examples/cors/Cargo.toml +++ b/examples/cors/Cargo.toml @@ -7,4 +7,4 @@ publish = false [dependencies] axum = { path = "../../axum" } tokio = { version = "1.0", features = ["full"] } -tower-http = { version = "0.4.4", features = ["cors"] } +tower-http = { version = "0.5.0", features = ["cors"] } diff --git a/examples/key-value-store/Cargo.toml b/examples/key-value-store/Cargo.toml index 4366e3c2fc..c23b28d268 100644 --- a/examples/key-value-store/Cargo.toml +++ b/examples/key-value-store/Cargo.toml @@ -8,7 +8,7 @@ publish = false axum = { path = "../../axum" } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util", "timeout", "load-shed", "limit"] } -tower-http = { version = "0.4.4", features = [ +tower-http = { version = "0.5.0", features = [ "add-extension", "auth", "compression-full", diff --git a/examples/multipart-form/Cargo.toml b/examples/multipart-form/Cargo.toml index 80d9519663..d93b9c08e8 100644 --- a/examples/multipart-form/Cargo.toml +++ b/examples/multipart-form/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] axum = { path = "../../axum", features = ["multipart"] } tokio = { version = "1.0", features = ["full"] } -tower-http = { version = "0.4.4", features = ["limit", "trace"] } +tower-http = { version = "0.5.0", features = ["limit", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/reqwest-response/Cargo.toml b/examples/reqwest-response/Cargo.toml index 8be5ce0ea2..18320e9f66 100644 --- a/examples/reqwest-response/Cargo.toml +++ b/examples/reqwest-response/Cargo.toml @@ -9,6 +9,6 @@ axum = { path = "../../axum" } reqwest = { version = "0.11", features = ["stream"] } tokio = { version = "1.0", features = ["full"] } tokio-stream = "0.1" -tower-http = { version = "0.4.4", features = ["trace"] } +tower-http = { version = "0.5.0", features = ["trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/sse/Cargo.toml b/examples/sse/Cargo.toml index 3976ecd45b..66ec56ab4a 100644 --- a/examples/sse/Cargo.toml +++ b/examples/sse/Cargo.toml @@ -11,6 +11,6 @@ futures = "0.3" headers = "0.3" tokio = { version = "1.0", features = ["full"] } tokio-stream = "0.1" -tower-http = { version = "0.4.4", features = ["fs", "trace"] } +tower-http = { version = "0.5.0", features = ["fs", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/static-file-server/Cargo.toml b/examples/static-file-server/Cargo.toml index b9e31b7de8..3f41d60816 100644 --- a/examples/static-file-server/Cargo.toml +++ b/examples/static-file-server/Cargo.toml @@ -9,6 +9,6 @@ axum = { path = "../../axum" } axum-extra = { path = "../../axum-extra" } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util"] } -tower-http = { version = "0.4.4", features = ["fs", "trace"] } +tower-http = { version = "0.5.0", features = ["fs", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/testing/Cargo.toml b/examples/testing/Cargo.toml index af6ea5acd4..0bdb6ed34a 100644 --- a/examples/testing/Cargo.toml +++ b/examples/testing/Cargo.toml @@ -11,7 +11,7 @@ hyper = { version = "1.0.0", features = ["full"] } mime = "0.3" serde_json = "1.0" tokio = { version = "1.0", features = ["full"] } -tower-http = { version = "0.4.4", features = ["trace"] } +tower-http = { version = "0.5.0", features = ["trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml index af3cc56452..dbd8b7125a 100644 --- a/examples/todos/Cargo.toml +++ b/examples/todos/Cargo.toml @@ -9,7 +9,7 @@ axum = { path = "../../axum" } serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util", "timeout"] } -tower-http = { version = "0.4.4", features = ["add-extension", "trace"] } +tower-http = { version = "0.5.0", features = ["add-extension", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } uuid = { version = "1.0", features = ["serde", "v4"] } diff --git a/examples/tracing-aka-logging/Cargo.toml b/examples/tracing-aka-logging/Cargo.toml index fb50adafeb..4004cd596b 100644 --- a/examples/tracing-aka-logging/Cargo.toml +++ b/examples/tracing-aka-logging/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] axum = { path = "../../axum", features = ["tracing"] } tokio = { version = "1.0", features = ["full"] } -tower-http = { version = "0.4.4", features = ["trace"] } +tower-http = { version = "0.5.0", features = ["trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/examples/websockets/Cargo.toml b/examples/websockets/Cargo.toml index 24f343dc3a..22c8fd5680 100644 --- a/examples/websockets/Cargo.toml +++ b/examples/websockets/Cargo.toml @@ -13,7 +13,7 @@ headers = "0.3" tokio = { version = "1.0", features = ["full"] } tokio-tungstenite = "0.20" tower = { version = "0.4", features = ["util"] } -tower-http = { version = "0.4.4", features = ["fs", "trace"] } +tower-http = { version = "0.5.0", features = ["fs", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } From b9983b6bc6d202f94f029bed6c6fa685aa0818c8 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 22 Nov 2023 23:03:41 +0100 Subject: [PATCH 51/57] convert multer's Field headers to http 1.0 --- Cargo.toml | 3 --- axum-extra/src/extract/multipart.rs | 22 +++++++++++++++++++--- axum/src/extract/multipart.rs | 25 +++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a6e3812efd..f9e9c8b59e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,3 @@ resolver = "2" [patch.crates-io] # for http 1.0. PR to update is merged but not published headers = { git = "https://github.com/hyperium/headers", rev = "4400aa90c47a7" } - -# for http 1.0. PR=https://github.com/rousan/multer-rs/pull/59 -multer = { git = "https://github.com/davidpdrsn/multer-rs", rev = "abe0e3a42a1fc" } diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 8c78a77722..3f9e10bef2 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -12,7 +12,7 @@ use axum::{ use futures_util::stream::Stream; use http::{ header::{HeaderMap, CONTENT_TYPE}, - Request, StatusCode, + HeaderName, HeaderValue, Request, StatusCode, }; use std::{ error::Error, @@ -115,7 +115,22 @@ impl Multipart { .map_err(MultipartError::from_multer)?; if let Some(field) = field { - Ok(Some(Field { inner: field })) + // multer still uses http 0.2 which means we cannot directly expose + // `multer::Field::headers`. Instead we have to eagerly convert the headers into http + // 1.0 + // + // When the next major version of multer is published we can remove this. + let mut headers = HeaderMap::with_capacity(field.headers().len()); + headers.extend(field.headers().clone().into_iter().map(|(name, value)| { + let name = name.map(|name| HeaderName::from_bytes(name.as_ref()).unwrap()); + let value = HeaderValue::from_bytes(value.as_ref()).unwrap(); + (name, value) + })); + + Ok(Some(Field { + inner: field, + headers, + })) } else { Ok(None) } @@ -134,6 +149,7 @@ impl Multipart { #[derive(Debug)] pub struct Field { inner: multer::Field<'static>, + headers: HeaderMap, } impl Stream for Field { @@ -168,7 +184,7 @@ impl Field { /// Get a map of headers as [`HeaderMap`]. pub fn headers(&self) -> &HeaderMap { - self.inner.headers() + &self.headers } /// Get the full data of the field as [`Bytes`]. diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 227e983a4b..2231f4eec5 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -11,6 +11,8 @@ use axum_core::response::{IntoResponse, Response}; use axum_core::RequestExt; use futures_util::stream::Stream; use http::header::{HeaderMap, CONTENT_TYPE}; +use http::HeaderName; +use http::HeaderValue; use http::StatusCode; use std::error::Error; use std::{ @@ -87,8 +89,21 @@ impl Multipart { .map_err(MultipartError::from_multer)?; if let Some(field) = field { + // multer still uses http 0.2 which means we cannot directly expose + // `multer::Field::headers`. Instead we have to eagerly convert the headers into http + // 1.0 + // + // When the next major version of multer is published we can remove this. + let mut headers = HeaderMap::with_capacity(field.headers().len()); + headers.extend(field.headers().clone().into_iter().map(|(name, value)| { + let name = name.map(|name| HeaderName::from_bytes(name.as_ref()).unwrap()); + let value = HeaderValue::from_bytes(value.as_ref()).unwrap(); + (name, value) + })); + Ok(Some(Field { inner: field, + headers, _multipart: self, })) } else { @@ -101,6 +116,7 @@ impl Multipart { #[derive(Debug)] pub struct Field<'a> { inner: multer::Field<'static>, + headers: HeaderMap, // multer requires there to only be one live `multer::Field` at any point. This enforces that // statically, which multer does not do, it returns an error instead. _multipart: &'a mut Multipart, @@ -138,7 +154,7 @@ impl<'a> Field<'a> { /// Get a map of headers as [`HeaderMap`]. pub fn headers(&self) -> &HeaderMap { - self.inner.headers() + &self.headers } /// Get the full data of the field as [`Bytes`]. @@ -320,6 +336,7 @@ mod tests { assert_eq!(field.file_name().unwrap(), FILE_NAME); assert_eq!(field.content_type().unwrap(), CONTENT_TYPE); + assert_eq!(field.headers()["foo"], "bar"); assert_eq!(field.bytes().await.unwrap(), BYTES); assert!(multipart.next_field().await.unwrap().is_none()); @@ -334,7 +351,11 @@ mod tests { reqwest::multipart::Part::bytes(BYTES) .file_name(FILE_NAME) .mime_str(CONTENT_TYPE) - .unwrap(), + .unwrap() + .headers(reqwest::header::HeaderMap::from_iter([( + reqwest::header::HeaderName::from_static("foo"), + reqwest::header::HeaderValue::from_static("bar"), + )])), ); client.post("/").multipart(form).send().await; From 8879fa47eadee33542db33c85720c3624df6f53b Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 23 Nov 2023 11:42:04 +0100 Subject: [PATCH 52/57] avoid some clones --- axum-extra/src/extract/multipart.rs | 4 ++-- axum/src/extract/multipart.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 3f9e10bef2..86725cab23 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -121,8 +121,8 @@ impl Multipart { // // When the next major version of multer is published we can remove this. let mut headers = HeaderMap::with_capacity(field.headers().len()); - headers.extend(field.headers().clone().into_iter().map(|(name, value)| { - let name = name.map(|name| HeaderName::from_bytes(name.as_ref()).unwrap()); + headers.extend(field.headers().into_iter().map(|(name, value)| { + let name = HeaderName::from_bytes(name.as_ref()).unwrap(); let value = HeaderValue::from_bytes(value.as_ref()).unwrap(); (name, value) })); diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 2231f4eec5..15976826f7 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -95,8 +95,8 @@ impl Multipart { // // When the next major version of multer is published we can remove this. let mut headers = HeaderMap::with_capacity(field.headers().len()); - headers.extend(field.headers().clone().into_iter().map(|(name, value)| { - let name = name.map(|name| HeaderName::from_bytes(name.as_ref()).unwrap()); + headers.extend(field.headers().into_iter().map(|(name, value)| { + let name = HeaderName::from_bytes(name.as_ref()).unwrap(); let value = HeaderValue::from_bytes(value.as_ref()).unwrap(); (name, value) })); From 3c1c45345905a17bd0b246f37abc92415b54c5f3 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 23 Nov 2023 11:43:14 +0100 Subject: [PATCH 53/57] bring back CompressionLayer in example --- axum/src/docs/routing/layer.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/axum/src/docs/routing/layer.md b/axum/src/docs/routing/layer.md index 98b27be5bc..1c029c7ff4 100644 --- a/axum/src/docs/routing/layer.md +++ b/axum/src/docs/routing/layer.md @@ -32,21 +32,20 @@ If you only want middleware on some routes you can use [`Router::merge`]: ```rust use axum::{routing::get, Router}; -use tower_http::{trace::TraceLayer, timeout::TimeoutLayer}; -use std::time::Duration; +use tower_http::{trace::TraceLayer, compression::CompressionLayer}; let with_tracing = Router::new() .route("/foo", get(|| async {})) .layer(TraceLayer::new_for_http()); -let with_timeout = Router::new() +let with_compression = Router::new() .route("/bar", get(|| async {})) - .layer(TimeoutLayer::new(Duration::from_secs(10))); + .layer(CompressionLayer::new()); // Merge everything into one `Router` let app = Router::new() .merge(with_tracing) - .merge(with_timeout); + .merge(with_compression); # let _: Router = app; ``` From dee6bffcbc50b51d5c4318fb00052a66bf8507f3 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 23 Nov 2023 11:44:44 +0100 Subject: [PATCH 54/57] format imports --- axum/src/extract/multipart.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index 15976826f7..5c7ae7267c 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -5,17 +5,18 @@ use super::{FromRequest, Request}; use crate::body::Bytes; use async_trait::async_trait; -use axum_core::__composite_rejection as composite_rejection; -use axum_core::__define_rejection as define_rejection; -use axum_core::response::{IntoResponse, Response}; -use axum_core::RequestExt; +use axum_core::{ + __composite_rejection as composite_rejection, __define_rejection as define_rejection, + response::{IntoResponse, Response}, + RequestExt, +}; use futures_util::stream::Stream; -use http::header::{HeaderMap, CONTENT_TYPE}; -use http::HeaderName; -use http::HeaderValue; -use http::StatusCode; -use std::error::Error; +use http::{ + header::{HeaderMap, CONTENT_TYPE}, + HeaderName, HeaderValue, StatusCode, +}; use std::{ + error::Error, fmt, pin::Pin, task::{Context, Poll}, From 4a02fba8af1a9690a9ca8e95f49e1849ae40152f Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 23 Nov 2023 11:51:09 +0100 Subject: [PATCH 55/57] make `serve` also work with http2 only --- axum/src/extract/connect_info.rs | 2 +- axum/src/handler/service.rs | 2 +- axum/src/lib.rs | 4 ++-- axum/src/routing/method_routing.rs | 2 +- axum/src/routing/mod.rs | 2 +- axum/src/serve.rs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/axum/src/extract/connect_info.rs b/axum/src/extract/connect_info.rs index be8e78e0e5..0085b3f869 100644 --- a/axum/src/extract/connect_info.rs +++ b/axum/src/extract/connect_info.rs @@ -83,7 +83,7 @@ pub trait Connected: Clone + Send + Sync + 'static { fn connect_info(target: T) -> Self; } -#[cfg(all(feature = "tokio", feature = "http1"))] +#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] const _: () = { use crate::serve::IncomingStream; diff --git a/axum/src/handler/service.rs b/axum/src/handler/service.rs index 9c148270e2..e6b8df9316 100644 --- a/axum/src/handler/service.rs +++ b/axum/src/handler/service.rs @@ -178,7 +178,7 @@ where } // for `axum::serve(listener, handler)` -#[cfg(all(feature = "tokio", feature = "http1"))] +#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] const _: () = { use crate::serve::IncomingStream; diff --git a/axum/src/lib.rs b/axum/src/lib.rs index 644b81ae1b..fa541e9a01 100644 --- a/axum/src/lib.rs +++ b/axum/src/lib.rs @@ -440,7 +440,7 @@ pub mod handler; pub mod middleware; pub mod response; pub mod routing; -#[cfg(all(feature = "tokio", feature = "http1"))] +#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] pub mod serve; #[cfg(test)] @@ -469,7 +469,7 @@ pub use axum_core::{BoxError, Error, RequestExt, RequestPartsExt}; #[cfg(feature = "macros")] pub use axum_macros::debug_handler; -#[cfg(all(feature = "tokio", feature = "http1"))] +#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] #[doc(inline)] pub use self::serve::serve; diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 8320984f3c..962a440111 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -1225,7 +1225,7 @@ where } // for `axum::serve(listener, router)` -#[cfg(all(feature = "tokio", feature = "http1"))] +#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] const _: () = { use crate::serve::IncomingStream; diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 6dc6c36688..3984805700 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -428,7 +428,7 @@ impl Router { } // for `axum::serve(listener, router)` -#[cfg(all(feature = "tokio", feature = "http1"))] +#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] const _: () = { use crate::serve::IncomingStream; diff --git a/axum/src/serve.rs b/axum/src/serve.rs index 70efee2be4..f28c1c2ba2 100644 --- a/axum/src/serve.rs +++ b/axum/src/serve.rs @@ -85,7 +85,7 @@ use tower_service::Service; /// [`Handler`]: crate::handler::Handler /// [`HandlerWithoutStateExt::into_make_service_with_connect_info`]: crate::handler::HandlerWithoutStateExt::into_make_service_with_connect_info /// [`HandlerService::into_make_service_with_connect_info`]: crate::handler::HandlerService::into_make_service_with_connect_info -#[cfg(all(feature = "tokio", feature = "http1"))] +#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] pub async fn serve(tcp_listener: TcpListener, mut make_service: M) -> io::Result<()> where M: for<'a> Service, Error = Infallible, Response = S>, From 10bb47796ebca68045100a2b4caacba6b41a2855 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 23 Nov 2023 11:54:16 +0100 Subject: [PATCH 56/57] Apply suggestions from code review Co-authored-by: Jonas Platte --- axum-core/src/ext_traits/request.rs | 2 +- axum/CHANGELOG.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/axum-core/src/ext_traits/request.rs b/axum-core/src/ext_traits/request.rs index ed32475757..5b7aee783a 100644 --- a/axum-core/src/ext_traits/request.rs +++ b/axum-core/src/ext_traits/request.rs @@ -257,7 +257,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// Apply the [default body limit](crate::extract::DefaultBodyLimit). /// - /// If it is disabled, the request is return as-is. + /// If it is disabled, the request is returned as-is. fn with_limited_body(self) -> Request; /// Consumes the request, returning the body wrapped in [`http_body_util::Limited`] if a diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index ae7f3edc23..b561dd8ac8 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -66,8 +66,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **added:** Implement `IntoResponse` for `(R,) where R: IntoResponse` ([#2143]) - **changed:** For SSE, add space between field and value for compatibility ([#2149]) - **added:** Add `NestedPath` extractor ([#1924]) -- **breaking:** `impl IntoResponse(Parts) for Extension` now requires `T: - Clone`, as that is required by the http crate ([#1882]) +- **breaking:** `impl IntoResponse(Parts) for Extension` now requires + `T: Clone`, as that is required by the http crate ([#1882]) - **added:** Add `axum::Json::from_bytes` ([#2244]) [#1664]: https://github.com/tokio-rs/axum/pull/1664 From 64b2e3977f64cdcabc839c7ab22578b2507ce4d2 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 23 Nov 2023 11:54:45 +0100 Subject: [PATCH 57/57] Update axum/src/lib.rs Co-authored-by: Jonas Platte --- axum/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/axum/src/lib.rs b/axum/src/lib.rs index fa541e9a01..3576c85aee 100644 --- a/axum/src/lib.rs +++ b/axum/src/lib.rs @@ -312,8 +312,7 @@ //! tower = "" //! ``` //! -//! The `"full"` feature for tokio isn't strictly necessary but it's the easiest way to get -//! started. +//! The `"full"` feature for tokio isn't necessary but it's the easiest way to get started. //! //! Tower isn't strictly necessary either but helpful for testing. See the //! testing example in the repo to learn more about testing axum apps.