diff --git a/axum/src/routing/handler_method_routing.rs b/axum/src/routing/handler_method_routing.rs deleted file mode 100644 index 47c98d44..00000000 --- a/axum/src/routing/handler_method_routing.rs +++ /dev/null @@ -1,464 +0,0 @@ -//! Routing for handlers based on HTTP methods. - -use crate::{ - body::{box_body, BoxBody}, - handler::Handler, - routing::{MethodFilter, MethodNotAllowed}, - util::{Either, EitherProj}, -}; -use futures_util::{future::BoxFuture, ready}; -use http::{Method, Request, Response}; -use http_body::Empty; -use pin_project_lite::pin_project; -use std::{ - convert::Infallible, - fmt, - future::Future, - marker::PhantomData, - pin::Pin, - task::{Context, Poll}, -}; -use tower::{util::Oneshot, ServiceExt}; -use tower_service::Service; - -/// Route requests with any standard HTTP method to the given handler. -/// -/// # Example -/// -/// ```rust -/// use axum::{ -/// routing::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(handler: H) -> MethodRouter -where - H: Handler, -{ - on(MethodFilter::all(), handler) -} - -/// Route `DELETE` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn delete(handler: H) -> MethodRouter -where - H: Handler, -{ - on(MethodFilter::DELETE, handler) -} - -/// Route `GET` requests to the given handler. -/// -/// # Example -/// -/// ```rust -/// use axum::{ -/// routing::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(handler: H) -> MethodRouter -where - H: Handler, -{ - on(MethodFilter::GET | MethodFilter::HEAD, handler) -} - -/// Route `HEAD` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn head(handler: H) -> MethodRouter -where - H: Handler, -{ - on(MethodFilter::HEAD, handler) -} - -/// Route `OPTIONS` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn options(handler: H) -> MethodRouter -where - H: Handler, -{ - on(MethodFilter::OPTIONS, handler) -} - -/// Route `PATCH` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn patch(handler: H) -> MethodRouter -where - H: Handler, -{ - on(MethodFilter::PATCH, handler) -} - -/// Route `POST` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn post(handler: H) -> MethodRouter -where - H: Handler, -{ - on(MethodFilter::POST, handler) -} - -/// Route `PUT` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn put(handler: H) -> MethodRouter -where - H: Handler, -{ - on(MethodFilter::PUT, handler) -} - -/// Route `TRACE` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn trace(handler: H) -> MethodRouter -where - H: Handler, -{ - on(MethodFilter::TRACE, handler) -} - -/// Route requests with the given method to the handler. -/// -/// # Example -/// -/// ```rust -/// use axum::{ -/// routing::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(method: MethodFilter, handler: H) -> MethodRouter -where - H: Handler, -{ - MethodRouter { - method, - handler, - fallback: MethodNotAllowed::new(), - _marker: PhantomData, - } -} - -/// A handler [`Service`] that accepts requests based on a [`MethodFilter`] and -/// allows chaining additional handlers. -pub struct MethodRouter { - pub(crate) method: MethodFilter, - pub(crate) handler: H, - pub(crate) fallback: F, - pub(crate) _marker: PhantomData (B, T)>, -} - -impl fmt::Debug for MethodRouter -where - T: fmt::Debug, - F: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MethodRouter") - .field("method", &self.method) - .field("handler", &format_args!("{}", std::any::type_name::())) - .field("fallback", &self.fallback) - .finish() - } -} - -impl Clone for MethodRouter -where - H: Clone, - F: Clone, -{ - fn clone(&self) -> Self { - Self { - method: self.method, - handler: self.handler.clone(), - fallback: self.fallback.clone(), - _marker: PhantomData, - } - } -} - -impl Copy for MethodRouter -where - H: Copy, - F: Copy, -{ -} - -impl MethodRouter { - /// Chain an additional handler that will accept all requests regardless of - /// its HTTP method. - /// - /// See [`MethodRouter::get`] for an example. - pub fn any(self, handler: H2) -> MethodRouter - where - H2: Handler, - { - self.on(MethodFilter::all(), handler) - } - - /// Chain an additional handler that will only accept `DELETE` requests. - /// - /// See [`MethodRouter::get`] for an example. - pub fn delete(self, handler: H2) -> MethodRouter - where - H2: Handler, - { - self.on(MethodFilter::DELETE, handler) - } - - /// Chain an additional handler that will only accept `GET` requests. - /// - /// # Example - /// - /// ```rust - /// use axum::{routing::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(self, handler: H2) -> MethodRouter - where - H2: Handler, - { - self.on(MethodFilter::GET | MethodFilter::HEAD, handler) - } - - /// Chain an additional handler that will only accept `HEAD` requests. - /// - /// See [`MethodRouter::get`] for an example. - pub fn head(self, handler: H2) -> MethodRouter - where - H2: Handler, - { - self.on(MethodFilter::HEAD, handler) - } - - /// Chain an additional handler that will only accept `OPTIONS` requests. - /// - /// See [`MethodRouter::get`] for an example. - pub fn options(self, handler: H2) -> MethodRouter - where - H2: Handler, - { - self.on(MethodFilter::OPTIONS, handler) - } - - /// Chain an additional handler that will only accept `PATCH` requests. - /// - /// See [`MethodRouter::get`] for an example. - pub fn patch(self, handler: H2) -> MethodRouter - where - H2: Handler, - { - self.on(MethodFilter::PATCH, handler) - } - - /// Chain an additional handler that will only accept `POST` requests. - /// - /// See [`MethodRouter::get`] for an example. - pub fn post(self, handler: H2) -> MethodRouter - where - H2: Handler, - { - self.on(MethodFilter::POST, handler) - } - - /// Chain an additional handler that will only accept `PUT` requests. - /// - /// See [`MethodRouter::get`] for an example. - pub fn put(self, handler: H2) -> MethodRouter - where - H2: Handler, - { - self.on(MethodFilter::PUT, handler) - } - - /// Chain an additional handler that will only accept `TRACE` requests. - /// - /// See [`MethodRouter::get`] for an example. - pub fn trace(self, handler: H2) -> MethodRouter - where - H2: Handler, - { - self.on(MethodFilter::TRACE, handler) - } - - /// Chain an additional handler that will accept requests matching the given - /// `MethodFilter`. - /// - /// # Example - /// - /// ```rust - /// use axum::{ - /// routing::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(self, method: MethodFilter, handler: H2) -> MethodRouter - where - H2: Handler, - { - MethodRouter { - method, - handler, - fallback: self, - _marker: PhantomData, - } - } -} - -impl Service> for MethodRouter -where - H: Handler, - F: Service, Response = Response, Error = Infallible> + Clone, - B: Send + 'static, -{ - type Response = Response; - type Error = Infallible; - type Future = MethodRouterFuture; - - #[inline] - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Request) -> 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 } - }; - - MethodRouterFuture { - inner: fut, - req_method, - } - } -} - -pin_project! { - /// The response future for [`MethodRouter`]. - pub struct MethodRouterFuture - where - F: Service> - { - #[pin] - pub(super) inner: Either< - BoxFuture<'static, Response>, - Oneshot>, - >, - pub(super) req_method: Method, - } -} - -impl Future for MethodRouterFuture -where - F: Service, Response = Response>, -{ - type Output = Result, F::Error>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - 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 fmt::Debug for MethodRouterFuture -where - F: Service>, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MethodRouterFuture").finish() - } -}