From 9c2c0fd08d1190251639e8d9b5311b6eaf5a6499 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 25 Nov 2022 21:29:55 +0100 Subject: [PATCH 01/15] Start working on v0.7.0 From 29d03be2721de318fc89f2ab6b53aa19965a4bd4 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 27 Nov 2022 11:57:47 +0100 Subject: [PATCH 02/15] 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 daff377ce85b98d11732dbef9d781ca55e9eff33 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 16 Feb 2023 23:32:05 +0100 Subject: [PATCH 03/15] 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 cd6d321c0c..6ef7065f36 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.10 (03. March, 2023) diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 7404f6238e..1ef79b9ad3 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -183,7 +183,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 bbe2c5f239..f4804be756 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 d87dac66dfbbb2a9d947c0ad82163babf020fabb Mon Sep 17 00:00:00 2001 From: Michael Scofield Date: Fri, 24 Feb 2023 22:35:31 +0100 Subject: [PATCH 04/15] 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 6ef7065f36..4aa76018ff 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.10 (03. March, 2023) diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 5c1167c4bc..9ed0780f49 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 c8cd23ff430fc6316d3a4ec1cc67bde325fe17c3 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 12 Mar 2023 16:37:32 +0100 Subject: [PATCH 05/15] 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 | 3 + 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 | 304 +++++++++++++----- 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, 948 insertions(+), 1143 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 268915489e..b542580152 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()) @@ -141,7 +141,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 aec7d37ef7..c4d13fd39b 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 extraxtors. /// -/// # 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 8afe112a62..fb221ccd69 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 ff74e30ee7..2085c59079 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, }; @@ -90,22 +90,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 }) } @@ -393,6 +389,6 @@ 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)); } } 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 99244287a2..f44f26d00e 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 3203bd8d23..0379363370 100644 --- a/axum-macros/CHANGELOG.md +++ b/axum-macros/CHANGELOG.md @@ -7,10 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased +- **breaking:** `#[debug_handler]` no longer accepts a `body = _` argument. The + body type is always `axum::body::Body` ([#1751]) - **fixed:** Improve `#[debug_handler]` message for known generic 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 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 2fe5de10fc..e291715e67 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 4aa76018ff..9ffad57ca0 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.10 (03. 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 6ad2f69581..d343f3ad19 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 06af699fc1..e06a2cb9f2 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 0d1245ec5d..5ac912bb31 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 2561b5ce9e..a80e51557e 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -2,10 +2,10 @@ //! //! 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::body::Body; use axum_core::RequestExt; use futures_util::stream::Stream; use http::header::{HeaderMap, CONTENT_TYPE}; @@ -55,22 +55,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 }) } @@ -248,7 +244,7 @@ define_rejection! { #[cfg(test)] mod tests { 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() { @@ -284,6 +280,8 @@ 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)); } } 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 981e88f029..888a3ae5d4 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::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 0777a5115a..3a9fcaf665 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 where S: fmt::Debug, { @@ -93,9 +93,8 @@ where 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`. @@ -113,7 +112,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() { @@ -155,11 +154,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. \ @@ -173,7 +172,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('/') { @@ -198,15 +197,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, { @@ -214,13 +213,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, { @@ -271,7 +266,7 @@ where #[track_caller] pub fn merge(mut self, other: R) -> Self where - R: Into>, + R: Into>, { let Router { routes, @@ -303,14 +298,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 @@ -335,11 +329,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!( @@ -368,7 +362,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)); @@ -380,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, Error = Infallible> + Clone + Send + 'static, T::Response: IntoResponse, T::Future: Send + 'static, { @@ -389,12 +383,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())) } @@ -419,9 +413,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; @@ -457,8 +451,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 { @@ -475,9 +468,9 @@ where fn call_route( &self, match_: matchit::Match<&RouteId>, - mut req: Request, + mut req: Request, state: S, - ) -> RouteFuture { + ) -> RouteFuture { let id = *match_.value; #[cfg(feature = "matched-path")] @@ -511,12 +504,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. /// @@ -555,13 +617,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> { @@ -570,10 +633,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 { @@ -615,13 +757,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, { @@ -633,13 +775,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 { @@ -649,7 +789,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), @@ -658,7 +798,7 @@ where } } -impl Clone for Fallback { +impl Clone for Fallback { fn clone(&self) -> Self { match self { Self::Default(inner) => Self::Default(inner.clone()), @@ -668,7 +808,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(), @@ -679,25 +819,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) => { @@ -711,7 +849,7 @@ where } } -impl Clone for Endpoint { +impl Clone for Endpoint { fn clone(&self) -> Self { match self { Self::MethodRouter(inner) => Self::MethodRouter(inner.clone()), @@ -721,7 +859,7 @@ impl Clone for Endpoint { } } -impl fmt::Debug for Endpoint +impl fmt::Debug for Endpoint where S: fmt::Debug, { @@ -736,16 +874,16 @@ where } } -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 33770e3344..2711ed9c6f 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))); @@ -621,7 +620,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) @@ -645,7 +644,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; @@ -686,14 +685,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); @@ -717,7 +716,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); @@ -725,7 +724,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 efdd3a1d2febc745bd7f71e6e7d990d25e703cec Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 13 Mar 2023 23:14:16 +0100 Subject: [PATCH 06/15] move `TypedHeader` to axum-extra --- axum-core/Cargo.toml | 3 +- axum-core/src/ext_traits/request.rs | 18 ++--- axum-core/src/ext_traits/request_parts.rs | 13 ++-- axum-extra/CHANGELOG.md | 2 +- axum-extra/Cargo.toml | 4 +- axum-extra/src/extract/cookie/mod.rs | 10 ++- axum-extra/src/extract/cookie/private.rs | 11 ++- axum-extra/src/extract/cookie/signed.rs | 11 ++- axum-extra/src/extract/mod.rs | 4 + axum-extra/src/extract/multipart.rs | 4 +- axum-extra/src/lib.rs | 4 + axum-extra/src/response/mod.rs | 4 + {axum => axum-extra}/src/typed_header.rs | 91 +++++++++++++++++------ axum-macros/Cargo.toml | 4 +- axum-macros/src/lib.rs | 24 ++++-- axum/CHANGELOG.md | 1 + axum/Cargo.toml | 2 - axum/src/docs/extract.md | 74 ++---------------- axum/src/lib.rs | 10 --- axum/src/middleware/from_fn.rs | 26 ++++--- examples/jwt/Cargo.toml | 4 +- examples/jwt/src/main.rs | 7 +- examples/oauth/Cargo.toml | 4 +- examples/oauth/src/main.rs | 5 +- examples/sessions/Cargo.toml | 3 +- examples/sessions/src/main.rs | 4 +- examples/sse/Cargo.toml | 3 +- examples/sse/src/main.rs | 2 +- examples/websockets/Cargo.toml | 3 +- examples/websockets/src/main.rs | 6 +- 30 files changed, 191 insertions(+), 170 deletions(-) rename {axum => axum-extra}/src/typed_header.rs (71%) diff --git a/axum-core/Cargo.toml b/axum-core/Cargo.toml index 6ead90a77a..95647a1346 100644 --- a/axum-core/Cargo.toml +++ b/axum-core/Cargo.toml @@ -25,7 +25,8 @@ tower-service = "0.3" rustversion = "1.0.9" [dev-dependencies] -axum = { path = "../axum", version = "0.6.0", features = ["headers"] } +axum = { path = "../axum", version = "0.6.0" } +axum-extra = { path = "../axum-extra", features = ["typed-header"] } futures-util = "0.3" hyper = "0.14.24" tokio = { version = "1.25.0", features = ["macros"] } diff --git a/axum-core/src/ext_traits/request.rs b/axum-core/src/ext_traits/request.rs index 1c69abd54e..de4fbd778e 100644 --- a/axum-core/src/ext_traits/request.rs +++ b/axum-core/src/ext_traits/request.rs @@ -141,16 +141,16 @@ pub trait RequestExt: sealed::Sealed + Sized { /// ``` /// use axum::{ /// async_trait, - /// extract::FromRequest, - /// headers::{authorization::Bearer, Authorization}, + /// extract::{Path, FromRequest}, /// http::Request, /// response::{IntoResponse, Response}, /// body::Body, - /// Json, RequestExt, TypedHeader, + /// Json, RequestExt, /// }; + /// use std::collections::HashMap; /// /// struct MyExtractor { - /// bearer_token: String, + /// path_params: HashMap, /// payload: T, /// } /// @@ -164,9 +164,10 @@ pub trait RequestExt: sealed::Sealed + Sized { /// type Rejection = Response; /// /// async fn from_request(mut req: Request, _state: &S) -> Result { - /// let TypedHeader(auth_header) = req - /// .extract_parts::>>() + /// let path_params = req + /// .extract_parts::>>() /// .await + /// .map(|Path(path_params)| path_params) /// .map_err(|err| err.into_response())?; /// /// let Json(payload) = req @@ -174,10 +175,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// .await /// .map_err(|err| err.into_response())?; /// - /// Ok(Self { - /// bearer_token: auth_header.token().to_owned(), - /// payload, - /// }) + /// Ok(Self { path_params, payload }) /// } /// } /// ``` diff --git a/axum-core/src/ext_traits/request_parts.rs b/axum-core/src/ext_traits/request_parts.rs index 07a7dbff30..e7063f4d8b 100644 --- a/axum-core/src/ext_traits/request_parts.rs +++ b/axum-core/src/ext_traits/request_parts.rs @@ -17,9 +17,8 @@ pub trait RequestPartsExt: sealed::Sealed + Sized { /// /// ``` /// use axum::{ - /// extract::{Query, TypedHeader, FromRequestParts}, + /// extract::{Query, Path, FromRequestParts}, /// response::{Response, IntoResponse}, - /// headers::UserAgent, /// http::request::Parts, /// RequestPartsExt, /// async_trait, @@ -27,7 +26,7 @@ pub trait RequestPartsExt: sealed::Sealed + Sized { /// use std::collections::HashMap; /// /// struct MyExtractor { - /// user_agent: String, + /// path_params: HashMap, /// query_params: HashMap, /// } /// @@ -39,10 +38,10 @@ pub trait RequestPartsExt: sealed::Sealed + Sized { /// type Rejection = Response; /// /// async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - /// let user_agent = parts - /// .extract::>() + /// let path_params = parts + /// .extract::>>() /// .await - /// .map(|user_agent| user_agent.as_str().to_owned()) + /// .map(|Path(path_params)| path_params) /// .map_err(|err| err.into_response())?; /// /// let query_params = parts @@ -51,7 +50,7 @@ pub trait RequestPartsExt: sealed::Sealed + Sized { /// .map(|Query(params)| params) /// .map_err(|err| err.into_response())?; /// - /// Ok(MyExtractor { user_agent, query_params }) + /// Ok(MyExtractor { path_params, query_params }) /// } /// } /// ``` diff --git a/axum-extra/CHANGELOG.md b/axum-extra/CHANGELOG.md index d69892e2b3..f52cf9e54f 100644 --- a/axum-extra/CHANGELOG.md +++ b/axum-extra/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning]. # Unreleased -- None. +- **added:** Added `TypedHeader` which used to be in `axum` # 0.7.0 (03. March, 2023) diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index db12300c34..1782ea624b 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -32,6 +32,7 @@ json-lines = [ multipart = ["dep:multer"] protobuf = ["dep:prost"] query = ["dep:serde", "dep:serde_html_form"] +typed-header = ["dep:headers"] typed-routing = ["dep:axum-macros", "dep:serde", "dep:percent-encoding", "dep:serde_html_form", "dep:form_urlencoded"] [dependencies] @@ -51,6 +52,7 @@ tower-service = "0.3" axum-macros = { path = "../axum-macros", version = "0.3.5", optional = true } cookie = { package = "cookie", version = "0.17", features = ["percent-encode"], optional = true } form_urlencoded = { version = "1.1.0", optional = true } +headers = { version = "0.3.8", optional = true } multer = { version = "2.0.0", optional = true } percent-encoding = { version = "2.1", optional = true } prost = { version = "0.11", optional = true } @@ -61,7 +63,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" } futures = "0.3" http-body = "0.4.4" hyper = "0.14" diff --git a/axum-extra/src/extract/cookie/mod.rs b/axum-extra/src/extract/cookie/mod.rs index 5cd021fa70..76a8fd667e 100644 --- a/axum-extra/src/extract/cookie/mod.rs +++ b/axum-extra/src/extract/cookie/mod.rs @@ -41,12 +41,16 @@ pub use cookie::Key; /// use axum::{ /// Router, /// routing::{post, get}, -/// extract::TypedHeader, /// response::{IntoResponse, Redirect}, -/// headers::authorization::{Authorization, Bearer}, /// http::StatusCode, /// }; -/// use axum_extra::extract::cookie::{CookieJar, Cookie}; +/// use axum_extra::{ +/// typed_header::{ +/// TypedHeader, +/// headers::authorization::{Authorization, Bearer}, +/// }, +/// extract::cookie::{CookieJar, Cookie}, +/// }; /// /// async fn create_session( /// TypedHeader(auth): TypedHeader>, diff --git a/axum-extra/src/extract/cookie/private.rs b/axum-extra/src/extract/cookie/private.rs index c1b55dbdcd..65e8042eeb 100644 --- a/axum-extra/src/extract/cookie/private.rs +++ b/axum-extra/src/extract/cookie/private.rs @@ -23,12 +23,17 @@ use std::{convert::Infallible, fmt, marker::PhantomData}; /// use axum::{ /// Router, /// routing::{post, get}, -/// extract::{TypedHeader, FromRef}, +/// extract::FromRef, /// response::{IntoResponse, Redirect}, -/// headers::authorization::{Authorization, Bearer}, /// http::StatusCode, /// }; -/// use axum_extra::extract::cookie::{PrivateCookieJar, Cookie, Key}; +/// use axum_extra::{ +/// typed_header::{ +/// TypedHeader, +/// headers::authorization::{Authorization, Bearer}, +/// }, +/// extract::cookie::{PrivateCookieJar, Cookie, Key}, +/// }; /// /// async fn set_secret( /// jar: PrivateCookieJar, diff --git a/axum-extra/src/extract/cookie/signed.rs b/axum-extra/src/extract/cookie/signed.rs index 911082f155..d12532cc08 100644 --- a/axum-extra/src/extract/cookie/signed.rs +++ b/axum-extra/src/extract/cookie/signed.rs @@ -24,12 +24,17 @@ use std::{convert::Infallible, fmt, marker::PhantomData}; /// use axum::{ /// Router, /// routing::{post, get}, -/// extract::{TypedHeader, FromRef}, +/// extract::FromRef, /// response::{IntoResponse, Redirect}, -/// headers::authorization::{Authorization, Bearer}, /// http::StatusCode, /// }; -/// use axum_extra::extract::cookie::{SignedCookieJar, Cookie, Key}; +/// use axum_extra::{ +/// typed_header::{ +/// TypedHeader, +/// headers::authorization::{Authorization, Bearer}, +/// }, +/// extract::cookie::{SignedCookieJar, Cookie, Key}, +/// }; /// /// async fn create_session( /// TypedHeader(auth): TypedHeader>, diff --git a/axum-extra/src/extract/mod.rs b/axum-extra/src/extract/mod.rs index 3ca7749e03..b868acaab3 100644 --- a/axum-extra/src/extract/mod.rs +++ b/axum-extra/src/extract/mod.rs @@ -40,4 +40,8 @@ pub use self::multipart::Multipart; #[doc(no_inline)] pub use crate::json_lines::JsonLines; +#[cfg(feature = "typed-header")] +#[doc(no_inline)] +pub use crate::typed_header::TypedHeader; + pub use self::with_rejection::WithRejection; diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 2085c59079..c79eccf7a0 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::{ @@ -353,7 +353,7 @@ impl std::error::Error for InvalidBoundary {} mod tests { use super::*; use crate::test_helpers::*; - use axum::{body::Body, response::IntoResponse, routing::post, Router}; + use axum::{response::IntoResponse, routing::post, Router}; #[tokio::test] async fn content_type_with_encoding() { diff --git a/axum-extra/src/lib.rs b/axum-extra/src/lib.rs index 9391fcd1dd..461627c6b3 100644 --- a/axum-extra/src/lib.rs +++ b/axum-extra/src/lib.rs @@ -21,6 +21,7 @@ //! `protobuf` | Enables the `Protobuf` extractor and response | No //! `query` | Enables the `Query` extractor | No //! `typed-routing` | Enables the `TypedPath` routing utilities | No +//! `typed-header` | Enables the `TypedHeader` extractor and response | No //! //! [`axum`]: https://crates.io/crates/axum @@ -79,6 +80,9 @@ pub mod routing; #[cfg(feature = "json-lines")] pub mod json_lines; +#[cfg(feature = "typed-header")] +pub mod typed_header; + #[cfg(feature = "protobuf")] pub mod protobuf; diff --git a/axum-extra/src/response/mod.rs b/axum-extra/src/response/mod.rs index 7926b8c815..6e438e8f11 100644 --- a/axum-extra/src/response/mod.rs +++ b/axum-extra/src/response/mod.rs @@ -9,3 +9,7 @@ pub use erased_json::ErasedJson; #[cfg(feature = "json-lines")] #[doc(no_inline)] pub use crate::json_lines::JsonLines; + +#[cfg(feature = "typed-header")] +#[doc(no_inline)] +pub use crate::typed_header::TypedHeader; diff --git a/axum/src/typed_header.rs b/axum-extra/src/typed_header.rs similarity index 71% rename from axum/src/typed_header.rs rename to axum-extra/src/typed_header.rs index 717bc2409a..7419066487 100644 --- a/axum/src/typed_header.rs +++ b/axum-extra/src/typed_header.rs @@ -1,10 +1,16 @@ -use crate::extract::FromRequestParts; -use async_trait::async_trait; -use axum_core::response::{IntoResponse, IntoResponseParts, Response, ResponseParts}; -use headers::HeaderMapExt; -use http::request::Parts; +//! Extractor and response for typed headers. + +use axum::{ + async_trait, + extract::FromRequestParts, + response::{IntoResponse, IntoResponseParts, Response, ResponseParts}, +}; +use headers::Header; +use http::{request::Parts, HeaderValue}; use std::{convert::Infallible, ops::Deref}; +pub use headers; + /// Extractor and response that works with typed header values from [`headers`]. /// /// # As extractor @@ -14,11 +20,11 @@ use std::{convert::Infallible, ops::Deref}; /// /// ```rust,no_run /// use axum::{ -/// TypedHeader, -/// headers::UserAgent, /// routing::get, /// Router, /// }; +/// use headers::UserAgent; +/// use axum_extra::typed_header::TypedHeader; /// /// async fn users_teams_show( /// TypedHeader(user_agent): TypedHeader, @@ -27,19 +33,17 @@ 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 /// /// ```rust /// use axum::{ -/// TypedHeader, /// response::IntoResponse, -/// headers::ContentType, /// }; +/// use headers::ContentType; +/// use axum_extra::typed_header::TypedHeader; /// /// async fn handler() -> (TypedHeader, &'static str) { /// ( @@ -48,7 +52,7 @@ use std::{convert::Infallible, ops::Deref}; /// ) /// } /// ``` -#[cfg(feature = "headers")] +#[cfg(feature = "typed-header")] #[derive(Debug, Clone, Copy)] #[must_use] pub struct TypedHeader(pub T); @@ -56,7 +60,7 @@ pub struct TypedHeader(pub T); #[async_trait] impl FromRequestParts for TypedHeader where - T: headers::Header, + T: Header, S: Send + Sync, { type Rejection = TypedHeaderRejection; @@ -88,29 +92,29 @@ impl Deref for TypedHeader { impl IntoResponseParts for TypedHeader where - T: headers::Header, + T: Header, { type Error = Infallible; fn into_response_parts(self, mut res: ResponseParts) -> Result { - res.headers_mut().typed_insert(self.0); + typed_insert(res.headers_mut(), self.0); Ok(res) } } impl IntoResponse for TypedHeader where - T: headers::Header, + T: Header, { fn into_response(self) -> Response { let mut res = ().into_response(); - res.headers_mut().typed_insert(self.0); + typed_insert(res.headers_mut(), self.0); res } } /// Rejection used for [`TypedHeader`](super::TypedHeader). -#[cfg(feature = "headers")] +#[cfg(feature = "typed-header")] #[derive(Debug)] pub struct TypedHeaderRejection { name: &'static http::header::HeaderName, @@ -130,7 +134,7 @@ impl TypedHeaderRejection { } /// Additional information regarding a [`TypedHeaderRejection`] -#[cfg(feature = "headers")] +#[cfg(feature = "typed-header")] #[derive(Debug)] #[non_exhaustive] pub enum TypedHeaderRejectionReason { @@ -168,12 +172,55 @@ impl std::error::Error for TypedHeaderRejection { } } +// copied from https://docs.rs/headers/latest/src/headers/map_ext.rs.html#22-52 +fn typed_insert(headers: &mut http::HeaderMap, header: H) +where + H: Header, +{ + struct ToValues<'a> { + state: State<'a>, + } + + enum State<'a> { + First(http::header::Entry<'a, HeaderValue>), + Latter(http::header::OccupiedEntry<'a, HeaderValue>), + Tmp, + } + + impl<'a> Extend for ToValues<'a> { + fn extend>(&mut self, iter: T) { + for value in iter { + let entry = match ::std::mem::replace(&mut self.state, State::Tmp) { + State::First(http::header::Entry::Occupied(mut e)) => { + e.insert(value); + e + } + State::First(http::header::Entry::Vacant(e)) => e.insert_entry(value), + State::Latter(mut e) => { + e.append(value); + e + } + State::Tmp => unreachable!("ToValues State::Tmp"), + }; + self.state = State::Latter(entry); + } + } + } + + let entry = headers.entry(H::name()); + let mut values = ToValues { + state: State::First(entry), + }; + header.encode(&mut values); +} + #[cfg(test)] mod tests { use super::*; - use crate::{response::IntoResponse, routing::get, test_helpers::*, Router}; + use crate::test_helpers::*; + use axum::{response::IntoResponse, routing::get, Router}; - #[crate::test] + #[tokio::test] async fn typed_header() { async fn handle( TypedHeader(user_agent): TypedHeader, diff --git a/axum-macros/Cargo.toml b/axum-macros/Cargo.toml index bce1830eff..4ab7a64f93 100644 --- a/axum-macros/Cargo.toml +++ b/axum-macros/Cargo.toml @@ -29,8 +29,8 @@ syn = { version = "1.0", features = [ ] } [dev-dependencies] -axum = { path = "../axum", version = "0.6.0", features = ["headers", "macros"] } -axum-extra = { path = "../axum-extra", version = "0.7.0", features = ["typed-routing", "cookie-private"] } +axum = { path = "../axum", version = "0.6.0", features = ["macros"] } +axum-extra = { path = "../axum-extra", version = "0.7.0", features = ["typed-routing", "cookie-private", "typed-header"] } rustversion = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/axum-macros/src/lib.rs b/axum-macros/src/lib.rs index e291715e67..45ec9b9337 100644 --- a/axum-macros/src/lib.rs +++ b/axum-macros/src/lib.rs @@ -71,10 +71,13 @@ use from_request::Trait::{FromRequest, FromRequestParts}; /// ``` /// use axum_macros::FromRequest; /// use axum::{ -/// extract::{Extension, TypedHeader}, -/// headers::ContentType, +/// extract::Extension, /// body::Bytes, /// }; +/// use axum_extra::typed_header::{ +/// TypedHeader, +/// headers::ContentType, +/// }; /// /// #[derive(FromRequest)] /// struct MyExtractor { @@ -116,10 +119,13 @@ use from_request::Trait::{FromRequest, FromRequestParts}; /// ``` /// use axum_macros::FromRequest; /// use axum::{ -/// extract::{Extension, TypedHeader}, -/// headers::ContentType, +/// extract::Extension, /// body::Bytes, /// }; +/// use axum_extra::typed_header::{ +/// TypedHeader, +/// headers::ContentType, +/// }; /// /// #[derive(FromRequest)] /// struct MyExtractor { @@ -158,8 +164,9 @@ use from_request::Trait::{FromRequest, FromRequestParts}; /// /// ``` /// use axum_macros::FromRequest; -/// use axum::{ -/// extract::{TypedHeader, rejection::TypedHeaderRejection}, +/// use axum_extra::typed_header::{ +/// TypedHeader, +/// TypedHeaderRejection, /// headers::{ContentType, UserAgent}, /// }; /// @@ -368,7 +375,10 @@ pub fn derive_from_request(item: TokenStream) -> TokenStream { /// ``` /// use axum_macros::FromRequestParts; /// use axum::{ -/// extract::{Query, TypedHeader}, +/// extract::Query, +/// }; +/// use axum_extra::typed_header::{ +/// TypedHeader, /// headers::ContentType, /// }; /// use std::collections::HashMap; diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 9ffad57ca0..aff74c25e7 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -28,6 +28,7 @@ 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:** `TypedHeader` has been move to `axum-extra` - **added:** Add `Router::as_service` and `Router::into_service` to workaround type inference issues when calling `ServiceExt` methods on a `Router` ([#1835]) diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 1ef79b9ad3..5c25acb114 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -85,7 +85,6 @@ tower-service = "0.3" # optional dependencies axum-macros = { path = "../axum-macros", version = "0.3.5", optional = true } base64 = { version = "0.21.0", optional = true } -headers = { version = "0.3.7", 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 } @@ -178,7 +177,6 @@ allowed = [ "futures_core", "futures_sink", "futures_util", - "headers_core", "http", "http_body", "hyper", diff --git a/axum/src/docs/extract.md b/axum/src/docs/extract.md index e06a2cb9f2..871a7bd61f 100644 --- a/axum/src/docs/extract.md +++ b/axum/src/docs/extract.md @@ -13,7 +13,6 @@ Types and traits for extracting data from requests. - [Accessing other extractors in `FromRequest` or `FromRequestParts` implementations](#accessing-other-extractors-in-fromrequest-or-fromrequestparts-implementations) - [Request body limits](#request-body-limits) - [Request body extractors](#request-body-extractors) -- [Running extractors from middleware](#running-extractors-from-middleware) - [Wrapping extractors](#wrapping-extractors) # Intro @@ -57,9 +56,8 @@ Some commonly used extractors are: ```rust,no_run use axum::{ - extract::{Json, TypedHeader, Path, Extension, Query}, + extract::{Json, Path, Extension, Query}, routing::post, - headers::UserAgent, http::{Request, header::HeaderMap}, body::{Bytes, Body}, Router, @@ -77,10 +75,6 @@ async fn query(Query(params): Query>) {} // `HeaderMap` gives you all the headers async fn headers(headers: HeaderMap) {} -// `TypedHeader` can be used to extract a single header -// note this requires you've enabled axum's `headers` feature -async fn user_agent(TypedHeader(user_agent): TypedHeader) {} - // `String` consumes the request body and ensures it is valid utf-8 async fn string(body: String) {} @@ -103,8 +97,6 @@ struct State { /* ... */ } let app = Router::new() .route("/path/:user_id", post(path)) .route("/query", post(query)) - .route("/user_agent", post(user_agent)) - .route("/headers", post(headers)) .route("/string", post(string)) .route("/bytes", post(bytes)) .route("/json", post(json)) @@ -576,9 +568,8 @@ in your implementation. ```rust use axum::{ async_trait, - extract::{Extension, FromRequestParts, TypedHeader}, - headers::{authorization::Bearer, Authorization}, - http::{StatusCode, request::Parts}, + extract::{Extension, FromRequestParts}, + http::{StatusCode, HeaderMap, request::Parts}, response::{IntoResponse, Response}, routing::get, Router, @@ -602,10 +593,9 @@ where async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { // You can either call them directly... - let TypedHeader(Authorization(token)) = - TypedHeader::>::from_request_parts(parts, state) - .await - .map_err(|err| err.into_response())?; + let headers = HeaderMap::from_request_parts(parts, state) + .await + .map_err(|err| match err {})?; // ... or use `extract` / `extract_with_state` from `RequestExt` / `RequestPartsExt` use axum::RequestPartsExt; @@ -624,9 +614,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 @@ -637,54 +625,6 @@ For security reasons, [`Bytes`] will, by default, not accept bodies larger than For more details, including how to disable this limit, see [`DefaultBodyLimit`]. -# Running extractors from middleware - -Extractors can also be run from middleware: - -```rust -use axum::{ - middleware::{self, Next}, - extract::{TypedHeader, FromRequestParts}, - http::{Request, StatusCode}, - response::Response, - headers::authorization::{Authorization, Bearer}, - RequestPartsExt, Router, -}; - -async fn auth_middleware( - request: Request, - next: Next, -) -> Result -where - B: Send, -{ - // running extractors requires a `axum::http::request::Parts` - let (mut parts, body) = request.into_parts(); - - // `TypedHeader>` extracts the auth token - let auth: TypedHeader> = parts.extract() - .await - .map_err(|_| StatusCode::UNAUTHORIZED)?; - - if !token_is_valid(auth.token()) { - return Err(StatusCode::UNAUTHORIZED); - } - - // reconstruct the request - let request = Request::from_parts(parts, body); - - Ok(next.run(request).await) -} - -fn token_is_valid(token: &str) -> bool { - // ... - # false -} - -let app = Router::new().layer(middleware::from_fn(auth_middleware)); -# let _: Router = app; -``` - # Wrapping extractors If you want write an extractor that generically wraps another extractor (that diff --git a/axum/src/lib.rs b/axum/src/lib.rs index f40dc71a81..f7e3c2e2c4 100644 --- a/axum/src/lib.rs +++ b/axum/src/lib.rs @@ -348,7 +348,6 @@ //! //! Name | Description | Default? //! ---|---|--- -//! `headers` | Enables extracting typed headers via [`TypedHeader`] | No //! `http1` | Enables hyper's `http1` feature | Yes //! `http2` | Enables hyper's `http2` feature | No //! `json` | Enables the [`Json`] type and some similar convenience functionality | Yes @@ -447,8 +446,6 @@ mod form; #[cfg(feature = "json")] mod json; mod service_ext; -#[cfg(feature = "headers")] -mod typed_header; mod util; pub mod body; @@ -464,9 +461,6 @@ mod test_helpers; #[doc(no_inline)] pub use async_trait::async_trait; -#[cfg(feature = "headers")] -#[doc(no_inline)] -pub use headers; #[doc(no_inline)] pub use http; #[cfg(feature = "tokio")] @@ -481,10 +475,6 @@ pub use self::json::Json; #[doc(inline)] pub use self::routing::Router; -#[doc(inline)] -#[cfg(feature = "headers")] -pub use self::typed_header::TypedHeader; - #[doc(inline)] #[cfg(feature = "form")] pub use self::form::Form; diff --git a/axum/src/middleware/from_fn.rs b/axum/src/middleware/from_fn.rs index ba595f338f..60f468bb35 100644 --- a/axum/src/middleware/from_fn.rs +++ b/axum/src/middleware/from_fn.rs @@ -63,9 +63,7 @@ use tower_service::Service; /// ```rust /// use axum::{ /// Router, -/// extract::TypedHeader, -/// http::StatusCode, -/// headers::authorization::{Authorization, Bearer}, +/// http::{StatusCode, HeaderMap}, /// http::Request, /// middleware::{self, Next}, /// response::Response, @@ -73,22 +71,30 @@ use tower_service::Service; /// }; /// /// async fn auth( -/// // run the `TypedHeader` extractor -/// TypedHeader(auth): TypedHeader>, +/// // run the `HeaderMap` extractor +/// headers: HeaderMap, /// // you can also add more extractors here but the last /// // extractor must implement `FromRequest` which /// // `Request` does /// request: Request, /// next: Next, /// ) -> Result { -/// if token_is_valid(auth.token()) { -/// let response = next.run(request).await; -/// Ok(response) -/// } else { -/// Err(StatusCode::UNAUTHORIZED) +/// match get_token(&headers) { +/// Some(token) if token_is_valid(token) => { +/// let response = next.run(request).await; +/// Ok(response) +/// } +/// _ => { +/// Err(StatusCode::UNAUTHORIZED) +/// } /// } /// } /// +/// fn get_token(headers: &HeaderMap) -> Option<&str> { +/// // ... +/// # None +/// } +/// /// fn token_is_valid(token: &str) -> bool { /// // ... /// # false diff --git a/examples/jwt/Cargo.toml b/examples/jwt/Cargo.toml index a18eb6ec78..b0c76c25d1 100644 --- a/examples/jwt/Cargo.toml +++ b/examples/jwt/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" publish = false [dependencies] -axum = { path = "../../axum", features = ["headers"] } -headers = "0.3" +axum = { path = "../../axum" } +axum-extra = { path = "../../axum-extra", features = ["typed-header"] } jsonwebtoken = "8.0" once_cell = "1.8" serde = { version = "1.0", features = ["derive"] } diff --git a/examples/jwt/src/main.rs b/examples/jwt/src/main.rs index 4ee20d4bf7..e5d13aa30a 100644 --- a/examples/jwt/src/main.rs +++ b/examples/jwt/src/main.rs @@ -8,13 +8,16 @@ use axum::{ async_trait, - extract::{FromRequestParts, TypedHeader}, - headers::{authorization::Bearer, Authorization}, + extract::FromRequestParts, http::{request::Parts, StatusCode}, response::{IntoResponse, Response}, routing::{get, post}, Json, RequestPartsExt, Router, }; +use axum_extra::typed_header::{ + headers::{authorization::Bearer, Authorization}, + TypedHeader, +}; use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; diff --git a/examples/oauth/Cargo.toml b/examples/oauth/Cargo.toml index 6613367379..bb0aafc92a 100644 --- a/examples/oauth/Cargo.toml +++ b/examples/oauth/Cargo.toml @@ -6,8 +6,8 @@ publish = false [dependencies] async-session = "3.0.0" -axum = { path = "../../axum", features = ["headers"] } -headers = "0.3" +axum = { path = "../../axum" } +axum-extra = { path = "../../axum-extra", features = ["typed-header"] } http = "0.2" oauth2 = "4.1" # Use Rustls because it makes it easier to cross-compile on CI diff --git a/examples/oauth/src/main.rs b/examples/oauth/src/main.rs index 1a01570f69..3fb68b5f1c 100644 --- a/examples/oauth/src/main.rs +++ b/examples/oauth/src/main.rs @@ -11,14 +11,13 @@ use async_session::{MemoryStore, Session, SessionStore}; use axum::{ async_trait, - extract::{ - rejection::TypedHeaderRejectionReason, FromRef, FromRequestParts, Query, State, TypedHeader, - }, + extract::{FromRef, FromRequestParts, Query, State}, http::{header::SET_COOKIE, HeaderMap}, response::{IntoResponse, Redirect, Response}, routing::get, RequestPartsExt, Router, }; +use axum_extra::typed_header::{headers, TypedHeader, TypedHeaderRejectionReason}; use http::{header, request::Parts}; use oauth2::{ basic::BasicClient, reqwest::async_http_client, AuthUrl, AuthorizationCode, ClientId, diff --git a/examples/sessions/Cargo.toml b/examples/sessions/Cargo.toml index 247df4d3c6..bb3d4bda1c 100644 --- a/examples/sessions/Cargo.toml +++ b/examples/sessions/Cargo.toml @@ -6,7 +6,8 @@ publish = false [dependencies] async-session = "3.0.0" -axum = { path = "../../axum", features = ["headers"] } +axum = { path = "../../axum" } +axum-extra = { path = "../../axum-extra", features = ["typed-header"] } serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] } tracing = "0.1" diff --git a/examples/sessions/src/main.rs b/examples/sessions/src/main.rs index 9bea9c1b06..addb61857e 100644 --- a/examples/sessions/src/main.rs +++ b/examples/sessions/src/main.rs @@ -7,8 +7,7 @@ use async_session::{MemoryStore, Session, SessionStore as _}; use axum::{ async_trait, - extract::{FromRef, FromRequestParts, TypedHeader}, - headers::Cookie, + extract::{FromRef, FromRequestParts}, http::{ self, header::{HeaderMap, HeaderValue}, @@ -19,6 +18,7 @@ use axum::{ routing::get, RequestPartsExt, Router, }; +use axum_extra::typed_header::{headers::Cookie, TypedHeader}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::net::SocketAddr; diff --git a/examples/sse/Cargo.toml b/examples/sse/Cargo.toml index c0e12979ff..0131991d8a 100644 --- a/examples/sse/Cargo.toml +++ b/examples/sse/Cargo.toml @@ -5,7 +5,8 @@ edition = "2021" publish = false [dependencies] -axum = { path = "../../axum", features = ["headers"] } +axum = { path = "../../axum" } +axum-extra = { path = "../../axum-extra", features = ["typed-header"] } futures = "0.3" headers = "0.3" tokio = { version = "1.0", features = ["full"] } diff --git a/examples/sse/src/main.rs b/examples/sse/src/main.rs index dab5a565cb..b62b81e1bf 100644 --- a/examples/sse/src/main.rs +++ b/examples/sse/src/main.rs @@ -5,11 +5,11 @@ //! ``` use axum::{ - extract::TypedHeader, response::sse::{Event, Sse}, routing::get, Router, }; +use axum_extra::typed_header::{headers, TypedHeader}; use futures::stream::{self, Stream}; use std::{convert::Infallible, net::SocketAddr, path::PathBuf, time::Duration}; use tokio_stream::StreamExt as _; diff --git a/examples/websockets/Cargo.toml b/examples/websockets/Cargo.toml index 30e6c3c0f1..c385b78944 100644 --- a/examples/websockets/Cargo.toml +++ b/examples/websockets/Cargo.toml @@ -5,7 +5,8 @@ edition = "2021" publish = false [dependencies] -axum = { path = "../../axum", features = ["ws", "headers"] } +axum = { path = "../../axum", features = ["ws"] } +axum-extra = { path = "../../axum-extra", features = ["typed-header"] } futures = "0.3" futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } headers = "0.3" diff --git a/examples/websockets/src/main.rs b/examples/websockets/src/main.rs index 73993c15f3..8619e4c01c 100644 --- a/examples/websockets/src/main.rs +++ b/examples/websockets/src/main.rs @@ -17,14 +17,12 @@ //! ``` use axum::{ - extract::{ - ws::{Message, WebSocket, WebSocketUpgrade}, - TypedHeader, - }, + extract::ws::{Message, WebSocket, WebSocketUpgrade}, response::IntoResponse, routing::get, Router, }; +use axum_extra::typed_header::TypedHeader; use std::borrow::Cow; use std::ops::ControlFlow; From bd7a1c77cdd4cc782ebf03669d9d2e06833fe468 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Tue, 14 Mar 2023 00:06:18 +0100 Subject: [PATCH 07/15] allow headers_core in axum-extra's public api --- axum-extra/Cargo.toml | 1 + axum-extra/src/typed_header.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index 1782ea624b..e56c2ede16 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -86,6 +86,7 @@ allowed = [ "cookie", "futures_core", "futures_util", + "headers_core", "http", "http_body", "hyper", diff --git a/axum-extra/src/typed_header.rs b/axum-extra/src/typed_header.rs index 7419066487..91dc933a75 100644 --- a/axum-extra/src/typed_header.rs +++ b/axum-extra/src/typed_header.rs @@ -113,7 +113,7 @@ where } } -/// Rejection used for [`TypedHeader`](super::TypedHeader). +/// Rejection used for [`TypedHeader`](TypedHeader). #[cfg(feature = "typed-header")] #[derive(Debug)] pub struct TypedHeaderRejection { From 02f4f9297f9ca399c4ad9228419866ec1ea76e12 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Tue, 14 Mar 2023 00:07:36 +0100 Subject: [PATCH 08/15] more clean up --- axum/src/extract/mod.rs | 4 ---- axum/src/extract/rejection.rs | 3 --- axum/src/lib.rs | 1 - axum/src/response/mod.rs | 4 ---- 4 files changed, 12 deletions(-) diff --git a/axum/src/extract/mod.rs b/axum/src/extract/mod.rs index b93cc7f459..8e6b779c63 100644 --- a/axum/src/extract/mod.rs +++ b/axum/src/extract/mod.rs @@ -77,10 +77,6 @@ pub use self::request_parts::OriginalUri; #[doc(inline)] pub use self::ws::WebSocketUpgrade; -#[cfg(feature = "headers")] -#[doc(no_inline)] -pub use crate::TypedHeader; - // this is duplicated in `axum-extra/src/extract/form.rs` pub(super) fn has_content_type(headers: &HeaderMap, expected_content_type: &mime::Mime) -> bool { let content_type = if let Some(content_type) = headers.get(header::CONTENT_TYPE) { diff --git a/axum/src/extract/rejection.rs b/axum/src/extract/rejection.rs index 7285d49ef7..d7125683ad 100644 --- a/axum/src/extract/rejection.rs +++ b/axum/src/extract/rejection.rs @@ -204,6 +204,3 @@ composite_rejection! { MatchedPathMissing, } } - -#[cfg(feature = "headers")] -pub use crate::typed_header::{TypedHeaderRejection, TypedHeaderRejectionReason}; diff --git a/axum/src/lib.rs b/axum/src/lib.rs index f7e3c2e2c4..351f89d700 100644 --- a/axum/src/lib.rs +++ b/axum/src/lib.rs @@ -361,7 +361,6 @@ //! `form` | Enables the `Form` extractor | Yes //! `query` | Enables the `Query` extractor | Yes //! -//! [`TypedHeader`]: crate::extract::TypedHeader //! [`MatchedPath`]: crate::extract::MatchedPath //! [`Multipart`]: crate::extract::Multipart //! [`OriginalUri`]: crate::extract::OriginalUri diff --git a/axum/src/response/mod.rs b/axum/src/response/mod.rs index 95bcb762e2..3c3877d51a 100644 --- a/axum/src/response/mod.rs +++ b/axum/src/response/mod.rs @@ -12,10 +12,6 @@ pub mod sse; #[cfg(feature = "json")] pub use crate::Json; -#[doc(no_inline)] -#[cfg(feature = "headers")] -pub use crate::TypedHeader; - #[cfg(feature = "form")] #[doc(no_inline)] pub use crate::form::Form; From ca879c1dcb80c72dcf45f1be4dc9571517e26d8b Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 17 Mar 2023 10:08:10 +0100 Subject: [PATCH 09/15] update ui tests --- .../debug_handler/fail/argument_not_extractor.stderr | 2 +- .../tests/from_request/fail/parts_extracting_body.stderr | 2 +- axum-macros/tests/from_request/pass/named.rs | 6 +++++- axum-macros/tests/from_request/pass/named_parts.rs | 8 ++++++-- axum-macros/tests/from_request/pass/named_via.rs | 9 +++++---- axum-macros/tests/from_request/pass/named_via_parts.rs | 9 +++++---- 6 files changed, 23 insertions(+), 13 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 8be92b7913..5f0e8664c8 100644 --- a/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr +++ b/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr @@ -15,7 +15,7 @@ error[E0277]: the trait bound `bool: FromRequestParts<()>` is not satisfied <(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> - and 26 others + and $N others = 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/from_request/fail/parts_extracting_body.stderr b/axum-macros/tests/from_request/fail/parts_extracting_body.stderr index 54c0dc0222..896cf2d991 100644 --- a/axum-macros/tests/from_request/fail/parts_extracting_body.stderr +++ b/axum-macros/tests/from_request/fail/parts_extracting_body.stderr @@ -15,4 +15,4 @@ error[E0277]: the trait bound `String: FromRequestParts` is not satisfied <(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> - and 27 others + and $N others diff --git a/axum-macros/tests/from_request/pass/named.rs b/axum-macros/tests/from_request/pass/named.rs index e775d41d84..fdb70f5f98 100644 --- a/axum-macros/tests/from_request/pass/named.rs +++ b/axum-macros/tests/from_request/pass/named.rs @@ -1,6 +1,10 @@ use axum::{ - extract::{FromRequest, TypedHeader, rejection::TypedHeaderRejection}, + extract::FromRequest, response::Response, +}; +use axum_extra::typed_header::{ + TypedHeader, + TypedHeaderRejection, headers::{self, UserAgent}, }; diff --git a/axum-macros/tests/from_request/pass/named_parts.rs b/axum-macros/tests/from_request/pass/named_parts.rs index 27dce64f21..d166084ae7 100644 --- a/axum-macros/tests/from_request/pass/named_parts.rs +++ b/axum-macros/tests/from_request/pass/named_parts.rs @@ -1,8 +1,12 @@ use axum::{ - extract::{rejection::TypedHeaderRejection, FromRequestParts, TypedHeader}, - headers::{self, UserAgent}, + extract::FromRequestParts, response::Response, }; +use axum_extra::typed_header::{ + TypedHeader, + TypedHeaderRejection, + headers::{self, UserAgent}, +}; #[derive(FromRequestParts)] struct Extractor { diff --git a/axum-macros/tests/from_request/pass/named_via.rs b/axum-macros/tests/from_request/pass/named_via.rs index be2e8c67a6..e829c29b39 100644 --- a/axum-macros/tests/from_request/pass/named_via.rs +++ b/axum-macros/tests/from_request/pass/named_via.rs @@ -1,9 +1,10 @@ use axum::{ response::Response, - extract::{ - rejection::TypedHeaderRejection, - Extension, FromRequest, TypedHeader, - }, + extract::{Extension, FromRequest}, +}; +use axum_extra::typed_header::{ + TypedHeader, + TypedHeaderRejection, headers::{self, UserAgent}, }; diff --git a/axum-macros/tests/from_request/pass/named_via_parts.rs b/axum-macros/tests/from_request/pass/named_via_parts.rs index 9a389e549b..76f5b3c1ca 100644 --- a/axum-macros/tests/from_request/pass/named_via_parts.rs +++ b/axum-macros/tests/from_request/pass/named_via_parts.rs @@ -1,9 +1,10 @@ use axum::{ response::Response, - extract::{ - rejection::TypedHeaderRejection, - Extension, FromRequestParts, TypedHeader, - }, + extract::{Extension, FromRequestParts}, +}; +use axum_extra::typed_header::{ + TypedHeader, + TypedHeaderRejection, headers::{self, UserAgent}, }; From 70360d1ac9ab9b464ee3e0d649f377d15ef2492b Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 7 Apr 2023 22:22:22 +0200 Subject: [PATCH 10/15] re-export things from the root --- axum-extra/src/extract/cookie/mod.rs | 6 +-- axum-extra/src/extract/cookie/private.rs | 6 +-- axum-extra/src/extract/cookie/signed.rs | 6 +-- axum-extra/src/lib.rs | 8 ++++ axum-extra/src/typed_header.rs | 56 +++--------------------- axum-macros/src/lib.rs | 10 ++--- examples/jwt/src/main.rs | 2 +- examples/oauth/src/main.rs | 2 +- examples/sessions/src/main.rs | 2 +- examples/sse/src/main.rs | 2 +- 10 files changed, 29 insertions(+), 71 deletions(-) diff --git a/axum-extra/src/extract/cookie/mod.rs b/axum-extra/src/extract/cookie/mod.rs index 76a8fd667e..e0b84c7cbb 100644 --- a/axum-extra/src/extract/cookie/mod.rs +++ b/axum-extra/src/extract/cookie/mod.rs @@ -45,10 +45,8 @@ pub use cookie::Key; /// http::StatusCode, /// }; /// use axum_extra::{ -/// typed_header::{ -/// TypedHeader, -/// headers::authorization::{Authorization, Bearer}, -/// }, +/// TypedHeader, +/// headers::authorization::{Authorization, Bearer}, /// extract::cookie::{CookieJar, Cookie}, /// }; /// diff --git a/axum-extra/src/extract/cookie/private.rs b/axum-extra/src/extract/cookie/private.rs index 65e8042eeb..381735f166 100644 --- a/axum-extra/src/extract/cookie/private.rs +++ b/axum-extra/src/extract/cookie/private.rs @@ -28,10 +28,8 @@ use std::{convert::Infallible, fmt, marker::PhantomData}; /// http::StatusCode, /// }; /// use axum_extra::{ -/// typed_header::{ -/// TypedHeader, -/// headers::authorization::{Authorization, Bearer}, -/// }, +/// TypedHeader, +/// headers::authorization::{Authorization, Bearer}, /// extract::cookie::{PrivateCookieJar, Cookie, Key}, /// }; /// diff --git a/axum-extra/src/extract/cookie/signed.rs b/axum-extra/src/extract/cookie/signed.rs index d12532cc08..6025d37cba 100644 --- a/axum-extra/src/extract/cookie/signed.rs +++ b/axum-extra/src/extract/cookie/signed.rs @@ -29,10 +29,8 @@ use std::{convert::Infallible, fmt, marker::PhantomData}; /// http::StatusCode, /// }; /// use axum_extra::{ -/// typed_header::{ -/// TypedHeader, -/// headers::authorization::{Authorization, Bearer}, -/// }, +/// TypedHeader, +/// headers::authorization::{Authorization, Bearer}, /// extract::cookie::{SignedCookieJar, Cookie, Key}, /// }; /// diff --git a/axum-extra/src/lib.rs b/axum-extra/src/lib.rs index 461627c6b3..7b3cdd864b 100644 --- a/axum-extra/src/lib.rs +++ b/axum-extra/src/lib.rs @@ -83,6 +83,14 @@ pub mod json_lines; #[cfg(feature = "typed-header")] pub mod typed_header; +#[cfg(feature = "typed-header")] +#[doc(no_inline)] +pub use headers; + +#[cfg(feature = "typed-header")] +#[doc(inline)] +pub use typed_header::TypedHeader; + #[cfg(feature = "protobuf")] pub mod protobuf; diff --git a/axum-extra/src/typed_header.rs b/axum-extra/src/typed_header.rs index 91dc933a75..282ff8d599 100644 --- a/axum-extra/src/typed_header.rs +++ b/axum-extra/src/typed_header.rs @@ -5,12 +5,10 @@ use axum::{ extract::FromRequestParts, response::{IntoResponse, IntoResponseParts, Response, ResponseParts}, }; -use headers::Header; -use http::{request::Parts, HeaderValue}; +use headers::{Header, HeaderMapExt}; +use http::request::Parts; use std::{convert::Infallible, ops::Deref}; -pub use headers; - /// Extractor and response that works with typed header values from [`headers`]. /// /// # As extractor @@ -24,7 +22,7 @@ pub use headers; /// Router, /// }; /// use headers::UserAgent; -/// use axum_extra::typed_header::TypedHeader; +/// use axum_extra::TypedHeader; /// /// async fn users_teams_show( /// TypedHeader(user_agent): TypedHeader, @@ -43,7 +41,7 @@ pub use headers; /// response::IntoResponse, /// }; /// use headers::ContentType; -/// use axum_extra::typed_header::TypedHeader; +/// use axum_extra::TypedHeader; /// /// async fn handler() -> (TypedHeader, &'static str) { /// ( @@ -97,7 +95,7 @@ where type Error = Infallible; fn into_response_parts(self, mut res: ResponseParts) -> Result { - typed_insert(res.headers_mut(), self.0); + res.headers_mut().typed_insert(self.0); Ok(res) } } @@ -108,7 +106,7 @@ where { fn into_response(self) -> Response { let mut res = ().into_response(); - typed_insert(res.headers_mut(), self.0); + res.headers_mut().typed_insert(self.0); res } } @@ -172,48 +170,6 @@ impl std::error::Error for TypedHeaderRejection { } } -// copied from https://docs.rs/headers/latest/src/headers/map_ext.rs.html#22-52 -fn typed_insert(headers: &mut http::HeaderMap, header: H) -where - H: Header, -{ - struct ToValues<'a> { - state: State<'a>, - } - - enum State<'a> { - First(http::header::Entry<'a, HeaderValue>), - Latter(http::header::OccupiedEntry<'a, HeaderValue>), - Tmp, - } - - impl<'a> Extend for ToValues<'a> { - fn extend>(&mut self, iter: T) { - for value in iter { - let entry = match ::std::mem::replace(&mut self.state, State::Tmp) { - State::First(http::header::Entry::Occupied(mut e)) => { - e.insert(value); - e - } - State::First(http::header::Entry::Vacant(e)) => e.insert_entry(value), - State::Latter(mut e) => { - e.append(value); - e - } - State::Tmp => unreachable!("ToValues State::Tmp"), - }; - self.state = State::Latter(entry); - } - } - } - - let entry = headers.entry(H::name()); - let mut values = ToValues { - state: State::First(entry), - }; - header.encode(&mut values); -} - #[cfg(test)] mod tests { use super::*; diff --git a/axum-macros/src/lib.rs b/axum-macros/src/lib.rs index 45ec9b9337..94ecc952a6 100644 --- a/axum-macros/src/lib.rs +++ b/axum-macros/src/lib.rs @@ -74,7 +74,7 @@ use from_request::Trait::{FromRequest, FromRequestParts}; /// extract::Extension, /// body::Bytes, /// }; -/// use axum_extra::typed_header::{ +/// use axum_extra::{ /// TypedHeader, /// headers::ContentType, /// }; @@ -122,7 +122,7 @@ use from_request::Trait::{FromRequest, FromRequestParts}; /// extract::Extension, /// body::Bytes, /// }; -/// use axum_extra::typed_header::{ +/// use axum_extra::{ /// TypedHeader, /// headers::ContentType, /// }; @@ -164,10 +164,10 @@ use from_request::Trait::{FromRequest, FromRequestParts}; /// /// ``` /// use axum_macros::FromRequest; -/// use axum_extra::typed_header::{ +/// use axum_extra::{ /// TypedHeader, -/// TypedHeaderRejection, /// headers::{ContentType, UserAgent}, +/// typed_header::TypedHeaderRejection, /// }; /// /// #[derive(FromRequest)] @@ -377,7 +377,7 @@ pub fn derive_from_request(item: TokenStream) -> TokenStream { /// use axum::{ /// extract::Query, /// }; -/// use axum_extra::typed_header::{ +/// use axum_extra::{ /// TypedHeader, /// headers::ContentType, /// }; diff --git a/examples/jwt/src/main.rs b/examples/jwt/src/main.rs index e5d13aa30a..5911cc21f1 100644 --- a/examples/jwt/src/main.rs +++ b/examples/jwt/src/main.rs @@ -14,7 +14,7 @@ use axum::{ routing::{get, post}, Json, RequestPartsExt, Router, }; -use axum_extra::typed_header::{ +use axum_extra::{ headers::{authorization::Bearer, Authorization}, TypedHeader, }; diff --git a/examples/oauth/src/main.rs b/examples/oauth/src/main.rs index 3fb68b5f1c..e7c298c71e 100644 --- a/examples/oauth/src/main.rs +++ b/examples/oauth/src/main.rs @@ -17,7 +17,7 @@ use axum::{ routing::get, RequestPartsExt, Router, }; -use axum_extra::typed_header::{headers, TypedHeader, TypedHeaderRejectionReason}; +use axum_extra::{headers, typed_header::TypedHeaderRejectionReason, TypedHeader}; use http::{header, request::Parts}; use oauth2::{ basic::BasicClient, reqwest::async_http_client, AuthUrl, AuthorizationCode, ClientId, diff --git a/examples/sessions/src/main.rs b/examples/sessions/src/main.rs index addb61857e..05664dc43d 100644 --- a/examples/sessions/src/main.rs +++ b/examples/sessions/src/main.rs @@ -18,7 +18,7 @@ use axum::{ routing::get, RequestPartsExt, Router, }; -use axum_extra::typed_header::{headers::Cookie, TypedHeader}; +use axum_extra::{headers::Cookie, TypedHeader}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::net::SocketAddr; diff --git a/examples/sse/src/main.rs b/examples/sse/src/main.rs index b62b81e1bf..1f5238fd84 100644 --- a/examples/sse/src/main.rs +++ b/examples/sse/src/main.rs @@ -9,7 +9,7 @@ use axum::{ routing::get, Router, }; -use axum_extra::typed_header::{headers, TypedHeader}; +use axum_extra::{headers, TypedHeader}; use futures::stream::{self, Stream}; use std::{convert::Infallible, net::SocketAddr, path::PathBuf, time::Duration}; use tokio_stream::StreamExt as _; From 27f852d48c032339b2436da2e765e325d2453256 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 7 Apr 2023 23:08:50 +0200 Subject: [PATCH 11/15] fixups --- axum-core/src/ext_traits/request.rs | 6 +++--- axum/src/docs/extract.md | 3 +-- axum/src/extract/multipart.rs | 5 +++-- axum/src/form.rs | 1 - axum/src/handler/future.rs | 1 - axum/src/middleware/from_fn.rs | 5 ++--- examples/testing/src/main.rs | 9 +-------- 7 files changed, 10 insertions(+), 20 deletions(-) diff --git a/axum-core/src/ext_traits/request.rs b/axum-core/src/ext_traits/request.rs index 02da91b8f5..a891922e47 100644 --- a/axum-core/src/ext_traits/request.rs +++ b/axum-core/src/ext_traits/request.rs @@ -139,7 +139,7 @@ pub trait RequestExt: sealed::Sealed + Sized { /// ``` /// use axum::{ /// async_trait, - /// extract::{Request, FromRequest}, + /// extract::{Path, Request, FromRequest}, /// response::{IntoResponse, Response}, /// body::Body, /// Json, RequestExt, @@ -165,8 +165,8 @@ pub trait RequestExt: sealed::Sealed + Sized { /// type Rejection = Response; /// /// async fn from_request(mut req: Request, _state: &S) -> Result { - /// let TypedHeader(auth_header) = req - /// .extract_parts::>>() + /// let path_params = req + /// .extract_parts::>() /// .await /// .map(|Path(path_params)| path_params) /// .map_err(|err| err.into_response())?; diff --git a/axum/src/docs/extract.md b/axum/src/docs/extract.md index a1023e3e1a..ce9cab7527 100644 --- a/axum/src/docs/extract.md +++ b/axum/src/docs/extract.md @@ -54,9 +54,8 @@ Some commonly used extractors are: ```rust,no_run use axum::{ - extract::{Request, Json, TypedHeader, Path, Extension, Query}, + extract::{Request, Json, Path, Extension, Query}, routing::post, - headers::UserAgent, http::header::HeaderMap, body::{Bytes, Body}, Router, diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index caf800e2db..de6207bab5 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -2,10 +2,11 @@ //! //! 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; +use axum_core::response::{IntoResponse, Response}; use axum_core::RequestExt; use futures_util::stream::Stream; use http::header::{HeaderMap, CONTENT_TYPE}; @@ -60,7 +61,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/form.rs b/axum/src/form.rs index d9e71f55a4..35c3c0113a 100644 --- a/axum/src/form.rs +++ b/axum/src/form.rs @@ -1,7 +1,6 @@ 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; diff --git a/axum/src/handler/future.rs b/axum/src/handler/future.rs index 8d96643fc5..751984d0c6 100644 --- a/axum/src/handler/future.rs +++ b/axum/src/handler/future.rs @@ -1,6 +1,5 @@ //! Handler future types. -use crate::body::Body; use crate::response::Response; use axum_core::extract::Request; use futures_util::future::Map; diff --git a/axum/src/middleware/from_fn.rs b/axum/src/middleware/from_fn.rs index 3c4a211841..b47a11a3e8 100644 --- a/axum/src/middleware/from_fn.rs +++ b/axum/src/middleware/from_fn.rs @@ -1,4 +1,3 @@ -use crate::body::{Body, Bytes, HttpBody}; use crate::response::{IntoResponse, Response}; use axum_core::extract::{FromRequest, FromRequestParts, Request}; use futures_util::future::BoxFuture; @@ -62,8 +61,8 @@ use tower_service::Service; /// ```rust /// use axum::{ /// Router, -/// extract::{Request, TypedHeader}, -/// http::StatusCode, +/// extract::Request, +/// http::{StatusCode, HeaderMap}, /// middleware::{self, Next}, /// response::Response, /// routing::get, diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 3f258fa45b..99cc0402cf 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -192,14 +192,7 @@ mod tests { .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); } } From 4c43e3e059a6fbb5a5a57730abf108e429597c3d Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 7 Apr 2023 23:14:22 +0200 Subject: [PATCH 12/15] changelog link --- axum-extra/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/axum-extra/CHANGELOG.md b/axum-extra/CHANGELOG.md index 999e2234a2..df39eb2c8e 100644 --- a/axum-extra/CHANGELOG.md +++ b/axum-extra/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning]. # Unreleased -- **added:** Added `TypedHeader` which used to be in `axum` +- **added:** Added `TypedHeader` which used to be in `axum` ([#1850]) + +[#1850]: https://github.com/tokio-rs/axum/pull/1850 # 0.7.2 (22. March, 2023) From acaeb8f197cabd8a59888dce2c0ec2cefe445070 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 7 Apr 2023 23:14:26 +0200 Subject: [PATCH 13/15] update public api crates --- axum-extra/Cargo.toml | 4 ++-- axum/Cargo.toml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index c11a13fa50..20de1aa866 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -83,15 +83,15 @@ rustdoc-args = ["--cfg", "docsrs"] allowed = [ "axum", "axum_core", + "axum_macros", "bytes", "cookie", "futures_core", "futures_util", + "headers", "headers_core", "http", "http_body", - "hyper", - "percent_encoding", "prost", "serde", "tokio", diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 94c713a01b..9c3ba4c9d1 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -179,7 +179,9 @@ features = [ [package.metadata.cargo-public-api-crates] allowed = [ + "async_trait", "axum_core", + "axum_macros", "bytes", "futures_core", "futures_sink", From 3b5ba7e03c17918c4cda6deeb67021b52c8b7f9e Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 7 Apr 2023 23:27:44 +0200 Subject: [PATCH 14/15] fix ui tests --- .../fail/argument_not_extractor.stderr | 2 +- .../fail/wrong_return_type.stderr | 2 +- .../fail/generic_without_via.stderr | 2 +- .../fail/generic_without_via_rejection.stderr | 2 +- ...rride_rejection_on_enum_without_via.stderr | 64 +++++++++---------- axum-macros/tests/from_request/pass/named.rs | 4 +- .../tests/from_request/pass/named_parts.rs | 4 +- .../tests/from_request/pass/named_via.rs | 4 +- .../from_request/pass/named_via_parts.rs | 4 +- .../from_request/pass/override_rejection.rs | 1 - examples/websockets/src/main.rs | 2 +- 11 files changed, 45 insertions(+), 46 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 8be92b7913..5f0e8664c8 100644 --- a/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr +++ b/axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr @@ -15,7 +15,7 @@ error[E0277]: the trait bound `bool: FromRequestParts<()>` is not satisfied <(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> - and 26 others + and $N others = 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/wrong_return_type.stderr b/axum-macros/tests/debug_handler/fail/wrong_return_type.stderr index 151c5f84bf..46d4673b75 100644 --- a/axum-macros/tests/debug_handler/fail/wrong_return_type.stderr +++ b/axum-macros/tests/debug_handler/fail/wrong_return_type.stderr @@ -18,4 +18,4 @@ note: required by a bound in `__axum_macros_check_handler_into_response::{closur --> tests/debug_handler/fail/wrong_return_type.rs:4:23 | 4 | async fn handler() -> bool { - | ^^^^ required by this bound in `__axum_macros_check_handler_into_response::{closure#0}::check` + | ^^^^ required by this bound in `check` 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 e875b443b7..961d6a5b35 100644 --- a/axum-macros/tests/from_request/fail/generic_without_via.stderr +++ b/axum-macros/tests/from_request/fail/generic_without_via.stderr @@ -20,5 +20,5 @@ 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` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `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.stderr b/axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr index 167b4a3681..00eb8d09aa 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,5 +20,5 @@ 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` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `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 7ed7dea626..ca8353b2b7 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,39 +5,39 @@ error: cannot use `rejection` without `via` | ^^^^^^^^^ 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}` - | | - | 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 + | +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>> 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 `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 - --> 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>> + --> 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) + --> $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) diff --git a/axum-macros/tests/from_request/pass/named.rs b/axum-macros/tests/from_request/pass/named.rs index fdb70f5f98..f63ae8e9db 100644 --- a/axum-macros/tests/from_request/pass/named.rs +++ b/axum-macros/tests/from_request/pass/named.rs @@ -2,9 +2,9 @@ use axum::{ extract::FromRequest, response::Response, }; -use axum_extra::typed_header::{ +use axum_extra::{ TypedHeader, - TypedHeaderRejection, + typed_header::TypedHeaderRejection, headers::{self, UserAgent}, }; diff --git a/axum-macros/tests/from_request/pass/named_parts.rs b/axum-macros/tests/from_request/pass/named_parts.rs index d166084ae7..cbb67e61da 100644 --- a/axum-macros/tests/from_request/pass/named_parts.rs +++ b/axum-macros/tests/from_request/pass/named_parts.rs @@ -2,9 +2,9 @@ use axum::{ extract::FromRequestParts, response::Response, }; -use axum_extra::typed_header::{ +use axum_extra::{ TypedHeader, - TypedHeaderRejection, + typed_header::TypedHeaderRejection, headers::{self, UserAgent}, }; diff --git a/axum-macros/tests/from_request/pass/named_via.rs b/axum-macros/tests/from_request/pass/named_via.rs index e829c29b39..691627b08d 100644 --- a/axum-macros/tests/from_request/pass/named_via.rs +++ b/axum-macros/tests/from_request/pass/named_via.rs @@ -2,9 +2,9 @@ use axum::{ response::Response, extract::{Extension, FromRequest}, }; -use axum_extra::typed_header::{ +use axum_extra::{ TypedHeader, - TypedHeaderRejection, + typed_header::TypedHeaderRejection, headers::{self, UserAgent}, }; diff --git a/axum-macros/tests/from_request/pass/named_via_parts.rs b/axum-macros/tests/from_request/pass/named_via_parts.rs index 76f5b3c1ca..0377af7b10 100644 --- a/axum-macros/tests/from_request/pass/named_via_parts.rs +++ b/axum-macros/tests/from_request/pass/named_via_parts.rs @@ -2,9 +2,9 @@ use axum::{ response::Response, extract::{Extension, FromRequestParts}, }; -use axum_extra::typed_header::{ +use axum_extra::{ TypedHeader, - TypedHeaderRejection, + typed_header::TypedHeaderRejection, headers::{self, UserAgent}, }; diff --git a/axum-macros/tests/from_request/pass/override_rejection.rs b/axum-macros/tests/from_request/pass/override_rejection.rs index 779058b9fc..25e399b4e0 100644 --- a/axum-macros/tests/from_request/pass/override_rejection.rs +++ b/axum-macros/tests/from_request/pass/override_rejection.rs @@ -4,7 +4,6 @@ use axum::{ http::StatusCode, response::{IntoResponse, Response}, routing::get, - body::Body, Extension, Router, }; diff --git a/examples/websockets/src/main.rs b/examples/websockets/src/main.rs index 491d107b29..1a7a9bd46e 100644 --- a/examples/websockets/src/main.rs +++ b/examples/websockets/src/main.rs @@ -22,7 +22,7 @@ use axum::{ routing::get, Router, }; -use axum_extra::typed_header::TypedHeader; +use axum_extra::TypedHeader; use std::borrow::Cow; use std::ops::ControlFlow; From 9f19a8838cce34d78d969d5b2e0d1750f8c965bb Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 7 Apr 2023 23:49:15 +0200 Subject: [PATCH 15/15] update nightly, does this make ci pass?! --- .github/workflows/CI.yml | 2 +- axum-macros/rust-toolchain | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index a983ca8665..62e6247e44 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -117,7 +117,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: # same as `axum-macros/rust-toolchain` - toolchain: nightly-2022-11-18 + toolchain: nightly-2023-04-06 override: true profile: minimal - uses: Swatinem/rust-cache@v1 diff --git a/axum-macros/rust-toolchain b/axum-macros/rust-toolchain index 8141c9d1e3..a1eb1fc7a7 100644 --- a/axum-macros/rust-toolchain +++ b/axum-macros/rust-toolchain @@ -1 +1 @@ -nightly-2022-11-18 +nightly-2023-04-06