From 7692baf83728775afbb7ed7f1a178d741ca74c40 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 24 Oct 2021 22:05:16 +0200 Subject: [PATCH] Reorganize method routers for handlers and services (#405) * Re-organize method routing for handlers * Re-organize method routing for services * changelog --- CHANGELOG.md | 13 +- examples/async-graphql/src/main.rs | 2 +- examples/chat/src/main.rs | 2 +- .../customize-extractor-error/src/main.rs | 2 +- .../src/main.rs | 2 +- examples/form/src/main.rs | 2 +- examples/global-404-handler/src/main.rs | 3 +- examples/graceful_shutdown/src/main.rs | 2 +- examples/hello-world/src/main.rs | 2 +- examples/jwt/src/main.rs | 2 +- examples/key-value-store/src/main.rs | 3 +- examples/low-level-rustls/src/main.rs | 2 +- examples/multipart-form/src/main.rs | 2 +- examples/oauth/src/main.rs | 2 +- examples/print-request-response/src/main.rs | 2 +- examples/reverse-proxy/src/main.rs | 2 +- examples/sessions/src/main.rs | 2 +- examples/sse/src/main.rs | 19 +- examples/static-file-server/src/main.rs | 5 +- examples/templates/src/main.rs | 2 +- examples/testing/src/main.rs | 2 +- examples/todos/src/main.rs | 2 +- examples/tokio-postgres/src/main.rs | 2 +- examples/tracing-aka-logging/src/main.rs | 2 +- examples/unix-domain-socket/src/main.rs | 2 +- examples/validator/src/main.rs | 2 +- examples/versioning/src/main.rs | 2 +- examples/websockets/src/main.rs | 4 +- src/body/stream_body.rs | 2 +- src/extract/connect_info.rs | 2 +- src/extract/content_length_limit.rs | 2 +- src/extract/extension.rs | 2 +- src/extract/extractor_middleware.rs | 2 +- src/extract/form.rs | 2 +- src/extract/mod.rs | 7 +- src/extract/multipart.rs | 2 +- src/extract/path/mod.rs | 10 +- src/extract/query.rs | 2 +- src/extract/raw_query.rs | 2 +- src/extract/request_parts.rs | 8 +- src/extract/typed_header.rs | 4 +- src/extract/ws.rs | 6 +- src/handler/future.rs | 68 +-- src/handler/mod.rs | 437 +--------------- src/json.rs | 4 +- src/lib.rs | 56 +- src/response/headers.rs | 2 +- src/response/mod.rs | 4 +- src/response/redirect.rs | 2 +- src/response/sse.rs | 2 +- src/routing/handler_method_router.rs | 485 ++++++++++++++++++ src/routing/mod.rs | 37 +- .../service_method_router.rs} | 168 +++--- src/service/future.rs | 62 --- src/tests/get_to_head.rs | 2 +- src/tests/mod.rs | 6 +- src/tests/or.rs | 6 +- 57 files changed, 743 insertions(+), 742 deletions(-) create mode 100644 src/routing/handler_method_router.rs rename src/{service/mod.rs => routing/service_method_router.rs} (74%) delete mode 100644 src/service/future.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f62dd2d5..ff8a18bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ```rust,no_run use axum::{ - handler::get, + routing::get, http::StatusCode, error_handling::HandleErrorLayer, response::IntoResponse, @@ -89,7 +89,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 use axum::{ Router, service, body::Body, - handler::get, + routing::service_method_router::get, response::IntoResponse, http::{Request, Response}, error_handling::HandleErrorExt, // for `.handle_error` @@ -100,7 +100,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 let app = Router::new() .route( "/", - service::get(service_fn(|_req: Request| async { + get(service_fn(|_req: Request| async { let contents = tokio::fs::read_to_string("some_file").await?; Ok::<_, io::Error>(Response::new(Body::from(contents))) })) @@ -111,6 +111,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 // ... } ``` +- **breaking:** Method routing for handlers have been moved from `axum::handler` + to `axum::routing`. So `axum::handler::get` now lives at `axum::routing::get` + ([#405]) +- **breaking:** Method routing for services have been moved from `axum::service` + to `axum::routing`. So `axum::service::get` now lives at + `axum::service_method_router::get` ([#405]) [#339]: https://github.com/tokio-rs/axum/pull/339 [#286]: https://github.com/tokio-rs/axum/pull/286 @@ -120,6 +126,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#396]: https://github.com/tokio-rs/axum/pull/396 [#402]: https://github.com/tokio-rs/axum/pull/402 [#404]: https://github.com/tokio-rs/axum/pull/404 +[#405]: https://github.com/tokio-rs/axum/pull/405 # 0.2.8 (07. October, 2021) diff --git a/examples/async-graphql/src/main.rs b/examples/async-graphql/src/main.rs index e06b9438..d6d9aa8c 100644 --- a/examples/async-graphql/src/main.rs +++ b/examples/async-graphql/src/main.rs @@ -3,7 +3,7 @@ mod starwars; use async_graphql::http::{playground_source, GraphQLPlaygroundConfig}; use async_graphql::{EmptyMutation, EmptySubscription, Request, Response, Schema}; use axum::response::IntoResponse; -use axum::{extract::Extension, handler::get, response::Html, AddExtensionLayer, Json, Router}; +use axum::{extract::Extension, response::Html, routing::get, AddExtensionLayer, Json, Router}; use starwars::{QueryRoot, StarWars, StarWarsSchema}; async fn graphql_handler(schema: Extension, req: Json) -> Json { diff --git a/examples/chat/src/main.rs b/examples/chat/src/main.rs index 0e35137f..68f0d7f2 100644 --- a/examples/chat/src/main.rs +++ b/examples/chat/src/main.rs @@ -11,8 +11,8 @@ use axum::{ ws::{Message, WebSocket, WebSocketUpgrade}, Extension, }, - handler::get, response::{Html, IntoResponse}, + routing::get, AddExtensionLayer, Router, }; use futures::{sink::SinkExt, stream::StreamExt}; diff --git a/examples/customize-extractor-error/src/main.rs b/examples/customize-extractor-error/src/main.rs index d7222e00..4b682faf 100644 --- a/examples/customize-extractor-error/src/main.rs +++ b/examples/customize-extractor-error/src/main.rs @@ -8,8 +8,8 @@ use axum::{ async_trait, extract::rejection::JsonRejection, extract::{FromRequest, RequestParts}, - handler::post, http::StatusCode, + routing::post, BoxError, Router, }; use serde::{de::DeserializeOwned, Deserialize}; diff --git a/examples/error-handling-and-dependency-injection/src/main.rs b/examples/error-handling-and-dependency-injection/src/main.rs index 4654564b..c55e385d 100644 --- a/examples/error-handling-and-dependency-injection/src/main.rs +++ b/examples/error-handling-and-dependency-injection/src/main.rs @@ -11,9 +11,9 @@ use axum::{ async_trait, body::{Bytes, Full}, extract::{Extension, Path}, - handler::{get, post}, http::{Response, StatusCode}, response::IntoResponse, + routing::{get, post}, AddExtensionLayer, Json, Router, }; use serde::{Deserialize, Serialize}; diff --git a/examples/form/src/main.rs b/examples/form/src/main.rs index 42b9643d..4061e52c 100644 --- a/examples/form/src/main.rs +++ b/examples/form/src/main.rs @@ -4,7 +4,7 @@ //! cargo run -p example-form //! ``` -use axum::{extract::Form, handler::get, response::Html, Router}; +use axum::{extract::Form, response::Html, routing::get, Router}; use serde::Deserialize; use std::net::SocketAddr; diff --git a/examples/global-404-handler/src/main.rs b/examples/global-404-handler/src/main.rs index f7464718..ca7e34d1 100644 --- a/examples/global-404-handler/src/main.rs +++ b/examples/global-404-handler/src/main.rs @@ -5,9 +5,10 @@ //! ``` use axum::{ - handler::{get, Handler}, + handler::Handler, http::StatusCode, response::{Html, IntoResponse}, + routing::get, Router, }; use std::net::SocketAddr; diff --git a/examples/graceful_shutdown/src/main.rs b/examples/graceful_shutdown/src/main.rs index efdb7a15..dd20f19c 100644 --- a/examples/graceful_shutdown/src/main.rs +++ b/examples/graceful_shutdown/src/main.rs @@ -5,7 +5,7 @@ //! kill or ctrl-c //! ``` -use axum::{handler::get, response::Html, Router}; +use axum::{response::Html, routing::get, Router}; use std::net::SocketAddr; #[tokio::main] diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index de069fa1..466caceb 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -4,7 +4,7 @@ //! cargo run -p example-hello-world //! ``` -use axum::{handler::get, response::Html, Router}; +use axum::{response::Html, routing::get, Router}; use std::net::SocketAddr; #[tokio::main] diff --git a/examples/jwt/src/main.rs b/examples/jwt/src/main.rs index e6bfee29..1a6e7681 100644 --- a/examples/jwt/src/main.rs +++ b/examples/jwt/src/main.rs @@ -10,9 +10,9 @@ use axum::{ async_trait, body::{Bytes, Full}, extract::{FromRequest, RequestParts, TypedHeader}, - handler::{get, post}, http::{Response, StatusCode}, response::IntoResponse, + routing::{get, post}, Json, Router, }; use headers::{authorization::Bearer, Authorization}; diff --git a/examples/key-value-store/src/main.rs b/examples/key-value-store/src/main.rs index c0ddba48..5745dd87 100644 --- a/examples/key-value-store/src/main.rs +++ b/examples/key-value-store/src/main.rs @@ -10,9 +10,10 @@ use axum::{ body::Bytes, error_handling::HandleErrorLayer, extract::{ContentLengthLimit, Extension, Path}, - handler::{delete, get, Handler}, + handler::Handler, http::StatusCode, response::IntoResponse, + routing::{delete, get}, Router, }; use std::{ diff --git a/examples/low-level-rustls/src/main.rs b/examples/low-level-rustls/src/main.rs index b82e6d56..60bda1d6 100644 --- a/examples/low-level-rustls/src/main.rs +++ b/examples/low-level-rustls/src/main.rs @@ -4,7 +4,7 @@ //! cargo run -p example-low-level-rustls //! ``` -use axum::{handler::get, Router}; +use axum::{routing::get, Router}; use hyper::server::conn::Http; use std::{fs::File, io::BufReader, sync::Arc}; use tokio::net::TcpListener; diff --git a/examples/multipart-form/src/main.rs b/examples/multipart-form/src/main.rs index 9c60ee3a..b734e65a 100644 --- a/examples/multipart-form/src/main.rs +++ b/examples/multipart-form/src/main.rs @@ -6,8 +6,8 @@ use axum::{ extract::{ContentLengthLimit, Multipart}, - handler::get, response::Html, + routing::get, Router, }; use std::net::SocketAddr; diff --git a/examples/oauth/src/main.rs b/examples/oauth/src/main.rs index 90ae2f14..51da6053 100644 --- a/examples/oauth/src/main.rs +++ b/examples/oauth/src/main.rs @@ -11,9 +11,9 @@ use axum::{ async_trait, body::{Bytes, Empty}, extract::{Extension, FromRequest, Query, RequestParts, TypedHeader}, - handler::get, http::{header::SET_COOKIE, HeaderMap, Response}, response::{IntoResponse, Redirect}, + routing::get, AddExtensionLayer, Router, }; use oauth2::{ diff --git a/examples/print-request-response/src/main.rs b/examples/print-request-response/src/main.rs index 023e3c69..6c66eebf 100644 --- a/examples/print-request-response/src/main.rs +++ b/examples/print-request-response/src/main.rs @@ -7,8 +7,8 @@ use axum::{ body::{Body, BoxBody, Bytes}, error_handling::HandleErrorLayer, - handler::post, http::{Request, Response, StatusCode}, + routing::post, Router, }; use std::net::SocketAddr; diff --git a/examples/reverse-proxy/src/main.rs b/examples/reverse-proxy/src/main.rs index 5ea93744..aefe1118 100644 --- a/examples/reverse-proxy/src/main.rs +++ b/examples/reverse-proxy/src/main.rs @@ -9,8 +9,8 @@ use axum::{ extract::Extension, - handler::get, http::{uri::Uri, Request, Response}, + routing::get, AddExtensionLayer, Router, }; use hyper::{client::HttpConnector, Body}; diff --git a/examples/sessions/src/main.rs b/examples/sessions/src/main.rs index db4dcbda..f16e800d 100644 --- a/examples/sessions/src/main.rs +++ b/examples/sessions/src/main.rs @@ -8,13 +8,13 @@ use async_session::{MemoryStore, Session, SessionStore as _}; use axum::{ async_trait, extract::{Extension, FromRequest, RequestParts}, - handler::get, http::{ self, header::{HeaderMap, HeaderValue}, StatusCode, }, response::IntoResponse, + routing::get, AddExtensionLayer, Router, }; use serde::{Deserialize, Serialize}; diff --git a/examples/sse/src/main.rs b/examples/sse/src/main.rs index 406ad4c0..a9eb4129 100644 --- a/examples/sse/src/main.rs +++ b/examples/sse/src/main.rs @@ -7,9 +7,9 @@ use axum::{ error_handling::HandleErrorExt, extract::TypedHeader, - handler::get, http::StatusCode, response::sse::{Event, Sse}, + routing::{get, service_method_router as service}, Router, }; use futures::stream::{self, Stream}; @@ -25,15 +25,14 @@ async fn main() { } tracing_subscriber::fmt::init(); - let static_files_service = axum::service::get( - ServeDir::new("examples/sse/assets").append_index_html_on_directories(true), - ) - .handle_error(|error: std::io::Error| { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Unhandled internal error: {}", error), - ) - }); + let static_files_service = + service::get(ServeDir::new("examples/sse/assets").append_index_html_on_directories(true)) + .handle_error(|error: std::io::Error| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Unhandled internal error: {}", error), + ) + }); // build our application with a route let app = Router::new() diff --git a/examples/static-file-server/src/main.rs b/examples/static-file-server/src/main.rs index ec449788..724b6e66 100644 --- a/examples/static-file-server/src/main.rs +++ b/examples/static-file-server/src/main.rs @@ -4,7 +4,10 @@ //! cargo run -p example-static-file-server //! ``` -use axum::{error_handling::HandleErrorExt, http::StatusCode, service, Router}; +use axum::{ + error_handling::HandleErrorExt, http::StatusCode, routing::service_method_router as service, + Router, +}; use std::net::SocketAddr; use tower_http::{services::ServeDir, trace::TraceLayer}; diff --git a/examples/templates/src/main.rs b/examples/templates/src/main.rs index 685efa0d..a014722f 100644 --- a/examples/templates/src/main.rs +++ b/examples/templates/src/main.rs @@ -8,9 +8,9 @@ use askama::Template; use axum::{ body::{Bytes, Full}, extract, - handler::get, http::{Response, StatusCode}, response::{Html, IntoResponse}, + routing::get, Router, }; use std::{convert::Infallible, net::SocketAddr}; diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 54eddb58..02c459ce 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -5,7 +5,7 @@ //! ``` use axum::{ - handler::{get, post}, + routing::{get, post}, Json, Router, }; use tower_http::trace::TraceLayer; diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 5e4ac543..719e78a2 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -16,9 +16,9 @@ use axum::{ error_handling::HandleErrorLayer, extract::{Extension, Path, Query}, - handler::{get, patch}, http::StatusCode, response::IntoResponse, + routing::{get, patch}, Json, Router, }; use serde::{Deserialize, Serialize}; diff --git a/examples/tokio-postgres/src/main.rs b/examples/tokio-postgres/src/main.rs index 6ea48030..f37de8bc 100644 --- a/examples/tokio-postgres/src/main.rs +++ b/examples/tokio-postgres/src/main.rs @@ -7,8 +7,8 @@ use axum::{ async_trait, extract::{Extension, FromRequest, RequestParts}, - handler::get, http::StatusCode, + routing::get, AddExtensionLayer, Router, }; use bb8::{Pool, PooledConnection}; diff --git a/examples/tracing-aka-logging/src/main.rs b/examples/tracing-aka-logging/src/main.rs index b798b553..863e686d 100644 --- a/examples/tracing-aka-logging/src/main.rs +++ b/examples/tracing-aka-logging/src/main.rs @@ -6,9 +6,9 @@ use axum::{ body::Bytes, - handler::get, http::{HeaderMap, Request, Response}, response::Html, + routing::get, Router, }; use std::{net::SocketAddr, time::Duration}; diff --git a/examples/unix-domain-socket/src/main.rs b/examples/unix-domain-socket/src/main.rs index a1ebff1d..ff224490 100644 --- a/examples/unix-domain-socket/src/main.rs +++ b/examples/unix-domain-socket/src/main.rs @@ -7,8 +7,8 @@ use axum::{ body::Body, extract::connect_info::{self, ConnectInfo}, - handler::get, http::{Method, Request, StatusCode, Uri}, + routing::get, Router, }; use futures::ready; diff --git a/examples/validator/src/main.rs b/examples/validator/src/main.rs index e49e6dbd..a1ddebca 100644 --- a/examples/validator/src/main.rs +++ b/examples/validator/src/main.rs @@ -14,9 +14,9 @@ use async_trait::async_trait; use axum::{ body::{Bytes, Full}, extract::{Form, FromRequest, RequestParts}, - handler::get, http::{Response, StatusCode}, response::{Html, IntoResponse}, + routing::get, BoxError, Router, }; use serde::{de::DeserializeOwned, Deserialize}; diff --git a/examples/versioning/src/main.rs b/examples/versioning/src/main.rs index 3a78a299..399ba210 100644 --- a/examples/versioning/src/main.rs +++ b/examples/versioning/src/main.rs @@ -8,9 +8,9 @@ use axum::{ async_trait, body::{Bytes, Full}, extract::{FromRequest, Path, RequestParts}, - handler::get, http::{Response, StatusCode}, response::IntoResponse, + routing::get, Router, }; use std::collections::HashMap; diff --git a/examples/websockets/src/main.rs b/examples/websockets/src/main.rs index fbde0803..12fb6b6e 100644 --- a/examples/websockets/src/main.rs +++ b/examples/websockets/src/main.rs @@ -12,9 +12,9 @@ use axum::{ ws::{Message, WebSocket, WebSocketUpgrade}, TypedHeader, }, - handler::get, http::StatusCode, response::IntoResponse, + routing::{get, service_method_router as service}, Router, }; use std::net::SocketAddr; @@ -35,7 +35,7 @@ async fn main() { let app = Router::new() .nest( "/", - axum::service::get( + service::get( ServeDir::new("examples/websockets/assets").append_index_html_on_directories(true), ) .handle_error(|error: std::io::Error| { diff --git a/src/body/stream_body.rs b/src/body/stream_body.rs index d60af154..b9e360d7 100644 --- a/src/body/stream_body.rs +++ b/src/body/stream_body.rs @@ -26,7 +26,7 @@ pin_project! { /// ``` /// use axum::{ /// Router, - /// handler::get, + /// routing::get, /// body::StreamBody, /// response::IntoResponse, /// }; diff --git a/src/extract/connect_info.rs b/src/extract/connect_info.rs index bcd97344..ba51a9cc 100644 --- a/src/extract/connect_info.rs +++ b/src/extract/connect_info.rs @@ -134,7 +134,7 @@ where mod tests { use super::*; use crate::Server; - use crate::{handler::get, Router}; + use crate::{routing::get, Router}; use std::net::{SocketAddr, TcpListener}; #[tokio::test] diff --git a/src/extract/content_length_limit.rs b/src/extract/content_length_limit.rs index 3313ac03..fc8cb8f4 100644 --- a/src/extract/content_length_limit.rs +++ b/src/extract/content_length_limit.rs @@ -11,7 +11,7 @@ use std::ops::Deref; /// ```rust,no_run /// use axum::{ /// extract::ContentLengthLimit, -/// handler::post, +/// routing::post, /// Router, /// }; /// diff --git a/src/extract/extension.rs b/src/extract/extension.rs index 7e6765d5..762af024 100644 --- a/src/extract/extension.rs +++ b/src/extract/extension.rs @@ -12,7 +12,7 @@ use std::ops::Deref; /// use axum::{ /// AddExtensionLayer, /// extract::Extension, -/// handler::get, +/// routing::get, /// Router, /// }; /// use std::sync::Arc; diff --git a/src/extract/extractor_middleware.rs b/src/extract/extractor_middleware.rs index 2bcb9ac4..f57472e2 100644 --- a/src/extract/extractor_middleware.rs +++ b/src/extract/extractor_middleware.rs @@ -38,7 +38,7 @@ use tower_service::Service; /// ```rust /// use axum::{ /// extract::{extractor_middleware, FromRequest, RequestParts}, -/// handler::{get, post}, +/// routing::{get, post}, /// Router, /// }; /// use http::StatusCode; diff --git a/src/extract/form.rs b/src/extract/form.rs index 1edccb11..c4d017ca 100644 --- a/src/extract/form.rs +++ b/src/extract/form.rs @@ -16,7 +16,7 @@ use std::ops::Deref; /// ```rust,no_run /// use axum::{ /// extract::Form, -/// handler::post, +/// routing::post, /// Router, /// }; /// use serde::Deserialize; diff --git a/src/extract/mod.rs b/src/extract/mod.rs index 2a32291c..0ab95e09 100644 --- a/src/extract/mod.rs +++ b/src/extract/mod.rs @@ -10,7 +10,8 @@ //! ```rust,no_run //! use axum::{ //! extract::Json, -//! handler::{post, Handler}, +//! routing::post, +//! handler::Handler, //! Router, //! }; //! use serde::Deserialize; @@ -39,7 +40,7 @@ //! use axum::{ //! async_trait, //! extract::{FromRequest, RequestParts}, -//! handler::get, +//! routing::get, //! Router, //! }; //! use http::{StatusCode, header::{HeaderValue, USER_AGENT}}; @@ -94,7 +95,7 @@ //! use axum::{ //! extract::{self, BodyStream}, //! body::Body, -//! handler::get, +//! routing::get, //! http::{header::HeaderMap, Request}, //! Router, //! }; diff --git a/src/extract/multipart.rs b/src/extract/multipart.rs index b2b6b26f..fcfd0642 100644 --- a/src/extract/multipart.rs +++ b/src/extract/multipart.rs @@ -22,7 +22,7 @@ use std::{ /// ```rust,no_run /// use axum::{ /// extract::Multipart, -/// handler::post, +/// routing::post, /// Router, /// }; /// use futures::stream::StreamExt; diff --git a/src/extract/path/mod.rs b/src/extract/path/mod.rs index a76a802d..2fcefd4c 100644 --- a/src/extract/path/mod.rs +++ b/src/extract/path/mod.rs @@ -24,7 +24,7 @@ use std::{ /// ```rust,no_run /// use axum::{ /// extract::Path, -/// handler::get, +/// routing::get, /// Router, /// }; /// use uuid::Uuid; @@ -46,7 +46,7 @@ use std::{ /// ```rust,no_run /// use axum::{ /// extract::Path, -/// handler::get, +/// routing::get, /// Router, /// }; /// use uuid::Uuid; @@ -68,7 +68,7 @@ use std::{ /// ```rust,no_run /// use axum::{ /// extract::Path, -/// handler::get, +/// routing::get, /// Router, /// }; /// use serde::Deserialize; @@ -97,7 +97,7 @@ use std::{ /// ```rust,no_run /// use axum::{ /// extract::Path, -/// handler::get, +/// routing::get, /// Router, /// }; /// use std::collections::HashMap; @@ -175,7 +175,7 @@ where mod tests { use super::*; use crate::tests::*; - use crate::{handler::get, Router}; + use crate::{routing::get, Router}; use std::collections::HashMap; #[tokio::test] diff --git a/src/extract/query.rs b/src/extract/query.rs index 975f9f85..7d08ced9 100644 --- a/src/extract/query.rs +++ b/src/extract/query.rs @@ -12,7 +12,7 @@ use std::ops::Deref; /// ```rust,no_run /// use axum::{ /// extract::Query, -/// handler::get, +/// routing::get, /// Router, /// }; /// use serde::Deserialize; diff --git a/src/extract/raw_query.rs b/src/extract/raw_query.rs index b802a675..f8d25393 100644 --- a/src/extract/raw_query.rs +++ b/src/extract/raw_query.rs @@ -9,7 +9,7 @@ use std::convert::Infallible; /// ```rust,no_run /// use axum::{ /// extract::RawQuery, -/// handler::get, +/// routing::get, /// Router, /// }; /// use futures::StreamExt; diff --git a/src/extract/request_parts.rs b/src/extract/request_parts.rs index 22c1c517..c76d39c8 100644 --- a/src/extract/request_parts.rs +++ b/src/extract/request_parts.rs @@ -94,7 +94,7 @@ where /// /// ``` /// use axum::{ -/// handler::get, +/// routing::get, /// Router, /// extract::OriginalUri, /// http::Uri @@ -181,7 +181,7 @@ where /// ```rust,no_run /// use axum::{ /// extract::BodyStream, -/// handler::get, +/// routing::get, /// Router, /// }; /// use futures::StreamExt; @@ -252,7 +252,7 @@ fn body_stream_traits() { /// ```rust,no_run /// use axum::{ /// extract::RawBody, -/// handler::get, +/// routing::get, /// Router, /// }; /// use futures::StreamExt; @@ -326,7 +326,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{body::Body, handler::post, tests::*, Router}; + use crate::{body::Body, routing::post, tests::*, Router}; use http::StatusCode; #[tokio::test] diff --git a/src/extract/typed_header.rs b/src/extract/typed_header.rs index b1f6945c..7b4abb73 100644 --- a/src/extract/typed_header.rs +++ b/src/extract/typed_header.rs @@ -13,7 +13,7 @@ use std::{convert::Infallible, ops::Deref}; /// ```rust,no_run /// use axum::{ /// extract::TypedHeader, -/// handler::get, +/// routing::get, /// Router, /// }; /// use headers::UserAgent; @@ -140,7 +140,7 @@ impl std::error::Error for TypedHeaderRejection { #[cfg(test)] mod tests { use super::*; - use crate::{handler::get, response::IntoResponse, tests::*, Router}; + use crate::{response::IntoResponse, routing::get, tests::*, Router}; #[tokio::test] async fn typed_header() { diff --git a/src/extract/ws.rs b/src/extract/ws.rs index 361b5dd5..4b76857c 100644 --- a/src/extract/ws.rs +++ b/src/extract/ws.rs @@ -5,7 +5,7 @@ //! ``` //! use axum::{ //! extract::ws::{WebSocketUpgrade, WebSocket}, -//! handler::get, +//! routing::get, //! response::IntoResponse, //! Router, //! }; @@ -96,7 +96,7 @@ use tokio_tungstenite::{ /// Extractor for establishing WebSocket connections. /// /// Note: This extractor requires the request method to be `GET` so it should -/// always be used with [`get`](crate::handler::get). Requests with other methods will be +/// always be used with [`get`](crate::routing::get). Requests with other methods will be /// rejected. /// /// See the [module docs](self) for an example. @@ -138,7 +138,7 @@ impl WebSocketUpgrade { /// ``` /// use axum::{ /// extract::ws::{WebSocketUpgrade, WebSocket}, - /// handler::get, + /// routing::get, /// response::IntoResponse, /// Router, /// }; diff --git a/src/handler/future.rs b/src/handler/future.rs index 71dcbe3b..144110a4 100644 --- a/src/handler/future.rs +++ b/src/handler/future.rs @@ -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 - where - F: Service> - { - #[pin] - pub(super) inner: Either< - BoxFuture<'static, Response>, - Oneshot>, - >, - pub(super) req_method: Method, - } -} - -impl Future for OnMethodFuture -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 OnMethodFuture -where - F: Service>, -{ - 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). diff --git a/src/handler/mod.rs b/src/handler/mod.rs index c0d1598b..1116bc58 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -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(handler: H) -> OnMethod -where - H: Handler, -{ - on(MethodFilter::all(), handler) -} - -/// Route `CONNECT` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn connect(handler: H) -> OnMethod -where - H: Handler, -{ - on(MethodFilter::CONNECT, handler) -} - -/// Route `DELETE` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn delete(handler: H) -> OnMethod -where - H: Handler, -{ - 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(handler: H) -> OnMethod -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) -> OnMethod -where - H: Handler, -{ - on(MethodFilter::HEAD, handler) -} - -/// Route `OPTIONS` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn options(handler: H) -> OnMethod -where - H: Handler, -{ - on(MethodFilter::OPTIONS, handler) -} - -/// Route `PATCH` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn patch(handler: H) -> OnMethod -where - H: Handler, -{ - on(MethodFilter::PATCH, handler) -} - -/// Route `POST` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn post(handler: H) -> OnMethod -where - H: Handler, -{ - on(MethodFilter::POST, handler) -} - -/// Route `PUT` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn put(handler: H) -> OnMethod -where - H: Handler, -{ - on(MethodFilter::PUT, handler) -} - -/// Route `TRACE` requests to the given handler. -/// -/// See [`get`] for an example. -pub fn trace(handler: H) -> OnMethod -where - H: Handler, -{ - 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(method: MethodFilter, handler: H) -> OnMethod -where - H: Handler, -{ - 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: 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: Clone + Send + Sized + 'static { /// ``` fn layer(self, layer: L) -> Layered where - L: Layer>, + L: Layer>, { - 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 Layered { } } -/// A handler [`Service`] that accepts requests based on a [`MethodFilter`] and -/// allows chaining additional handlers. -pub struct OnMethod { - pub(crate) method: MethodFilter, - pub(crate) handler: H, - pub(crate) fallback: F, - pub(crate) _marker: PhantomData (B, T)>, -} - #[test] fn traits() { use crate::tests::*; - assert_send::>(); - assert_sync::>(); -} - -impl fmt::Debug for OnMethod -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::())) - .field("fallback", &self.fallback) - .finish() - } -} - -impl Clone for OnMethod -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 OnMethod -where - H: Copy, - F: Copy, -{ -} - -impl OnMethod { - /// Chain an additional handler that will accept all requests regardless of - /// its HTTP method. - /// - /// See [`OnMethod::get`] for an example. - pub fn any(self, handler: H2) -> OnMethod - where - H2: Handler, - { - self.on(MethodFilter::all(), handler) - } - - /// Chain an additional handler that will only accept `CONNECT` requests. - /// - /// See [`OnMethod::get`] for an example. - pub fn connect(self, handler: H2) -> OnMethod - where - H2: Handler, - { - self.on(MethodFilter::CONNECT, handler) - } - - /// Chain an additional handler that will only accept `DELETE` requests. - /// - /// See [`OnMethod::get`] for an example. - pub fn delete(self, handler: H2) -> OnMethod - where - H2: Handler, - { - 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(self, handler: H2) -> OnMethod - where - H2: Handler, - { - 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(self, handler: H2) -> OnMethod - where - H2: Handler, - { - self.on(MethodFilter::HEAD, handler) - } - - /// Chain an additional handler that will only accept `OPTIONS` requests. - /// - /// See [`OnMethod::get`] for an example. - pub fn options(self, handler: H2) -> OnMethod - where - H2: Handler, - { - self.on(MethodFilter::OPTIONS, handler) - } - - /// Chain an additional handler that will only accept `PATCH` requests. - /// - /// See [`OnMethod::get`] for an example. - pub fn patch(self, handler: H2) -> OnMethod - where - H2: Handler, - { - self.on(MethodFilter::PATCH, handler) - } - - /// Chain an additional handler that will only accept `POST` requests. - /// - /// See [`OnMethod::get`] for an example. - pub fn post(self, handler: H2) -> OnMethod - where - H2: Handler, - { - self.on(MethodFilter::POST, handler) - } - - /// Chain an additional handler that will only accept `PUT` requests. - /// - /// See [`OnMethod::get`] for an example. - pub fn put(self, handler: H2) -> OnMethod - where - H2: Handler, - { - self.on(MethodFilter::PUT, handler) - } - - /// Chain an additional handler that will only accept `TRACE` requests. - /// - /// See [`OnMethod::get`] for an example. - pub fn trace(self, handler: H2) -> OnMethod - where - H2: Handler, - { - 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(self, method: MethodFilter, handler: H2) -> OnMethod - where - H2: Handler, - { - OnMethod { - method, - handler, - fallback: self, - _marker: PhantomData, - } - } -} - -impl Service> for OnMethod -where - H: Handler, - F: Service, Response = Response, Error = Infallible> + Clone, - B: Send + 'static, -{ - type Response = Response; - type Error = Infallible; - type Future = future::OnMethodFuture; - - 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 } - }; - - future::OnMethodFuture { - inner: fut, - req_method, - } - } + assert_send::>(); + assert_sync::>(); } diff --git a/src/json.rs b/src/json.rs index 11db3360..93f9963a 100644 --- a/src/json.rs +++ b/src/json.rs @@ -29,7 +29,7 @@ use std::{ /// ```rust,no_run /// use axum::{ /// extract, -/// handler::post, +/// routing::post, /// Router, /// }; /// use serde::Deserialize; @@ -58,7 +58,7 @@ use std::{ /// ``` /// use axum::{ /// extract::Path, -/// handler::get, +/// routing::get, /// Router, /// Json, /// }; diff --git a/src/lib.rs b/src/lib.rs index 19d84c98..0774ba17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,7 +55,7 @@ //! //! ```rust,no_run //! use axum::{ -//! handler::get, +//! routing::get, //! Router, //! }; //! @@ -134,7 +134,7 @@ //! ::: axum/src/handler/mod.rs:116:8 //! | //! 116 | H: Handler, -//! | ------------- required by this bound in `axum::handler::get` +//! | ------------- required by this bound in `axum::routing::get` //! ``` //! //! This error doesn't tell you _why_ your function doesn't implement @@ -147,7 +147,7 @@ //! //! ```rust,no_run //! use axum::{ -//! handler::get, +//! routing::get, //! Router, //! }; //! @@ -186,8 +186,8 @@ //! ```rust,no_run //! use axum::{ //! Router, -//! service, //! body::Body, +//! routing::service_method_router as service, //! error_handling::HandleErrorExt, //! http::{Request, StatusCode}, //! }; @@ -248,8 +248,7 @@ //! ```compile_fail //! use axum::{ //! Router, -//! service, -//! handler::get, +//! routing::{get, service_method_router as service}, //! http::{Request, Response}, //! body::Body, //! }; @@ -277,9 +276,9 @@ //! //! ``` //! use axum::{ -//! Router, service, +//! Router, //! body::Body, -//! handler::get, +//! routing::{get, service_method_router as service}, //! response::IntoResponse, //! http::{Request, Response}, //! error_handling::HandleErrorExt, @@ -321,7 +320,7 @@ //! //! ```rust,no_run //! use axum::{ -//! handler::get, +//! routing::get, //! Router, //! }; //! @@ -337,7 +336,7 @@ //! //! ```rust,no_run //! use axum::{ -//! handler::get, +//! routing::get, //! extract::Path, //! Router, //! }; @@ -358,7 +357,7 @@ //! use axum::{ //! body::{Body, BoxBody}, //! http::Request, -//! handler::get, +//! routing::get, //! Router, //! }; //! use tower_http::services::ServeFile; @@ -387,7 +386,7 @@ //! the prefix stripped. //! //! ```rust -//! use axum::{handler::get, http::Uri, Router}; +//! use axum::{routing::get, http::Uri, Router}; //! //! let app = Router::new() //! .route("/foo/*rest", get(|uri: Uri| async { @@ -412,7 +411,7 @@ //! ```rust,no_run //! use axum::{ //! extract::Json, -//! handler::post, +//! routing::post, //! Router, //! }; //! use serde::Deserialize; @@ -442,7 +441,7 @@ //! ```rust,no_run //! use axum::{ //! extract::{Json, TypedHeader, Path, Extension, Query}, -//! handler::post, +//! routing::post, //! http::{Request, header::HeaderMap}, //! body::{Bytes, Body}, //! Router, @@ -506,7 +505,7 @@ //! ```rust,no_run //! use axum::{ //! extract, -//! handler::get, +//! routing::get, //! Router, //! }; //! use uuid::Uuid; @@ -550,7 +549,7 @@ //! ```rust,no_run //! use axum::{ //! extract::TypedHeader, -//! handler::get, +//! routing::get, //! http::header::HeaderMap, //! Router, //! }; @@ -574,7 +573,7 @@ //! //! ```rust,no_run //! use axum::{ -//! handler::get, +//! routing::get, //! http::Request, //! body::Body, //! Router, @@ -601,7 +600,7 @@ //! ```rust,no_run //! use axum::{ //! extract::Json, -//! handler::post, +//! routing::post, //! Router, //! }; //! use serde_json::Value; @@ -626,7 +625,7 @@ //! ```rust,no_run //! use axum::{ //! extract::{Json, rejection::JsonRejection}, -//! handler::post, +//! routing::post, //! Router, //! }; //! use serde_json::Value; @@ -680,7 +679,8 @@ //! ```rust,no_run //! use axum::{ //! body::Body, -//! handler::{get, Handler}, +//! routing::get, +//! handler::Handler, //! http::{Request, header::{HeaderMap, HeaderName, HeaderValue}}, //! response::{IntoResponse, Html, Json, Headers}, //! Router, @@ -819,7 +819,8 @@ //! //! ```rust,no_run //! use axum::{ -//! handler::{get, Handler}, +//! handler::Handler, +//! routing::get, //! Router, //! }; //! use tower::limit::ConcurrencyLimitLayer; @@ -842,7 +843,7 @@ //! //! ```rust,no_run //! use axum::{ -//! handler::{get, post}, +//! routing::{get, post}, //! Router, //! }; //! use tower::limit::ConcurrencyLimitLayer; @@ -865,7 +866,7 @@ //! //! ```rust,no_run //! use axum::{ -//! handler::{get, post}, +//! routing::{get, post}, //! Router, //! }; //! use tower::limit::ConcurrencyLimitLayer; @@ -895,7 +896,7 @@ //! ```rust,no_run //! use axum::{ //! body::Body, -//! handler::get, +//! routing::get, //! http::{Request, StatusCode}, //! error_handling::HandleErrorLayer, //! response::IntoResponse, @@ -945,7 +946,7 @@ //! ```rust,no_run //! use axum::{ //! body::{Body, BoxBody}, -//! handler::get, +//! routing::get, //! http::{Request, Response}, //! error_handling::HandleErrorLayer, //! Router, @@ -1004,7 +1005,7 @@ //! ``` //! use axum::{ //! body::{Body, BoxBody}, -//! handler::get, +//! routing::get, //! http::{Request, Response}, //! Router, //! }; @@ -1069,7 +1070,7 @@ //! use axum::{ //! AddExtensionLayer, //! extract, -//! handler::get, +//! routing::get, //! Router, //! }; //! use std::sync::Arc; @@ -1220,7 +1221,6 @@ pub mod extract; pub mod handler; pub mod response; pub mod routing; -pub mod service; #[cfg(test)] mod tests; diff --git a/src/response/headers.rs b/src/response/headers.rs index 03e24a85..2b086e1f 100644 --- a/src/response/headers.rs +++ b/src/response/headers.rs @@ -18,7 +18,7 @@ use tower::util::Either; /// use axum::{ /// Router, /// response::{IntoResponse, Headers}, -/// handler::get, +/// routing::get, /// }; /// use http::header::{HeaderName, HeaderValue}; /// diff --git a/src/response/mod.rs b/src/response/mod.rs index 41e5299b..94d39054 100644 --- a/src/response/mod.rs +++ b/src/response/mod.rs @@ -40,7 +40,7 @@ pub use self::{headers::Headers, redirect::Redirect, sse::Sse}; /// use axum::{ /// Router, /// body::Body, -/// handler::get, +/// routing::get, /// http::{Response, StatusCode}, /// response::IntoResponse, /// }; @@ -87,7 +87,7 @@ pub use self::{headers::Headers, redirect::Redirect, sse::Sse}; /// /// ```rust /// use axum::{ -/// handler::get, +/// routing::get, /// response::IntoResponse, /// Router, /// }; diff --git a/src/response/redirect.rs b/src/response/redirect.rs index 088d6151..91df811c 100644 --- a/src/response/redirect.rs +++ b/src/response/redirect.rs @@ -10,7 +10,7 @@ use std::convert::TryFrom; /// /// ```rust /// use axum::{ -/// handler::get, +/// routing::get, /// response::Redirect, /// Router, /// }; diff --git a/src/response/sse.rs b/src/response/sse.rs index 3d8e5dff..b6108cf4 100644 --- a/src/response/sse.rs +++ b/src/response/sse.rs @@ -5,7 +5,7 @@ //! ``` //! use axum::{ //! Router, -//! handler::get, +//! routing::get, //! response::sse::{Event, KeepAlive, Sse}, //! }; //! use std::{time::Duration, convert::Infallible}; diff --git a/src/routing/handler_method_router.rs b/src/routing/handler_method_router.rs new file mode 100644 index 00000000..6ae950e1 --- /dev/null +++ b/src/routing/handler_method_router.rs @@ -0,0 +1,485 @@ +//! Routing for handlers based on HTTP methods. + +use crate::{ + body::{box_body, BoxBody}, + handler::Handler, + routing::{EmptyRouter, MethodFilter}, + util::{Either, EitherProj}, +}; +use futures_util::{future::BoxFuture, ready}; +use http::Method; +use http::{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; +use tower::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 `CONNECT` requests to the given handler. +/// +/// See [`get`] for an example. +pub fn connect(handler: H) -> MethodRouter +where + H: Handler, +{ + on(MethodFilter::CONNECT, 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: EmptyRouter::method_not_allowed(), + _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 `CONNECT` requests. + /// + /// See [`MethodRouter::get`] for an example. + pub fn connect(self, handler: H2) -> MethodRouter + where + H2: Handler, + { + self.on(MethodFilter::CONNECT, 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; + + 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() + } +} diff --git a/src/routing/mod.rs b/src/routing/mod.rs index c489b234..59bbef70 100644 --- a/src/routing/mod.rs +++ b/src/routing/mod.rs @@ -1,4 +1,4 @@ -//! Routing between [`Service`]s. +//! Routing between [`Service`]s and handlers. use self::future::{EmptyRouterFuture, NestedFuture, RouteFuture, RoutesFuture}; use crate::{ @@ -28,12 +28,19 @@ use tower_layer::Layer; use tower_service::Service; pub mod future; +pub mod handler_method_router; +pub mod service_method_router; mod method_filter; mod or; pub use self::method_filter::MethodFilter; +#[doc(no_inline)] +pub use self::handler_method_router::{ + any, connect, delete, get, head, on, options, patch, post, put, trace, MethodRouter, +}; + #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct RouteId(u64); @@ -109,7 +116,7 @@ where /// # Example /// /// ```rust - /// use axum::{handler::{get, delete}, Router}; + /// use axum::{routing::{get, delete}, Router}; /// /// let app = Router::new() /// .route("/", get(root)) @@ -136,7 +143,7 @@ where /// Panics if the route overlaps with another route: /// /// ```should_panic - /// use axum::{handler::get, Router}; + /// use axum::{routing::get, Router}; /// /// let app = Router::new() /// .route("/", get(|| async {})) @@ -149,7 +156,7 @@ where /// This also applies to `nest` which is similar to a wildcard route: /// /// ```should_panic - /// use axum::{handler::get, Router}; + /// use axum::{routing::get, Router}; /// /// let app = Router::new() /// // this is similar to `/api/*` @@ -164,7 +171,7 @@ where /// Note that routes like `/:key` and `/foo` are considered overlapping: /// /// ```should_panic - /// use axum::{handler::get, Router}; + /// use axum::{routing::get, Router}; /// /// let app = Router::new() /// .route("/foo", get(|| async {})) @@ -204,7 +211,7 @@ where /// /// ``` /// use axum::{ - /// handler::get, + /// routing::get, /// Router, /// }; /// use http::Uri; @@ -239,7 +246,7 @@ where /// ``` /// use axum::{ /// extract::Path, - /// handler::get, + /// routing::get, /// Router, /// }; /// use std::collections::HashMap; @@ -265,7 +272,7 @@ where /// ``` /// use axum::{ /// Router, - /// service::get, + /// routing::service_method_router::get, /// error_handling::HandleErrorExt, /// http::StatusCode, /// }; @@ -294,7 +301,7 @@ where /// the prefix stripped. /// /// ```rust - /// use axum::{handler::get, http::Uri, Router}; + /// use axum::{routing::get, http::Uri, Router}; /// /// let app = Router::new() /// .route("/foo/*rest", get(|uri: Uri| async { @@ -366,7 +373,7 @@ where /// /// ```rust /// use axum::{ - /// handler::get, + /// routing::get, /// Router, /// }; /// use tower::limit::{ConcurrencyLimitLayer, ConcurrencyLimit}; @@ -395,7 +402,7 @@ where /// /// ```rust /// use axum::{ - /// handler::get, + /// routing::get, /// Router, /// }; /// use tower_http::trace::TraceLayer; @@ -440,7 +447,7 @@ where /// /// ``` /// use axum::{ - /// handler::get, + /// routing::get, /// Router, /// }; /// @@ -470,7 +477,7 @@ where /// ``` /// use axum::{ /// extract::ConnectInfo, - /// handler::get, + /// routing::get, /// Router, /// }; /// use std::net::SocketAddr; @@ -496,7 +503,7 @@ where /// ``` /// use axum::{ /// extract::connect_info::{ConnectInfo, Connected}, - /// handler::get, + /// routing::get, /// Router, /// }; /// use hyper::server::conn::AddrStream; @@ -555,7 +562,7 @@ where /// /// ``` /// use axum::{ - /// handler::get, + /// routing::get, /// Router, /// }; /// # diff --git a/src/service/mod.rs b/src/routing/service_method_router.rs similarity index 74% rename from src/service/mod.rs rename to src/routing/service_method_router.rs index 05e1006e..49c4d491 100644 --- a/src/service/mod.rs +++ b/src/routing/service_method_router.rs @@ -1,4 +1,4 @@ -//! Use Tower [`Service`]s to handle requests. +//! Routing for [`Service`'s] based on HTTP methods. //! //! Most of the time applications will be written by composing //! [handlers](crate::handler), however sometimes you might have some general @@ -13,10 +13,9 @@ //! use tower_http::services::Redirect; //! use axum::{ //! body::Body, -//! handler::get, +//! routing::{get, service_method_router as service}, //! http::Request, //! Router, -//! service, //! }; //! //! async fn handler(request: Request) { /* ... */ } @@ -66,7 +65,7 @@ //! //! ```rust //! use axum::{ -//! handler::get, +//! routing::get, //! Router, //! }; //! use tower::ServiceBuilder; @@ -95,30 +94,36 @@ //! //! [`Redirect`]: tower_http::services::Redirect //! [load shed]: tower::load_shed +//! [`Service`'s]: tower::Service -use crate::BoxError; use crate::{ - body::BoxBody, + body::{box_body, BoxBody}, routing::{EmptyRouter, MethodFilter}, + util::{Either, EitherProj}, + BoxError, }; use bytes::Bytes; -use http::{Request, Response}; +use futures_util::ready; +use http::{Method, Request, Response}; +use http_body::Empty; +use pin_project_lite::pin_project; +use std::marker::PhantomData; use std::{ - marker::PhantomData, + future::Future, + pin::Pin, task::{Context, Poll}, }; +use tower::util::Oneshot; use tower::ServiceExt as _; use tower_service::Service; -pub mod future; - /// Route requests with any standard HTTP method to the given service. /// /// See [`get`] for an example. /// /// Note that this only accepts the standard HTTP methods. If you need to /// support non-standard methods you can route directly to a [`Service`]. -pub fn any(svc: S) -> OnMethod, B> +pub fn any(svc: S) -> MethodRouter, B> where S: Service> + Clone, { @@ -128,7 +133,7 @@ where /// Route `CONNECT` requests to the given service. /// /// See [`get`] for an example. -pub fn connect(svc: S) -> OnMethod, B> +pub fn connect(svc: S) -> MethodRouter, B> where S: Service> + Clone, { @@ -138,7 +143,7 @@ where /// Route `DELETE` requests to the given service. /// /// See [`get`] for an example. -pub fn delete(svc: S) -> OnMethod, B> +pub fn delete(svc: S) -> MethodRouter, B> where S: Service> + Clone, { @@ -153,7 +158,7 @@ where /// use axum::{ /// http::Request, /// Router, -/// service, +/// routing::service_method_router as service, /// }; /// use http::Response; /// use std::convert::Infallible; @@ -173,7 +178,7 @@ where /// 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(svc: S) -> OnMethod, B> +pub fn get(svc: S) -> MethodRouter, B> where S: Service> + Clone, { @@ -183,7 +188,7 @@ where /// Route `HEAD` requests to the given service. /// /// See [`get`] for an example. -pub fn head(svc: S) -> OnMethod, B> +pub fn head(svc: S) -> MethodRouter, B> where S: Service> + Clone, { @@ -193,7 +198,7 @@ where /// Route `OPTIONS` requests to the given service. /// /// See [`get`] for an example. -pub fn options(svc: S) -> OnMethod, B> +pub fn options(svc: S) -> MethodRouter, B> where S: Service> + Clone, { @@ -203,7 +208,7 @@ where /// Route `PATCH` requests to the given service. /// /// See [`get`] for an example. -pub fn patch(svc: S) -> OnMethod, B> +pub fn patch(svc: S) -> MethodRouter, B> where S: Service> + Clone, { @@ -213,7 +218,7 @@ where /// Route `POST` requests to the given service. /// /// See [`get`] for an example. -pub fn post(svc: S) -> OnMethod, B> +pub fn post(svc: S) -> MethodRouter, B> where S: Service> + Clone, { @@ -223,7 +228,7 @@ where /// Route `PUT` requests to the given service. /// /// See [`get`] for an example. -pub fn put(svc: S) -> OnMethod, B> +pub fn put(svc: S) -> MethodRouter, B> where S: Service> + Clone, { @@ -233,7 +238,7 @@ where /// Route `TRACE` requests to the given service. /// /// See [`get`] for an example. -pub fn trace(svc: S) -> OnMethod, B> +pub fn trace(svc: S) -> MethodRouter, B> where S: Service> + Clone, { @@ -247,10 +252,9 @@ where /// ```rust /// use axum::{ /// http::Request, -/// handler::on, -/// service, +/// routing::on, /// Router, -/// routing::MethodFilter, +/// routing::{MethodFilter, service_method_router as service}, /// }; /// use http::Response; /// use std::convert::Infallible; @@ -266,11 +270,11 @@ where /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` -pub fn on(method: MethodFilter, svc: S) -> OnMethod, B> +pub fn on(method: MethodFilter, svc: S) -> MethodRouter, B> where S: Service> + Clone, { - OnMethod { + MethodRouter { method, svc, fallback: EmptyRouter::method_not_allowed(), @@ -281,14 +285,14 @@ where /// A [`Service`] that accepts requests based on a [`MethodFilter`] and allows /// chaining additional services. #[derive(Debug)] // TODO(david): don't require debug for B -pub struct OnMethod { +pub struct MethodRouter { pub(crate) method: MethodFilter, pub(crate) svc: S, pub(crate) fallback: F, pub(crate) _request_body: PhantomData B>, } -impl Clone for OnMethod +impl Clone for MethodRouter where S: Clone, F: Clone, @@ -303,12 +307,12 @@ where } } -impl OnMethod { +impl MethodRouter { /// Chain an additional service that will accept all requests regardless of /// its HTTP method. /// - /// See [`OnMethod::get`] for an example. - pub fn any(self, svc: T) -> OnMethod + /// See [`MethodRouter::get`] for an example. + pub fn any(self, svc: T) -> MethodRouter where T: Service> + Clone, { @@ -317,8 +321,8 @@ impl OnMethod { /// Chain an additional service that will only accept `CONNECT` requests. /// - /// See [`OnMethod::get`] for an example. - pub fn connect(self, svc: T) -> OnMethod + /// See [`MethodRouter::get`] for an example. + pub fn connect(self, svc: T) -> MethodRouter where T: Service> + Clone, { @@ -327,8 +331,8 @@ impl OnMethod { /// Chain an additional service that will only accept `DELETE` requests. /// - /// See [`OnMethod::get`] for an example. - pub fn delete(self, svc: T) -> OnMethod + /// See [`MethodRouter::get`] for an example. + pub fn delete(self, svc: T) -> MethodRouter where T: Service> + Clone, { @@ -342,10 +346,8 @@ impl OnMethod { /// ```rust /// use axum::{ /// http::Request, - /// handler::on, - /// service, /// Router, - /// routing::MethodFilter, + /// routing::{MethodFilter, on, service_method_router as service}, /// }; /// use http::Response; /// use std::convert::Infallible; @@ -370,7 +372,7 @@ impl OnMethod { /// 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, svc: T) -> OnMethod + pub fn get(self, svc: T) -> MethodRouter where T: Service> + Clone, { @@ -379,8 +381,8 @@ impl OnMethod { /// Chain an additional service that will only accept `HEAD` requests. /// - /// See [`OnMethod::get`] for an example. - pub fn head(self, svc: T) -> OnMethod + /// See [`MethodRouter::get`] for an example. + pub fn head(self, svc: T) -> MethodRouter where T: Service> + Clone, { @@ -389,8 +391,8 @@ impl OnMethod { /// Chain an additional service that will only accept `OPTIONS` requests. /// - /// See [`OnMethod::get`] for an example. - pub fn options(self, svc: T) -> OnMethod + /// See [`MethodRouter::get`] for an example. + pub fn options(self, svc: T) -> MethodRouter where T: Service> + Clone, { @@ -399,8 +401,8 @@ impl OnMethod { /// Chain an additional service that will only accept `PATCH` requests. /// - /// See [`OnMethod::get`] for an example. - pub fn patch(self, svc: T) -> OnMethod + /// See [`MethodRouter::get`] for an example. + pub fn patch(self, svc: T) -> MethodRouter where T: Service> + Clone, { @@ -409,8 +411,8 @@ impl OnMethod { /// Chain an additional service that will only accept `POST` requests. /// - /// See [`OnMethod::get`] for an example. - pub fn post(self, svc: T) -> OnMethod + /// See [`MethodRouter::get`] for an example. + pub fn post(self, svc: T) -> MethodRouter where T: Service> + Clone, { @@ -419,8 +421,8 @@ impl OnMethod { /// Chain an additional service that will only accept `PUT` requests. /// - /// See [`OnMethod::get`] for an example. - pub fn put(self, svc: T) -> OnMethod + /// See [`MethodRouter::get`] for an example. + pub fn put(self, svc: T) -> MethodRouter where T: Service> + Clone, { @@ -429,8 +431,8 @@ impl OnMethod { /// Chain an additional service that will only accept `TRACE` requests. /// - /// See [`OnMethod::get`] for an example. - pub fn trace(self, svc: T) -> OnMethod + /// See [`MethodRouter::get`] for an example. + pub fn trace(self, svc: T) -> MethodRouter where T: Service> + Clone, { @@ -445,10 +447,8 @@ impl OnMethod { /// ```rust /// use axum::{ /// http::Request, - /// handler::on, - /// service, /// Router, - /// routing::MethodFilter, + /// routing::{MethodFilter, on, service_method_router as service}, /// }; /// use http::Response; /// use std::convert::Infallible; @@ -468,11 +468,11 @@ impl OnMethod { /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` - pub fn on(self, method: MethodFilter, svc: T) -> OnMethod + pub fn on(self, method: MethodFilter, svc: T) -> MethodRouter where T: Service> + Clone, { - OnMethod { + MethodRouter { method, svc, fallback: self, @@ -481,9 +481,7 @@ impl OnMethod { } } -// this is identical to `routing::OnMethod`'s implementation. Would be nice to find a way to clean -// that up, but not sure its possible. -impl Service> for OnMethod +impl Service> for MethodRouter where S: Service, Response = Response> + Clone, ResBody: http_body::Body + Send + Sync + 'static, @@ -492,15 +490,13 @@ where { type Response = Response; type Error = S::Error; - type Future = future::OnMethodFuture; + type Future = MethodRouterFuture; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: Request) -> Self::Future { - use crate::util::Either; - let req_method = req.method().clone(); let f = if self.method.matches(req.method()) { @@ -511,17 +507,59 @@ where Either::B { inner: fut } }; - future::OnMethodFuture { + MethodRouterFuture { inner: f, req_method, } } } +pin_project! { + /// The response future for [`MethodRouter`]. + pub struct MethodRouterFuture + where + S: Service>, + F: Service> + { + #[pin] + pub(super) inner: Either< + Oneshot>, + Oneshot>, + >, + pub(super) req_method: Method, + } +} + +impl Future for MethodRouterFuture +where + S: Service, Response = Response> + Clone, + ResBody: http_body::Body + Send + Sync + 'static, + ResBody::Error: Into, + F: Service, Response = Response, Error = S::Error>, +{ + type Output = Result, S::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))?.map(box_body), + 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)) + } + } +} + #[test] fn traits() { use crate::tests::*; - assert_send::>(); - assert_sync::>(); + assert_send::>(); + assert_sync::>(); } diff --git a/src/service/future.rs b/src/service/future.rs deleted file mode 100644 index ad5cb38a..00000000 --- a/src/service/future.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! [`Service`](tower::Service) future types. - -use crate::{ - body::{box_body, BoxBody}, - util::{Either, EitherProj}, - BoxError, -}; -use bytes::Bytes; -use futures_util::ready; -use http::{Method, Request, Response}; -use http_body::Empty; -use pin_project_lite::pin_project; -use std::{ - 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 - where - S: Service>, - F: Service> - { - #[pin] - pub(super) inner: Either< - Oneshot>, - Oneshot>, - >, - // pub(super) inner: crate::routing::future::RouteFuture, - pub(super) req_method: Method, - } -} - -impl Future for OnMethodFuture -where - S: Service, Response = Response> + Clone, - ResBody: http_body::Body + Send + Sync + 'static, - ResBody::Error: Into, - F: Service, Response = Response, Error = S::Error>, -{ - type Output = Result, S::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))?.map(box_body), - 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)) - } - } -} diff --git a/src/tests/get_to_head.rs b/src/tests/get_to_head.rs index 37b39e04..ed2ba619 100644 --- a/src/tests/get_to_head.rs +++ b/src/tests/get_to_head.rs @@ -38,7 +38,7 @@ mod for_handlers { mod for_services { use super::*; - use crate::service::get; + use crate::routing::service_method_router::get; use http::header::HeaderValue; #[tokio::test] diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 30ad64c5..c1813cb4 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -4,10 +4,10 @@ use crate::error_handling::HandleErrorLayer; use crate::BoxError; use crate::{ extract::{self, Path}, - handler::{any, delete, get, on, patch, post, Handler}, + handler::Handler, response::IntoResponse, - routing::MethodFilter, - service, Json, Router, + routing::{any, delete, get, on, patch, post, service_method_router as service, MethodFilter}, + Json, Router, }; use bytes::Bytes; use http::{ diff --git a/src/tests/or.rs b/src/tests/or.rs index b930a5f7..128f14e1 100644 --- a/src/tests/or.rs +++ b/src/tests/or.rs @@ -196,16 +196,18 @@ async fn many_ors() { #[tokio::test] async fn services() { + use crate::routing::service_method_router::get; + let app = Router::new() .route( "/foo", - crate::service::get(service_fn(|_: Request| async { + get(service_fn(|_: Request| async { Ok::<_, Infallible>(Response::new(Body::empty())) })), ) .or(Router::new().route( "/bar", - crate::service::get(service_fn(|_: Request| async { + get(service_fn(|_: Request| async { Ok::<_, Infallible>(Response::new(Body::empty())) })), ));