Add type safe state extractor (#1155)

* begin threading the state through

* Pass state to extractors

* make state extractor work

* make sure nesting with different states work

* impl Service for MethodRouter<()>

* Fix some of axum-macro's tests

* Implement more traits for `State`

* Update examples to use `State`

* consistent naming of request body param

* swap type params

* Default the state param to ()

* fix docs references

* Docs and handler state refactoring

* docs clean ups

* more consistent naming

* when does MethodRouter implement Service?

* add missing docs

* use `Router`'s default state type param

* changelog

* don't use default type param for FromRequest and RequestParts

probably safer for library authors so you don't accidentally forget

* fix examples

* minor docs tweaks

* clarify how to convert handlers into services

* group methods in one impl block

* make sure merged `MethodRouter`s can access state

* fix docs link

* test merge with same state type

* Document how to access state from middleware

* Port cookie extractors to use state to extract keys (#1250)

* Updates ECOSYSTEM with a new sample project (#1252)

* Avoid unhelpful compiler suggestion (#1251)

* fix docs typo

* document how library authors should access state

* Add `RequestParts::with_state`

* fix example

* apply suggestions from review

* add relevant changes to axum-extra and axum-core changelogs

* Add `route_service_with_tsr`

* fix trybuild expectations

* make sure `SpaRouter` works with routers that have state

* Change order of type params on FromRequest and RequestParts

* reverse order of `RequestParts::with_state` args to match type params

* Add `FromRef` trait (#1268)

* Add `FromRef` trait

* Remove unnecessary type params

* format

* fix docs link

* format examples

* Avoid unnecessary `MethodRouter`

* apply suggestions from review

Co-authored-by: Dani Pardo <[email protected]>
Co-authored-by: Jonas Platte <[email protected]>
This commit is contained in:
David Pedersen
2022-08-17 15:13:31 +00:00
committed by GitHub
co-authored by Dani Pardo Jonas Platte
parent 90dbd52ee4
commit 423308de3c
132 changed files with 2404 additions and 1126 deletions
+1 -1
View File
@@ -69,7 +69,7 @@ let some_fallible_service = tower::service_fn(|_req| async {
Ok::<_, anyhow::Error>(Response::new(Body::empty()))
});
let app = Router::new().route(
let app = Router::new().route_service(
"/",
// we cannot route to `some_fallible_service` directly since it might fail.
// we have to use `handle_error` which converts its errors into responses
+7 -5
View File
@@ -421,13 +421,14 @@ use http::{StatusCode, header::{HeaderValue, USER_AGENT}};
struct ExtractUserAgent(HeaderValue);
#[async_trait]
impl<B> FromRequest<B> for ExtractUserAgent
impl<S, B> FromRequest<S, B> for ExtractUserAgent
where
B: Send,
S: Send,
{
type Rejection = (StatusCode, &'static str);
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if let Some(user_agent) = req.headers().get(USER_AGENT) {
Ok(ExtractUserAgent(user_agent.clone()))
} else {
@@ -472,13 +473,14 @@ struct AuthenticatedUser {
}
#[async_trait]
impl<B> FromRequest<B> for AuthenticatedUser
impl<S, B> FromRequest<S, B> for AuthenticatedUser
where
B: Send,
S: Send,
{
type Rejection = Response;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let TypedHeader(Authorization(token)) =
TypedHeader::<Authorization<Bearer>>::from_request(req)
.await
@@ -633,7 +635,7 @@ fn token_is_valid(token: &str) -> bool {
}
let app = Router::new().layer(middleware::from_fn(auth_middleware));
# let _: Router = app;
# let _: Router<()> = app;
```
[`body::Body`]: crate::body::Body
+3 -5
View File
@@ -11,7 +11,7 @@ use axum::{
http::{StatusCode, Method, Uri},
};
let handler = get(|| async {}).fallback(fallback.into_service());
let handler = get(|| async {}).fallback(fallback);
let app = Router::new().route("/", handler);
@@ -36,11 +36,9 @@ use axum::{
http::{StatusCode, Uri},
};
let one = get(|| async {})
.fallback(fallback_one.into_service());
let one = get(|| async {}).fallback(fallback_one);
let two = post(|| async {})
.fallback(fallback_two.into_service());
let two = post(|| async {}).fallback(fallback_two);
let method_route = one.merge(two);
+120 -6
View File
@@ -6,7 +6,8 @@
- [Ordering](#ordering)
- [Writing middleware](#writing-middleware)
- [Routing to services/middleware and backpressure](#routing-to-servicesmiddleware-and-backpressure)
- [Sharing state between handlers and middleware](#sharing-state-between-handlers-and-middleware)
- [Accessing state in middleware](#accessing-state-in-middleware)
- [Passing state from middleware to handlers](#passing-state-from-middleware-to-handlers)
# Intro
@@ -95,7 +96,7 @@ let app = Router::new()
.layer(layer_one)
.layer(layer_two)
.layer(layer_three);
# let app: Router<axum::body::Body> = app;
# let _: Router<(), axum::body::Body> = app;
```
Think of the middleware as being layered like an onion where each new layer
@@ -154,7 +155,7 @@ let app = Router::new()
.layer(layer_two)
.layer(layer_three),
);
# let app: Router<axum::body::Body> = app;
# let _: Router<(), axum::body::Body> = app;
```
`ServiceBuilder` works by composing all layers into one such that they run top
@@ -386,9 +387,119 @@ Also note that handlers created from async functions don't care about
backpressure and are always ready. So if you're not using any Tower
middleware you don't have to worry about any of this.
# Sharing state between handlers and middleware
# Accessing state in middleware
State can be shared between middleware and handlers using [request extensions]:
Handlers can access state using the [`State`] extractor but this isn't available
to middleware. Instead you have to pass the state directly to middleware using
either closure captures (for [`axum::middleware::from_fn`]) or regular struct
fields (if you're implementing a [`tower::Layer`])
## Accessing state in `axum::middleware::from_fn`
```rust
use axum::{
Router,
routing::get,
middleware::{self, Next},
response::Response,
extract::State,
http::Request,
};
#[derive(Clone)]
struct AppState {}
async fn my_middleware<B>(
state: AppState,
req: Request<B>,
next: Next<B>,
) -> Response {
next.run(req).await
}
async fn handler(_: State<AppState>) {}
let state = AppState {};
let app = Router::with_state(state.clone())
.route("/", get(handler))
.layer(middleware::from_fn(move |req, next| {
my_middleware(state.clone(), req, next)
}));
# let _: Router<_> = app;
```
## Accessing state in custom `tower::Layer`s
```rust
use axum::{
Router,
routing::get,
middleware::{self, Next},
response::Response,
extract::State,
http::Request,
};
use tower::{Layer, Service};
use std::task::{Context, Poll};
#[derive(Clone)]
struct AppState {}
#[derive(Clone)]
struct MyLayer {
state: AppState,
}
impl<S> Layer<S> for MyLayer {
type Service = MyService<S>;
fn layer(&self, inner: S) -> Self::Service {
MyService {
inner,
state: self.state.clone(),
}
}
}
#[derive(Clone)]
struct MyService<S> {
inner: S,
state: AppState,
}
impl<S, B> Service<Request<B>> for MyService<S>
where
S: Service<Request<B>>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: Request<B>) -> Self::Future {
// do something with `self.state`
self.inner.call(req)
}
}
async fn handler(_: State<AppState>) {}
let state = AppState {};
let app = Router::with_state(state.clone())
.route("/", get(handler))
.layer(MyLayer { state });
# let _: Router<_> = app;
```
# Passing state from middleware to handlers
State can be passed from middleware to handlers using [request extensions]:
```rust
use axum::{
@@ -415,6 +526,8 @@ async fn auth<B>(mut req: Request<B>, next: Next<B>) -> Result<Response, StatusC
};
if let Some(current_user) = authorize_current_user(auth_header).await {
// insert the current user into a request extension so the handler can
// extract it
req.extensions_mut().insert(current_user);
Ok(next.run(req).await)
} else {
@@ -437,7 +550,7 @@ async fn handler(
let app = Router::new()
.route("/", get(handler))
.route_layer(middleware::from_fn(auth));
# let app: Router = app;
# let _: Router<()> = app;
```
[Response extensions] can also be used but note that request extensions are not
@@ -462,3 +575,4 @@ extensions you need.
[`MethodRouter::route_layer`]: crate::routing::MethodRouter::route_layer
[request extensions]: https://docs.rs/http/latest/http/request/struct.Request.html#method.extensions
[Response extensions]: https://docs.rs/http/latest/http/response/struct.Response.html#method.extensions
[`State`]: crate::extract::State
+2 -2
View File
@@ -1,4 +1,4 @@
Add a fallback service to the router.
Add a fallback [`Handler`] to the router.
This service will be called if no routes matches the incoming request.
@@ -13,7 +13,7 @@ use axum::{
let app = Router::new()
.route("/foo", get(|| async { /* ... */ }))
.fallback(fallback.into_service());
.fallback(fallback);
async fn fallback(uri: Uri) -> (StatusCode, String) {
(StatusCode::NOT_FOUND, format!("No route for {}", uri))
+3 -3
View File
@@ -104,7 +104,7 @@ let api_routes = Router::new().nest("/users", get(|| async {}));
let app = Router::new()
.nest("/api", api_routes)
.fallback(fallback.into_service());
.fallback(fallback);
# let _: Router = app;
```
@@ -132,11 +132,11 @@ async fn api_fallback() -> (StatusCode, Json<Value>) {
let api_routes = Router::new()
.nest("/users", get(|| async {}))
// add dedicated fallback for requests starting with `/api`
.fallback(api_fallback.into_service());
.fallback(api_fallback);
let app = Router::new()
.nest("/api", api_routes)
.fallback(fallback.into_service());
.fallback(fallback);
# let _: Router = app;
```
+4 -85
View File
@@ -3,10 +3,10 @@ Add another route to the router.
`path` is a string of path segments separated by `/`. Each segment
can be either static, a capture, or a wildcard.
`service` is the [`Service`] that should receive the request if the path matches
`path`. `service` will commonly be a handler wrapped in a method router like
[`get`](crate::routing::get). See [`handler`](crate::handler) for more details
on handlers.
`method_router` is the [`MethodRouter`] that should receive the request if the
path matches `path`. `method_router` will commonly be a handler wrapped in a method
router like [`get`](crate::routing::get). See [`handler`](crate::handler) for
more details on handlers.
# Static paths
@@ -105,69 +105,6 @@ async fn serve_asset(Path(path): Path<String>) {}
# };
```
# Routing to any [`Service`]
axum also supports routing to general [`Service`]s:
```rust,no_run
use axum::{
Router,
body::Body,
routing::{any_service, get_service},
http::{Request, StatusCode},
error_handling::HandleErrorLayer,
};
use tower_http::services::ServeFile;
use http::Response;
use std::{convert::Infallible, io};
use tower::service_fn;
let app = Router::new()
.route(
// Any request to `/` goes to a service
"/",
// Services whose response body is not `axum::body::BoxBody`
// can be wrapped in `axum::routing::any_service` (or one of the other routing filters)
// to have the response body mapped
any_service(service_fn(|_: Request<Body>| async {
let res = Response::new(Body::from("Hi from `GET /`"));
Ok::<_, Infallible>(res)
}))
)
.route(
"/foo",
// This service's response body is `axum::body::BoxBody` so
// it can be routed to directly.
service_fn(|req: Request<Body>| async move {
let body = Body::from(format!("Hi from `{} /foo`", req.method()));
let body = axum::body::boxed(body);
let res = Response::new(body);
Ok::<_, Infallible>(res)
})
)
.route(
// GET `/static/Cargo.toml` goes to a service from tower-http
"/static/Cargo.toml",
get_service(ServeFile::new("Cargo.toml"))
// though we must handle any potential errors
.handle_error(|error: io::Error| async move {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
)
})
);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
Routing to arbitrary services in this way has complications for backpressure
([`Service::poll_ready`]). See the [Routing to services and backpressure] module
for more details.
[Routing to services and backpressure]: middleware/index.html#routing-to-servicesmiddleware-and-backpressure
# Panics
Panics if the route overlaps with another route:
@@ -187,21 +124,3 @@ The static route `/foo` and the dynamic route `/:key` are not considered to
overlap and `/foo` will take precedence.
Also panics if `path` is empty.
## Nesting
`route` cannot be used to nest `Router`s. Instead use [`Router::nest`].
Attempting to will result in a panic:
```rust,should_panic
use axum::{routing::get, Router};
let app = Router::new().route(
"/",
Router::new().route("/foo", get(|| async {})),
);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
+81
View File
@@ -0,0 +1,81 @@
Add another route to the router that calls a [`Service`].
# Example
```rust,no_run
use axum::{
Router,
body::Body,
routing::{any_service, get_service},
http::{Request, StatusCode},
error_handling::HandleErrorLayer,
};
use tower_http::services::ServeFile;
use http::Response;
use std::{convert::Infallible, io};
use tower::service_fn;
let app = Router::new()
.route(
// Any request to `/` goes to a service
"/",
// Services whose response body is not `axum::body::BoxBody`
// can be wrapped in `axum::routing::any_service` (or one of the other routing filters)
// to have the response body mapped
any_service(service_fn(|_: Request<Body>| async {
let res = Response::new(Body::from("Hi from `GET /`"));
Ok::<_, Infallible>(res)
}))
)
.route_service(
"/foo",
// This service's response body is `axum::body::BoxBody` so
// it can be routed to directly.
service_fn(|req: Request<Body>| async move {
let body = Body::from(format!("Hi from `{} /foo`", req.method()));
let body = axum::body::boxed(body);
let res = Response::new(body);
Ok::<_, Infallible>(res)
})
)
.route(
// GET `/static/Cargo.toml` goes to a service from tower-http
"/static/Cargo.toml",
get_service(ServeFile::new("Cargo.toml"))
// though we must handle any potential errors
.handle_error(|error: io::Error| async move {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
)
})
);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
Routing to arbitrary services in this way has complications for backpressure
([`Service::poll_ready`]). See the [Routing to services and backpressure] module
for more details.
# Panics
Panics for the same reasons as [`Router::route`] or if you attempt to route to a
`Router`:
```rust,should_panic
use axum::{routing::get, Router};
let app = Router::new().route_service(
"/",
Router::new().route("/foo", get(|| async {})),
);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
Use [`Router::nest`] instead.
[Routing to services and backpressure]: middleware/index.html#routing-to-servicesmiddleware-and-backpressure
+11 -12
View File
@@ -1,7 +1,6 @@
#![doc = include_str!("../docs/error_handling.md")]
use crate::{
body::boxed,
extract::{FromRequest, RequestParts},
http::{Request, StatusCode},
response::{IntoResponse, Response},
@@ -113,16 +112,16 @@ where
}
}
impl<S, F, ReqBody, Fut, Res> Service<Request<ReqBody>> for HandleError<S, F, ()>
impl<S, F, B, Fut, Res> Service<Request<B>> for HandleError<S, F, ()>
where
S: Service<Request<ReqBody>> + Clone + Send + 'static,
S: Service<Request<B>> + Clone + Send + 'static,
S::Response: IntoResponse + Send,
S::Error: Send,
S::Future: Send,
F: FnOnce(S::Error) -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
ReqBody: Send + 'static,
B: Send + 'static,
{
type Response = Response;
type Error = Infallible;
@@ -132,7 +131,7 @@ where
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
fn call(&mut self, req: Request<B>) -> Self::Future {
let f = self.f.clone();
let clone = self.inner.clone();
@@ -152,18 +151,18 @@ where
#[allow(unused_macros)]
macro_rules! impl_service {
( $($ty:ident),* $(,)? ) => {
impl<S, F, ReqBody, Res, Fut, $($ty,)*> Service<Request<ReqBody>>
impl<S, F, B, Res, Fut, $($ty,)*> Service<Request<B>>
for HandleError<S, F, ($($ty,)*)>
where
S: Service<Request<ReqBody>> + Clone + Send + 'static,
S: Service<Request<B>> + Clone + Send + 'static,
S::Response: IntoResponse + Send,
S::Error: Send,
S::Future: Send,
F: FnOnce($($ty),*, S::Error) -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
$( $ty: FromRequest<ReqBody> + Send,)*
ReqBody: Send + 'static,
$( $ty: FromRequest<(), B> + Send,)*
B: Send + 'static,
{
type Response = Response;
type Error = Infallible;
@@ -175,7 +174,7 @@ macro_rules! impl_service {
}
#[allow(non_snake_case)]
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
fn call(&mut self, req: Request<B>) -> Self::Future {
let f = self.f.clone();
let clone = self.inner.clone();
@@ -187,7 +186,7 @@ macro_rules! impl_service {
$(
let $ty = match $ty::from_request(&mut req).await {
Ok(value) => value,
Err(rejection) => return Ok(rejection.into_response().map(boxed)),
Err(rejection) => return Ok(rejection.into_response()),
};
)*
@@ -200,7 +199,7 @@ macro_rules! impl_service {
match inner.oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Ok(f($($ty),*, err).await.into_response().map(boxed)),
Err(err) => Ok(f($($ty),*, err).await.into_response()),
}
});
+3 -2
View File
@@ -73,14 +73,15 @@ use tower_service::Service;
pub struct Extension<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for Extension<T>
impl<T, S, B> FromRequest<S, B> for Extension<T>
where
T: Clone + Send + Sync + 'static,
B: Send,
S: Send,
{
type Rejection = ExtensionRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let value = req
.extensions()
.get::<T>()
+4 -3
View File
@@ -128,14 +128,15 @@ opaque_future! {
pub struct ConnectInfo<T>(pub T);
#[async_trait]
impl<B, T> FromRequest<B> for ConnectInfo<T>
impl<S, B, T> FromRequest<S, B> for ConnectInfo<T>
where
B: Send,
S: Send,
T: Clone + Send + Sync + 'static,
{
type Rejection = <Extension<Self> as FromRequest<B>>::Rejection;
type Rejection = <Extension<Self> as FromRequest<S, B>>::Rejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let Extension(connect_info) = Extension::<Self>::from_request(req).await?;
Ok(connect_info)
}
+4 -3
View File
@@ -36,15 +36,16 @@ use std::ops::Deref;
pub struct ContentLengthLimit<T, const N: u64>(pub T);
#[async_trait]
impl<T, B, const N: u64> FromRequest<B> for ContentLengthLimit<T, N>
impl<T, S, B, const N: u64> FromRequest<S, B> for ContentLengthLimit<T, N>
where
T: FromRequest<B>,
T: FromRequest<S, B>,
T::Rejection: IntoResponse,
B: Send,
S: Send,
{
type Rejection = ContentLengthLimitRejection<T::Rejection>;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let content_length = req
.headers()
.get(http::header::CONTENT_LENGTH)
+3 -2
View File
@@ -21,13 +21,14 @@ const X_FORWARDED_HOST_HEADER_KEY: &str = "X-Forwarded-Host";
pub struct Host(pub String);
#[async_trait]
impl<B> FromRequest<B> for Host
impl<S, B> FromRequest<S, B> for Host
where
B: Send,
S: Send,
{
type Rejection = HostRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if let Some(host) = parse_forwarded(req.headers()) {
return Ok(Host(host.to_owned()));
}
+7 -4
View File
@@ -64,13 +64,14 @@ impl MatchedPath {
}
#[async_trait]
impl<B> FromRequest<B> for MatchedPath
impl<S, B> FromRequest<S, B> for MatchedPath
where
B: Send,
S: Send,
{
type Rejection = MatchedPathRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let matched_path = req
.extensions()
.get::<Self>()
@@ -84,7 +85,9 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::{extract::Extension, handler::Handler, routing::get, test_helpers::*, Router};
use crate::{
extract::Extension, handler::HandlerWithoutStateExt, routing::get, test_helpers::*, Router,
};
use http::{Request, StatusCode};
use std::task::{Context, Poll};
use tower::layer::layer_fn;
@@ -93,7 +96,7 @@ mod tests {
#[derive(Clone)]
struct SetMatchedPathExtension<S>(S);
impl<B, S> Service<Request<B>> for SetMatchedPathExtension<S>
impl<S, B> Service<Request<B>> for SetMatchedPathExtension<S>
where
S: Service<Request<B>>,
{
+6 -4
View File
@@ -14,9 +14,10 @@ mod content_length_limit;
mod host;
mod raw_query;
mod request_parts;
mod state;
#[doc(inline)]
pub use axum_core::extract::{FromRequest, RequestParts};
pub use axum_core::extract::{FromRef, FromRequest, RequestParts};
#[doc(inline)]
#[allow(deprecated)]
@@ -27,6 +28,7 @@ pub use self::{
path::Path,
raw_query::RawQuery,
request_parts::{BodyStream, RawBody},
state::State,
};
#[doc(no_inline)]
@@ -73,13 +75,13 @@ pub use self::ws::WebSocketUpgrade;
#[doc(no_inline)]
pub use crate::TypedHeader;
pub(crate) fn take_body<B>(req: &mut RequestParts<B>) -> Result<B, BodyAlreadyExtracted> {
pub(crate) fn take_body<S, B>(req: &mut RequestParts<S, B>) -> Result<B, BodyAlreadyExtracted> {
req.take_body().ok_or_else(BodyAlreadyExtracted::default)
}
// this is duplicated in `axum-extra/src/extract/form.rs`
pub(super) fn has_content_type<B>(
req: &RequestParts<B>,
pub(super) fn has_content_type<S, B>(
req: &RequestParts<S, B>,
expected_content_type: &mime::Mime,
) -> bool {
let content_type = if let Some(content_type) = req.headers().get(header::CONTENT_TYPE) {
+4 -3
View File
@@ -50,14 +50,15 @@ pub struct Multipart {
}
#[async_trait]
impl<B> FromRequest<B> for Multipart
impl<S, B> FromRequest<S, B> for Multipart
where
B: HttpBody<Data = Bytes> + Default + Unpin + Send + 'static,
B::Error: Into<BoxError>,
S: Send,
{
type Rejection = MultipartRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let stream = BodyStream::from_request(req).await?;
let headers = req.headers();
let boundary = parse_boundary(headers).ok_or(InvalidBoundary)?;
@@ -179,7 +180,7 @@ impl<'a> Field<'a> {
/// }
///
/// let app = Router::new().route("/upload", post(upload));
/// # let _: Router<axum::body::Body> = app;
/// # let _: Router = app;
/// ```
pub async fn chunk(&mut self) -> Result<Option<Bytes>, MultipartError> {
self.inner
+3 -2
View File
@@ -163,14 +163,15 @@ impl<T> DerefMut for Path<T> {
}
#[async_trait]
impl<T, B> FromRequest<B> for Path<T>
impl<T, S, B> FromRequest<S, B> for Path<T>
where
T: DeserializeOwned + Send,
B: Send,
S: Send,
{
type Rejection = PathRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let params = match req.extensions_mut().get::<UrlParams>() {
Some(UrlParams::Params(params)) => params,
Some(UrlParams::InvalidUtf8InPathParam { key }) => {
+5 -3
View File
@@ -49,14 +49,15 @@ use std::ops::Deref;
pub struct Query<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for Query<T>
impl<T, S, B> FromRequest<S, B> for Query<T>
where
T: DeserializeOwned,
B: Send,
S: Send,
{
type Rejection = QueryRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let query = req.uri().query().unwrap_or_default();
let value = serde_urlencoded::from_str(query)
.map_err(FailedToDeserializeQueryString::__private_new)?;
@@ -81,7 +82,8 @@ mod tests {
use std::fmt::Debug;
async fn check<T: DeserializeOwned + PartialEq + Debug>(uri: impl AsRef<str>, value: T) {
let mut req = RequestParts::new(Request::builder().uri(uri.as_ref()).body(()).unwrap());
let req = Request::builder().uri(uri.as_ref()).body(()).unwrap();
let mut req = RequestParts::new(req);
assert_eq!(Query::<T>::from_request(&mut req).await.unwrap().0, value);
}
+3 -2
View File
@@ -27,13 +27,14 @@ use std::convert::Infallible;
pub struct RawQuery(pub Option<String>);
#[async_trait]
impl<B> FromRequest<B> for RawQuery
impl<S, B> FromRequest<S, B> for RawQuery
where
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let query = req.uri().query().map(|query| query.to_owned());
Ok(Self(query))
}
+9 -6
View File
@@ -86,13 +86,14 @@ pub struct OriginalUri(pub Uri);
#[cfg(feature = "original-uri")]
#[async_trait]
impl<B> FromRequest<B> for OriginalUri
impl<S, B> FromRequest<S, B> for OriginalUri
where
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let uri = Extension::<Self>::from_request(req)
.await
.unwrap_or_else(|_| Extension(OriginalUri(req.uri().clone())))
@@ -140,15 +141,16 @@ impl Stream for BodyStream {
}
#[async_trait]
impl<B> FromRequest<B> for BodyStream
impl<S, B> FromRequest<S, B> for BodyStream
where
B: HttpBody + Send + 'static,
B::Data: Into<Bytes>,
B::Error: Into<BoxError>,
S: Send,
{
type Rejection = BodyAlreadyExtracted;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let body = take_body(req)?
.map_data(Into::into)
.map_err(|err| Error::new(err.into()));
@@ -196,13 +198,14 @@ fn body_stream_traits() {
pub struct RawBody<B = Body>(pub B);
#[async_trait]
impl<B> FromRequest<B> for RawBody<B>
impl<S, B> FromRequest<S, B> for RawBody<B>
where
B: Send,
S: Send,
{
type Rejection = BodyAlreadyExtracted;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let body = take_body(req)?;
Ok(Self(body))
}
+207
View File
@@ -0,0 +1,207 @@
use async_trait::async_trait;
use axum_core::extract::{FromRef, FromRequest, RequestParts};
use std::{
convert::Infallible,
ops::{Deref, DerefMut},
};
/// Extractor for state.
///
/// Note this extractor is not available to middleware. See ["Accessing state in
/// middleware"][state-from-middleware] for how to access state in middleware.
///
/// [state-from-middleware]: ../middleware/index.html#accessing-state-in-middleware
///
/// # With `Router`
///
/// ```
/// use axum::{Router, routing::get, extract::State};
///
/// // the application state
/// //
/// // here you can put configuration, database connection pools, or whatever
/// // state you need
/// #[derive(Clone)]
/// struct AppState {}
///
/// let state = AppState {};
///
/// // create a `Router` that holds our state
/// let app = Router::with_state(state).route("/", get(handler));
///
/// async fn handler(
/// // access the state via the `State` extractor
/// // extracting a state of the wrong type results in a compile error
/// State(state): State<AppState>,
/// ) {
/// // use `state`...
/// }
/// # let _: Router<AppState> = app;
/// ```
///
/// # With `MethodRouter`
///
/// ```
/// use axum::{routing::get, extract::State};
///
/// #[derive(Clone)]
/// struct AppState {}
///
/// let state = AppState {};
///
/// let method_router_with_state = get(handler)
/// // provide the state so the handler can access it
/// .with_state(state);
///
/// async fn handler(State(state): State<AppState>) {
/// // use `state`...
/// }
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(method_router_with_state.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// # With `Handler`
///
/// ```
/// use axum::{routing::get, handler::Handler, extract::State};
///
/// #[derive(Clone)]
/// struct AppState {}
///
/// let state = AppState {};
///
/// async fn handler(State(state): State<AppState>) {
/// // use `state`...
/// }
///
/// // provide the state so the handler can access it
/// let handler_with_state = handler.with_state(state);
///
/// # async {
/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
/// .serve(handler_with_state.into_make_service())
/// .await
/// .expect("server failed");
/// # };
/// ```
///
/// # Substates
///
/// [`State`] only allows a single state type but you can use [`From`] to extract "substates":
///
/// ```
/// use axum::{Router, routing::get, extract::{State, FromRef}};
///
/// // the application state
/// #[derive(Clone)]
/// struct AppState {
/// // that holds some api specific state
/// api_state: ApiState,
/// }
///
/// // the api specific state
/// #[derive(Clone)]
/// struct ApiState {}
///
/// // support converting an `AppState` in an `ApiState`
/// impl FromRef<AppState> for ApiState {
/// fn from_ref(app_state: &AppState) -> ApiState {
/// app_state.api_state.clone()
/// }
/// }
///
/// let state = AppState {
/// api_state: ApiState {},
/// };
///
/// let app = Router::with_state(state)
/// .route("/", get(handler))
/// .route("/api/users", get(api_users));
///
/// async fn api_users(
/// // access the api specific state
/// State(api_state): State<ApiState>,
/// ) {
/// }
///
/// async fn handler(
/// // we can still access to top level state
/// State(state): State<AppState>,
/// ) {
/// }
/// # let _: Router<AppState> = app;
/// ```
///
/// # For library authors
///
/// If you're writing a library that has an extractor that needs state, this is the recommended way
/// to do it:
///
/// ```rust
/// use axum_core::extract::{FromRequest, RequestParts, FromRef};
/// use async_trait::async_trait;
/// use std::convert::Infallible;
///
/// // the extractor your library provides
/// struct MyLibraryExtractor;
///
/// #[async_trait]
/// impl<S, B> FromRequest<S, B> for MyLibraryExtractor
/// where
/// B: Send,
/// // keep `S` generic but require that it can produce a `MyLibraryState`
/// // this means users will have to implement `FromRef<UserState> for MyLibraryState`
/// MyLibraryState: FromRef<S>,
/// S: Send,
/// {
/// type Rejection = Infallible;
///
/// async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
/// // get a `MyLibraryState` from a reference to the state
/// let state = MyLibraryState::from_ref(req.state());
///
/// // ...
/// # todo!()
/// }
/// }
///
/// // the state your library needs
/// struct MyLibraryState {
/// // ...
/// }
/// ```
///
/// Note that you don't need to use the `State` extractor since you can access the state directly
/// from [`RequestParts`].
#[derive(Debug, Default, Clone, Copy)]
pub struct State<S>(pub S);
#[async_trait]
impl<B, OuterState, InnerState> FromRequest<OuterState, B> for State<InnerState>
where
B: Send,
InnerState: FromRef<OuterState>,
OuterState: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<OuterState, B>) -> Result<Self, Self::Rejection> {
let inner_state = InnerState::from_ref(req.state());
Ok(Self(inner_state))
}
}
impl<S> Deref for State<S> {
type Target = S;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<S> DerefMut for State<S> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
+5 -4
View File
@@ -275,13 +275,14 @@ impl WebSocketUpgrade {
}
#[async_trait]
impl<B> FromRequest<B> for WebSocketUpgrade
impl<S, B> FromRequest<S, B> for WebSocketUpgrade
where
B: Send,
S: Send,
{
type Rejection = WebSocketUpgradeRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if req.method() != Method::GET {
return Err(MethodNotGet.into());
}
@@ -320,7 +321,7 @@ where
}
}
fn header_eq<B>(req: &RequestParts<B>, key: HeaderName, value: &'static str) -> bool {
fn header_eq<S, B>(req: &RequestParts<S, B>, key: HeaderName, value: &'static str) -> bool {
if let Some(header) = req.headers().get(&key) {
header.as_bytes().eq_ignore_ascii_case(value.as_bytes())
} else {
@@ -328,7 +329,7 @@ fn header_eq<B>(req: &RequestParts<B>, key: HeaderName, value: &'static str) ->
}
}
fn header_contains<B>(req: &RequestParts<B>, key: HeaderName, value: &'static str) -> bool {
fn header_contains<S, B>(req: &RequestParts<S, B>, key: HeaderName, value: &'static str) -> bool {
let header = if let Some(header) = req.headers().get(&key) {
header
} else {
+34 -36
View File
@@ -56,16 +56,17 @@ use std::ops::Deref;
pub struct Form<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for Form<T>
impl<T, S, B> FromRequest<S, B> for Form<T>
where
T: DeserializeOwned,
B: HttpBody + Send,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send,
{
type Rejection = FormRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if req.method() == Method::GET {
let query = req.uri().query().unwrap_or_default();
let value = serde_urlencoded::from_str(query)
@@ -125,29 +126,27 @@ mod tests {
}
async fn check_query<T: DeserializeOwned + PartialEq + Debug>(uri: impl AsRef<str>, value: T) {
let mut req = RequestParts::new(
Request::builder()
.uri(uri.as_ref())
.body(Empty::<Bytes>::new())
.unwrap(),
);
let req = Request::builder()
.uri(uri.as_ref())
.body(Empty::<Bytes>::new())
.unwrap();
let mut req = RequestParts::new(req);
assert_eq!(Form::<T>::from_request(&mut req).await.unwrap().0, value);
}
async fn check_body<T: Serialize + DeserializeOwned + PartialEq + Debug>(value: T) {
let mut req = RequestParts::new(
Request::builder()
.uri("http://example.com/test")
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.body(Full::<Bytes>::new(
serde_urlencoded::to_string(&value).unwrap().into(),
))
.unwrap(),
);
let req = Request::builder()
.uri("http://example.com/test")
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.body(Full::<Bytes>::new(
serde_urlencoded::to_string(&value).unwrap().into(),
))
.unwrap();
let mut req = RequestParts::new(req);
assert_eq!(Form::<T>::from_request(&mut req).await.unwrap().0, value);
}
@@ -204,21 +203,20 @@ mod tests {
#[tokio::test]
async fn test_incorrect_content_type() {
let mut req = RequestParts::new(
Request::builder()
.uri("http://example.com/test")
.method(Method::POST)
.header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref())
.body(Full::<Bytes>::new(
serde_urlencoded::to_string(&Pagination {
size: Some(10),
page: None,
})
.unwrap()
.into(),
))
.unwrap(),
);
let req = Request::builder()
.uri("http://example.com/test")
.method(Method::POST)
.header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref())
.body(Full::<Bytes>::new(
serde_urlencoded::to_string(&Pagination {
size: Some(10),
page: None,
})
.unwrap()
.into(),
))
.unwrap();
let mut req = RequestParts::new(req);
assert!(matches!(
Form::<Pagination>::from_request(&mut req)
.await
+8 -8
View File
@@ -19,29 +19,29 @@ opaque_future! {
pin_project! {
/// The response future for [`Layered`](super::Layered).
pub struct LayeredFuture<S, ReqBody>
pub struct LayeredFuture<B, S>
where
S: Service<Request<ReqBody>>,
S: Service<Request<B>>,
{
#[pin]
inner: Map<Oneshot<S, Request<ReqBody>>, fn(Result<S::Response, S::Error>) -> Response>,
inner: Map<Oneshot<S, Request<B>>, fn(Result<S::Response, S::Error>) -> Response>,
}
}
impl<S, ReqBody> LayeredFuture<S, ReqBody>
impl<B, S> LayeredFuture<B, S>
where
S: Service<Request<ReqBody>>,
S: Service<Request<B>>,
{
pub(super) fn new(
inner: Map<Oneshot<S, Request<ReqBody>>, fn(Result<S::Response, S::Error>) -> Response>,
inner: Map<Oneshot<S, Request<B>>, fn(Result<S::Response, S::Error>) -> Response>,
) -> Self {
Self { inner }
}
}
impl<S, ReqBody> Future for LayeredFuture<S, ReqBody>
impl<B, S> Future for LayeredFuture<B, S>
where
S: Service<Request<ReqBody>>,
S: Service<Request<B>>,
{
type Output = Response;
+25 -11
View File
@@ -11,29 +11,40 @@ use tower_service::Service;
/// An adapter that makes a [`Handler`] into a [`Service`].
///
/// Created with [`Handler::into_service`].
pub struct IntoService<H, T, B> {
/// Created with [`HandlerWithoutStateExt::into_service`].
///
/// [`HandlerWithoutStateExt::into_service`]: super::HandlerWithoutStateExt::into_service
pub struct IntoService<H, T, S, B> {
handler: H,
state: S,
_marker: PhantomData<fn() -> (T, B)>,
}
impl<H, T, S, B> IntoService<H, T, S, B> {
/// Get a reference to the state.
pub fn state(&self) -> &S {
&self.state
}
}
#[test]
fn traits() {
use crate::test_helpers::*;
assert_send::<IntoService<(), NotSendSync, NotSendSync>>();
assert_sync::<IntoService<(), NotSendSync, NotSendSync>>();
assert_send::<IntoService<(), NotSendSync, (), NotSendSync>>();
assert_sync::<IntoService<(), NotSendSync, (), NotSendSync>>();
}
impl<H, T, B> IntoService<H, T, B> {
pub(super) fn new(handler: H) -> Self {
impl<H, T, S, B> IntoService<H, T, S, B> {
pub(super) fn new(handler: H, state: S) -> Self {
Self {
handler,
state,
_marker: PhantomData,
}
}
}
impl<H, T, B> fmt::Debug for IntoService<H, T, B> {
impl<H, T, S, B> fmt::Debug for IntoService<H, T, S, B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("IntoService")
.field(&format_args!("..."))
@@ -41,22 +52,25 @@ impl<H, T, B> fmt::Debug for IntoService<H, T, B> {
}
}
impl<H, T, B> Clone for IntoService<H, T, B>
impl<H, T, S, B> Clone for IntoService<H, T, S, B>
where
H: Clone,
S: Clone,
{
fn clone(&self) -> Self {
Self {
handler: self.handler.clone(),
state: self.state.clone(),
_marker: PhantomData,
}
}
}
impl<H, T, B> Service<Request<B>> for IntoService<H, T, B>
impl<H, T, S, B> Service<Request<B>> for IntoService<H, T, S, B>
where
H: Handler<T, B> + Clone + Send + 'static,
H: Handler<T, S, B> + Clone + Send + 'static,
B: Send + 'static,
S: Clone,
{
type Response = Response;
type Error = Infallible;
@@ -74,7 +88,7 @@ where
use futures_util::future::FutureExt;
let handler = self.handler.clone();
let future = Handler::call(handler, req);
let future = Handler::call(handler, self.state.clone(), req);
let future = future.map(Ok as _);
super::future::IntoServiceFuture::new(future)
@@ -0,0 +1,85 @@
use super::Handler;
use crate::response::Response;
use http::Request;
use std::{
convert::Infallible,
fmt,
marker::PhantomData,
task::{Context, Poll},
};
use tower_service::Service;
pub(crate) struct IntoServiceStateInExtension<H, T, S, B> {
handler: H,
_marker: PhantomData<fn() -> (T, S, B)>,
}
#[test]
fn traits() {
use crate::test_helpers::*;
assert_send::<IntoServiceStateInExtension<(), NotSendSync, (), NotSendSync>>();
assert_sync::<IntoServiceStateInExtension<(), NotSendSync, (), NotSendSync>>();
}
impl<H, T, S, B> IntoServiceStateInExtension<H, T, S, B> {
pub(crate) fn new(handler: H) -> Self {
Self {
handler,
_marker: PhantomData,
}
}
}
impl<H, T, S, B> fmt::Debug for IntoServiceStateInExtension<H, T, S, B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("IntoServiceStateInExtension")
.field(&format_args!("..."))
.finish()
}
}
impl<H, T, S, B> Clone for IntoServiceStateInExtension<H, T, S, B>
where
H: Clone,
{
fn clone(&self) -> Self {
Self {
handler: self.handler.clone(),
_marker: PhantomData,
}
}
}
impl<H, T, S, B> Service<Request<B>> for IntoServiceStateInExtension<H, T, S, B>
where
H: Handler<T, S, B> + Clone + Send + 'static,
B: Send + 'static,
S: Clone + Send + Sync + 'static,
{
type Response = Response;
type Error = Infallible;
type Future = super::future::IntoServiceFuture<H::Future>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
// `IntoServiceStateInExtension` can only be constructed from async functions which are always ready, or
// from `Layered` which bufferes in `<Layered as Handler>::call` and is therefore
// also always ready.
Poll::Ready(Ok(()))
}
fn call(&mut self, mut req: Request<B>) -> Self::Future {
use futures_util::future::FutureExt;
let state = req
.extensions_mut()
.remove::<S>()
.expect("state extension missing. This is a bug in axum, please file an issue");
let handler = self.handler.clone();
let future = Handler::call(handler, state, req);
let future = future.map(Ok as _);
super::future::IntoServiceFuture::new(future)
}
}
+142 -134
View File
@@ -49,8 +49,11 @@ use tower_service::Service;
pub mod future;
mod into_service;
mod into_service_state_in_extension;
mod with_state;
pub use self::into_service::IntoService;
pub(crate) use self::into_service_state_in_extension::IntoServiceStateInExtension;
pub use self::{into_service::IntoService, with_state::WithState};
/// Trait for async functions that can be used to handle requests.
///
@@ -59,13 +62,45 @@ pub use self::into_service::IntoService;
///
/// See the [module docs](crate::handler) for more details.
///
/// # Converting `Handler`s into [`Service`]s
///
/// To convert `Handler`s into [`Service`]s you have to call either
/// [`HandlerWithoutStateExt::into_service`] or [`Handler::with_state`]:
///
/// ```
/// use tower::Service;
/// use axum::{
/// extract::State,
/// body::Body,
/// http::Request,
/// handler::{HandlerWithoutStateExt, Handler},
/// };
///
/// // this handler doesn't require any state
/// async fn one() {}
/// // so it can be converted to a service with `HandlerWithoutStateExt::into_service`
/// assert_service(one.into_service());
///
/// // this handler requires state
/// async fn two(_: State<String>) {}
/// // so we have to provide it
/// let handler_with_state = two.with_state(String::new());
/// // which gives us a `Service`
/// assert_service(handler_with_state);
///
/// // helper to check that a value implements `Service`
/// fn assert_service<S>(service: S)
/// where
/// S: Service<Request<Body>>,
/// {}
/// ```
#[doc = include_str!("../docs/debugging_handler_type_errors.md")]
pub trait Handler<T, B = Body>: Clone + Send + Sized + 'static {
pub trait Handler<T, S = (), B = Body>: Clone + Send + Sized + 'static {
/// The type of future calling this handler returns.
type Future: Future<Output = Response> + Send + 'static;
/// Call the handler with the given request.
fn call(self, req: Request<B>) -> Self::Future;
fn call(self, state: S, req: Request<B>) -> Self::Future;
/// Apply a [`tower::Layer`] to the handler.
///
@@ -103,112 +138,26 @@ pub trait Handler<T, B = Body>: Clone + Send + Sized + 'static {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
fn layer<L>(self, layer: L) -> Layered<L::Service, T>
fn layer<L>(self, layer: L) -> Layered<L, Self, T, S, B>
where
L: Layer<IntoService<Self, T, B>>,
L: Layer<WithState<Self, T, S, B>>,
{
Layered::new(layer.layer(self.into_service()))
Layered {
layer,
handler: self,
_marker: PhantomData,
}
}
/// Convert the handler into a [`Service`].
///
/// This is commonly used together with [`Router::fallback`]:
///
/// ```rust
/// use axum::{
/// Server,
/// handler::Handler,
/// http::{Uri, Method, StatusCode},
/// response::IntoResponse,
/// routing::{get, Router},
/// };
/// use tower::make::Shared;
/// use std::net::SocketAddr;
///
/// async fn handler(method: Method, uri: Uri) -> (StatusCode, String) {
/// (StatusCode::NOT_FOUND, format!("Nothing to see at {} {}", method, uri))
/// }
///
/// let app = Router::new()
/// .route("/", get(|| async {}))
/// .fallback(handler.into_service());
///
/// # async {
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
/// .serve(app.into_make_service())
/// .await?;
/// # Ok::<_, hyper::Error>(())
/// # };
/// ```
///
/// [`Router::fallback`]: crate::routing::Router::fallback
fn into_service(self) -> IntoService<Self, T, B> {
IntoService::new(self)
}
/// Convert the handler into a [`MakeService`].
///
/// This allows you to serve a single handler if you don't need any routing:
///
/// ```rust
/// use axum::{
/// Server, handler::Handler, http::{Uri, Method}, response::IntoResponse,
/// };
/// use std::net::SocketAddr;
///
/// async fn handler(method: Method, uri: Uri, body: String) -> String {
/// format!("received `{} {}` with body `{:?}`", method, uri, body)
/// }
///
/// # async {
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
/// .serve(handler.into_make_service())
/// .await?;
/// # Ok::<_, hyper::Error>(())
/// # };
/// ```
///
/// [`MakeService`]: tower::make::MakeService
fn into_make_service(self) -> IntoMakeService<IntoService<Self, T, B>> {
IntoMakeService::new(self.into_service())
}
/// Convert the handler into a [`MakeService`] which stores information
/// about the incoming connection.
///
/// See [`Router::into_make_service_with_connect_info`] for more details.
///
/// ```rust
/// use axum::{
/// Server,
/// handler::Handler,
/// response::IntoResponse,
/// extract::ConnectInfo,
/// };
/// use std::net::SocketAddr;
///
/// async fn handler(ConnectInfo(addr): ConnectInfo<SocketAddr>) -> String {
/// format!("Hello {}", addr)
/// }
///
/// # async {
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
/// .serve(handler.into_make_service_with_connect_info::<SocketAddr>())
/// .await?;
/// # Ok::<_, hyper::Error>(())
/// # };
/// ```
///
/// [`MakeService`]: tower::make::MakeService
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
fn into_make_service_with_connect_info<C>(
self,
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, T, B>, C> {
IntoMakeServiceWithConnectInfo::new(self.into_service())
/// Convert the handler into a [`Service`] by providing the state
fn with_state(self, state: S) -> WithState<Self, T, S, B> {
WithState {
service: IntoService::new(self, state),
}
}
}
impl<F, Fut, Res, B> Handler<(), B> for F
impl<F, Fut, Res, S, B> Handler<(), S, B> for F
where
F: FnOnce() -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
@@ -217,7 +166,7 @@ where
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, _req: Request<B>) -> Self::Future {
fn call(self, _state: S, _req: Request<B>) -> Self::Future {
Box::pin(async move { self().await.into_response() })
}
}
@@ -225,19 +174,20 @@ where
macro_rules! impl_handler {
( $($ty:ident),* $(,)? ) => {
#[allow(non_snake_case)]
impl<F, Fut, B, Res, $($ty,)*> Handler<($($ty,)*), B> for F
impl<F, Fut, S, B, Res, $($ty,)*> Handler<($($ty,)*), S, B> for F
where
F: FnOnce($($ty,)*) -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
B: Send + 'static,
S: Send + 'static,
Res: IntoResponse,
$( $ty: FromRequest<B> + Send,)*
$( $ty: FromRequest<S, B> + Send,)*
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, req: Request<B>) -> Self::Future {
fn call(self, state: S, req: Request<B>) -> Self::Future {
Box::pin(async move {
let mut req = RequestParts::new(req);
let mut req = RequestParts::with_state(state, req);
$(
let $ty = match $ty::from_request(&mut req).await {
@@ -260,58 +210,116 @@ 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<S, T> {
svc: S,
_input: PhantomData<fn() -> T>,
pub struct Layered<L, H, T, S, B> {
layer: L,
handler: H,
_marker: PhantomData<fn() -> (T, S, B)>,
}
impl<S, T> fmt::Debug for Layered<S, T>
impl<L, H, T, S, B> fmt::Debug for Layered<L, H, T, S, B>
where
S: fmt::Debug,
L: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Layered").field("svc", &self.svc).finish()
f.debug_struct("Layered")
.field("layer", &self.layer)
.finish()
}
}
impl<S, T> Clone for Layered<S, T>
impl<L, H, T, S, B> Clone for Layered<L, H, T, S, B>
where
S: Clone,
L: Clone,
H: Clone,
{
fn clone(&self) -> Self {
Self::new(self.svc.clone())
Self {
layer: self.layer.clone(),
handler: self.handler.clone(),
_marker: PhantomData,
}
}
}
impl<S, T, ReqBody> Handler<T, ReqBody> for Layered<S, T>
impl<H, S, T, B, L> Handler<T, S, B> for Layered<L, H, T, S, B>
where
S: Service<Request<ReqBody>, Error = Infallible> + Clone + Send + 'static,
S::Response: IntoResponse,
S::Future: Send,
L: Layer<WithState<H, T, S, B>> + Clone + Send + 'static,
H: Handler<T, S, B>,
L::Service: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
<L::Service as Service<Request<B>>>::Response: IntoResponse,
<L::Service as Service<Request<B>>>::Future: Send,
T: 'static,
ReqBody: Send + 'static,
S: 'static,
B: Send + 'static,
{
type Future = future::LayeredFuture<S, ReqBody>;
type Future = future::LayeredFuture<B, L::Service>;
fn call(self, req: Request<ReqBody>) -> Self::Future {
fn call(self, state: S, req: Request<B>) -> Self::Future {
use futures_util::future::{FutureExt, Map};
let future: Map<_, fn(Result<S::Response, S::Error>) -> _> =
self.svc.oneshot(req).map(|result| match result {
Ok(res) => res.into_response(),
Err(err) => match err {},
});
let svc = self.handler.with_state(state);
let svc = self.layer.layer(svc);
let future: Map<
_,
fn(
Result<
<L::Service as Service<Request<B>>>::Response,
<L::Service as Service<Request<B>>>::Error,
>,
) -> _,
> = svc.oneshot(req).map(|result| match result {
Ok(res) => res.into_response(),
Err(err) => match err {},
});
future::LayeredFuture::new(future)
}
}
impl<S, T> Layered<S, T> {
pub(crate) fn new(svc: S) -> Self {
Self {
svc,
_input: PhantomData,
}
/// Extension trait for [`Handler`]s that don't have state.
///
/// This provides convenience methods to convert the [`Handler`] into a [`Service`] or [`MakeService`].
///
/// [`MakeService`]: tower::make::MakeService
pub trait HandlerWithoutStateExt<T, B>: Handler<T, (), B> {
/// Convert the handler into a [`Service`] and no state.
fn into_service(self) -> WithState<Self, T, (), B>;
/// Convert the handler into a [`MakeService`] and no state.
///
/// See [`WithState::into_make_service`] for more details.
///
/// [`MakeService`]: tower::make::MakeService
fn into_make_service(self) -> IntoMakeService<IntoService<Self, T, (), B>>;
/// Convert the handler into a [`MakeService`] which stores information
/// about the incoming connection and has no state.
///
/// See [`WithState::into_make_service_with_connect_info`] for more details.
///
/// [`MakeService`]: tower::make::MakeService
fn into_make_service_with_connect_info<C>(
self,
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, T, (), B>, C>;
}
impl<H, T, B> HandlerWithoutStateExt<T, B> for H
where
H: Handler<T, (), B>,
{
fn into_service(self) -> WithState<Self, T, (), B> {
self.with_state(())
}
fn into_make_service(self) -> IntoMakeService<IntoService<Self, T, (), B>> {
self.with_state(()).into_make_service()
}
fn into_make_service_with_connect_info<C>(
self,
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, T, (), B>, C> {
self.with_state(()).into_make_service_with_connect_info()
}
}
+144
View File
@@ -0,0 +1,144 @@
use super::{Handler, IntoService};
use crate::{extract::connect_info::IntoMakeServiceWithConnectInfo, routing::IntoMakeService};
use http::Request;
use std::task::{Context, Poll};
use tower_service::Service;
/// A [`Handler`] which has access to some state.
///
/// Implements [`Service`].
///
/// The state can be extracted with [`State`](crate::extract::State).
///
/// Created with [`Handler::with_state`].
pub struct WithState<H, T, S, B> {
pub(super) service: IntoService<H, T, S, B>,
}
impl<H, T, S, B> WithState<H, T, S, B> {
/// Get a reference to the state.
pub fn state(&self) -> &S {
self.service.state()
}
}
impl<H, T, S, B> WithState<H, T, S, B> {
/// Convert the handler into a [`MakeService`].
///
/// This allows you to serve a single handler if you don't need any routing:
///
/// ```rust
/// use axum::{
/// Server,
/// handler::Handler,
/// extract::State,
/// http::{Uri, Method},
/// response::IntoResponse,
/// };
/// use std::net::SocketAddr;
///
/// #[derive(Clone)]
/// struct AppState {}
///
/// async fn handler(State(state): State<AppState>) {
/// // ...
/// }
///
/// let app = handler.with_state(AppState {});
///
/// # async {
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
/// .serve(app.into_make_service())
/// .await?;
/// # Ok::<_, hyper::Error>(())
/// # };
/// ```
///
/// [`MakeService`]: tower::make::MakeService
pub fn into_make_service(self) -> IntoMakeService<IntoService<H, T, S, B>> {
IntoMakeService::new(self.service)
}
/// Convert the handler into a [`MakeService`] which stores information
/// about the incoming connection.
///
/// See [`Router::into_make_service_with_connect_info`] for more details.
///
/// ```rust
/// use axum::{
/// Server,
/// handler::Handler,
/// response::IntoResponse,
/// extract::{ConnectInfo, State},
/// };
/// use std::net::SocketAddr;
///
/// #[derive(Clone)]
/// struct AppState {};
///
/// async fn handler(
/// ConnectInfo(addr): ConnectInfo<SocketAddr>,
/// State(state): State<AppState>,
/// ) -> String {
/// format!("Hello {}", addr)
/// }
///
/// let app = handler.with_state(AppState {});
///
/// # async {
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
/// .serve(app.into_make_service_with_connect_info::<SocketAddr>())
/// .await?;
/// # Ok::<_, hyper::Error>(())
/// # };
/// ```
///
/// [`MakeService`]: tower::make::MakeService
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
pub fn into_make_service_with_connect_info<C>(
self,
) -> IntoMakeServiceWithConnectInfo<IntoService<H, T, S, B>, C> {
IntoMakeServiceWithConnectInfo::new(self.service)
}
}
impl<H, T, S, B> Service<Request<B>> for WithState<H, T, S, B>
where
H: Handler<T, S, B> + Clone + Send + 'static,
B: Send + 'static,
S: Clone,
{
type Response = <IntoService<H, T, S, B> as Service<Request<B>>>::Response;
type Error = <IntoService<H, T, S, B> as Service<Request<B>>>::Error;
type Future = <IntoService<H, T, S, B> as Service<Request<B>>>::Future;
#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
#[inline]
fn call(&mut self, req: Request<B>) -> Self::Future {
self.service.call(req)
}
}
impl<H, T, S, B> std::fmt::Debug for WithState<H, T, S, B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WithState")
.field("service", &self.service)
.finish()
}
}
impl<H, T, S, B> Clone for WithState<H, T, S, B>
where
H: Clone,
S: Clone,
{
fn clone(&self) -> Self {
Self {
service: self.service.clone(),
}
}
}
+4 -3
View File
@@ -94,16 +94,17 @@ use std::ops::{Deref, DerefMut};
pub struct Json<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for Json<T>
impl<T, S, B> FromRequest<S, B> for Json<T>
where
T: DeserializeOwned,
B: HttpBody + Send,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send,
{
type Rejection = JsonRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if json_content_type(req) {
let bytes = Bytes::from_request(req).await?;
@@ -136,7 +137,7 @@ where
}
}
fn json_content_type<B>(req: &RequestParts<B>) -> bool {
fn json_content_type<S, B>(req: &RequestParts<S, B>) -> bool {
let content_type = if let Some(content_type) = req.headers().get(header::CONTENT_TYPE) {
content_type
} else {
+46 -10
View File
@@ -168,13 +168,48 @@
//! pool of database connections or clients to other services.
//!
//! The two most common ways of doing that are:
//! - Using the [`State`] extractor.
//! - Using request extensions
//! - Using closure captures
//!
//! ## Using the [`State`] extractor
//!
//! ```rust,no_run
//! use axum::{
//! extract::State,
//! routing::get,
//! Router,
//! };
//! use std::sync::Arc;
//!
//! struct AppState {
//! // ...
//! }
//!
//! let shared_state = Arc::new(AppState { /* ... */ });
//!
//! let app = Router::with_state(shared_state)
//! .route("/", get(handler));
//!
//! async fn handler(
//! State(state): State<Arc<AppState>>,
//! ) {
//! // ...
//! }
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
//! ```
//!
//! You should prefer using [`State`] if possible since it's more type safe. The downside is that
//! its less dynamic than request extensions.
//!
//! See [`State`] for more details about accessing state.
//!
//! ## Using request extensions
//!
//! The easiest way to extract state in handlers is using [`Extension`](crate::extract::Extension)
//! as layer and extractor:
//! Another way to extract state in handlers is using [`Extension`](crate::extract::Extension) as
//! layer and extractor:
//!
//! ```rust,no_run
//! use axum::{
@@ -184,18 +219,18 @@
//! };
//! use std::sync::Arc;
//!
//! struct State {
//! struct AppState {
//! // ...
//! }
//!
//! let shared_state = Arc::new(State { /* ... */ });
//! let shared_state = Arc::new(AppState { /* ... */ });
//!
//! let app = Router::new()
//! .route("/", get(handler))
//! .layer(Extension(shared_state));
//!
//! async fn handler(
//! Extension(state): Extension<Arc<State>>,
//! Extension(state): Extension<Arc<AppState>>,
//! ) {
//! // ...
//! }
@@ -223,11 +258,11 @@
//! use std::sync::Arc;
//! use serde::Deserialize;
//!
//! struct State {
//! struct AppState {
//! // ...
//! }
//!
//! let shared_state = Arc::new(State { /* ... */ });
//! let shared_state = Arc::new(AppState { /* ... */ });
//!
//! let app = Router::new()
//! .route(
@@ -245,11 +280,11 @@
//! }),
//! );
//!
//! async fn get_user(Path(user_id): Path<String>, state: Arc<State>) {
//! async fn get_user(Path(user_id): Path<String>, state: Arc<AppState>) {
//! // ...
//! }
//!
//! async fn create_user(Json(payload): Json<CreateUserPayload>, state: Arc<State>) {
//! async fn create_user(Json(payload): Json<CreateUserPayload>, state: Arc<AppState>) {
//! // ...
//! }
//!
@@ -263,7 +298,7 @@
//! ```
//!
//! The downside to this approach is that it's a little more verbose than using
//! extensions.
//! [`State`] or extensions.
//!
//! # Building integrations for axum
//!
@@ -350,6 +385,7 @@
//! [`Infallible`]: std::convert::Infallible
//! [load shed]: tower::load_shed
//! [`axum-core`]: http://crates.io/crates/axum-core
//! [`State`]: crate::extract::State
#![warn(
clippy::all,
+26 -22
View File
@@ -45,13 +45,14 @@ use tower_service::Service;
/// struct RequireAuth;
///
/// #[async_trait]
/// impl<B> FromRequest<B> for RequireAuth
/// impl<S, B> FromRequest<S, B> for RequireAuth
/// where
/// B: Send,
/// S: Send,
/// {
/// type Rejection = StatusCode;
///
/// async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
/// async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
/// let auth_header = req
/// .headers()
/// .get(header::AUTHORIZATION)
@@ -166,23 +167,23 @@ where
}
}
impl<S, E, ReqBody> Service<Request<ReqBody>> for FromExtractor<S, E>
impl<S, E, B> Service<Request<B>> for FromExtractor<S, E>
where
E: FromRequest<ReqBody> + 'static,
ReqBody: Default + Send + 'static,
S: Service<Request<ReqBody>> + Clone,
E: FromRequest<(), B> + 'static,
B: Default + Send + 'static,
S: Service<Request<B>> + Clone,
S::Response: IntoResponse,
{
type Response = Response;
type Error = S::Error;
type Future = ResponseFuture<ReqBody, S, E>;
type Future = ResponseFuture<B, S, E>;
#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
fn call(&mut self, req: Request<B>) -> Self::Future {
let extract_future = Box::pin(async move {
let mut req = RequestParts::new(req);
let extracted = E::from_request(&mut req).await;
@@ -201,35 +202,37 @@ where
pin_project! {
/// Response future for [`FromExtractor`].
#[allow(missing_debug_implementations)]
pub struct ResponseFuture<ReqBody, S, E>
pub struct ResponseFuture<B, S, E>
where
E: FromRequest<ReqBody>,
S: Service<Request<ReqBody>>,
E: FromRequest<(), B>,
S: Service<Request<B>>,
{
#[pin]
state: State<ReqBody, S, E>,
state: State<B, S, E>,
svc: Option<S>,
}
}
pin_project! {
#[project = StateProj]
enum State<ReqBody, S, E>
enum State<B, S, E>
where
E: FromRequest<ReqBody>,
S: Service<Request<ReqBody>>,
E: FromRequest<(), B>,
S: Service<Request<B>>,
{
Extracting { future: BoxFuture<'static, (RequestParts<ReqBody>, Result<E, E::Rejection>)> },
Extracting {
future: BoxFuture<'static, (RequestParts<(), B>, Result<E, E::Rejection>)>,
},
Call { #[pin] future: S::Future },
}
}
impl<ReqBody, S, E> Future for ResponseFuture<ReqBody, S, E>
impl<B, S, E> Future for ResponseFuture<B, S, E>
where
E: FromRequest<ReqBody>,
S: Service<Request<ReqBody>>,
E: FromRequest<(), B>,
S: Service<Request<B>>,
S::Response: IntoResponse,
ReqBody: Default,
B: Default,
{
type Output = Result<Response, S::Error>;
@@ -277,13 +280,14 @@ mod tests {
struct RequireAuth;
#[async_trait::async_trait]
impl<B> FromRequest<B> for RequireAuth
impl<S, B> FromRequest<S, B> for RequireAuth
where
B: Send,
S: Send,
{
type Rejection = StatusCode;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if let Some(auth) = req
.headers()
.get(header::AUTHORIZATION)
+11 -11
View File
@@ -251,19 +251,19 @@ where
macro_rules! impl_service {
( $($ty:ident),* $(,)? ) => {
#[allow(non_snake_case)]
impl<F, Fut, Out, S, ReqBody, $($ty,)*> Service<Request<ReqBody>> for FromFn<F, S, ($($ty,)*)>
impl<F, Fut, Out, S, B, $($ty,)*> Service<Request<B>> for FromFn<F, S, ($($ty,)*)>
where
F: FnMut($($ty),*, Next<ReqBody>) -> Fut + Clone + Send + 'static,
$( $ty: FromRequest<ReqBody> + Send, )*
F: FnMut($($ty),*, Next<B>) -> Fut + Clone + Send + 'static,
$( $ty: FromRequest<(), B> + Send, )*
Fut: Future<Output = Out> + Send + 'static,
Out: IntoResponse + 'static,
S: Service<Request<ReqBody>, Error = Infallible>
S: Service<Request<B>, Error = Infallible>
+ Clone
+ Send
+ 'static,
S::Response: IntoResponse,
S::Future: Send + 'static,
ReqBody: Send + 'static,
B: Send + 'static,
{
type Response = Response;
type Error = Infallible;
@@ -273,7 +273,7 @@ macro_rules! impl_service {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
fn call(&mut self, req: Request<B>) -> Self::Future {
let not_ready_inner = self.inner.clone();
let ready_inner = std::mem::replace(&mut self.inner, not_ready_inner);
@@ -320,13 +320,13 @@ where
}
/// The remainder of a middleware stack, including the handler.
pub struct Next<ReqBody> {
inner: BoxCloneService<Request<ReqBody>, Response, Infallible>,
pub struct Next<B> {
inner: BoxCloneService<Request<B>, Response, Infallible>,
}
impl<ReqBody> Next<ReqBody> {
impl<B> Next<B> {
/// Execute the remaining middleware stack.
pub async fn run(mut self, req: Request<ReqBody>) -> Response {
pub async fn run(mut self, req: Request<B>) -> Response {
match self.inner.call(req).await {
Ok(res) => res,
Err(err) => match err {},
@@ -334,7 +334,7 @@ impl<ReqBody> Next<ReqBody> {
}
}
impl<ReqBody> fmt::Debug for Next<ReqBody> {
impl<B> fmt::Debug for Next<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FromFnLayer")
.field("inner", &self.inner)
+2 -2
View File
@@ -93,7 +93,7 @@ mod tests {
}
}
Router::<Body>::new()
Router::<_, Body>::new()
.route("/", get(impl_trait_ok))
.route("/", get(impl_trait_err))
.route("/", get(impl_trait_both))
@@ -203,7 +203,7 @@ mod tests {
)
}
Router::<Body>::new()
Router::<_, Body>::new()
.route("/", get(status))
.route("/", get(status_headermap))
.route("/", get(status_header_array))
+337 -114
View File
@@ -1,9 +1,11 @@
//! Route to services and handlers based on HTTP methods.
use super::IntoMakeService;
use crate::{
body::{boxed, Body, Bytes, Empty, HttpBody},
body::{Body, Bytes, HttpBody},
error_handling::{HandleError, HandleErrorLayer},
extract::connect_info::IntoMakeServiceWithConnectInfo,
handler::Handler,
handler::{Handler, IntoServiceStateInExtension},
http::{Method, Request, StatusCode},
response::Response,
routing::{future::RouteFuture, Fallback, MethodFilter, Route},
@@ -13,6 +15,7 @@ use bytes::BytesMut;
use std::{
convert::Infallible,
fmt,
marker::PhantomData,
task::{Context, Poll},
};
use tower::{service_fn, util::MapResponseLayer};
@@ -74,11 +77,12 @@ macro_rules! top_level_service_fn {
$name:ident, $method:ident
) => {
$(#[$m])+
pub fn $name<S, ReqBody>(svc: S) -> MethodRouter<ReqBody, S::Error>
pub fn $name<T, S, B>(svc: T) -> MethodRouter<S, B, T::Error>
where
S: Service<Request<ReqBody>> + Clone + Send + 'static,
S::Response: IntoResponse + 'static,
S::Future: Send + 'static,
T: Service<Request<B>> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
B: Send + 'static,
{
on_service(MethodFilter::$method, svc)
}
@@ -134,11 +138,12 @@ macro_rules! top_level_handler_fn {
$name:ident, $method:ident
) => {
$(#[$m])+
pub fn $name<H, T, B>(handler: H) -> MethodRouter<B, Infallible>
pub fn $name<H, T, S, B>(handler: H) -> MethodRouter<S, B, Infallible>
where
H: Handler<T, B>,
H: Handler<T, S, B>,
B: Send + 'static,
T: 'static,
S: Clone + Send + Sync + 'static,
{
on(MethodFilter::$method, handler)
}
@@ -206,14 +211,14 @@ macro_rules! chained_service_fn {
) => {
$(#[$m])+
#[track_caller]
pub fn $name<S>(self, svc: S) -> Self
pub fn $name<T>(self, svc: T) -> Self
where
S: Service<Request<ReqBody>, Error = E>
T: Service<Request<B>, Error = E>
+ Clone
+ Send
+ 'static,
S::Response: IntoResponse + 'static,
S::Future: Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
{
self.on_service(MethodFilter::$method, svc)
}
@@ -272,8 +277,9 @@ macro_rules! chained_handler_fn {
#[track_caller]
pub fn $name<H, T>(self, handler: H) -> Self
where
H: Handler<T, B>,
H: Handler<T, S, B>,
T: 'static,
S: Clone + Send + Sync + 'static,
{
self.on(MethodFilter::$method, handler)
}
@@ -314,11 +320,12 @@ top_level_service_fn!(trace_service, TRACE);
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn on_service<S, ReqBody>(filter: MethodFilter, svc: S) -> MethodRouter<ReqBody, S::Error>
pub fn on_service<T, S, B>(filter: MethodFilter, svc: T) -> MethodRouter<S, B, T::Error>
where
S: Service<Request<ReqBody>> + Clone + Send + 'static,
S::Response: IntoResponse + 'static,
S::Future: Send + 'static,
T: Service<Request<B>> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
B: Send + 'static,
{
MethodRouter::new().on_service(filter, svc)
}
@@ -376,13 +383,16 @@ where
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn any_service<S, ReqBody>(svc: S) -> MethodRouter<ReqBody, S::Error>
pub fn any_service<T, S, B>(svc: T) -> MethodRouter<S, B, T::Error>
where
S: Service<Request<ReqBody>> + Clone + Send + 'static,
S::Response: IntoResponse + 'static,
S::Future: Send + 'static,
T: Service<Request<B>> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
B: Send + 'static,
{
MethodRouter::new().fallback(svc).skip_allow_header()
MethodRouter::new()
.fallback_service(svc)
.skip_allow_header()
}
top_level_handler_fn!(delete, DELETE);
@@ -413,11 +423,12 @@ top_level_handler_fn!(trace, TRACE);
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn on<H, T, B>(filter: MethodFilter, handler: H) -> MethodRouter<B, Infallible>
pub fn on<H, T, S, B>(filter: MethodFilter, handler: H) -> MethodRouter<S, B, Infallible>
where
H: Handler<T, B>,
H: Handler<T, S, B>,
B: Send + 'static,
T: 'static,
S: Clone + Send + Sync + 'static,
{
MethodRouter::new().on(filter, handler)
}
@@ -459,20 +470,48 @@ where
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn any<H, T, B>(handler: H) -> MethodRouter<B, Infallible>
pub fn any<H, T, S, B>(handler: H) -> MethodRouter<S, B, Infallible>
where
H: Handler<T, B>,
H: Handler<T, S, B>,
B: Send + 'static,
T: 'static,
S: Clone + Send + Sync + 'static,
{
MethodRouter::new()
.fallback_boxed_response_body(handler.into_service())
.fallback_boxed_response_body(IntoServiceStateInExtension::new(handler))
.skip_allow_header()
}
/// A [`Service`] that accepts requests based on a [`MethodFilter`] and
/// allows chaining additional handlers and services.
pub struct MethodRouter<B = Body, E = Infallible> {
///
/// # When does `MethodRouter` implement [`Service`]?
///
/// Whether or not `MethodRouter` implements [`Service`] depends on the state type it requires.
///
/// ```
/// use tower::Service;
/// use axum::{routing::get, extract::State, body::Body, http::Request};
///
/// // this `MethodRouter` doesn't require any state, i.e. the state is `()`,
/// let method_router = get(|| async {});
/// // and thus it implements `Service`
/// assert_service(method_router);
///
/// // this requires a `String` and doesn't implement `Service`
/// let method_router = get(|_: State<String>| async {});
/// // until you provide the `String` with `.with_state(...)`
/// let method_router_with_state = method_router.with_state(String::new());
/// // and then it implements `Service`
/// assert_service(method_router_with_state);
///
/// // helper to check that a value implements `Service`
/// fn assert_service<S>(service: S)
/// where
/// S: Service<Request<Body>>,
/// {}
/// ```
pub struct MethodRouter<S = (), B = Body, E = Infallible> {
get: Option<Route<B, E>>,
head: Option<Route<B, E>>,
delete: Option<Route<B, E>>,
@@ -483,6 +522,7 @@ pub struct MethodRouter<B = Body, E = Infallible> {
trace: Option<Route<B, E>>,
fallback: Fallback<B, E>,
allow_header: AllowHeader,
_marker: PhantomData<fn() -> S>,
}
#[derive(Clone)]
@@ -511,7 +551,7 @@ impl AllowHeader {
}
}
impl<B, E> fmt::Debug for MethodRouter<B, E> {
impl<S, B, E> fmt::Debug for MethodRouter<S, B, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MethodRouter")
.field("get", &self.get)
@@ -527,32 +567,7 @@ impl<B, E> fmt::Debug for MethodRouter<B, E> {
}
}
impl<B, E> MethodRouter<B, E> {
/// 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<B>| async {
let mut response = Response::new(boxed(Empty::new()));
*response.status_mut() = StatusCode::METHOD_NOT_ALLOWED;
Ok(response)
}));
Self {
get: None,
head: None,
delete: None,
options: None,
patch: None,
post: None,
put: None,
trace: None,
allow_header: AllowHeader::None,
fallback: Fallback::Default(fallback),
}
}
}
impl<B> MethodRouter<B, Infallible>
impl<S, B> MethodRouter<S, B, Infallible>
where
B: Send + 'static,
{
@@ -582,10 +597,11 @@ where
#[track_caller]
pub fn on<H, T>(self, filter: MethodFilter, handler: H) -> Self
where
H: Handler<T, B>,
H: Handler<T, S, B>,
T: 'static,
S: Clone + Send + Sync + 'static,
{
self.on_service_boxed_response_body(filter, handler.into_service())
self.on_service_boxed_response_body(filter, IntoServiceStateInExtension::new(handler))
}
chained_handler_fn!(delete, DELETE);
@@ -597,6 +613,21 @@ where
chained_handler_fn!(put, PUT);
chained_handler_fn!(trace, TRACE);
/// Add a fallback [`Handler`] to the router.
pub fn fallback<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
T: 'static,
S: Clone + Send + Sync + 'static,
{
self.fallback_service(IntoServiceStateInExtension::new(handler))
}
}
impl<B> MethodRouter<(), B, Infallible>
where
B: Send + 'static,
{
/// Convert the handler into a [`MakeService`].
///
/// This allows you to serve a single handler if you don't need any routing:
@@ -666,7 +697,58 @@ where
}
}
impl<ReqBody, E> MethodRouter<ReqBody, E> {
impl<S, B, E> MethodRouter<S, B, E>
where
B: Send + 'static,
{
/// 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<B>| async {
Ok(StatusCode::METHOD_NOT_ALLOWED.into_response())
}));
Self {
get: None,
head: None,
delete: None,
options: None,
patch: None,
post: None,
put: None,
trace: None,
allow_header: AllowHeader::None,
fallback: Fallback::Default(fallback),
_marker: PhantomData,
}
}
/// Provide the state.
///
/// See [`State`](crate::extract::State) for more details about accessing state.
pub fn with_state(self, state: S) -> WithState<S, B, E> {
WithState {
method_router: self,
state,
}
}
pub(crate) fn downcast_state<S2>(self) -> MethodRouter<S2, B, E> {
MethodRouter {
get: self.get,
head: self.head,
delete: self.delete,
options: self.options,
patch: self.patch,
post: self.post,
put: self.put,
trace: self.trace,
fallback: self.fallback,
allow_header: self.allow_header,
_marker: PhantomData,
}
}
/// Chain an additional service that will accept requests matching the given
/// `MethodFilter`.
///
@@ -693,11 +775,11 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
/// # };
/// ```
#[track_caller]
pub fn on_service<S>(self, filter: MethodFilter, svc: S) -> Self
pub fn on_service<T>(self, filter: MethodFilter, svc: T) -> Self
where
S: Service<Request<ReqBody>, Error = E> + Clone + Send + 'static,
S::Response: IntoResponse + 'static,
S::Future: Send + 'static,
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
{
self.on_service_boxed_response_body(filter, svc)
}
@@ -712,30 +794,30 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
chained_service_fn!(trace_service, TRACE);
#[doc = include_str!("../docs/method_routing/fallback.md")]
pub fn fallback<S>(mut self, svc: S) -> Self
pub fn fallback_service<T>(mut self, svc: T) -> Self
where
S: Service<Request<ReqBody>, Error = E> + Clone + Send + 'static,
S::Response: IntoResponse + 'static,
S::Future: Send + 'static,
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
{
self.fallback = Fallback::Custom(Route::new(svc));
self
}
fn fallback_boxed_response_body<S>(mut self, svc: S) -> Self
fn fallback_boxed_response_body<T>(mut self, svc: T) -> Self
where
S: Service<Request<ReqBody>, Error = E> + Clone + Send + 'static,
S::Response: IntoResponse + 'static,
S::Future: Send + 'static,
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
{
self.fallback = Fallback::Custom(Route::new(svc));
self
}
#[doc = include_str!("../docs/method_routing/layer.md")]
pub fn layer<L, NewReqBody, NewError>(self, layer: L) -> MethodRouter<NewReqBody, NewError>
pub fn layer<L, NewReqBody, NewError>(self, layer: L) -> MethodRouter<S, NewReqBody, NewError>
where
L: Layer<Route<ReqBody, E>>,
L: Layer<Route<B, E>>,
L::Service: Service<Request<NewReqBody>, Error = NewError> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
@@ -757,16 +839,17 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
trace: self.trace.map(layer_fn),
fallback: self.fallback.map(layer_fn),
allow_header: self.allow_header,
_marker: self._marker,
}
}
#[doc = include_str!("../docs/method_routing/route_layer.md")]
pub fn route_layer<L>(mut self, layer: L) -> MethodRouter<ReqBody, E>
pub fn route_layer<L>(mut self, layer: L) -> MethodRouter<S, B, E>
where
L: Layer<Route<ReqBody, E>>,
L::Service: Service<Request<ReqBody>, Error = E> + Clone + Send + 'static,
<L::Service as Service<Request<ReqBody>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<ReqBody>>>::Future: Send + 'static,
L: Layer<Route<B, E>>,
L::Service: Service<Request<B>, Error = E> + Clone + Send + 'static,
<L::Service as Service<Request<B>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<B>>>::Future: Send + 'static,
{
let layer_fn = |svc| {
let svc = layer.layer(svc);
@@ -788,7 +871,7 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
#[doc = include_str!("../docs/method_routing/merge.md")]
#[track_caller]
pub fn merge(mut self, other: MethodRouter<ReqBody, E>) -> Self {
pub fn merge(mut self, other: MethodRouter<S, B, E>) -> Self {
// written using inner functions to generate less IR
#[track_caller]
fn merge_inner<T>(name: &str, first: Option<T>, second: Option<T>) -> Option<T> {
@@ -836,26 +919,25 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
/// Apply a [`HandleErrorLayer`].
///
/// This is a convenience method for doing `self.layer(HandleErrorLayer::new(f))`.
pub fn handle_error<F, T>(self, f: F) -> MethodRouter<ReqBody, Infallible>
pub fn handle_error<F, T>(self, f: F) -> MethodRouter<S, B, Infallible>
where
F: Clone + Send + 'static,
HandleError<Route<ReqBody, E>, F, T>: Service<Request<ReqBody>, Error = Infallible>,
<HandleError<Route<ReqBody, E>, F, T> as Service<Request<ReqBody>>>::Future: Send,
<HandleError<Route<ReqBody, E>, F, T> as Service<Request<ReqBody>>>::Response:
IntoResponse + Send,
HandleError<Route<B, E>, F, T>: Service<Request<B>, Error = Infallible>,
<HandleError<Route<B, E>, F, T> as Service<Request<B>>>::Future: Send,
<HandleError<Route<B, E>, F, T> as Service<Request<B>>>::Response: IntoResponse + Send,
T: 'static,
E: 'static,
ReqBody: 'static,
B: 'static,
{
self.layer(HandleErrorLayer::new(f))
}
#[track_caller]
fn on_service_boxed_response_body<S>(mut self, filter: MethodFilter, svc: S) -> Self
fn on_service_boxed_response_body<T>(mut self, filter: MethodFilter, svc: T) -> Self
where
S: Service<Request<ReqBody>, Error = E> + Clone + Send + 'static,
S::Response: IntoResponse + 'static,
S::Future: Send + 'static,
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
{
// written using an inner function to generate less IR
fn set_service<T>(
@@ -991,7 +1073,25 @@ fn append_allow_header(allow_header: &mut AllowHeader, method: &'static str) {
}
}
impl<B, E> Clone for MethodRouter<B, E> {
impl<B, E> Service<Request<B>> for MethodRouter<(), B, E>
where
B: HttpBody + Send + 'static,
{
type Response = Response;
type Error = E;
type Future = RouteFuture<B, E>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<B>) -> Self::Future {
self.clone().with_state(()).call(req)
}
}
impl<S, B, E> Clone for MethodRouter<S, B, E> {
fn clone(&self) -> Self {
Self {
get: self.get.clone(),
@@ -1004,11 +1104,12 @@ impl<B, E> Clone for MethodRouter<B, E> {
trace: self.trace.clone(),
fallback: self.fallback.clone(),
allow_header: self.allow_header.clone(),
_marker: self._marker,
}
}
}
impl<B, E> Default for MethodRouter<B, E>
impl<S, B, E> Default for MethodRouter<S, B, E>
where
B: Send + 'static,
{
@@ -1017,9 +1118,72 @@ where
}
}
impl<B, E> Service<Request<B>> for MethodRouter<B, E>
/// A [`MethodRouter`] which has access to some state.
///
/// Implements [`Service`].
///
/// The state can be extracted with [`State`](crate::extract::State).
///
/// Created with [`MethodRouter::with_state`]
pub struct WithState<S, B, E> {
method_router: MethodRouter<S, B, E>,
state: S,
}
impl<S, B, E> WithState<S, B, E> {
/// Get a reference to the state.
pub fn state(&self) -> &S {
&self.state
}
/// Convert the handler into a [`MakeService`].
///
/// See [`MethodRouter::into_make_service`] for more details.
///
/// [`MakeService`]: tower::make::MakeService
pub fn into_make_service(self) -> IntoMakeService<Self> {
IntoMakeService::new(self)
}
/// Convert the router into a [`MakeService`] which stores information
/// about the incoming connection.
///
/// See [`MethodRouter::into_make_service_with_connect_info`] for more details.
///
/// [`MakeService`]: tower::make::MakeService
pub fn into_make_service_with_connect_info<C>(self) -> IntoMakeServiceWithConnectInfo<Self, C> {
IntoMakeServiceWithConnectInfo::new(self)
}
}
impl<S, B, E> Clone for WithState<S, B, E>
where
S: Clone,
{
fn clone(&self) -> Self {
Self {
method_router: self.method_router.clone(),
state: self.state.clone(),
}
}
}
impl<S, B, E> fmt::Debug for WithState<S, B, E>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WithState")
.field("method_router", &self.method_router)
.field("state", &self.state)
.finish()
}
}
impl<S, B, E> Service<Request<B>> for WithState<S, B, E>
where
B: HttpBody,
S: Clone + Send + Sync + 'static,
{
type Response = Response;
type Error = E;
@@ -1030,7 +1194,7 @@ where
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<B>) -> Self::Future {
fn call(&mut self, mut req: Request<B>) -> Self::Future {
macro_rules! call {
(
$req:expr,
@@ -1051,18 +1215,25 @@ where
// written with a pattern match like this to ensure we call all routes
let Self {
get,
head,
delete,
options,
patch,
post,
put,
trace,
fallback,
allow_header,
state,
method_router:
MethodRouter {
get,
head,
delete,
options,
patch,
post,
put,
trace,
fallback,
allow_header,
_marker: _,
},
} = self;
req.extensions_mut().insert(state.clone());
call!(req, method, HEAD, head);
call!(req, method, HEAD, get);
call!(req, method, GET, get);
@@ -1091,7 +1262,7 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::{body::Body, error_handling::HandleErrorLayer};
use crate::{body::Body, error_handling::HandleErrorLayer, extract::State};
use axum_core::response::IntoResponse;
use http::{header::ALLOW, HeaderMap};
use std::time::Duration;
@@ -1106,6 +1277,19 @@ mod tests {
assert!(body.is_empty());
}
#[tokio::test]
async fn get_service_fn() {
async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("ok")))
}
let mut svc = get_service(service_fn(handle));
let (status, _, body) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body, "ok");
}
#[tokio::test]
async fn get_handler() {
let mut svc = MethodRouter::new().get(ok);
@@ -1183,7 +1367,7 @@ mod tests {
delete_service(ServeDir::new("."))
.handle_error(|_| async { StatusCode::NOT_FOUND }),
)
.fallback((|| async { StatusCode::NOT_FOUND }).into_service())
.fallback(|| async { StatusCode::NOT_FOUND })
.put(ok)
.layer(
ServiceBuilder::new()
@@ -1243,9 +1427,9 @@ mod tests {
#[tokio::test]
async fn allow_header_with_fallback() {
let mut svc = MethodRouter::new().get(ok).fallback(
(|| async { (StatusCode::METHOD_NOT_ALLOWED, "Method not allowed") }).into_service(),
);
let mut svc = MethodRouter::new()
.get(ok)
.fallback(|| async { (StatusCode::METHOD_NOT_ALLOWED, "Method not allowed") });
let (status, headers, _) = call(Method::DELETE, &mut svc).await;
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
@@ -1267,9 +1451,7 @@ mod tests {
}
}
let mut svc = MethodRouter::new()
.get(ok)
.fallback(fallback.into_service());
let mut svc = MethodRouter::new().get(ok).fallback(fallback);
let (status, _, _) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::OK);
@@ -1287,7 +1469,7 @@ mod tests {
expected = "Overlapping method route. Cannot add two method routes that both handle `GET`"
)]
async fn handler_overlaps() {
let _: MethodRouter = get(ok).get(ok);
let _: MethodRouter<()> = get(ok).get(ok);
}
#[tokio::test]
@@ -1295,17 +1477,58 @@ mod tests {
expected = "Overlapping method route. Cannot add two method routes that both handle `POST`"
)]
async fn service_overlaps() {
let _: MethodRouter = post_service(ok.into_service()).post_service(ok.into_service());
let _: MethodRouter<()> = post_service(IntoServiceStateInExtension::<_, _, (), _>::new(ok))
.post_service(IntoServiceStateInExtension::<_, _, (), _>::new(ok));
}
#[tokio::test]
async fn get_head_does_not_overlap() {
let _: MethodRouter = get(ok).head(ok);
let _: MethodRouter<()> = get(ok).head(ok);
}
#[tokio::test]
async fn head_get_does_not_overlap() {
let _: MethodRouter = head(ok).get(ok);
let _: MethodRouter<()> = head(ok).get(ok);
}
#[tokio::test]
async fn accessing_state() {
let mut svc = MethodRouter::new()
.get(|State(state): State<&'static str>| async move { state })
.with_state("state");
let (status, _, text) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(text, "state");
}
#[tokio::test]
async fn fallback_accessing_state() {
let mut svc = MethodRouter::new()
.fallback(|State(state): State<&'static str>| async move { state })
.with_state("state");
let (status, _, text) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(text, "state");
}
#[tokio::test]
async fn merge_accessing_state() {
let one = get(|State(state): State<&'static str>| async move { state });
let two = post(|State(state): State<&'static str>| async move { state });
let mut svc = one.merge(two).with_state("state");
let (status, _, text) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(text, "state");
let (status, _, _) = call(Method::POST, &mut svc).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(text, "state");
}
async fn call<S>(method: Method, svc: &mut S) -> (StatusCode, HeaderMap, String)
+130 -56
View File
@@ -4,8 +4,10 @@ use self::{future::RouteFuture, not_found::NotFound};
use crate::{
body::{Body, HttpBody},
extract::connect_info::IntoMakeServiceWithConnectInfo,
handler::Handler,
response::Response,
util::try_downcast,
Extension,
};
use axum_core::response::IntoResponse;
use http::Request;
@@ -22,10 +24,10 @@ use tower_layer::Layer;
use tower_service::Service;
pub mod future;
pub mod method_routing;
mod into_make_service;
mod method_filter;
mod method_routing;
mod not_found;
mod route;
mod strip_prefix;
@@ -59,15 +61,20 @@ impl RouteId {
}
/// The router type for composing handlers and services.
pub struct Router<B = Body> {
routes: HashMap<RouteId, Endpoint<B>>,
pub struct Router<S = (), B = Body> {
state: S,
routes: HashMap<RouteId, Endpoint<S, B>>,
node: Arc<Node>,
fallback: Fallback<B>,
}
impl<B> Clone for Router<B> {
impl<S, B> Clone for Router<S, B>
where
S: Clone,
{
fn clone(&self) -> Self {
Self {
state: self.state.clone(),
routes: self.routes.clone(),
node: Arc::clone(&self.node),
fallback: self.fallback.clone(),
@@ -75,18 +82,23 @@ impl<B> Clone for Router<B> {
}
}
impl<B> Default for Router<B>
impl<S, B> Default for Router<S, B>
where
B: HttpBody + Send + 'static,
S: Default + Clone + Send + Sync + 'static,
{
fn default() -> Self {
Self::new()
Self::with_state(S::default())
}
}
impl<B> fmt::Debug for Router<B> {
impl<S, B> fmt::Debug for Router<S, B>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Router")
.field("state", &self.state)
.field("routes", &self.routes)
.field("node", &self.node)
.field("fallback", &self.fallback)
@@ -97,7 +109,7 @@ impl<B> fmt::Debug for Router<B> {
pub(crate) const NEST_TAIL_PARAM: &str = "__private__axum_nest_tail_param";
const NEST_TAIL_PARAM_CAPTURE: &str = "/*__private__axum_nest_tail_param";
impl<B> Router<B>
impl<B> Router<(), B>
where
B: HttpBody + Send + 'static,
{
@@ -106,7 +118,24 @@ where
/// Unless you add additional routes this will respond with `404 Not Found` to
/// all requests.
pub fn new() -> Self {
Self::with_state(())
}
}
impl<S, B> Router<S, B>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
/// Create a new `Router` with the given state.
///
/// See [`State`](crate::extract::State) for more details about accessing state.
///
/// Unless you add additional routes this will respond with `404 Not Found` to
/// all requests.
pub fn with_state(state: S) -> Self {
Self {
state,
routes: Default::default(),
node: Default::default(),
fallback: Fallback::Default(Route::new(NotFound)),
@@ -115,12 +144,8 @@ where
#[doc = include_str!("../docs/routing/route.md")]
#[track_caller]
pub fn route<T>(mut self, path: &str, service: T) -> Self
where
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
T::Response: IntoResponse,
T::Future: Send + 'static,
{
pub fn route(mut self, path: &str, method_router: MethodRouter<S, B>) -> Self {
#[track_caller]
fn validate_path(path: &str) {
if path.is_empty() {
panic!("Paths must start with a `/`. Use \"/\" for root routes");
@@ -131,39 +156,53 @@ where
validate_path(path);
let service = match try_downcast::<Router<B>, _>(service) {
let id = RouteId::next();
let endpoint = if let Some((route_id, Endpoint::MethodRouter(prev_method_router))) = self
.node
.path_to_route_id
.get(path)
.and_then(|route_id| self.routes.get(route_id).map(|svc| (*route_id, svc)))
{
// if we're adding a new `MethodRouter` to a route that already has one just
// merge them. This makes `.route("/", get(_)).route("/", post(_))` work
let service = Endpoint::MethodRouter(prev_method_router.clone().merge(method_router));
self.routes.insert(route_id, service);
return self;
} else {
Endpoint::MethodRouter(method_router)
};
self.set_node(path, id);
self.routes.insert(id, endpoint);
self
}
#[doc = include_str!("../docs/routing/route_service.md")]
pub fn route_service<T>(mut self, path: &str, service: T) -> Self
where
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
T::Response: IntoResponse,
T::Future: Send + 'static,
{
if path.is_empty() {
panic!("Paths must start with a `/`. Use \"/\" for root routes");
} else if !path.starts_with('/') {
panic!("Paths must start with a `/`");
}
let service = match try_downcast::<Router<S, B>, _>(service) {
Ok(_) => {
panic!("Invalid route: `Router::route` cannot be used with `Router`s. Use `Router::nest` instead")
panic!("Invalid route: `Router::route_service` cannot be used with `Router`s. Use `Router::nest` instead")
}
Err(svc) => svc,
};
let id = RouteId::next();
let service = match try_downcast::<MethodRouter<B, Infallible>, _>(service) {
Ok(method_router) => {
if let Some((route_id, Endpoint::MethodRouter(prev_method_router))) = self
.node
.path_to_route_id
.get(path)
.and_then(|route_id| self.routes.get(route_id).map(|svc| (*route_id, svc)))
{
// if we're adding a new `MethodRouter` to a route that already has one just
// merge them. This makes `.route("/", get(_)).route("/", post(_))` work
let service =
Endpoint::MethodRouter(prev_method_router.clone().merge(method_router));
self.routes.insert(route_id, service);
return self;
} else {
Endpoint::MethodRouter(method_router)
}
}
Err(service) => Endpoint::Route(Route::new(service)),
};
let endpoint = Endpoint::Route(Route::new(service));
self.set_node(path, id);
self.routes.insert(id, service);
self.routes.insert(id, endpoint);
self
}
@@ -204,15 +243,15 @@ where
};
let svc = strip_prefix::StripPrefix::new(svc, prefix);
self = self.route(&path, svc.clone());
self = self.route_service(&path, svc.clone());
// `/*rest` is not matched by `/` so we need to also register a router at the
// prefix itself. Otherwise if you were to nest at `/foo` then `/foo` itself
// wouldn't match, which it should
self = self.route(prefix, svc.clone());
self = self.route_service(prefix, svc.clone());
if !prefix.ends_with('/') {
// same goes for `/foo/`, that should also match
self = self.route(&format!("{prefix}/"), svc);
self = self.route_service(&format!("{prefix}/"), svc);
}
self
@@ -220,11 +259,13 @@ where
#[doc = include_str!("../docs/routing/merge.md")]
#[track_caller]
pub fn merge<R>(mut self, other: R) -> Self
pub fn merge<S2, R>(mut self, other: R) -> Self
where
R: Into<Router<B>>,
R: Into<Router<S2, B>>,
S2: Clone + Send + Sync + 'static,
{
let Router {
state,
routes,
node,
fallback,
@@ -236,8 +277,15 @@ where
.get(&id)
.expect("no path for route id. This is a bug in axum. Please file an issue");
self = match route {
Endpoint::MethodRouter(route) => self.route(path, route),
Endpoint::Route(route) => self.route(path, route),
Endpoint::MethodRouter(method_router) => self.route(
path,
method_router
// this will set the state for each route
// such we don't override the inner state later in `MethodRouterWithState`
.layer(Extension(state.clone()))
.downcast_state(),
),
Endpoint::Route(route) => self.route_service(path, route),
};
}
@@ -254,7 +302,7 @@ where
}
#[doc = include_str!("../docs/routing/layer.md")]
pub fn layer<L, NewReqBody>(self, layer: L) -> Router<NewReqBody>
pub fn layer<L, NewReqBody>(self, layer: L) -> Router<S, NewReqBody>
where
L: Layer<Route<B>>,
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static,
@@ -285,6 +333,7 @@ where
let fallback = self.fallback.map(|svc| Route::new(layer.layer(svc)));
Router {
state: self.state,
routes,
node: self.node,
fallback,
@@ -321,6 +370,7 @@ where
.collect();
Router {
state: self.state,
routes,
node: self.node,
fallback: self.fallback,
@@ -328,7 +378,19 @@ where
}
#[doc = include_str!("../docs/routing/fallback.md")]
pub fn fallback<T>(mut self, svc: T) -> Self
pub fn fallback<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
T: 'static,
{
let state = self.state.clone();
self.fallback_service(handler.with_state(state))
}
/// Add a fallback [`Service`] to the router.
///
/// See [`Router::fallback`] for more details.
pub fn fallback_service<T>(mut self, svc: T) -> Self
where
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
T::Response: IntoResponse,
@@ -422,15 +484,21 @@ where
.clone();
match &mut route {
Endpoint::MethodRouter(inner) => inner.call(req),
Endpoint::MethodRouter(inner) => inner.clone().with_state(self.state.clone()).call(req),
Endpoint::Route(inner) => inner.call(req),
}
}
/// Get a reference to the state.
pub fn state(&self) -> &S {
&self.state
}
}
impl<B> Service<Request<B>> for Router<B>
impl<S, B> Service<Request<B>> for Router<S, B>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
type Response = Response;
type Error = Infallible;
@@ -545,12 +613,15 @@ impl<B, E> Fallback<B, E> {
}
}
enum Endpoint<B> {
MethodRouter(MethodRouter<B>),
enum Endpoint<S, B> {
MethodRouter(MethodRouter<S, B, Infallible>),
Route(Route<B>),
}
impl<B> Clone for Endpoint<B> {
impl<S, B> Clone for Endpoint<S, B>
where
S: Clone,
{
fn clone(&self) -> Self {
match self {
Endpoint::MethodRouter(inner) => Endpoint::MethodRouter(inner.clone()),
@@ -559,7 +630,10 @@ impl<B> Clone for Endpoint<B> {
}
}
impl<B> fmt::Debug for Endpoint<B> {
impl<S, B> fmt::Debug for Endpoint<S, B>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MethodRouter(inner) => inner.fmt(f),
@@ -572,5 +646,5 @@ impl<B> fmt::Debug for Endpoint<B> {
#[allow(warnings)]
fn traits() {
use crate::test_helpers::*;
assert_send::<Router<()>>();
assert_send::<Router<(), ()>>();
}
+2 -2
View File
@@ -48,13 +48,13 @@ impl<B, E> Route<B, E> {
}
}
impl<ReqBody, E> Clone for Route<ReqBody, E> {
impl<B, E> Clone for Route<B, E> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<ReqBody, E> fmt::Debug for Route<ReqBody, E> {
impl<B, E> fmt::Debug for Route<B, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Route").finish()
}
+15 -6
View File
@@ -1,11 +1,10 @@
use super::*;
use crate::handler::Handler;
#[tokio::test]
async fn basic() {
let app = Router::new()
.route("/foo", get(|| async {}))
.fallback((|| async { "fallback" }).into_service());
.fallback(|| async { "fallback" });
let client = TestClient::new(app);
@@ -20,7 +19,7 @@ async fn basic() {
async fn nest() {
let app = Router::new()
.nest("/foo", Router::new().route("/bar", get(|| async {})))
.fallback((|| async { "fallback" }).into_service());
.fallback(|| async { "fallback" });
let client = TestClient::new(app);
@@ -36,9 +35,7 @@ async fn or() {
let one = Router::new().route("/one", get(|| async {}));
let two = Router::new().route("/two", get(|| async {}));
let app = one
.merge(two)
.fallback((|| async { "fallback" }).into_service());
let app = one.merge(two).fallback(|| async { "fallback" });
let client = TestClient::new(app);
@@ -49,3 +46,15 @@ async fn or() {
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "fallback");
}
#[tokio::test]
async fn fallback_accessing_state() {
let app = Router::with_state("state")
.fallback(|State(state): State<&'static str>| async move { state });
let client = TestClient::new(app);
let res = client.get("/does-not-exist").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "state");
}
+69
View File
@@ -408,3 +408,72 @@ async fn middleware_that_return_early() {
);
assert_eq!(client.get("/public").send().await.status(), StatusCode::OK);
}
#[tokio::test]
async fn merge_with_different_state_type() {
let inner = Router::with_state("inner".to_owned()).route(
"/foo",
get(|State(state): State<String>| async move { state }),
);
let app = Router::with_state("outer").merge(inner).route(
"/bar",
get(|State(state): State<&'static str>| async move { state }),
);
let client = TestClient::new(app);
let res = client.get("/foo").send().await;
assert_eq!(res.text().await, "inner");
let res = client.get("/bar").send().await;
assert_eq!(res.text().await, "outer");
}
#[tokio::test]
async fn merging_routes_different_method_different_states() {
let get = Router::with_state("get state").route(
"/",
get(|State(state): State<&'static str>| async move { state }),
);
let post = Router::with_state("post state").route(
"/",
post(|State(state): State<&'static str>| async move { state }),
);
let app = Router::new().merge(get).merge(post);
let client = TestClient::new(app);
let res = client.get("/").send().await;
assert_eq!(res.text().await, "get state");
let res = client.post("/").send().await;
assert_eq!(res.text().await, "post state");
}
#[tokio::test]
async fn merging_routes_different_paths_different_states() {
let foo = Router::with_state("foo state").route(
"/foo",
get(|State(state): State<&'static str>| async move { state }),
);
let bar = Router::with_state("bar state").route(
"/bar",
get(|State(state): State<&'static str>| async move { state }),
);
let app = Router::new().merge(foo).merge(bar);
let client = TestClient::new(app);
let res = client.get("/foo").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "foo state");
let res = client.get("/bar").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "bar state");
}
+55 -7
View File
@@ -1,8 +1,8 @@
use crate::{
body::{Bytes, Empty},
error_handling::HandleErrorLayer,
extract::{self, Path},
handler::Handler,
extract::{self, FromRef, Path, State},
handler::{Handler, HandlerWithoutStateExt},
response::IntoResponse,
routing::{delete, get, get_service, on, on_service, patch, patch_service, post, MethodFilter},
test_helpers::*,
@@ -444,10 +444,10 @@ async fn middleware_still_run_for_unmatched_requests() {
#[tokio::test]
#[should_panic(
expected = "Invalid route: `Router::route` cannot be used with `Router`s. Use `Router::nest` instead"
expected = "Invalid route: `Router::route_service` cannot be used with `Router`s. Use `Router::nest` instead"
)]
async fn routing_to_router_panics() {
TestClient::new(Router::new().route("/", Router::new()));
TestClient::new(Router::new().route_service("/", Router::new()));
}
#[tokio::test]
@@ -499,8 +499,8 @@ async fn different_methods_added_in_different_routes() {
#[should_panic(expected = "Cannot merge two `Router`s that both have a fallback")]
async fn merging_routers_with_fallbacks_panics() {
async fn fallback() {}
let one = Router::new().fallback(fallback.into_service());
let two = Router::new().fallback(fallback.into_service());
let one = Router::new().fallback(fallback);
let two = Router::new().fallback(fallback);
TestClient::new(one.merge(two));
}
@@ -539,7 +539,7 @@ async fn head_content_length_through_hyper_server() {
#[tokio::test]
async fn head_content_length_through_hyper_server_that_hits_fallback() {
let app = Router::new().fallback((|| async { "foo" }).into_service());
let app = Router::new().fallback(|| async { "foo" });
let client = TestClient::new(app);
@@ -641,6 +641,54 @@ async fn limited_body_with_streaming_body() {
assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
}
#[tokio::test]
async fn extract_state() {
#[derive(Clone)]
struct AppState {
value: i32,
inner: InnerState,
}
#[derive(Clone)]
struct InnerState {
value: i32,
}
impl FromRef<AppState> for InnerState {
fn from_ref(state: &AppState) -> Self {
state.inner.clone()
}
}
async fn handler(State(outer): State<AppState>, State(inner): State<InnerState>) {
assert_eq!(outer.value, 1);
assert_eq!(inner.value, 2);
}
let state = AppState {
value: 1,
inner: InnerState { value: 2 },
};
let app = Router::with_state(state).route("/", get(handler));
let client = TestClient::new(app);
let res = client.get("/").send().await;
assert_eq!(res.status(), StatusCode::OK);
}
#[tokio::test]
async fn explicitly_set_state() {
let app = Router::with_state("...").route_service(
"/",
get(|State(state): State<&'static str>| async move { state }).with_state("foo"),
);
let client = TestClient::new(app);
let res = client.get("/").send().await;
assert_eq!(res.text().await, "foo");
}
#[tokio::test]
async fn layer_response_into_response() {
fn map_response<B>(_res: Response<B>) -> Result<Response<B>, impl IntoResponse> {
+37 -7
View File
@@ -182,7 +182,7 @@ async fn nested_service_sees_stripped_uri() {
"/foo",
Router::new().nest(
"/bar",
Router::new().route(
Router::new().route_service(
"/baz",
service_fn(|req: Request<Body>| async move {
let body = boxed(Body::from(req.uri().to_string()));
@@ -264,7 +264,7 @@ async fn multiple_top_level_nests() {
#[tokio::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::<_, Body>::new().nest("/one/*rest", Router::new());
}
#[tokio::test]
@@ -275,7 +275,7 @@ async fn outer_middleware_still_see_whole_url() {
#[derive(Clone)]
struct Uri(http::Uri);
impl<B, S> Service<Request<B>> for SetUriExtension<S>
impl<S, B> Service<Request<B>> for SetUriExtension<S>
where
S: Service<Request<B>>,
{
@@ -303,7 +303,7 @@ async fn outer_middleware_still_see_whole_url() {
.route("/foo", get(handler))
.route("/foo/bar", get(handler))
.nest("/one", Router::new().route("/two", get(handler)))
.fallback(handler.into_service())
.fallback(handler)
.layer(tower::layer::layer_fn(SetUriExtension));
let client = TestClient::new(app);
@@ -356,7 +356,7 @@ async fn nest_with_and_without_trailing() {
async fn doesnt_call_outer_fallback() {
let app = Router::new()
.nest("/foo", Router::new().route("/", get(|| async {})))
.fallback((|| async { (StatusCode::NOT_FOUND, "outer fallback") }).into_service());
.fallback(|| async { (StatusCode::NOT_FOUND, "outer fallback") });
let client = TestClient::new(app);
@@ -396,9 +396,9 @@ async fn fallback_on_inner() {
"/foo",
Router::new()
.route("/", get(|| async {}))
.fallback((|| async { (StatusCode::NOT_FOUND, "inner fallback") }).into_service()),
.fallback(|| async { (StatusCode::NOT_FOUND, "inner fallback") }),
)
.fallback((|| async { (StatusCode::NOT_FOUND, "outer fallback") }).into_service());
.fallback(|| async { (StatusCode::NOT_FOUND, "outer fallback") });
let client = TestClient::new(app);
@@ -442,3 +442,33 @@ nested_route_test!(nest_9, nest = "/a", route = "/a/", expected = "/a/a/");
nested_route_test!(nest_11, nest = "/a/", route = "/", expected = "/a/");
nested_route_test!(nest_12, nest = "/a/", route = "/a", expected = "/a/a");
nested_route_test!(nest_13, nest = "/a/", route = "/a/", expected = "/a/a/");
#[tokio::test]
async fn nesting_with_different_state() {
let inner = Router::with_state("inner".to_owned()).route(
"/foo",
get(|State(state): State<String>| async move { state }),
);
let outer = Router::with_state("outer")
.route(
"/foo",
get(|State(state): State<&'static str>| async move { state }),
)
.nest("/nested", inner)
.route(
"/bar",
get(|State(state): State<&'static str>| async move { state }),
);
let client = TestClient::new(outer);
let res = client.get("/foo").send().await;
assert_eq!(res.text().await, "outer");
let res = client.get("/nested/foo").send().await;
assert_eq!(res.text().await, "inner");
let res = client.get("/bar").send().await;
assert_eq!(res.text().await, "outer");
}
+3 -2
View File
@@ -52,14 +52,15 @@ use std::{convert::Infallible, ops::Deref};
pub struct TypedHeader<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for TypedHeader<T>
impl<T, S, B> FromRequest<S, B> for TypedHeader<T>
where
T: headers::Header,
B: Send,
S: Send,
{
type Rejection = TypedHeaderRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
match req.headers().typed_try_get::<T>() {
Ok(Some(value)) => Ok(Self(value)),
Ok(None) => Err(TypedHeaderRejection {