check point

This commit is contained in:
David Pedersen
2022-07-03 03:17:52 +02:00
parent a09fe48693
commit 4ecdcf691e
9 changed files with 508 additions and 263 deletions
+1 -1
View File
@@ -149,7 +149,7 @@ mod tests {
"/public", "/public",
Router::new().route("/assets/*path", get(handler)), Router::new().route("/assets/*path", get(handler)),
) )
.nest_service("/foo", handler.into_service()) .nest_service("/foo", handler.into_service(()))
.layer(tower::layer::layer_fn(SetMatchedPathExtension)) .layer(tower::layer::layer_fn(SetMatchedPathExtension))
.state(()); .state(());
@@ -0,0 +1,67 @@
use super::Handler;
use crate::response::Response;
use http::Request;
use std::{
convert::Infallible,
marker::PhantomData,
task::{Context, Poll},
};
use tower_service::Service;
/// A `Handler` converted into a `Service` that reads the state from request extensions. Panics if
/// the state is missing.
pub(crate) struct IntoExtensionService<H, S, T, B> {
handler: H,
_marker: PhantomData<fn() -> (S, T, B)>,
}
impl<H, S, T, B> IntoExtensionService<H, S, T, B> {
pub(crate) fn new(handler: H) -> Self {
Self {
handler,
_marker: PhantomData,
}
}
}
impl<H, S, T, B> Clone for IntoExtensionService<H, S, T, B>
where
H: Clone,
{
fn clone(&self) -> Self {
Self {
handler: self.handler.clone(),
_marker: PhantomData,
}
}
}
impl<H, S, T, B> Service<Request<B>> for IntoExtensionService<H, S, T, B>
where
H: Handler<S, T, 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>> {
// `IntoService` 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, req: Request<B>) -> Self::Future {
use futures_util::future::FutureExt;
let handler = self.handler.clone();
let state = req.extensions().get::<S>().unwrap().clone();
let future = Handler::call(handler, state, req);
let future = future.map(Ok as _);
super::future::IntoServiceFuture::new(future)
}
}
+7 -7
View File
@@ -18,13 +18,6 @@ pub struct IntoService<H, S, T, B> {
_marker: PhantomData<fn() -> (T, B)>, _marker: PhantomData<fn() -> (T, B)>,
} }
#[test]
fn traits() {
use crate::test_helpers::*;
assert_send::<IntoService<(), (), NotSendSync, NotSendSync>>();
assert_sync::<IntoService<(), (), NotSendSync, NotSendSync>>();
}
impl<H, S, T, B> IntoService<H, S, T, B> { impl<H, S, T, B> IntoService<H, S, T, B> {
pub(super) fn new(handler: H, state: S) -> Self { pub(super) fn new(handler: H, state: S) -> Self {
Self { Self {
@@ -89,3 +82,10 @@ where
super::future::IntoServiceFuture::new(future) super::future::IntoServiceFuture::new(future)
} }
} }
#[test]
fn traits() {
use crate::test_helpers::*;
assert_send::<IntoService<(), (), NotSendSync, NotSendSync>>();
assert_sync::<IntoService<(), (), NotSendSync, NotSendSync>>();
}
+67 -42
View File
@@ -43,16 +43,19 @@ use crate::{
BoxError, BoxError,
}; };
use http::Request; use http::Request;
use std::{fmt, future::Future, marker::PhantomData, pin::Pin}; use std::{convert::Infallible, fmt, future::Future, marker::PhantomData, pin::Pin};
use tower::ServiceExt; use tower::ServiceExt;
use tower_layer::Layer; use tower_layer::Layer;
use tower_service::Service; use tower_service::Service;
pub mod future; mod into_extension_service;
mod into_service; mod into_service;
pub(crate) use self::into_extension_service::IntoExtensionService;
pub use self::into_service::IntoService; pub use self::into_service::IntoService;
pub mod future;
/// Trait for async functions that can be used to handle requests. /// Trait for async functions that can be used to handle requests.
/// ///
/// You shouldn't need to depend on this trait directly. It is automatically /// You shouldn't need to depend on this trait directly. It is automatically
@@ -61,7 +64,8 @@ pub use self::into_service::IntoService;
/// See the [module docs](crate::handler) for more details. /// See the [module docs](crate::handler) for more details.
/// ///
#[doc = include_str!("../docs/debugging_handler_type_errors.md")] #[doc = include_str!("../docs/debugging_handler_type_errors.md")]
pub trait Handler<S, T, B = Body>: Clone + Send + Sized + 'static { // TODO(david): Add back `B = Body` default
pub trait Handler<S, T, B>: Clone + Send + Sized + 'static {
/// The type of future calling this handler returns. /// The type of future calling this handler returns.
type Future: Future<Output = Response> + Send + 'static; type Future: Future<Output = Response> + Send + 'static;
@@ -104,13 +108,15 @@ pub trait Handler<S, T, B = Body>: Clone + Send + Sized + 'static {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # 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<Self, S, B, L>
where where
L: Layer<IntoService<Self, S, T, B>>, L: Layer<IntoService<Self, S, T, B>>,
{ {
// TODO(david): write this, somehow Layered {
todo!() handler: self,
// Layered::new(layer.layer(self.into_service())) layer,
_marker: PhantomData,
}
} }
/// Convert the handler into a [`Service`]. /// Convert the handler into a [`Service`].
@@ -145,6 +151,7 @@ pub trait Handler<S, T, B = Body>: Clone + Send + Sized + 'static {
/// ``` /// ```
/// ///
/// [`Router::fallback`]: crate::routing::Router::fallback /// [`Router::fallback`]: crate::routing::Router::fallback
// TODO(david): remove this
fn into_service(self, state: S) -> IntoService<Self, S, T, B> { fn into_service(self, state: S) -> IntoService<Self, S, T, B> {
IntoService::new(self, state) IntoService::new(self, state)
} }
@@ -172,6 +179,7 @@ pub trait Handler<S, T, B = Body>: Clone + Send + Sized + 'static {
/// ``` /// ```
/// ///
/// [`MakeService`]: tower::make::MakeService /// [`MakeService`]: tower::make::MakeService
// TODO(david): remove this
fn into_make_service(self, state: S) -> IntoMakeService<IntoService<Self, S, T, B>> { fn into_make_service(self, state: S) -> IntoMakeService<IntoService<Self, S, T, B>> {
IntoMakeService::new(self.into_service(state)) IntoMakeService::new(self.into_service(state))
} }
@@ -204,6 +212,7 @@ pub trait Handler<S, T, B = Body>: Clone + Send + Sized + 'static {
/// ///
/// [`MakeService`]: tower::make::MakeService /// [`MakeService`]: tower::make::MakeService
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info /// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
// TODO(david): remove this
fn into_make_service_with_connect_info<C>( fn into_make_service_with_connect_info<C>(
self, self,
state: S, state: S,
@@ -264,63 +273,79 @@ all_the_tuples!(impl_handler);
/// A [`Service`] created from a [`Handler`] by applying a Tower middleware. /// A [`Service`] created from a [`Handler`] by applying a Tower middleware.
/// ///
/// Created with [`Handler::layer`]. See that method for more details. /// Created with [`Handler::layer`]. See that method for more details.
pub struct Layered<S, T> { pub struct Layered<H, S, B, L> {
svc: S, handler: H,
_input: PhantomData<fn() -> T>, layer: L,
_marker: PhantomData<(S, B)>,
} }
impl<S, T> fmt::Debug for Layered<S, T> impl<H, S, B, L> Clone for Layered<H, S, B, L>
where where
S: fmt::Debug, H: Clone,
{ L: Clone,
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Layered").field("svc", &self.svc).finish()
}
}
impl<S, T> Clone for Layered<S, T>
where
S: Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self::new(self.svc.clone()) Self {
handler: self.handler.clone(),
layer: self.layer.clone(),
_marker: self._marker,
}
} }
} }
impl<S, T, ReqBody, ResBody, St> Handler<St, T, ReqBody> for Layered<S, T> impl<H, S, B, L> Copy for Layered<H, S, B, L>
where where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static, H: Copy,
S::Error: IntoResponse, L: Copy,
S::Future: Send, {
T: 'static, }
ReqBody: Send + 'static,
impl<H, S, B, L> fmt::Debug for Layered<H, S, B, L>
where
L: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
handler: _,
layer,
_marker,
} = self;
f.debug_struct("Layered").field("layer", &layer).finish()
}
}
impl<H, L, S, T, B, ResBody> Handler<S, T, B> for Layered<H, S, B, L>
where
H: Handler<S, T, B> + Clone + Send + 'static,
S: Send + 'static,
L: Layer<IntoService<H, S, T, B>> + Clone + Send + 'static,
L::Service: Service<Request<B>, Response = Response<ResBody>, Error = Infallible>
+ Clone
+ Send
+ 'static,
<L::Service as Service<Request<B>>>::Future: Send,
B: Send + 'static,
ResBody: HttpBody<Data = Bytes> + Send + 'static, ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>, ResBody::Error: Into<BoxError>,
{ {
type Future = future::LayeredFuture<S, ReqBody>; type Future = future::LayeredFuture<L::Service, B>;
fn call(self, state: St, req: Request<ReqBody>) -> Self::Future { fn call(self, state: S, req: Request<B>) -> Self::Future {
use futures_util::future::{FutureExt, Map}; use futures_util::future::{FutureExt, Map};
let future: Map<_, fn(Result<S::Response, S::Error>) -> _> = let svc = self.handler.into_service(state);
self.svc.oneshot(req).map(|result| match result { let svc = self.layer.layer(svc);
let future: Map<_, fn(Result<Response<ResBody>, Infallible>) -> _> =
svc.oneshot(req).map(|result| match result {
Ok(res) => res.map(boxed), Ok(res) => res.map(boxed),
Err(res) => res.into_response(), Err(err) => match err {},
}); });
future::LayeredFuture::new(future) future::LayeredFuture::new(future)
} }
} }
impl<S, T> Layered<S, T> {
pub(crate) fn new(svc: S) -> Self {
Self {
svc,
_input: PhantomData,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
+187 -102
View File
@@ -1,9 +1,9 @@
use super::IntoMakeService; use super::{IntoMakeService, MissingState, WithState};
use crate::{ use crate::{
body::{boxed, Body, Bytes, Empty, HttpBody}, body::{boxed, Body, Bytes, Empty, HttpBody},
error_handling::{HandleError, HandleErrorLayer}, error_handling::{HandleError, HandleErrorLayer},
extract::connect_info::IntoMakeServiceWithConnectInfo, extract::{connect_info::IntoMakeServiceWithConnectInfo, State},
handler::Handler, handler::{Handler, IntoExtensionService},
http::{Method, Request, StatusCode}, http::{Method, Request, StatusCode},
response::Response, response::Response,
routing::{future::RouteFuture, Fallback, MethodFilter, Route}, routing::{future::RouteFuture, Fallback, MethodFilter, Route},
@@ -76,10 +76,10 @@ macro_rules! top_level_service_fn {
$name:ident, $method:ident $name:ident, $method:ident
) => { ) => {
$(#[$m])+ $(#[$m])+
pub fn $name<S, ReqBody, ResBody>(svc: S) -> MethodRouter<ReqBody, S::Error> pub fn $name<T, ReqBody, ResBody, S>(svc: T) -> MethodRouter<S, ReqBody, T::Error, MissingState>
where where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static, T: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
S::Future: Send + 'static, T::Future: Send + 'static,
ResBody: HttpBody<Data = Bytes> + Send + 'static, ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>, ResBody::Error: Into<BoxError>,
{ {
@@ -137,11 +137,12 @@ macro_rules! top_level_handler_fn {
$name:ident, $method:ident $name:ident, $method:ident
) => { ) => {
$(#[$m])+ $(#[$m])+
pub fn $name<H, T, B>(handler: H) -> MethodRouter<B, Infallible> pub fn $name<H, S, T, B>(handler: H) -> MethodRouter<S, B, Infallible, MissingState>
where where
H: Handler<T, B>, H: Handler<S, T, B>,
B: Send + 'static, B: Send + 'static,
T: 'static, T: 'static,
S: Clone + Send + Sync + 'static,
{ {
on(MethodFilter::$method, handler) on(MethodFilter::$method, handler)
} }
@@ -208,13 +209,13 @@ macro_rules! chained_service_fn {
$name:ident, $method:ident $name:ident, $method:ident
) => { ) => {
$(#[$m])+ $(#[$m])+
pub fn $name<S, ResBody>(self, svc: S) -> Self pub fn $name<T, ResBody>(self, svc: T) -> Self
where where
S: Service<Request<ReqBody>, Response = Response<ResBody>, Error = E> T: Service<Request<B>, Response = Response<ResBody>, Error = E>
+ Clone + Clone
+ Send + Send
+ 'static, + 'static,
S::Future: Send + 'static, T::Future: Send + 'static,
ResBody: HttpBody<Data = Bytes> + Send + 'static, ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>, ResBody::Error: Into<BoxError>,
{ {
@@ -274,8 +275,9 @@ macro_rules! chained_handler_fn {
$(#[$m])+ $(#[$m])+
pub fn $name<H, T>(self, handler: H) -> Self pub fn $name<H, T>(self, handler: H) -> Self
where where
H: Handler<T, B>, H: Handler<S, T, B>,
T: 'static, T: 'static,
S: Clone + Send + Sync + 'static,
{ {
self.on(MethodFilter::$method, handler) self.on(MethodFilter::$method, handler)
} }
@@ -316,13 +318,13 @@ top_level_service_fn!(trace_service, TRACE);
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # }; /// # };
/// ``` /// ```
pub fn on_service<S, ReqBody, ResBody>( pub fn on_service<T, ReqBody, ResBody, S>(
filter: MethodFilter, filter: MethodFilter,
svc: S, svc: T,
) -> MethodRouter<ReqBody, S::Error> ) -> MethodRouter<S, ReqBody, T::Error, MissingState>
where where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static, T: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
S::Future: Send + 'static, T::Future: Send + 'static,
ResBody: HttpBody<Data = Bytes> + Send + 'static, ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>, ResBody::Error: Into<BoxError>,
{ {
@@ -382,14 +384,18 @@ where
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # }; /// # };
/// ``` /// ```
pub fn any_service<S, ReqBody, ResBody>(svc: S) -> MethodRouter<ReqBody, S::Error> pub fn any_service<T, ReqBody, ResBody, S>(
svc: T,
) -> MethodRouter<S, ReqBody, T::Error, MissingState>
where where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static, T: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
S::Future: Send + 'static, T::Future: Send + 'static,
ResBody: HttpBody<Data = Bytes> + Send + 'static, ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>, ResBody::Error: Into<BoxError>,
{ {
MethodRouter::new().fallback(svc).skip_allow_header() MethodRouter::new()
.fallback_service(svc)
.skip_allow_header()
} }
top_level_handler_fn!(delete, DELETE); top_level_handler_fn!(delete, DELETE);
@@ -420,11 +426,15 @@ top_level_handler_fn!(trace, TRACE);
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # 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, S, T, B>(
filter: MethodFilter,
handler: H,
) -> MethodRouter<S, B, Infallible, MissingState>
where where
H: Handler<T, B>, H: Handler<S, T, B>,
B: Send + 'static, B: Send + 'static,
T: 'static, T: 'static,
S: Clone + Send + Sync + 'static,
{ {
MethodRouter::new().on(filter, handler) MethodRouter::new().on(filter, handler)
} }
@@ -466,20 +476,26 @@ where
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # 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, S, T, B>(handler: H) -> MethodRouter<S, B, Infallible, MissingState>
where where
H: Handler<T, B>, H: Handler<S, T, B>,
B: Send + 'static, B: Send + 'static,
T: 'static, T: 'static,
S: Clone + Send + Sync + 'static,
{ {
MethodRouter::new() MethodRouter::new()
.fallback_boxed_response_body(handler.into_service()) .fallback_boxed_response_body(IntoExtensionService::new(handler))
.skip_allow_header() .skip_allow_header()
} }
/// A [`Service`] that accepts requests based on a [`MethodFilter`] and /// A [`Service`] that accepts requests based on a [`MethodFilter`] and
/// allows chaining additional handlers and services. /// allows chaining additional handlers and services.
pub struct MethodRouter<B = Body, E = Infallible> { // TODO(david): Bring back `B = Body, E = Infallible` defaults
pub struct MethodRouter<S, B, E, R> {
// Invariant: If `R == MissingState` then `state` is `None`
// If `R == WithState` then state is `Some`
// `R` cannot have other values
state: Option<S>,
get: Option<Route<B, E>>, get: Option<Route<B, E>>,
head: Option<Route<B, E>>, head: Option<Route<B, E>>,
delete: Option<Route<B, E>>, delete: Option<Route<B, E>>,
@@ -490,7 +506,7 @@ pub struct MethodRouter<B = Body, E = Infallible> {
trace: Option<Route<B, E>>, trace: Option<Route<B, E>>,
fallback: Fallback<B, E>, fallback: Fallback<B, E>,
allow_header: AllowHeader, allow_header: AllowHeader,
_request_body: PhantomData<fn() -> (B, E)>, _marker: PhantomData<R>,
} }
#[derive(Clone)] #[derive(Clone)]
@@ -503,9 +519,13 @@ enum AllowHeader {
Bytes(BytesMut), Bytes(BytesMut),
} }
impl<B, E> fmt::Debug for MethodRouter<B, E> { impl<S, B, E, R> fmt::Debug for MethodRouter<S, B, E, R>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MethodRouter") f.debug_struct("MethodRouter")
.field("state", &self.state)
.field("get", &self.get) .field("get", &self.get)
.field("head", &self.head) .field("head", &self.head)
.field("delete", &self.delete) .field("delete", &self.delete)
@@ -519,7 +539,7 @@ impl<B, E> fmt::Debug for MethodRouter<B, E> {
} }
} }
impl<B, E> MethodRouter<B, E> { impl<S, B, E> MethodRouter<S, B, E, MissingState> {
/// Create a default `MethodRouter` that will respond with `405 Method Not Allowed` to all /// Create a default `MethodRouter` that will respond with `405 Method Not Allowed` to all
/// requests. /// requests.
pub fn new() -> Self { pub fn new() -> Self {
@@ -530,6 +550,7 @@ impl<B, E> MethodRouter<B, E> {
})); }));
Self { Self {
state: None,
get: None, get: None,
head: None, head: None,
delete: None, delete: None,
@@ -540,14 +561,47 @@ impl<B, E> MethodRouter<B, E> {
trace: None, trace: None,
allow_header: AllowHeader::None, allow_header: AllowHeader::None,
fallback: Fallback::Default(fallback), fallback: Fallback::Default(fallback),
_request_body: PhantomData, _marker: PhantomData,
}
}
/// TODO(david): docs
pub fn state(self, state: S) -> MethodRouter<S, B, E, WithState> {
MethodRouter {
state: Some(state),
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,
} }
} }
} }
impl<B> MethodRouter<B, Infallible> impl<S, B, E> MethodRouter<S, B, E, WithState> {
/// TODO(david): docs
pub fn with_state(state: S) -> Self {
MethodRouter::new().state(state)
}
}
impl<B, E> MethodRouter<(), B, E, WithState> {
/// TODO(david): docs
pub fn without_state() -> Self {
MethodRouter::with_state(())
}
}
impl<S, B, R> MethodRouter<S, B, Infallible, R>
where where
B: Send + 'static, B: Send + 'static,
S: Clone + Send + Sync + 'static,
{ {
/// Chain an additional handler that will accept requests matching the given /// Chain an additional handler that will accept requests matching the given
/// `MethodFilter`. /// `MethodFilter`.
@@ -574,10 +628,10 @@ where
/// ``` /// ```
pub fn on<H, T>(self, filter: MethodFilter, handler: H) -> Self pub fn on<H, T>(self, filter: MethodFilter, handler: H) -> Self
where where
H: Handler<T, B>, H: Handler<S, T, B>,
T: 'static, T: 'static,
{ {
self.on_service_boxed_response_body(filter, handler.into_service()) self.on_service_boxed_response_body(filter, IntoExtensionService::new(handler))
} }
chained_handler_fn!(delete, DELETE); chained_handler_fn!(delete, DELETE);
@@ -589,6 +643,22 @@ where
chained_handler_fn!(put, PUT); chained_handler_fn!(put, PUT);
chained_handler_fn!(trace, TRACE); chained_handler_fn!(trace, TRACE);
#[doc = include_str!("../docs/routing/fallback.md")]
pub fn fallback<H, T>(mut self, handler: H) -> Self
where
H: Handler<S, T, B>,
T: 'static,
S: Clone + Send + Sync + 'static,
{
self.fallback_boxed_response_body(IntoExtensionService::new(handler))
}
}
impl<S, B> MethodRouter<S, B, Infallible, WithState>
where
B: Send + 'static,
S: Clone + Send + Sync + 'static,
{
/// Convert the handler into a [`MakeService`]. /// Convert the handler into a [`MakeService`].
/// ///
/// This allows you to serve a single handler if you don't need any routing: /// This allows you to serve a single handler if you don't need any routing:
@@ -658,7 +728,7 @@ where
} }
} }
impl<ReqBody, E> MethodRouter<ReqBody, E> { impl<S, B, E, R> MethodRouter<S, B, E, R> {
/// Chain an additional service that will accept requests matching the given /// Chain an additional service that will accept requests matching the given
/// `MethodFilter`. /// `MethodFilter`.
/// ///
@@ -684,13 +754,10 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # }; /// # };
/// ``` /// ```
pub fn on_service<S, ResBody>(self, filter: MethodFilter, svc: S) -> Self pub fn on_service<T, ResBody>(self, filter: MethodFilter, svc: T) -> Self
where where
S: Service<Request<ReqBody>, Response = Response<ResBody>, Error = E> T: Service<Request<B>, Response = Response<ResBody>, Error = E> + Clone + Send + 'static,
+ Clone T::Future: Send + 'static,
+ Send
+ 'static,
S::Future: Send + 'static,
ResBody: HttpBody<Data = Bytes> + Send + 'static, ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>, ResBody::Error: Into<BoxError>,
{ {
@@ -707,13 +774,10 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
chained_service_fn!(trace_service, TRACE); chained_service_fn!(trace_service, TRACE);
#[doc = include_str!("../docs/method_routing/fallback.md")] #[doc = include_str!("../docs/method_routing/fallback.md")]
pub fn fallback<S, ResBody>(mut self, svc: S) -> Self pub fn fallback_service<T, ResBody>(mut self, svc: T) -> Self
where where
S: Service<Request<ReqBody>, Response = Response<ResBody>, Error = E> T: Service<Request<B>, Response = Response<ResBody>, Error = E> + Clone + Send + 'static,
+ Clone T::Future: Send + 'static,
+ Send
+ 'static,
S::Future: Send + 'static,
ResBody: HttpBody<Data = Bytes> + Send + 'static, ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>, ResBody::Error: Into<BoxError>,
{ {
@@ -721,10 +785,10 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
self self
} }
fn fallback_boxed_response_body<S>(mut self, svc: S) -> Self fn fallback_boxed_response_body<T>(mut self, svc: T) -> Self
where where
S: Service<Request<ReqBody>, Response = Response, Error = E> + Clone + Send + 'static, T: Service<Request<B>, Response = Response, Error = E> + Clone + Send + 'static,
S::Future: Send + 'static, T::Future: Send + 'static,
{ {
self.fallback = Fallback::Custom(Route::new(svc)); self.fallback = Fallback::Custom(Route::new(svc));
self self
@@ -734,9 +798,9 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
pub fn layer<L, NewReqBody, NewResBody, NewError>( pub fn layer<L, NewReqBody, NewResBody, NewError>(
self, self,
layer: L, layer: L,
) -> MethodRouter<NewReqBody, NewError> ) -> MethodRouter<S, NewReqBody, NewError, R>
where where
L: Layer<Route<ReqBody, E>>, L: Layer<Route<B, E>>,
L::Service: Service<Request<NewReqBody>, Response = Response<NewResBody>, Error = NewError> L::Service: Service<Request<NewReqBody>, Response = Response<NewResBody>, Error = NewError>
+ Clone + Clone
+ Send + Send
@@ -753,6 +817,7 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
let layer_fn = |s| layer.layer(s); let layer_fn = |s| layer.layer(s);
MethodRouter { MethodRouter {
state: self.state,
get: self.get.map(layer_fn), get: self.get.map(layer_fn),
head: self.head.map(layer_fn), head: self.head.map(layer_fn),
delete: self.delete.map(layer_fn), delete: self.delete.map(layer_fn),
@@ -763,19 +828,19 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
trace: self.trace.map(layer_fn), trace: self.trace.map(layer_fn),
fallback: self.fallback.map(layer_fn), fallback: self.fallback.map(layer_fn),
allow_header: self.allow_header, allow_header: self.allow_header,
_request_body: PhantomData, _marker: self._marker,
} }
} }
#[doc = include_str!("../docs/method_routing/route_layer.md")] #[doc = include_str!("../docs/method_routing/route_layer.md")]
pub fn route_layer<L, NewResBody>(self, layer: L) -> MethodRouter<ReqBody, E> pub fn route_layer<L, NewResBody>(self, layer: L) -> MethodRouter<S, B, E, R>
where where
L: Layer<Route<ReqBody, E>>, L: Layer<Route<B, E>>,
L::Service: Service<Request<ReqBody>, Response = Response<NewResBody>, Error = E> L::Service: Service<Request<B>, Response = Response<NewResBody>, Error = E>
+ Clone + Clone
+ Send + Send
+ 'static, + 'static,
<L::Service as Service<Request<ReqBody>>>::Future: Send + 'static, <L::Service as Service<Request<B>>>::Future: Send + 'static,
NewResBody: HttpBody<Data = Bytes> + Send + 'static, NewResBody: HttpBody<Data = Bytes> + Send + 'static,
NewResBody::Error: Into<BoxError>, NewResBody::Error: Into<BoxError>,
{ {
@@ -787,6 +852,7 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
let layer_fn = |s| layer.layer(s); let layer_fn = |s| layer.layer(s);
MethodRouter { MethodRouter {
state: self.state,
get: self.get.map(layer_fn), get: self.get.map(layer_fn),
head: self.head.map(layer_fn), head: self.head.map(layer_fn),
delete: self.delete.map(layer_fn), delete: self.delete.map(layer_fn),
@@ -797,12 +863,12 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
trace: self.trace.map(layer_fn), trace: self.trace.map(layer_fn),
fallback: self.fallback, fallback: self.fallback,
allow_header: self.allow_header, allow_header: self.allow_header,
_request_body: PhantomData, _marker: self._marker,
} }
} }
#[doc = include_str!("../docs/method_routing/merge.md")] #[doc = include_str!("../docs/method_routing/merge.md")]
pub fn merge(self, other: MethodRouter<ReqBody, E>) -> Self { pub fn merge(self, other: MethodRouter<S, B, E, MissingState>) -> Self {
macro_rules! merge { macro_rules! merge {
( $first:ident, $second:ident ) => { ( $first:ident, $second:ident ) => {
match ($first, $second) { match ($first, $second) {
@@ -819,6 +885,7 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
} }
let Self { let Self {
state,
get, get,
head, head,
delete, delete,
@@ -829,10 +896,11 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
trace, trace,
fallback, fallback,
allow_header, allow_header,
_request_body: _, _marker: _,
} = self; } = self;
let Self { let MethodRouter {
state: state_other,
get: get_other, get: get_other,
head: head_other, head: head_other,
delete: delete_other, delete: delete_other,
@@ -843,8 +911,9 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
trace: trace_other, trace: trace_other,
fallback: fallback_other, fallback: fallback_other,
allow_header: allow_header_other, allow_header: allow_header_other,
_request_body: _, _marker: _,
} = other; } = other;
debug_assert!(state_other.is_none());
let get = merge!(get, get_other); let get = merge!(get, get_other);
let head = merge!(head, head_other); let head = merge!(head, head_other);
@@ -877,6 +946,7 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
}; };
Self { Self {
state,
get, get,
head, head,
delete, delete,
@@ -887,30 +957,30 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
trace, trace,
fallback, fallback,
allow_header, allow_header,
_request_body: PhantomData, _marker: PhantomData,
} }
} }
/// Apply a [`HandleErrorLayer`]. /// Apply a [`HandleErrorLayer`].
/// ///
/// This is a convenience method for doing `self.layer(HandleErrorLayer::new(f))`. /// 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, R>
where where
F: Clone + Send + 'static, F: Clone + Send + 'static,
HandleError<Route<ReqBody, E>, F, T>: HandleError<Route<B, E>, F, T>:
Service<Request<ReqBody>, Response = Response, Error = Infallible>, Service<Request<B>, Response = Response, Error = Infallible>,
<HandleError<Route<ReqBody, E>, F, T> as Service<Request<ReqBody>>>::Future: Send, <HandleError<Route<B, E>, F, T> as Service<Request<B>>>::Future: Send,
T: 'static, T: 'static,
E: 'static, E: 'static,
ReqBody: 'static, B: 'static,
{ {
self.layer(HandleErrorLayer::new(f)) self.layer(HandleErrorLayer::new(f))
} }
fn on_service_boxed_response_body<S>(self, filter: MethodFilter, svc: S) -> Self fn on_service_boxed_response_body<T>(self, filter: MethodFilter, svc: T) -> Self
where where
S: Service<Request<ReqBody>, Response = Response, Error = E> + Clone + Send + 'static, T: Service<Request<B>, Response = Response, Error = E> + Clone + Send + 'static,
S::Future: Send + 'static, T::Future: Send + 'static,
{ {
macro_rules! set_service { macro_rules! set_service {
( (
@@ -940,6 +1010,7 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
// written with a pattern match like this to ensure we update all fields // written with a pattern match like this to ensure we update all fields
let Self { let Self {
state,
mut get, mut get,
mut head, mut head,
mut delete, mut delete,
@@ -950,7 +1021,7 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
mut trace, mut trace,
fallback, fallback,
mut allow_header, mut allow_header,
_request_body: _, _marker,
} = self; } = self;
let svc = Some(Route::new(svc)); let svc = Some(Route::new(svc));
set_service!( set_service!(
@@ -969,6 +1040,7 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
] ]
); );
Self { Self {
state,
get, get,
head, head,
delete, delete,
@@ -979,7 +1051,7 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
trace, trace,
fallback, fallback,
allow_header, allow_header,
_request_body: PhantomData, _marker,
} }
} }
@@ -1009,9 +1081,13 @@ fn append_allow_header(allow_header: &mut AllowHeader, method: &'static str) {
} }
} }
impl<B, E> Clone for MethodRouter<B, E> { impl<S, B, E, R> Clone for MethodRouter<S, B, E, R>
where
S: Clone,
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
state: self.state.clone(),
get: self.get.clone(), get: self.get.clone(),
head: self.head.clone(), head: self.head.clone(),
delete: self.delete.clone(), delete: self.delete.clone(),
@@ -1022,12 +1098,12 @@ impl<B, E> Clone for MethodRouter<B, E> {
trace: self.trace.clone(), trace: self.trace.clone(),
fallback: self.fallback.clone(), fallback: self.fallback.clone(),
allow_header: self.allow_header.clone(), allow_header: self.allow_header.clone(),
_request_body: PhantomData, _marker: self._marker,
} }
} }
} }
impl<B, E> Default for MethodRouter<B, E> impl<S, B, E> Default for MethodRouter<S, B, E, MissingState>
where where
B: Send + 'static, B: Send + 'static,
{ {
@@ -1036,9 +1112,10 @@ where
} }
} }
impl<B, E> Service<Request<B>> for MethodRouter<B, E> impl<S, B, E> Service<Request<B>> for MethodRouter<S, B, E, WithState>
where where
B: HttpBody, B: HttpBody,
S: Clone + Send + Sync + 'static,
{ {
type Response = Response; type Response = Response;
type Error = E; type Error = E;
@@ -1049,7 +1126,7 @@ where
Poll::Ready(Ok(())) 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 { macro_rules! call {
( (
$req:expr, $req:expr,
@@ -1070,6 +1147,7 @@ where
// written with a pattern match like this to ensure we call all routes // written with a pattern match like this to ensure we call all routes
let Self { let Self {
state,
get, get,
head, head,
delete, delete,
@@ -1080,9 +1158,17 @@ where
trace, trace,
fallback, fallback,
allow_header, allow_header,
_request_body: _, _marker,
} = self; } = self;
if req.extensions().get::<State<S>>().is_none() {
// the `unwrap` is safe because `self.state` is always some if `R = WithState`, which it is
let prev = req
.extensions_mut()
.insert(State(state.as_ref().unwrap().clone()));
debug_assert!(prev.is_none());
}
call!(req, method, HEAD, head); call!(req, method, HEAD, head);
call!(req, method, HEAD, get); call!(req, method, HEAD, get);
call!(req, method, GET, get); call!(req, method, GET, get);
@@ -1120,7 +1206,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn method_not_allowed_by_default() { async fn method_not_allowed_by_default() {
let mut svc = MethodRouter::new(); let mut svc = MethodRouter::new().state(());
let (status, _, body) = call(Method::GET, &mut svc).await; let (status, _, body) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
assert!(body.is_empty()); assert!(body.is_empty());
@@ -1128,7 +1214,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn get_handler() { async fn get_handler() {
let mut svc = MethodRouter::new().get(ok); let mut svc = MethodRouter::without_state().get(ok);
let (status, _, body) = call(Method::GET, &mut svc).await; let (status, _, body) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::OK); assert_eq!(status, StatusCode::OK);
assert_eq!(body, "ok"); assert_eq!(body, "ok");
@@ -1136,7 +1222,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn get_accepts_head() { async fn get_accepts_head() {
let mut svc = MethodRouter::new().get(ok); let mut svc = MethodRouter::without_state().get(ok);
let (status, _, body) = call(Method::HEAD, &mut svc).await; let (status, _, body) = call(Method::HEAD, &mut svc).await;
assert_eq!(status, StatusCode::OK); assert_eq!(status, StatusCode::OK);
assert!(body.is_empty()); assert!(body.is_empty());
@@ -1144,7 +1230,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn head_takes_precedence_over_get() { async fn head_takes_precedence_over_get() {
let mut svc = MethodRouter::new().head(created).get(ok); let mut svc = MethodRouter::without_state().head(created).get(ok);
let (status, _, body) = call(Method::HEAD, &mut svc).await; let (status, _, body) = call(Method::HEAD, &mut svc).await;
assert_eq!(status, StatusCode::CREATED); assert_eq!(status, StatusCode::CREATED);
assert!(body.is_empty()); assert!(body.is_empty());
@@ -1152,7 +1238,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn merge() { async fn merge() {
let mut svc = get(ok).merge(post(ok)); let mut svc = get(ok).merge(post(ok)).state(());
let (status, _, _) = call(Method::GET, &mut svc).await; let (status, _, _) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::OK); assert_eq!(status, StatusCode::OK);
@@ -1163,7 +1249,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn layer() { async fn layer() {
let mut svc = MethodRouter::new() let mut svc = MethodRouter::without_state()
.get(|| async { std::future::pending::<()>().await }) .get(|| async { std::future::pending::<()>().await })
.layer(RequireAuthorizationLayer::bearer("password")); .layer(RequireAuthorizationLayer::bearer("password"));
@@ -1178,7 +1264,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn route_layer() { async fn route_layer() {
let mut svc = MethodRouter::new() let mut svc = MethodRouter::without_state()
.get(|| async { std::future::pending::<()>().await }) .get(|| async { std::future::pending::<()>().await })
.route_layer(RequireAuthorizationLayer::bearer("password")); .route_layer(RequireAuthorizationLayer::bearer("password"));
@@ -1204,7 +1290,7 @@ mod tests {
delete_service(ServeDir::new(".")) delete_service(ServeDir::new("."))
.handle_error(|_| async { StatusCode::NOT_FOUND }), .handle_error(|_| async { StatusCode::NOT_FOUND }),
) )
.fallback((|| async { StatusCode::NOT_FOUND }).into_service()) .fallback(|| async { StatusCode::NOT_FOUND })
.put(ok) .put(ok)
.layer( .layer(
ServiceBuilder::new() ServiceBuilder::new()
@@ -1221,7 +1307,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn sets_allow_header() { async fn sets_allow_header() {
let mut svc = MethodRouter::new().put(ok).patch(ok); let mut svc = MethodRouter::without_state().put(ok).patch(ok);
let (status, headers, _) = call(Method::GET, &mut svc).await; let (status, headers, _) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
assert_eq!(headers[ALLOW], "PUT,PATCH"); assert_eq!(headers[ALLOW], "PUT,PATCH");
@@ -1229,7 +1315,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn sets_allow_header_get_head() { async fn sets_allow_header_get_head() {
let mut svc = MethodRouter::new().get(ok).head(ok); let mut svc = MethodRouter::without_state().get(ok).head(ok);
let (status, headers, _) = call(Method::PUT, &mut svc).await; let (status, headers, _) = call(Method::PUT, &mut svc).await;
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
assert_eq!(headers[ALLOW], "GET,HEAD"); assert_eq!(headers[ALLOW], "GET,HEAD");
@@ -1237,7 +1323,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn empty_allow_header_by_default() { async fn empty_allow_header_by_default() {
let mut svc = MethodRouter::new(); let mut svc = MethodRouter::without_state();
let (status, headers, _) = call(Method::PATCH, &mut svc).await; let (status, headers, _) = call(Method::PATCH, &mut svc).await;
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
assert_eq!(headers[ALLOW], ""); assert_eq!(headers[ALLOW], "");
@@ -1247,7 +1333,7 @@ mod tests {
async fn allow_header_when_merging() { async fn allow_header_when_merging() {
let a = put(ok).patch(ok); let a = put(ok).patch(ok);
let b = get(ok).head(ok); let b = get(ok).head(ok);
let mut svc = a.merge(b); let mut svc = a.merge(b).state(());
let (status, headers, _) = call(Method::DELETE, &mut svc).await; let (status, headers, _) = call(Method::DELETE, &mut svc).await;
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
@@ -1256,7 +1342,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn allow_header_any() { async fn allow_header_any() {
let mut svc = any(ok); let mut svc = any(ok).state(());
let (status, headers, _) = call(Method::GET, &mut svc).await; let (status, headers, _) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::OK); assert_eq!(status, StatusCode::OK);
@@ -1265,9 +1351,9 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn allow_header_with_fallback() { async fn allow_header_with_fallback() {
let mut svc = MethodRouter::new().get(ok).fallback( let mut svc = MethodRouter::without_state()
(|| async { (StatusCode::METHOD_NOT_ALLOWED, "Method not allowed") }).into_service(), .get(ok)
); .fallback(|| async { (StatusCode::METHOD_NOT_ALLOWED, "Method not allowed") });
let (status, headers, _) = call(Method::DELETE, &mut svc).await; let (status, headers, _) = call(Method::DELETE, &mut svc).await;
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
@@ -1289,9 +1375,7 @@ mod tests {
} }
} }
let mut svc = MethodRouter::new() let mut svc = MethodRouter::without_state().get(ok).fallback(fallback);
.get(ok)
.fallback(fallback.into_service());
let (status, _, _) = call(Method::GET, &mut svc).await; let (status, _, _) = call(Method::GET, &mut svc).await;
assert_eq!(status, StatusCode::OK); assert_eq!(status, StatusCode::OK);
@@ -1309,7 +1393,7 @@ mod tests {
expected = "Overlapping method route. Cannot add two method routes that both handle `GET`" expected = "Overlapping method route. Cannot add two method routes that both handle `GET`"
)] )]
async fn handler_overlaps() { async fn handler_overlaps() {
let _: MethodRouter = get(ok).get(ok); let _: MethodRouter<(), Body, Infallible, _> = get(ok).get(ok);
} }
#[tokio::test] #[tokio::test]
@@ -1317,17 +1401,18 @@ mod tests {
expected = "Overlapping method route. Cannot add two method routes that both handle `POST`" expected = "Overlapping method route. Cannot add two method routes that both handle `POST`"
)] )]
async fn service_overlaps() { async fn service_overlaps() {
let _: MethodRouter = post_service(ok.into_service()).post_service(ok.into_service()); let _: MethodRouter<(), Body, Infallible, _> =
post_service(ok.into_service(())).post_service(ok.into_service(()));
} }
#[tokio::test] #[tokio::test]
async fn get_head_does_not_overlap() { async fn get_head_does_not_overlap() {
let _: MethodRouter = get(ok).head(ok); let _: MethodRouter<(), Body, Infallible, _> = get(ok).head(ok);
} }
#[tokio::test] #[tokio::test]
async fn head_get_does_not_overlap() { async fn head_get_does_not_overlap() {
let _: MethodRouter = head(ok).get(ok); let _: MethodRouter<(), Body, Infallible, _> = head(ok).get(ok);
} }
async fn call<S>(method: Method, svc: &mut S) -> (StatusCode, HeaderMap, String) async fn call<S>(method: Method, svc: &mut S) -> (StatusCode, HeaderMap, String)
+147 -88
View File
@@ -4,6 +4,7 @@ use self::{future::RouteFuture, not_found::NotFound};
use crate::{ use crate::{
body::{boxed, Body, Bytes, HttpBody}, body::{boxed, Body, Bytes, HttpBody},
extract::connect_info::IntoMakeServiceWithConnectInfo, extract::connect_info::IntoMakeServiceWithConnectInfo,
handler::{Handler, IntoExtensionService},
response::Response, response::Response,
routing::strip_prefix::StripPrefix, routing::strip_prefix::StripPrefix,
util::try_downcast, util::try_downcast,
@@ -68,7 +69,7 @@ pub struct Router<S, B = Body, R = MissingState> {
// If `R == WithState` then state is `Some` // If `R == WithState` then state is `Some`
// `R` cannot have other values // `R` cannot have other values
state: Option<S>, state: Option<S>,
routes: HashMap<RouteId, Endpoint<B>>, routes: HashMap<RouteId, Endpoint<S, B, R>>,
node: Arc<Node>, node: Arc<Node>,
fallback: Fallback<B>, fallback: Fallback<B>,
_marker: PhantomData<R>, _marker: PhantomData<R>,
@@ -141,10 +142,27 @@ where
} }
/// TODO(david): docs /// TODO(david): docs
pub fn state(self, state: S) -> Router<S, B, WithState> { pub fn state(self, state: S) -> Router<S, B, WithState>
where
S: Clone,
{
let routes = self
.routes
.into_iter()
.map(|(id, endpoint)| {
let endpoint = match endpoint {
Endpoint::MethodRouter(router) => {
Endpoint::MethodRouter(router.state(state.clone()))
}
Endpoint::Route(route) => Endpoint::Route(route),
};
(id, endpoint)
})
.collect();
Router { Router {
state: Some(state), state: Some(state),
routes: self.routes, routes,
node: self.node, node: self.node,
fallback: self.fallback, fallback: self.fallback,
_marker: PhantomData, _marker: PhantomData,
@@ -155,6 +173,7 @@ where
impl<S, B> Router<S, B, WithState> impl<S, B> Router<S, B, WithState>
where where
B: HttpBody + Send + 'static, B: HttpBody + Send + 'static,
S: Clone,
{ {
/// TODO(david): docs /// TODO(david): docs
pub fn with_state(state: S) -> Self { pub fn with_state(state: S) -> Self {
@@ -162,6 +181,16 @@ where
} }
} }
impl<B> Router<(), B, WithState>
where
B: HttpBody + Send + 'static,
{
/// TODO(david): docs
pub fn without_state() -> Self {
Router::with_state(())
}
}
impl<S, B, R> Router<S, B, R> impl<S, B, R> Router<S, B, R>
where where
B: HttpBody + Send + 'static, B: HttpBody + Send + 'static,
@@ -169,72 +198,78 @@ where
R: 'static, R: 'static,
{ {
#[doc = include_str!("../docs/routing/route.md")] #[doc = include_str!("../docs/routing/route.md")]
pub fn route<T>(mut self, path: &str, service: T) -> Self pub fn route(
mut self,
path: &str,
// TODO(david): constrain this so it only accepts methods
// routers containing handlers
method_router: MethodRouter<S, B, Infallible, MissingState>,
) -> Self {
self
}
/// TODO(david): docs
pub fn route_service<T>(mut self, path: &str, service: T) -> Self
where where
T: Service<Request<B>, Response = Response, Error = Infallible> + Clone + Send + 'static, T: Service<Request<B>, Response = Response, Error = Infallible> + Clone + Send + 'static,
T::Future: Send + 'static, 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 `/`");
}
// Downcase to `WithState` rather than `R` because `Router<S, B, R>` only implements
// `Service` if `R == WithState` so any other type of `R` cannot be passed to `.router` in
// the first place
let service = match try_downcast::<Router<S, B, WithState>, _>(service) {
Ok(_) => {
panic!("Invalid route: `Router::route` 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 mut node =
Arc::try_unwrap(Arc::clone(&self.node)).unwrap_or_else(|node| (*node).clone());
if let Err(err) = node.insert(path, id) {
panic!("Invalid route: {}", err);
}
self.node = Arc::new(node);
self.routes.insert(id, service);
self self
// 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 `/`");
// }
// // Downcase to `WithState` rather than `R` because `Router<S, B, R>` only implements
// // `Service` if `R == WithState` so any other type of `R` cannot be passed to `.router` in
// // the first place
// let service = match try_downcast::<Router<S, B, WithState>, _>(service) {
// Ok(_) => {
// panic!("Invalid route: `Router::route` 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 mut node =
// Arc::try_unwrap(Arc::clone(&self.node)).unwrap_or_else(|node| (*node).clone());
// if let Err(err) = node.insert(path, id) {
// panic!("Invalid route: {}", err);
// }
// self.node = Arc::new(node);
// self.routes.insert(id, service);
// self
} }
#[doc = include_str!("../docs/routing/nest.md")] #[doc = include_str!("../docs/routing/nest.md")]
pub fn nest(mut self, mut path: &str, router: Router<S, B, MissingState>) -> Self { pub fn nest(mut self, mut path: &str, router: Router<S, B, MissingState>) -> Self {
if path.is_empty() { validate_path_for_nest(&mut path);
// nesting at `""` and `"/"` should mean the same thing
path = "/";
}
if path.contains('*') {
panic!("Invalid route: nested routes cannot contain wildcards (*)");
}
let prefix = path; let prefix = path;
@@ -268,7 +303,9 @@ where
&full_path, &full_path,
method_router.layer(layer_fn(|s| StripPrefix::new(s, prefix))), method_router.layer(layer_fn(|s| StripPrefix::new(s, prefix))),
), ),
Endpoint::Route(route) => self.route(&full_path, StripPrefix::new(route, prefix)), Endpoint::Route(route) => {
self.route_service(&full_path, StripPrefix::new(route, prefix))
}
}; };
} }
@@ -283,14 +320,7 @@ where
T: Service<Request<B>, Response = Response, Error = Infallible> + Clone + Send + 'static, T: Service<Request<B>, Response = Response, Error = Infallible> + Clone + Send + 'static,
T::Future: Send + 'static, T::Future: Send + 'static,
{ {
if path.is_empty() { validate_path_for_nest(&mut path);
// nesting at `""` and `"/"` should mean the same thing
path = "/";
}
if path.contains('*') {
panic!("Invalid route: nested routes cannot contain wildcards (*)");
}
let prefix = path; let prefix = path;
@@ -301,14 +331,14 @@ where
}; };
let svc = strip_prefix::StripPrefix::new(svc, prefix); 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 // `/*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 // prefix itself. Otherwise if you were to nest at `/foo` then `/foo` itself
// wouldn't match, which it should // wouldn't match, which it should
self = self.route(prefix, svc.clone()); self = self.route_service(prefix, svc.clone());
// same goes for `/foo/`, that should also match // same goes for `/foo/`, that should also match
self = self.route(&format!("{}/", prefix), svc); self = self.route_service(&format!("{}/", prefix), svc);
self self
} }
@@ -335,7 +365,7 @@ where
.expect("no path for route id. This is a bug in axum. Please file an issue"); .expect("no path for route id. This is a bug in axum. Please file an issue");
self = match route { self = match route {
Endpoint::MethodRouter(route) => self.route(path, route), Endpoint::MethodRouter(route) => self.route(path, route),
Endpoint::Route(route) => self.route(path, route), Endpoint::Route(route) => self.route_service(path, route),
}; };
} }
@@ -433,7 +463,17 @@ where
} }
#[doc = include_str!("../docs/routing/fallback.md")] #[doc = include_str!("../docs/routing/fallback.md")]
pub fn fallback<T>(mut self, svc: T) -> Self pub fn fallback<H, T>(mut self, handler: H) -> Self
where
H: Handler<S, T, B>,
T: 'static,
S: Clone + Send + Sync + 'static,
{
self.fallback_service(IntoExtensionService::new(handler))
}
/// TODO(david): docs
pub fn fallback_service<T>(mut self, svc: T) -> Self
where where
T: Service<Request<B>, Response = Response, Error = Infallible> + Clone + Send + 'static, T: Service<Request<B>, Response = Response, Error = Infallible> + Clone + Send + 'static,
T::Future: Send + 'static, T::Future: Send + 'static,
@@ -441,7 +481,13 @@ where
self.fallback = Fallback::Custom(Route::new(svc)); self.fallback = Fallback::Custom(Route::new(svc));
self self
} }
}
impl<S, B> Router<S, B, WithState>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
/// Convert this router into a [`MakeService`], that is a [`Service`] whose /// Convert this router into a [`MakeService`], that is a [`Service`] whose
/// response is another service. /// response is another service.
/// ///
@@ -473,13 +519,7 @@ where
pub fn into_make_service_with_connect_info<C>(self) -> IntoMakeServiceWithConnectInfo<Self, C> { pub fn into_make_service_with_connect_info<C>(self) -> IntoMakeServiceWithConnectInfo<Self, C> {
IntoMakeServiceWithConnectInfo::new(self) IntoMakeServiceWithConnectInfo::new(self)
} }
}
impl<S, B> Router<S, B, WithState>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
#[inline] #[inline]
fn call_route( fn call_route(
&self, &self,
@@ -515,10 +555,6 @@ where
url_params::insert_url_params(req.extensions_mut(), match_.params); url_params::insert_url_params(req.extensions_mut(), match_.params);
// the `unwrap` is safe because `self.state` is always some if `R = WithState`, which it is
req.extensions_mut()
.insert(crate::extract::State(self.state.as_ref().unwrap().clone()));
let mut route = self let mut route = self
.routes .routes
.get(&id) .get(&id)
@@ -560,6 +596,12 @@ where
let path = req.uri().path().to_owned(); let path = req.uri().path().to_owned();
// the `unwrap` is safe because `self.state` is always some if `R = WithState`, which it is
let prev = req
.extensions_mut()
.insert(crate::extract::State(self.state.as_ref().unwrap().clone()));
debug_assert!(prev.is_none());
match self.node.at(&path) { match self.node.at(&path) {
Ok(match_) => self.call_route(match_, req), Ok(match_) => self.call_route(match_, req),
Err( Err(
@@ -580,6 +622,17 @@ pub enum MissingState {}
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum WithState {} pub enum WithState {}
fn validate_path_for_nest(path: &mut &str) {
if path.is_empty() {
// nesting at `""` and `"/"` should mean the same thing
*path = "/";
}
if path.contains('*') {
panic!("Invalid route: nested routes cannot contain wildcards (*)");
}
}
/// Wrapper around `matchit::Router` that supports merging two `Router`s. /// Wrapper around `matchit::Router` that supports merging two `Router`s.
#[derive(Clone, Default)] #[derive(Clone, Default)]
struct Node { struct Node {
@@ -656,12 +709,15 @@ impl<B, E> Fallback<B, E> {
} }
} }
enum Endpoint<B> { enum Endpoint<S, B, R> {
MethodRouter(MethodRouter<B>), MethodRouter(MethodRouter<S, B, Infallible, R>),
Route(Route<B>), Route(Route<B>),
} }
impl<B> Clone for Endpoint<B> { impl<S, B, R> Clone for Endpoint<S, B, R>
where
S: Clone,
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
match self { match self {
Endpoint::MethodRouter(inner) => Endpoint::MethodRouter(inner.clone()), Endpoint::MethodRouter(inner) => Endpoint::MethodRouter(inner.clone()),
@@ -670,7 +726,10 @@ impl<B> Clone for Endpoint<B> {
} }
} }
impl<B> fmt::Debug for Endpoint<B> { impl<S, B, R> fmt::Debug for Endpoint<S, B, R>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::MethodRouter(inner) => inner.fmt(f), Self::MethodRouter(inner) => inner.fmt(f),
+3 -7
View File
@@ -1,11 +1,10 @@
use super::*; use super::*;
use crate::handler::Handler;
#[tokio::test] #[tokio::test]
async fn basic() { async fn basic() {
let app = Router::new() let app = Router::new()
.route("/foo", get(|| async {})) .route("/foo", get(|| async {}))
.fallback((|| async { "fallback" }).into_service()) .fallback(|| async { "fallback" })
.state(()); .state(());
let client = TestClient::new(app); let client = TestClient::new(app);
@@ -21,7 +20,7 @@ async fn basic() {
async fn nest() { async fn nest() {
let app = Router::new() let app = Router::new()
.nest("/foo", Router::new().route("/bar", get(|| async {}))) .nest("/foo", Router::new().route("/bar", get(|| async {})))
.fallback((|| async { "fallback" }).into_service()) .fallback(|| async { "fallback" })
.state(()); .state(());
let client = TestClient::new(app); let client = TestClient::new(app);
@@ -38,10 +37,7 @@ async fn or() {
let one = Router::new().route("/one", get(|| async {})); let one = Router::new().route("/one", get(|| async {}));
let two = Router::new().route("/two", get(|| async {})); let two = Router::new().route("/two", get(|| async {}));
let app = one let app = one.merge(two).fallback(|| async { "fallback" }).state(());
.merge(two)
.fallback((|| async { "fallback" }).into_service())
.state(());
let client = TestClient::new(app); let client = TestClient::new(app);
+13 -6
View File
@@ -147,7 +147,10 @@ async fn routing_between_services() {
}), }),
), ),
) )
.route("/two", on_service(MethodFilter::GET, handle.into_service())); .route(
"/two",
on_service(MethodFilter::GET, handle.into_service(())),
);
let client = TestClient::new(app.state(())); let client = TestClient::new(app.state(()));
@@ -445,7 +448,11 @@ async fn middleware_still_run_for_unmatched_requests() {
expected = "Invalid route: `Router::route` cannot be used with `Router`s. Use `Router::nest` instead" expected = "Invalid route: `Router::route` cannot be used with `Router`s. Use `Router::nest` instead"
)] )]
async fn routing_to_router_panics() { async fn routing_to_router_panics() {
TestClient::new(Router::new().route("/", Router::new().state(())).state(())); TestClient::new(
Router::new()
.route_service("/", Router::new().state(()))
.state(()),
);
} }
#[tokio::test] #[tokio::test]
@@ -520,8 +527,8 @@ async fn different_methods_added_in_different_routes_deeply_nested() {
#[should_panic(expected = "Cannot merge two `Router`s that both have a fallback")] #[should_panic(expected = "Cannot merge two `Router`s that both have a fallback")]
async fn merging_routers_with_fallbacks_panics() { async fn merging_routers_with_fallbacks_panics() {
async fn fallback() {} async fn fallback() {}
let one = Router::new().fallback(fallback.into_service()); let one = Router::new().fallback(fallback);
let two = Router::new().fallback(fallback.into_service()); let two = Router::new().fallback(fallback);
TestClient::new(one.merge(two).state(())); TestClient::new(one.merge(two).state(()));
} }
@@ -529,7 +536,7 @@ async fn merging_routers_with_fallbacks_panics() {
#[should_panic(expected = "Cannot nest `Router`s that has a fallback")] #[should_panic(expected = "Cannot nest `Router`s that has a fallback")]
async fn nesting_router_with_fallbacks_panics() { async fn nesting_router_with_fallbacks_panics() {
async fn fallback() {} async fn fallback() {}
let one = Router::new().fallback(fallback.into_service()); let one = Router::new().fallback(fallback);
let app = Router::new().nest("/", one); let app = Router::new().nest("/", one);
TestClient::new(app.state(())); TestClient::new(app.state(()));
} }
@@ -569,7 +576,7 @@ async fn head_content_length_through_hyper_server() {
#[tokio::test] #[tokio::test]
async fn head_content_length_through_hyper_server_that_hits_fallback() { 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.state(())); let client = TestClient::new(app.state(()));
+16 -10
View File
@@ -114,7 +114,10 @@ async fn nesting_router_at_empty_path() {
#[tokio::test] #[tokio::test]
async fn nesting_handler_at_root() { async fn nesting_handler_at_root() {
let app = Router::new().nest_service("/", get(|uri: Uri| async move { uri.to_string() })); let app = Router::new().nest_service(
"/",
get(|uri: Uri| async move { uri.to_string() }).state(()),
);
let client = TestClient::new(app.state(())); let client = TestClient::new(app.state(()));
@@ -183,7 +186,7 @@ async fn nested_service_sees_stripped_uri() {
"/foo", "/foo",
Router::new().nest( Router::new().nest(
"/bar", "/bar",
Router::new().route( Router::new().route_service(
"/baz", "/baz",
service_fn(|req: Request<Body>| async move { service_fn(|req: Request<Body>| async move {
let body = boxed(Body::from(req.uri().to_string())); let body = boxed(Body::from(req.uri().to_string()));
@@ -204,12 +207,15 @@ async fn nested_service_sees_stripped_uri() {
async fn nest_static_file_server() { async fn nest_static_file_server() {
let app = Router::new().nest_service( let app = Router::new().nest_service(
"/static", "/static",
get_service(ServeDir::new(".")).handle_error(|error| async move { get_service(ServeDir::new("."))
( .handle_error(|error| async move {
StatusCode::INTERNAL_SERVER_ERROR, (
format!("Unhandled internal error: {}", error), StatusCode::INTERNAL_SERVER_ERROR,
) format!("Unhandled internal error: {}", error),
}), )
})
// TODO(david): having to do this on services isn't good
.state(()),
); );
let client = TestClient::new(app.state(())); let client = TestClient::new(app.state(()));
@@ -330,7 +336,7 @@ async fn outer_middleware_still_see_whole_url() {
.route("/foo", get(handler)) .route("/foo", get(handler))
.route("/foo/bar", get(handler)) .route("/foo/bar", get(handler))
.nest("/one", Router::new().route("/two", get(handler))) .nest("/one", Router::new().route("/two", get(handler)))
.fallback(handler.into_service()) .fallback(handler)
.layer(tower::layer::layer_fn(SetUriExtension)); .layer(tower::layer::layer_fn(SetUriExtension));
let client = TestClient::new(app.state(())); let client = TestClient::new(app.state(()));
@@ -366,7 +372,7 @@ async fn nest_at_capture() {
#[tokio::test] #[tokio::test]
async fn nest_with_and_without_trailing() { async fn nest_with_and_without_trailing() {
let app = Router::new().nest_service("/foo", get(|| async {})); let app = Router::new().nest_service("/foo", get(|| async {}).state(()));
let client = TestClient::new(app.state(())); let client = TestClient::new(app.state(()));