Reorganize method routers for handlers and services (#405)

* Re-organize method routing for handlers

* Re-organize method routing for services

* changelog
This commit is contained in:
David Pedersen
2021-10-24 20:05:16 +00:00
committed by GitHub
parent 0ee7379d4f
commit 7692baf837
57 changed files with 743 additions and 742 deletions
+4 -64
View File
@@ -1,69 +1,9 @@
//! Handler future types.
use crate::body::{box_body, BoxBody};
use crate::util::{Either, EitherProj};
use futures_util::{
future::{BoxFuture, Map},
ready,
};
use http::{Method, Request, Response};
use http_body::Empty;
use pin_project_lite::pin_project;
use std::{
convert::Infallible,
fmt,
future::Future,
pin::Pin,
task::{Context, Poll},
};
use tower::util::Oneshot;
use tower_service::Service;
pin_project! {
/// The response future for [`OnMethod`](super::OnMethod).
pub struct OnMethodFuture<F, B>
where
F: Service<Request<B>>
{
#[pin]
pub(super) inner: Either<
BoxFuture<'static, Response<BoxBody>>,
Oneshot<F, Request<B>>,
>,
pub(super) req_method: Method,
}
}
impl<F, B> Future for OnMethodFuture<F, B>
where
F: Service<Request<B>, Response = Response<BoxBody>>,
{
type Output = Result<Response<BoxBody>, F::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let response = match this.inner.project() {
EitherProj::A { inner } => ready!(inner.poll(cx)),
EitherProj::B { inner } => ready!(inner.poll(cx))?,
};
if this.req_method == &Method::HEAD {
let response = response.map(|_| box_body(Empty::new()));
Poll::Ready(Ok(response))
} else {
Poll::Ready(Ok(response))
}
}
}
impl<F, B> fmt::Debug for OnMethodFuture<F, B>
where
F: Service<Request<B>>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OnMethodFuture").finish()
}
}
use crate::body::BoxBody;
use futures_util::future::{BoxFuture, Map};
use http::Response;
use std::convert::Infallible;
opaque_future! {
/// The response future for [`IntoService`](super::IntoService).
+8 -429
View File
@@ -4,20 +4,13 @@ use crate::{
body::{box_body, BoxBody},
extract::{FromRequest, RequestParts},
response::IntoResponse,
routing::{EmptyRouter, MethodFilter},
util::Either,
routing::{EmptyRouter, MethodRouter},
BoxError,
};
use async_trait::async_trait;
use bytes::Bytes;
use http::{Request, Response};
use std::{
convert::Infallible,
fmt,
future::Future,
marker::PhantomData,
task::{Context, Poll},
};
use std::{fmt, future::Future, marker::PhantomData};
use tower::ServiceExt;
use tower_layer::Layer;
use tower_service::Service;
@@ -27,187 +20,6 @@ mod into_service;
pub use self::into_service::IntoService;
/// Route requests with any standard HTTP method to the given handler.
///
/// # Example
///
/// ```rust
/// use axum::{
/// handler::any,
/// Router,
/// };
///
/// async fn handler() {}
///
/// let app = Router::new().route("/", any(handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// Note that this only accepts the standard HTTP methods. If you need to
/// support non-standard methods use [`Handler::into_service`]:
///
/// ```rust
/// use axum::{
/// handler::Handler,
/// Router,
/// };
///
/// async fn handler() {}
///
/// let app = Router::new().route("/", handler.into_service());
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn any<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
on(MethodFilter::all(), handler)
}
/// Route `CONNECT` requests to the given handler.
///
/// See [`get`] for an example.
pub fn connect<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
on(MethodFilter::CONNECT, handler)
}
/// Route `DELETE` requests to the given handler.
///
/// See [`get`] for an example.
pub fn delete<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
on(MethodFilter::DELETE, handler)
}
/// Route `GET` requests to the given handler.
///
/// # Example
///
/// ```rust
/// use axum::{
/// handler::get,
/// Router,
/// };
///
/// async fn handler() {}
///
/// // Requests to `GET /` will go to `handler`.
/// let app = Router::new().route("/", get(handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// Note that `get` routes will also be called for `HEAD` requests but will have
/// the response body removed. Make sure to add explicit `HEAD` routes
/// afterwards.
pub fn get<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
on(MethodFilter::GET | MethodFilter::HEAD, handler)
}
/// Route `HEAD` requests to the given handler.
///
/// See [`get`] for an example.
pub fn head<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
on(MethodFilter::HEAD, handler)
}
/// Route `OPTIONS` requests to the given handler.
///
/// See [`get`] for an example.
pub fn options<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
on(MethodFilter::OPTIONS, handler)
}
/// Route `PATCH` requests to the given handler.
///
/// See [`get`] for an example.
pub fn patch<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
on(MethodFilter::PATCH, handler)
}
/// Route `POST` requests to the given handler.
///
/// See [`get`] for an example.
pub fn post<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
on(MethodFilter::POST, handler)
}
/// Route `PUT` requests to the given handler.
///
/// See [`get`] for an example.
pub fn put<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
on(MethodFilter::PUT, handler)
}
/// Route `TRACE` requests to the given handler.
///
/// See [`get`] for an example.
pub fn trace<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
on(MethodFilter::TRACE, handler)
}
/// Route requests with the given method to the handler.
///
/// # Example
///
/// ```rust
/// use axum::{
/// handler::on,
/// Router,
/// routing::MethodFilter,
/// };
///
/// async fn handler() {}
///
/// // Requests to `POST /` will go to `handler`.
/// let app = Router::new().route("/", on(MethodFilter::POST, handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn on<H, B, T>(method: MethodFilter, handler: H) -> OnMethod<H, B, T, EmptyRouter>
where
H: Handler<B, T>,
{
OnMethod {
method,
handler,
fallback: EmptyRouter::method_not_allowed(),
_marker: PhantomData,
}
}
pub(crate) mod sealed {
#![allow(unreachable_pub, missing_docs, missing_debug_implementations)]
@@ -250,7 +62,8 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
///
/// ```rust
/// use axum::{
/// handler::{get, Handler},
/// routing::get,
/// handler::Handler,
/// Router,
/// };
/// use tower::limit::{ConcurrencyLimitLayer, ConcurrencyLimit};
@@ -265,9 +78,9 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
/// ```
fn layer<L>(self, layer: L) -> Layered<L::Service, T>
where
L: Layer<OnMethod<Self, B, T, EmptyRouter>>,
L: Layer<MethodRouter<Self, B, T, EmptyRouter>>,
{
Layered::new(layer.layer(any(self)))
Layered::new(layer.layer(crate::routing::any(self)))
}
/// Convert the handler into a [`Service`].
@@ -424,243 +237,9 @@ impl<S, T> Layered<S, T> {
}
}
/// A handler [`Service`] that accepts requests based on a [`MethodFilter`] and
/// allows chaining additional handlers.
pub struct OnMethod<H, B, T, F> {
pub(crate) method: MethodFilter,
pub(crate) handler: H,
pub(crate) fallback: F,
pub(crate) _marker: PhantomData<fn() -> (B, T)>,
}
#[test]
fn traits() {
use crate::tests::*;
assert_send::<OnMethod<(), NotSendSync, NotSendSync, ()>>();
assert_sync::<OnMethod<(), NotSendSync, NotSendSync, ()>>();
}
impl<H, B, T, F> fmt::Debug for OnMethod<H, B, T, F>
where
T: fmt::Debug,
F: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OnMethod")
.field("method", &self.method)
.field("handler", &format_args!("{}", std::any::type_name::<H>()))
.field("fallback", &self.fallback)
.finish()
}
}
impl<H, B, T, F> Clone for OnMethod<H, B, T, F>
where
H: Clone,
F: Clone,
{
fn clone(&self) -> Self {
Self {
method: self.method,
handler: self.handler.clone(),
fallback: self.fallback.clone(),
_marker: PhantomData,
}
}
}
impl<H, B, T, F> Copy for OnMethod<H, B, T, F>
where
H: Copy,
F: Copy,
{
}
impl<H, B, T, F> OnMethod<H, B, T, F> {
/// Chain an additional handler that will accept all requests regardless of
/// its HTTP method.
///
/// See [`OnMethod::get`] for an example.
pub fn any<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
self.on(MethodFilter::all(), handler)
}
/// Chain an additional handler that will only accept `CONNECT` requests.
///
/// See [`OnMethod::get`] for an example.
pub fn connect<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
self.on(MethodFilter::CONNECT, handler)
}
/// Chain an additional handler that will only accept `DELETE` requests.
///
/// See [`OnMethod::get`] for an example.
pub fn delete<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
self.on(MethodFilter::DELETE, handler)
}
/// Chain an additional handler that will only accept `GET` requests.
///
/// # Example
///
/// ```rust
/// use axum::{handler::post, Router};
///
/// async fn handler() {}
///
/// async fn other_handler() {}
///
/// // Requests to `GET /` will go to `handler` and `POST /` will go to
/// // `other_handler`.
/// let app = Router::new().route("/", post(handler).get(other_handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// Note that `get` routes will also be called for `HEAD` requests but will have
/// the response body removed. Make sure to add explicit `HEAD` routes
/// afterwards.
pub fn get<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
self.on(MethodFilter::GET | MethodFilter::HEAD, handler)
}
/// Chain an additional handler that will only accept `HEAD` requests.
///
/// See [`OnMethod::get`] for an example.
pub fn head<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
self.on(MethodFilter::HEAD, handler)
}
/// Chain an additional handler that will only accept `OPTIONS` requests.
///
/// See [`OnMethod::get`] for an example.
pub fn options<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
self.on(MethodFilter::OPTIONS, handler)
}
/// Chain an additional handler that will only accept `PATCH` requests.
///
/// See [`OnMethod::get`] for an example.
pub fn patch<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
self.on(MethodFilter::PATCH, handler)
}
/// Chain an additional handler that will only accept `POST` requests.
///
/// See [`OnMethod::get`] for an example.
pub fn post<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
self.on(MethodFilter::POST, handler)
}
/// Chain an additional handler that will only accept `PUT` requests.
///
/// See [`OnMethod::get`] for an example.
pub fn put<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
self.on(MethodFilter::PUT, handler)
}
/// Chain an additional handler that will only accept `TRACE` requests.
///
/// See [`OnMethod::get`] for an example.
pub fn trace<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
self.on(MethodFilter::TRACE, handler)
}
/// Chain an additional handler that will accept requests matching the given
/// `MethodFilter`.
///
/// # Example
///
/// ```rust
/// use axum::{
/// handler::get,
/// Router,
/// routing::MethodFilter
/// };
///
/// async fn handler() {}
///
/// async fn other_handler() {}
///
/// // Requests to `GET /` will go to `handler` and `DELETE /` will go to
/// // `other_handler`
/// let app = Router::new().route("/", get(handler).on(MethodFilter::DELETE, other_handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn on<H2, T2>(self, method: MethodFilter, handler: H2) -> OnMethod<H2, B, T2, Self>
where
H2: Handler<B, T2>,
{
OnMethod {
method,
handler,
fallback: self,
_marker: PhantomData,
}
}
}
impl<H, B, T, F> Service<Request<B>> for OnMethod<H, B, T, F>
where
H: Handler<B, T>,
F: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible> + Clone,
B: Send + 'static,
{
type Response = Response<BoxBody>;
type Error = Infallible;
type Future = future::OnMethodFuture<F, B>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<B>) -> Self::Future {
let req_method = req.method().clone();
let fut = if self.method.matches(req.method()) {
let fut = Handler::call(self.handler.clone(), req);
Either::A { inner: fut }
} else {
let fut = self.fallback.clone().oneshot(req);
Either::B { inner: fut }
};
future::OnMethodFuture {
inner: fut,
req_method,
}
}
assert_send::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
assert_sync::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
}