From c97967252de9741b602f400dc2b25c8a33216039 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 22 Mar 2023 23:42:14 +0100 Subject: [PATCH] Add `serve` function and remove `Server` re-export (#1868) --- axum-extra/src/extract/query.rs | 4 +- axum-extra/src/lib.rs | 2 +- axum-extra/src/protobuf.rs | 8 +- axum-macros/src/lib.rs | 18 +- axum/CHANGELOG.md | 3 + axum/Cargo.toml | 5 + axum/README.md | 9 +- axum/benches/benches.rs | 4 +- axum/src/body/stream_body.rs | 4 +- axum/src/docs/error_handling.md | 12 +- axum/src/docs/extract.md | 32 +-- axum/src/docs/method_routing/layer.md | 4 +- axum/src/docs/method_routing/route_layer.md | 4 +- axum/src/docs/middleware.md | 18 +- .../into_make_service_with_connect_info.md | 22 +- axum/src/docs/routing/nest.md | 12 +- axum/src/docs/routing/route.md | 12 +- axum/src/docs/routing/route_layer.md | 4 +- axum/src/docs/routing/route_service.md | 8 +- axum/src/docs/routing/with_state.md | 40 ++- axum/src/extension.rs | 4 +- axum/src/extract/connect_info.rs | 53 ++-- axum/src/extract/matched_path.rs | 4 +- axum/src/extract/multipart.rs | 4 +- axum/src/extract/path/mod.rs | 16 +- axum/src/extract/query.rs | 4 +- axum/src/extract/raw_form.rs | 4 +- axum/src/extract/raw_query.rs | 4 +- axum/src/extract/request_parts.rs | 8 +- axum/src/extract/state.rs | 10 +- axum/src/extract/ws.rs | 19 +- axum/src/handler/mod.rs | 4 +- axum/src/handler/service.rs | 41 ++- axum/src/json.rs | 8 +- axum/src/lib.rs | 40 ++- axum/src/middleware/from_extractor.rs | 4 +- axum/src/routing/method_routing.rs | 87 +++---- axum/src/routing/mod.rs | 26 +- axum/src/serve.rs | 234 ++++++++++++++++++ axum/src/test_helpers/mod.rs | 2 +- axum/src/test_helpers/test_client.rs | 29 +-- axum/src/typed_header.rs | 4 +- deny.toml | 2 + examples/anyhow-error-response/src/main.rs | 8 +- examples/chat/src/main.rs | 8 +- .../src/main.rs | 8 +- examples/cors/src/main.rs | 6 +- .../customize-extractor-error/src/main.rs | 8 +- examples/customize-path-rejection/src/main.rs | 8 +- .../src/main.rs | 9 +- examples/form/src/main.rs | 10 +- examples/global-404-handler/src/main.rs | 8 +- examples/graceful-shutdown/Cargo.toml | 1 + examples/graceful-shutdown/src/main.rs | 2 +- examples/handle-head-request/src/main.rs | 8 +- examples/hello-world/src/main.rs | 8 +- examples/http-proxy/src/main.rs | 2 +- examples/jwt/src/main.rs | 10 +- examples/key-value-store/src/main.rs | 8 +- examples/listen-multiple-addrs/src/main.rs | 2 +- examples/multipart-form/src/main.rs | 8 +- examples/oauth/src/main.rs | 10 +- .../src/main.rs | 8 +- examples/print-request-response/src/main.rs | 8 +- examples/prometheus-metrics/src/main.rs | 19 +- .../src/main.rs | 5 +- examples/readme/src/main.rs | 9 +- examples/rest-grpc-multiplex/src/main.rs | 2 +- examples/reverse-proxy/src/main.rs | 15 +- .../src/main.rs | 8 +- examples/sessions/src/main.rs | 8 +- examples/sqlx-postgres/src/main.rs | 12 +- examples/sse/src/main.rs | 9 +- examples/static-file-server/src/main.rs | 6 +- examples/stream-to-file/src/main.rs | 9 +- examples/templates/src/main.rs | 8 +- examples/testing-websockets/src/main.rs | 19 +- examples/testing/src/main.rs | 20 +- examples/tls-rustls/src/main.rs | 7 +- examples/todos/src/main.rs | 8 +- examples/tokio-postgres/src/main.rs | 10 +- examples/tracing-aka-logging/src/main.rs | 12 +- examples/unix-domain-socket/src/main.rs | 2 +- examples/validator/src/main.rs | 12 +- examples/versioning/src/main.rs | 9 +- examples/websockets/src/main.rs | 12 +- 86 files changed, 641 insertions(+), 564 deletions(-) create mode 100644 axum/src/serve.rs diff --git a/axum-extra/src/extract/query.rs b/axum-extra/src/extract/query.rs index 43be2ec7..304d72b8 100644 --- a/axum-extra/src/extract/query.rs +++ b/axum-extra/src/extract/query.rs @@ -41,9 +41,7 @@ use std::fmt; /// } /// /// let app = Router::new().route("/list_things", get(list_things)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// If the query string cannot be parsed it will reject the request with a `400 diff --git a/axum-extra/src/lib.rs b/axum-extra/src/lib.rs index a964de37..6a63a23c 100644 --- a/axum-extra/src/lib.rs +++ b/axum-extra/src/lib.rs @@ -105,7 +105,7 @@ use axum_macros::__private_axum_test as test; pub(crate) mod test_helpers { #![allow(unused_imports)] - use axum::{body::HttpBody, BoxError, Router}; + use axum::{extract::Request, response::Response, serve}; mod test_client { #![allow(dead_code)] diff --git a/axum-extra/src/protobuf.rs b/axum-extra/src/protobuf.rs index ce08bd71..e3c4b51d 100644 --- a/axum-extra/src/protobuf.rs +++ b/axum-extra/src/protobuf.rs @@ -45,9 +45,7 @@ use prost::Message; /// } /// /// let app = Router::new().route("/users", post(create_user)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// # As response @@ -85,9 +83,7 @@ use prost::Message; /// } /// /// let app = Router::new().route("/users/:id", get(get_user)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[derive(Debug, Clone, Copy, Default)] #[cfg_attr(docsrs, doc(cfg(feature = "protobuf")))] diff --git a/axum-macros/src/lib.rs b/axum-macros/src/lib.rs index ac8c5b6e..269665f8 100644 --- a/axum-macros/src/lib.rs +++ b/axum-macros/src/lib.rs @@ -416,10 +416,8 @@ pub fn derive_from_request_parts(item: TokenStream) -> TokenStream { /// async fn main() { /// let app = Router::new().route("/", get(handler)); /// -/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -/// .serve(app.into_make_service()) -/// .await -/// .unwrap(); +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, app).await.unwrap(); /// } /// /// fn handler() -> &'static str { @@ -438,10 +436,8 @@ pub fn derive_from_request_parts(item: TokenStream) -> TokenStream { /// # async fn main() { /// # let app = Router::new().route("/", get(handler)); /// # -/// # axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -/// # .serve(app.into_make_service()) -/// # .await -/// # .unwrap(); +/// # let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// # axum::serve(listener, app).await.unwrap(); /// # } /// # /// #[debug_handler] @@ -468,10 +464,8 @@ pub fn derive_from_request_parts(item: TokenStream) -> TokenStream { /// # async { /// let app = Router::new().route("/", get(handler)); /// -/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -/// .serve(app.into_make_service()) -/// .await -/// .unwrap(); +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, app).await.unwrap(); /// # }; /// } /// diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index c877677e..59e5be21 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -40,12 +40,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **added:** Add `axum::extract::Request` type alias where the body is `axum::body::Body` ([#1789]) - **added:** Add `Router::as_service` and `Router::into_service` to workaround type inference issues when calling `ServiceExt` methods on a `Router` ([#1835]) +- **breaking:** Removed `axum::Server` as it was removed in hyper 1.0. Instead + use `axum::serve(listener, service)` or hyper/hyper-util for more configuration options ([#1868]) [#1664]: https://github.com/tokio-rs/axum/pull/1664 [#1751]: https://github.com/tokio-rs/axum/pull/1751 [#1762]: https://github.com/tokio-rs/axum/pull/1762 [#1835]: https://github.com/tokio-rs/axum/pull/1835 [#1789]: https://github.com/tokio-rs/axum/pull/1789 +[#1868]: https://github.com/tokio-rs/axum/pull/1868 # 0.6.16 (18. April, 2023) diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 61f834c8..89f00612 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -51,6 +51,10 @@ tower = { version = "0.4.13", default-features = false, features = ["util"] } tower-layer = "0.3.2" tower-service = "0.3" +# wont need this when axum uses http-body 1.0 +hyper1 = { package = "hyper", version = "1.0.0-rc.3", features = ["server", "http1"] } +tower-hyper-http-body-compat = { version = "0.1.4", features = ["server", "http1"] } + # optional dependencies axum-macros = { path = "../axum-macros", version = "0.3.7", optional = true } base64 = { version = "0.21.0", optional = true } @@ -192,6 +196,7 @@ allowed = [ "http_body", "hyper", "serde", + "tokio", "tower_layer", "tower_service", ] diff --git a/axum/README.md b/axum/README.md index ab07aafc..b5948864 100644 --- a/axum/README.md +++ b/axum/README.md @@ -54,13 +54,8 @@ async fn main() { .route("/users", post(create_user)); // run our app with hyper - // `axum::Server` is a re-export of `hyper::Server` - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + axum::serve(listener, app).await.unwrap(); } // basic handler that responds with a static string diff --git a/axum/benches/benches.rs b/axum/benches/benches.rs index c3b9c19e..4fad5e3d 100644 --- a/axum/benches/benches.rs +++ b/axum/benches/benches.rs @@ -1,7 +1,7 @@ use axum::{ extract::State, routing::{get, post}, - Extension, Json, Router, Server, + Extension, Json, Router, }; use hyper::server::conn::AddrIncoming; use serde::{Deserialize, Serialize}; @@ -164,7 +164,7 @@ impl BenchmarkBuilder { std::thread::spawn(move || { rt.block_on(async move { let incoming = AddrIncoming::from_listener(listener).unwrap(); - Server::builder(incoming) + hyper::Server::builder(incoming) .serve(app.into_make_service()) .await .unwrap(); diff --git a/axum/src/body/stream_body.rs b/axum/src/body/stream_body.rs index 6331567a..933d1051 100644 --- a/axum/src/body/stream_body.rs +++ b/axum/src/body/stream_body.rs @@ -47,9 +47,7 @@ pin_project! { /// } /// /// let app = Router::new().route("/", get(handler)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` /// /// [`Stream`]: futures_util::stream::Stream diff --git a/axum/src/docs/error_handling.md b/axum/src/docs/error_handling.md index 45c768c6..b9abce6f 100644 --- a/axum/src/docs/error_handling.md +++ b/axum/src/docs/error_handling.md @@ -85,9 +85,7 @@ async fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) { format!("Something went wrong: {}", err), ) } -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Applying fallible middleware @@ -129,9 +127,7 @@ async fn handle_timeout_error(err: BoxError) -> (StatusCode, String) { ) } } -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Running extractors for error handling @@ -171,9 +167,7 @@ async fn handle_timeout_error( format!("`{} {}` failed with {}", method, uri, err), ) } -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` [`tower::Service`]: `tower::Service` diff --git a/axum/src/docs/extract.md b/axum/src/docs/extract.md index e5e188a9..07268e59 100644 --- a/axum/src/docs/extract.md +++ b/axum/src/docs/extract.md @@ -47,9 +47,7 @@ async fn create_user(Json(payload): Json) { } let app = Router::new().route("/users", post(create_user)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Common extractors @@ -111,9 +109,7 @@ let app = Router::new() .route("/json", post(json)) .route("/request", post(request)) .route("/extension", post(extension)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Applying multiple extractors @@ -151,9 +147,7 @@ async fn get_user_things( // ... } -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # The order of extractors @@ -253,9 +247,7 @@ async fn create_user(payload: Option>) { } let app = Router::new().route("/users", post(create_user)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` Wrapping extractors in `Result` makes them optional and gives you the reason @@ -295,9 +287,7 @@ async fn create_user(payload: Result, JsonRejection>) { } let app = Router::new().route("/users", post(create_user)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Customizing extractor responses @@ -452,9 +442,7 @@ async fn handler(ExtractUserAgent(user_agent): ExtractUserAgent) { } let app = Router::new().route("/foo", get(handler)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` ## Implementing `FromRequest` @@ -501,9 +489,7 @@ async fn handler(ValidatedBody(body): ValidatedBody) { } let app = Router::new().route("/foo", get(handler)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` ## Cannot implement both `FromRequest` and `FromRequestParts` @@ -624,9 +610,7 @@ async fn handler(user: AuthenticatedUser) { let state = State { /* ... */ }; let app = Router::new().route("/", get(handler)).layer(Extension(state)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Request body limits diff --git a/axum/src/docs/method_routing/layer.md b/axum/src/docs/method_routing/layer.md index cdf6f933..7ceadd4d 100644 --- a/axum/src/docs/method_routing/layer.md +++ b/axum/src/docs/method_routing/layer.md @@ -23,7 +23,5 @@ let app = Router::new().route( // All requests to `GET /` will be sent through `ConcurrencyLimitLayer` get(hander).layer(ConcurrencyLimitLayer::new(64)), ); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` diff --git a/axum/src/docs/method_routing/route_layer.md b/axum/src/docs/method_routing/route_layer.md index f497e8b1..7a7ff36a 100644 --- a/axum/src/docs/method_routing/route_layer.md +++ b/axum/src/docs/method_routing/route_layer.md @@ -28,7 +28,5 @@ let app = Router::new().route( // `GET /foo` with a valid token will receive `200 OK` // `GET /foo` with a invalid token will receive `401 Unauthorized` // `POST /FOO` with a invalid token will receive `405 Method Not Allowed` -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` diff --git a/axum/src/docs/middleware.md b/axum/src/docs/middleware.md index 6aca7a3e..70eda706 100644 --- a/axum/src/docs/middleware.md +++ b/axum/src/docs/middleware.md @@ -55,9 +55,7 @@ let app = Router::new() .layer(TraceLayer::new_for_http()) .layer(Extension(State {})) ); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Commonly used middleware @@ -319,9 +317,7 @@ let app = Router::new() })) .layer(TimeoutLayer::new(Duration::from_secs(10))) ); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` See [`error_handling`](crate::error_handling) for more details on axum's error @@ -376,9 +372,7 @@ let app = Router::new().route("/", get(handler)); let app = ServiceBuilder::new() .layer(some_backpressure_sensitive_middleware) .service(app); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` However when applying middleware around your whole application in this way @@ -563,10 +557,8 @@ let app = Router::new(); let app_with_middleware = middleware.layer(app); # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(app_with_middleware.into_make_service()) - .await - .unwrap(); +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, app_with_middleware.into_make_service()).await.unwrap(); # }; ``` diff --git a/axum/src/docs/routing/into_make_service_with_connect_info.md b/axum/src/docs/routing/into_make_service_with_connect_info.md index 05ee750c..67dd6524 100644 --- a/axum/src/docs/routing/into_make_service_with_connect_info.md +++ b/axum/src/docs/routing/into_make_service_with_connect_info.md @@ -21,12 +21,8 @@ async fn handler(ConnectInfo(addr): ConnectInfo) -> String { } # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve( - app.into_make_service_with_connect_info::() - ) - .await - .expect("server failed"); +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, app.into_make_service_with_connect_info::()).await.unwrap(); # }; ``` @@ -36,9 +32,9 @@ You can implement custom a [`Connected`] like so: use axum::{ extract::connect_info::{ConnectInfo, Connected}, routing::get, + serve::IncomingStream, Router, }; -use hyper::server::conn::AddrStream; let app = Router::new().route("/", get(handler)); @@ -53,8 +49,8 @@ struct MyConnectInfo { // ... } -impl Connected<&AddrStream> for MyConnectInfo { - fn connect_info(target: &AddrStream) -> Self { +impl Connected> for MyConnectInfo { + fn connect_info(target: IncomingStream<'_>) -> Self { MyConnectInfo { // ... } @@ -62,12 +58,8 @@ impl Connected<&AddrStream> for MyConnectInfo { } # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve( - app.into_make_service_with_connect_info::() - ) - .await - .expect("server failed"); +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, app.into_make_service_with_connect_info::()).await.unwrap(); # }; ``` diff --git a/axum/src/docs/routing/nest.md b/axum/src/docs/routing/nest.md index b40d0fc9..c3f7308f 100644 --- a/axum/src/docs/routing/nest.md +++ b/axum/src/docs/routing/nest.md @@ -24,9 +24,7 @@ let app = Router::new().nest("/api", api_routes); // Our app now accepts // - GET /api/users/:id // - POST /api/teams -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # How the URI changes @@ -59,9 +57,7 @@ async fn users_get(Path(params): Path>) { let users_api = Router::new().route("/users/:id", get(users_get)); let app = Router::new().nest("/:version/api", users_api); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Differences from wildcard routes @@ -83,9 +79,7 @@ let app = Router::new() // `uri` will contain `/foo` })) .nest("/bar", nested_router); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Fallbacks diff --git a/axum/src/docs/routing/route.md b/axum/src/docs/routing/route.md index ae425142..394ae59b 100644 --- a/axum/src/docs/routing/route.md +++ b/axum/src/docs/routing/route.md @@ -77,9 +77,7 @@ async fn get_root() {} async fn post_root() {} async fn delete_root() {} -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # More examples @@ -105,9 +103,7 @@ async fn show_user(Path(id): Path) {} async fn do_users_action(Path((version, id)): Path<(String, u64)>) {} async fn serve_asset(Path(path): Path) {} -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` # Panics @@ -120,9 +116,7 @@ use axum::{routing::get, Router}; let app = Router::new() .route("/", get(|| async {})) .route("/", get(|| async {})); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` The static route `/foo` and the dynamic route `/:key` are not considered to diff --git a/axum/src/docs/routing/route_layer.md b/axum/src/docs/routing/route_layer.md index fe5b8faa..5e60eaf5 100644 --- a/axum/src/docs/routing/route_layer.md +++ b/axum/src/docs/routing/route_layer.md @@ -26,7 +26,5 @@ let app = Router::new() // `GET /foo` with a valid token will receive `200 OK` // `GET /foo` with a invalid token will receive `401 Unauthorized` // `GET /not-found` with a invalid token will receive `404 Not Found` -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` diff --git a/axum/src/docs/routing/route_service.md b/axum/src/docs/routing/route_service.md index 190e0183..7f016105 100644 --- a/axum/src/docs/routing/route_service.md +++ b/axum/src/docs/routing/route_service.md @@ -43,9 +43,7 @@ let app = Router::new() "/static/Cargo.toml", ServeFile::new("Cargo.toml"), ); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` Routing to arbitrary services in this way has complications for backpressure @@ -64,9 +62,7 @@ let app = Router::new().route_service( "/", Router::new().route("/foo", get(|| async {})), ); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; +# let _: Router = app; ``` Use [`Router::nest`] instead. diff --git a/axum/src/docs/routing/with_state.md b/axum/src/docs/routing/with_state.md index 3f9c8151..bece920f 100644 --- a/axum/src/docs/routing/with_state.md +++ b/axum/src/docs/routing/with_state.md @@ -13,9 +13,8 @@ let routes = Router::new() .with_state(AppState {}); # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(routes.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, routes).await.unwrap(); # }; ``` @@ -40,9 +39,8 @@ fn routes() -> Router { let routes = routes().with_state(AppState {}); # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(routes.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, routes).await.unwrap(); # }; ``` @@ -64,9 +62,8 @@ fn routes(state: AppState) -> Router { let routes = routes(AppState {}); # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(routes.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, routes).await.unwrap(); # }; ``` @@ -92,9 +89,8 @@ fn routes(state: AppState) -> Router { let routes = Router::new().nest("/api", routes(AppState {})); # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(routes.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, routes).await.unwrap(); # }; ``` @@ -133,9 +129,8 @@ let router: Router<()> = router.with_state(AppState {}); // You cannot call `into_make_service` on a `Router` // because it is still missing an `AppState`. # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(router.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, router).await.unwrap(); # }; ``` @@ -163,9 +158,8 @@ let final_router: Router<()> = string_router.with_state("foo".to_owned()); // Since we have a `Router<()>` we can run it. # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(final_router.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, final_router).await.unwrap(); # }; ``` @@ -190,9 +184,8 @@ let app = routes(AppState {}); // We can only call `Router::into_make_service` on a `Router<()>` // but `app` is a `Router` # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(app.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, app).await.unwrap(); # }; ``` @@ -214,9 +207,8 @@ let app = routes(AppState {}); // We can now call `Router::into_make_service` # async { -axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(app.into_make_service()) - .await; +let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +axum::serve(listener, app).await.unwrap(); # }; ``` diff --git a/axum/src/extension.rs b/axum/src/extension.rs index d66c9466..42f208c4 100644 --- a/axum/src/extension.rs +++ b/axum/src/extension.rs @@ -40,9 +40,7 @@ use tower_service::Service; /// // Add middleware that inserts the state into all incoming request's /// // extensions. /// .layer(Extension(state)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// If the extension is missing it will reject the request with a `500 Internal diff --git a/axum/src/extract/connect_info.rs b/axum/src/extract/connect_info.rs index f22b8981..216022e8 100644 --- a/axum/src/extract/connect_info.rs +++ b/axum/src/extract/connect_info.rs @@ -5,7 +5,7 @@ //! [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info use super::{Extension, FromRequestParts}; -use crate::middleware::AddExtension; +use crate::{middleware::AddExtension, serve::IncomingStream}; use async_trait::async_trait; use http::request::Parts; use hyper::server::conn::AddrStream; @@ -89,6 +89,12 @@ impl Connected<&AddrStream> for SocketAddr { } } +impl Connected> for SocketAddr { + fn connect_info(target: IncomingStream<'_>) -> Self { + target.remote_addr() + } +} + impl Service for IntoMakeServiceWithConnectInfo where S: Clone, @@ -213,8 +219,9 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{routing::get, test_helpers::TestClient, Router, Server}; - use std::net::{SocketAddr, TcpListener}; + use crate::{routing::get, test_helpers::TestClient, Router}; + use std::net::SocketAddr; + use tokio::net::TcpListener; #[crate::test] async fn socket_addr() { @@ -222,17 +229,19 @@ mod tests { format!("{addr}") } - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let addr = listener.local_addr().unwrap(); let (tx, rx) = tokio::sync::oneshot::channel(); tokio::spawn(async move { let app = Router::new().route("/", get(handler)); - let server = Server::from_tcp(listener) - .unwrap() - .serve(app.into_make_service_with_connect_info::()); tx.send(()).unwrap(); - server.await.expect("server error"); + crate::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await + .unwrap(); }); rx.await.unwrap(); @@ -250,8 +259,8 @@ mod tests { value: &'static str, } - impl Connected<&AddrStream> for MyConnectInfo { - fn connect_info(_target: &AddrStream) -> Self { + impl Connected> for MyConnectInfo { + fn connect_info(_target: IncomingStream<'_>) -> Self { Self { value: "it worked!", } @@ -262,17 +271,19 @@ mod tests { addr.value } - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let addr = listener.local_addr().unwrap(); let (tx, rx) = tokio::sync::oneshot::channel(); tokio::spawn(async move { let app = Router::new().route("/", get(handler)); - let server = Server::from_tcp(listener) - .unwrap() - .serve(app.into_make_service_with_connect_info::()); tx.send(()).unwrap(); - server.await.expect("server error"); + crate::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await + .unwrap(); }); rx.await.unwrap(); @@ -306,7 +317,7 @@ mod tests { format!("{addr}") } - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let addr = listener.local_addr().unwrap(); tokio::spawn(async move { @@ -314,10 +325,12 @@ mod tests { .route("/", get(handler)) .layer(MockConnectInfo(SocketAddr::from(([0, 0, 0, 0], 1337)))); - let server = Server::from_tcp(listener) - .unwrap() - .serve(app.into_make_service_with_connect_info::()); - server.await.expect("server error"); + crate::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await + .unwrap(); }); let client = reqwest::Client::new(); diff --git a/axum/src/extract/matched_path.rs b/axum/src/extract/matched_path.rs index 50224c32..8cef754f 100644 --- a/axum/src/extract/matched_path.rs +++ b/axum/src/extract/matched_path.rs @@ -20,9 +20,7 @@ use std::{collections::HashMap, sync::Arc}; /// // `path` will be "/users/:id" /// }) /// ); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// # Accessing `MatchedPath` via extensions diff --git a/axum/src/extract/multipart.rs b/axum/src/extract/multipart.rs index ae8f9da1..c8478d09 100644 --- a/axum/src/extract/multipart.rs +++ b/axum/src/extract/multipart.rs @@ -48,9 +48,7 @@ use std::{ /// } /// /// let app = Router::new().route("/upload", post(upload)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[cfg_attr(docsrs, doc(cfg(feature = "multipart")))] #[derive(Debug)] diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 189e476e..5f7c7107 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -42,9 +42,7 @@ use std::{fmt, sync::Arc}; /// } /// /// let app = Router::new().route("/users/:user_id/team/:team_id", get(users_teams_show)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// If the path contains only one parameter, then you can omit the tuple. @@ -62,9 +60,7 @@ use std::{fmt, sync::Arc}; /// } /// /// let app = Router::new().route("/users/:user_id", get(user_info)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// Path segments also can be deserialized into any type that implements @@ -103,9 +99,7 @@ use std::{fmt, sync::Arc}; /// "/users/:user_id/team/:team_id", /// get(users_teams_show).post(users_teams_create), /// ); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// If you wish to capture all path parameters you can use `HashMap` or `Vec`: @@ -132,9 +126,7 @@ use std::{fmt, sync::Arc}; /// /// let app = Router::new() /// .route("/users/:user_id/team/:team_id", get(params_map).post(params_vec)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// # Providing detailed rejection output diff --git a/axum/src/extract/query.rs b/axum/src/extract/query.rs index 49efe8cb..81b28b61 100644 --- a/axum/src/extract/query.rs +++ b/axum/src/extract/query.rs @@ -32,9 +32,7 @@ use serde::de::DeserializeOwned; /// } /// /// let app = Router::new().route("/list_things", get(list_things)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// If the query string cannot be parsed it will reject the request with a `400 diff --git a/axum/src/extract/raw_form.rs b/axum/src/extract/raw_form.rs index b1a6f973..f9c7d357 100644 --- a/axum/src/extract/raw_form.rs +++ b/axum/src/extract/raw_form.rs @@ -25,9 +25,7 @@ use super::{ /// async fn handler(RawForm(form): RawForm) {} /// /// let app = Router::new().route("/", get(handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[derive(Debug)] pub struct RawForm(pub Bytes); diff --git a/axum/src/extract/raw_query.rs b/axum/src/extract/raw_query.rs index 98a60b09..d8c56f84 100644 --- a/axum/src/extract/raw_query.rs +++ b/axum/src/extract/raw_query.rs @@ -20,9 +20,7 @@ use std::convert::Infallible; /// } /// /// let app = Router::new().route("/users", get(handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[derive(Debug)] pub struct RawQuery(pub Option); diff --git a/axum/src/extract/request_parts.rs b/axum/src/extract/request_parts.rs index 7dfb2f26..9756665b 100644 --- a/axum/src/extract/request_parts.rs +++ b/axum/src/extract/request_parts.rs @@ -28,9 +28,7 @@ use std::convert::Infallible; /// ); /// /// let app = Router::new().nest("/api", api_routes); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// # Extracting via request extensions @@ -65,9 +63,7 @@ use std::convert::Infallible; /// ); /// /// let app = Router::new().nest("/api", api_routes); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[cfg(feature = "original-uri")] #[derive(Debug, Clone)] diff --git a/axum/src/extract/state.rs b/axum/src/extract/state.rs index e2307d39..1592a813 100644 --- a/axum/src/extract/state.rs +++ b/axum/src/extract/state.rs @@ -131,13 +131,11 @@ use std::{ /// let method_router_with_state = get(handler) /// // provide the state so the handler can access it /// .with_state(state); +/// # let _: axum::routing::MethodRouter = method_router_with_state; /// /// async fn handler(State(state): State) { /// // use `state`... /// } -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(method_router_with_state.into_make_service()).await.unwrap(); -/// # }; /// ``` /// /// # With `Handler` @@ -158,10 +156,8 @@ use std::{ /// let handler_with_state = handler.with_state(state); /// /// # async { -/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -/// .serve(handler_with_state.into_make_service()) -/// .await -/// .expect("server failed"); +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, handler_with_state.into_make_service()).await.unwrap(); /// # }; /// ``` /// diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index d3a014ce..991ee0b8 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -31,9 +31,7 @@ //! } //! } //! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! # Passing data and/or state to an `on_upgrade` callback @@ -62,9 +60,7 @@ //! let app = Router::new() //! .route("/ws", get(handler)) //! .with_state(AppState { /* ... */ }); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! # Read and write concurrently @@ -108,7 +104,6 @@ use http::{ request::Parts, Method, StatusCode, }; -use hyper::upgrade::{OnUpgrade, Upgraded}; use sha1::{Digest, Sha1}; use std::{ borrow::Cow, @@ -137,7 +132,7 @@ pub struct WebSocketUpgrade { /// The chosen protocol sent in the `Sec-WebSocket-Protocol` header of the response. protocol: Option, sec_websocket_key: HeaderValue, - on_upgrade: OnUpgrade, + on_upgrade: hyper1::upgrade::OnUpgrade, on_failed_upgrade: F, sec_websocket_protocol: Option, } @@ -206,9 +201,7 @@ impl WebSocketUpgrade { /// // ... /// }) /// } - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` pub fn protocols(mut self, protocols: I) -> Self where @@ -393,7 +386,7 @@ where let on_upgrade = parts .extensions - .remove::() + .remove::() .ok_or(ConnectionNotUpgradable)?; let sec_websocket_protocol = parts.headers.get(header::SEC_WEBSOCKET_PROTOCOL).cloned(); @@ -436,7 +429,7 @@ fn header_contains(headers: &HeaderMap, key: HeaderName, value: &'static str) -> /// See [the module level documentation](self) for more details. #[derive(Debug)] pub struct WebSocket { - inner: WebSocketStream, + inner: WebSocketStream, protocol: Option, } diff --git a/axum/src/handler/mod.rs b/axum/src/handler/mod.rs index 69e7879c..d1370145 100644 --- a/axum/src/handler/mod.rs +++ b/axum/src/handler/mod.rs @@ -135,9 +135,7 @@ pub trait Handler: Clone + Send + Sized + 'static { /// /// let layered_handler = handler.layer(ConcurrencyLimitLayer::new(64)); /// let app = Router::new().route("/", get(layered_handler)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` fn layer(self, layer: L) -> Layered where diff --git a/axum/src/handler/service.rs b/axum/src/handler/service.rs index 86b93afe..35974a4c 100644 --- a/axum/src/handler/service.rs +++ b/axum/src/handler/service.rs @@ -37,7 +37,6 @@ impl HandlerService { /// /// ```rust /// use axum::{ - /// Server, /// handler::Handler, /// extract::State, /// http::{Uri, Method}, @@ -55,10 +54,8 @@ impl HandlerService { /// let app = handler.with_state(AppState {}); /// /// # async { - /// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))) - /// .serve(app.into_make_service()) - /// .await?; - /// # Ok::<_, hyper::Error>(()) + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, app.into_make_service()).await.unwrap(); /// # }; /// ``` /// @@ -74,7 +71,6 @@ impl HandlerService { /// /// ```rust /// use axum::{ - /// Server, /// handler::Handler, /// response::IntoResponse, /// extract::{ConnectInfo, State}, @@ -94,10 +90,11 @@ impl HandlerService { /// let app = handler.with_state(AppState {}); /// /// # async { - /// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))) - /// .serve(app.into_make_service_with_connect_info::()) - /// .await?; - /// # Ok::<_, hyper::Error>(()) + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve( + /// listener, + /// app.into_make_service_with_connect_info::(), + /// ).await.unwrap(); /// # }; /// ``` /// @@ -179,3 +176,27 @@ where super::future::IntoServiceFuture::new(future) } } + +// for `axum::serve(listener, handler)` +#[cfg(feature = "tokio")] +const _: () = { + use crate::serve::IncomingStream; + + impl Service> for HandlerService + where + H: Clone, + S: Clone, + { + type Response = Self; + type Error = Infallible; + type Future = std::future::Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: IncomingStream<'_>) -> Self::Future { + std::future::ready(Ok(self.clone())) + } + } +}; diff --git a/axum/src/json.rs b/axum/src/json.rs index 2cab54e9..a78f0644 100644 --- a/axum/src/json.rs +++ b/axum/src/json.rs @@ -50,9 +50,7 @@ use serde::{de::DeserializeOwned, Serialize}; /// } /// /// let app = Router::new().route("/users", post(create_user)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// When used as a response, it can serialize any type that implements [`serde::Serialize`] to @@ -87,9 +85,7 @@ use serde::{de::DeserializeOwned, Serialize}; /// } /// /// let app = Router::new().route("/users/:id", get(get_user)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` #[derive(Debug, Clone, Copy, Default)] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] diff --git a/axum/src/lib.rs b/axum/src/lib.rs index da60aef5..3813de14 100644 --- a/axum/src/lib.rs +++ b/axum/src/lib.rs @@ -53,11 +53,9 @@ //! // build our application with a single route //! let app = Router::new().route("/", get(|| async { "Hello, World!" })); //! -//! // run it with hyper on localhost:3000 -//! axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -//! .serve(app.into_make_service()) -//! .await -//! .unwrap(); +//! // run it on localhost:3000 +//! let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +//! axum::serve(listener, app).await.unwrap(); //! } //! ``` //! @@ -82,9 +80,7 @@ //! async fn get_foo() {} //! async fn post_foo() {} //! async fn foo_bar() {} -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! See [`Router`] for more details on routing. @@ -145,9 +141,7 @@ //! let app = Router::new() //! .route("/plain_text", get(plain_text)) //! .route("/json", get(json)); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! See [`response`](crate::response) for more details on building responses. @@ -202,9 +196,7 @@ //! ) { //! // ... //! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! You should prefer using [`State`] if possible since it's more type safe. The downside is that @@ -240,9 +232,7 @@ //! ) { //! // ... //! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! The downside to this approach is that you'll get runtime errors @@ -298,9 +288,7 @@ //! struct CreateUserPayload { //! // ... //! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; +//! # let _: Router = app; //! ``` //! //! The downside to this approach is that it's a little more verbose than using @@ -356,7 +344,7 @@ //! `matched-path` | Enables capturing of every request's router path and the [`MatchedPath`] extractor | Yes //! `multipart` | Enables parsing `multipart/form-data` requests with [`Multipart`] | No //! `original-uri` | Enables capturing of every request's original URI and the [`OriginalUri`] extractor | Yes -//! `tokio` | Enables `tokio` as a dependency and `axum::Server`, `SSE` and `extract::connect_info` types. | Yes +//! `tokio` | Enables `tokio` as a dependency and `axum::serve`, `SSE` and `extract::connect_info` types. | Yes //! `tower-log` | Enables `tower`'s `log` feature | Yes //! `tracing` | Log rejections from built-in extractors | No //! `ws` | Enables WebSockets support via [`extract::ws`] | No @@ -377,7 +365,6 @@ //! [`Timeout`]: tower::timeout::Timeout //! [examples]: https://github.com/tokio-rs/axum/tree/main/examples //! [`Router::merge`]: crate::routing::Router::merge -//! [`axum::Server`]: hyper::server::Server //! [`Service`]: tower::Service //! [`Service::poll_ready`]: tower::Service::poll_ready //! [`Service`'s]: tower::Service @@ -459,6 +446,8 @@ pub mod handler; pub mod middleware; pub mod response; pub mod routing; +#[cfg(feature = "tokio")] +pub mod serve; #[cfg(test)] mod test_helpers; @@ -470,9 +459,6 @@ pub use async_trait::async_trait; pub use headers; #[doc(no_inline)] pub use http; -#[cfg(feature = "tokio")] -#[doc(no_inline)] -pub use hyper::Server; #[doc(inline)] pub use self::extension::Extension; @@ -496,6 +482,10 @@ pub use axum_core::{BoxError, Error, RequestExt, RequestPartsExt}; #[cfg(feature = "macros")] pub use axum_macros::debug_handler; +#[cfg(feature = "tokio")] +#[doc(inline)] +pub use self::serve::serve; + pub use self::service_ext::ServiceExt; #[cfg(test)] diff --git a/axum/src/middleware/from_extractor.rs b/axum/src/middleware/from_extractor.rs index 8c9a2483..e120ffc1 100644 --- a/axum/src/middleware/from_extractor.rs +++ b/axum/src/middleware/from_extractor.rs @@ -84,9 +84,7 @@ use tower_service::Service; /// .route("/foo", post(other_handler)) /// // The extractor will run before all routes /// .route_layer(from_extractor::()); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// [`Bytes`]: bytes::Bytes diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 9040ee30..dc8f9ec0 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -48,9 +48,7 @@ macro_rules! top_level_service_fn { /// /// // Requests to `GET /` will go to `service`. /// let app = Router::new().route("/", get_service(service)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` /// /// Note that `get` routes will also be called for `HEAD` requests but will have @@ -109,9 +107,7 @@ macro_rules! top_level_handler_fn { /// /// // 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(); - /// # }; + /// # let _: Router = app; /// ``` /// /// Note that `get` routes will also be called for `HEAD` requests but will have @@ -180,9 +176,7 @@ macro_rules! chained_service_fn { /// // Requests to `POST /` will go to `service` and `GET /` will go to /// // `other_service`. /// let app = Router::new().route("/", post_service(service).get_service(other_service)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` /// /// Note that `get` routes will also be called for `HEAD` requests but will have @@ -244,9 +238,7 @@ macro_rules! chained_handler_fn { /// // Requests to `POST /` will go to `handler` and `GET /` 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(); - /// # }; + /// # let _: Router = app; /// ``` /// /// Note that `get` routes will also be called for `HEAD` requests but will have @@ -316,9 +308,7 @@ top_level_service_fn!(trace_service, TRACE); /// /// // Requests to `POST /` will go to `service`. /// let app = Router::new().route("/", on_service(MethodFilter::POST, service)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` pub fn on_service(filter: MethodFilter, svc: T) -> MethodRouter where @@ -350,9 +340,7 @@ where /// /// // All requests to `/` will go to `service`. /// let app = Router::new().route("/", any_service(service)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// Additional methods can still be chained: @@ -379,9 +367,7 @@ where /// /// // `POST /` goes to `other_service`. All other requests go to `service` /// let app = Router::new().route("/", any_service(service).post_service(other_service)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` pub fn any_service(svc: T) -> MethodRouter where @@ -419,9 +405,7 @@ top_level_handler_fn!(trace, TRACE); /// /// // 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(); -/// # }; +/// # let _: Router = app; /// ``` pub fn on(filter: MethodFilter, handler: H) -> MethodRouter where @@ -446,9 +430,7 @@ where /// /// // All requests to `/` will go to `handler`. /// let app = Router::new().route("/", any(handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// Additional methods can still be chained: @@ -465,9 +447,7 @@ where /// /// // `POST /` goes to `other_handler`. All other requests go to `handler` /// let app = Router::new().route("/", any(handler).post(other_handler)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` pub fn any(handler: H) -> MethodRouter where @@ -587,9 +567,7 @@ where /// // 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(); - /// # }; + /// # let _: Router = app; /// ``` #[track_caller] pub fn on(self, filter: MethodFilter, handler: H) -> Self @@ -632,7 +610,6 @@ impl MethodRouter<(), Infallible> { /// /// ```rust /// use axum::{ - /// Server, /// handler::Handler, /// http::{Uri, Method}, /// response::IntoResponse, @@ -647,10 +624,8 @@ impl MethodRouter<(), Infallible> { /// let router = get(handler).post(handler); /// /// # async { - /// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))) - /// .serve(router.into_make_service()) - /// .await?; - /// # Ok::<_, hyper::Error>(()) + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, router.into_make_service()).await.unwrap(); /// # }; /// ``` /// @@ -666,7 +641,6 @@ impl MethodRouter<(), Infallible> { /// /// ```rust /// use axum::{ - /// Server, /// handler::Handler, /// response::IntoResponse, /// extract::ConnectInfo, @@ -681,10 +655,8 @@ impl MethodRouter<(), Infallible> { /// let router = get(handler).post(handler); /// /// # async { - /// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))) - /// .serve(router.into_make_service_with_connect_info::()) - /// .await?; - /// # Ok::<_, hyper::Error>(()) + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, router.into_make_service()).await.unwrap(); /// # }; /// ``` /// @@ -758,9 +730,7 @@ where /// /// // Requests to `DELETE /` will go to `service` /// let app = Router::new().route("/", on_service(MethodFilter::DELETE, service)); - /// # async { - /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); - /// # }; + /// # let _: Router = app; /// ``` #[track_caller] pub fn on_service(self, filter: MethodFilter, svc: T) -> Self @@ -1261,6 +1231,26 @@ where } } +// for `axum::serve(listener, router)` +#[cfg(feature = "tokio")] +const _: () = { + use crate::serve::IncomingStream; + + impl Service> for MethodRouter<()> { + type Response = Self; + type Error = Infallible; + type Future = std::future::Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: IncomingStream<'_>) -> Self::Future { + std::future::ready(Ok(self.clone())) + } + } +}; + #[cfg(test)] mod tests { use super::*; @@ -1361,7 +1351,7 @@ mod tests { } #[allow(dead_code)] - fn buiding_complex_router() { + async fn buiding_complex_router() { let app = crate::Router::new().route( "/", // use the all the things :bomb: @@ -1380,7 +1370,8 @@ mod tests { ), ); - crate::Server::bind(&"0.0.0.0:0".parse().unwrap()).serve(app.into_make_service()); + let listener = tokio::net::TcpListener::bind("0.0.0.0:0").await.unwrap(); + crate::serve(listener, app).await.unwrap(); } #[crate::test] diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 74660fe6..c45b823e 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -413,10 +413,8 @@ impl Router { /// let app = Router::new().route("/", get(|| async { "Hi!" })); /// /// # async { - /// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - /// .serve(app.into_make_service()) - /// .await - /// .expect("server failed"); + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, app).await.unwrap(); /// # }; /// ``` /// @@ -436,6 +434,26 @@ impl Router { } } +// for `axum::serve(listener, router)` +#[cfg(feature = "tokio")] +const _: () = { + use crate::serve::IncomingStream; + + impl Service> for Router<()> { + type Response = Self; + type Error = Infallible; + type Future = std::future::Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: IncomingStream<'_>) -> Self::Future { + std::future::ready(Ok(self.clone())) + } + } +}; + impl Service> for Router<()> where B: HttpBody + Send + 'static, diff --git a/axum/src/serve.rs b/axum/src/serve.rs new file mode 100644 index 00000000..83f529c8 --- /dev/null +++ b/axum/src/serve.rs @@ -0,0 +1,234 @@ +//! Serve services. + +use std::{convert::Infallible, io, net::SocketAddr}; + +use axum_core::{body::Body, extract::Request, response::Response}; +use futures_util::{future::poll_fn, FutureExt}; +use hyper1::server::conn::http1; +use tokio::net::{TcpListener, TcpStream}; +use tower_hyper_http_body_compat::{HttpBody04ToHttpBody1, HttpBody1ToHttpBody04}; +use tower_service::Service; + +/// Serve the service with the supplied listener. +/// +/// This method of running a service is intentionally simple and doesn't support any configuration. +/// Use hyper or hyper-util if you need configuration. +/// +/// It only supports HTTP/1. +/// +/// # Examples +/// +/// Serving a [`Router`]: +/// +/// ``` +/// use axum::{Router, routing::get}; +/// +/// # async { +/// let router = Router::new().route("/", get(|| async { "Hello, World!" })); +/// +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, router).await.unwrap(); +/// # }; +/// ``` +/// +/// See also [`Router::into_make_service_with_connect_info`]. +/// +/// Serving a [`MethodRouter`]: +/// +/// ``` +/// use axum::routing::get; +/// +/// # async { +/// let router = get(|| async { "Hello, World!" }); +/// +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, router).await.unwrap(); +/// # }; +/// ``` +/// +/// See also [`MethodRouter::into_make_service_with_connect_info`]. +/// +/// Serving a [`Handler`]: +/// +/// ``` +/// use axum::handler::HandlerWithoutStateExt; +/// +/// # async { +/// async fn handler() -> &'static str { +/// "Hello, World!" +/// } +/// +/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +/// axum::serve(listener, handler.into_make_service()).await.unwrap(); +/// # }; +/// ``` +/// +/// See also [`HandlerWithoutStateExt::into_make_service_with_connect_info`] and +/// [`HandlerService::into_make_service_with_connect_info`]. +/// +/// [`Router`]: crate::Router +/// [`Router::into_make_service_with_connect_info`]: crate::Router::into_make_service_with_connect_info +/// [`MethodRouter`]: crate::routing::MethodRouter +/// [`MethodRouter::into_make_service_with_connect_info`]: crate::routing::MethodRouter::into_make_service_with_connect_info +/// [`Handler`]: crate::handler::Handler +/// [`HandlerWithoutStateExt::into_make_service_with_connect_info`]: crate::handler::HandlerWithoutStateExt::into_make_service_with_connect_info +/// [`HandlerService::into_make_service_with_connect_info`]: crate::handler::HandlerService::into_make_service_with_connect_info +#[cfg(feature = "tokio")] +pub async fn serve(tcp_listener: TcpListener, mut make_service: M) -> io::Result<()> +where + M: for<'a> Service, Error = Infallible, Response = S>, + S: Service + Clone + Send + 'static, + S::Future: Send, +{ + loop { + let (tcp_stream, remote_addr) = tcp_listener.accept().await?; + + poll_fn(|cx| make_service.poll_ready(cx)) + .await + .unwrap_or_else(|err| match err {}); + + let mut service = make_service + .call(IncomingStream { + tcp_stream: &tcp_stream, + remote_addr, + }) + .await + .unwrap_or_else(|err| match err {}); + + let service = hyper1::service::service_fn(move |req: Request| { + let req = req.map(|body| { + // wont need this when axum uses http-body 1.0 + let http_body_04 = HttpBody1ToHttpBody04::new(body); + Body::new(http_body_04) + }); + + // doing this saves cloning the service just to await the service being ready + // + // services like `Router` are always ready, so assume the service + // we're running here is also always ready... + match futures_util::future::poll_fn(|cx| service.poll_ready(cx)).now_or_never() { + Some(Ok(())) => {} + Some(Err(err)) => match err {}, + None => { + // ...otherwise load shed + let mut res = Response::new(HttpBody04ToHttpBody1::new(Body::empty())); + *res.status_mut() = http::StatusCode::SERVICE_UNAVAILABLE; + return std::future::ready(Ok(res)).left_future(); + } + } + + let future = service.call(req); + + async move { + let response = future + .await + .unwrap_or_else(|err| match err {}) + // wont need this when axum uses http-body 1.0 + .map(HttpBody04ToHttpBody1::new); + + Ok::<_, Infallible>(response) + } + .right_future() + }); + + tokio::task::spawn(async move { + match http1::Builder::new() + .serve_connection(tcp_stream, service) + // for websockets + .with_upgrades() + .await + { + Ok(()) => {} + Err(_err) => { + // This error only appears when the client doesn't send a request and + // terminate the connection. + // + // If client sends one request then terminate connection whenever, it doesn't + // appear. + } + } + }); + } +} + +/// An incoming stream. +/// +/// Used with [`serve`] and [`IntoMakeServiceWithConnectInfo`]. +/// +/// [`IntoMakeServiceWithConnectInfo`]: crate::extract::connect_info::IntoMakeServiceWithConnectInfo +#[derive(Debug)] +pub struct IncomingStream<'a> { + tcp_stream: &'a TcpStream, + remote_addr: SocketAddr, +} + +impl IncomingStream<'_> { + /// Returns the local address that this stream is bound to. + pub fn local_addr(&self) -> std::io::Result { + self.tcp_stream.local_addr() + } + + /// Returns the remote address that this stream is bound to. + pub fn remote_addr(&self) -> SocketAddr { + self.remote_addr + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + handler::{Handler, HandlerWithoutStateExt}, + routing::get, + Router, + }; + + #[allow(dead_code, unused_must_use)] + async fn if_it_compiles_it_works() { + let router: Router = Router::new(); + + let addr = "0.0.0.0:0"; + + // router + serve(TcpListener::bind(addr).await.unwrap(), router.clone()); + serve( + TcpListener::bind(addr).await.unwrap(), + router.clone().into_make_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + router.into_make_service_with_connect_info::(), + ); + + // method router + serve(TcpListener::bind(addr).await.unwrap(), get(handler)); + serve( + TcpListener::bind(addr).await.unwrap(), + get(handler).into_make_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + get(handler).into_make_service_with_connect_info::(), + ); + + // handler + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + handler.with_state(()), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_make_service(), + ); + serve( + TcpListener::bind(addr).await.unwrap(), + handler.into_make_service_with_connect_info::(), + ); + } + + async fn handler() {} +} diff --git a/axum/src/test_helpers/mod.rs b/axum/src/test_helpers/mod.rs index a8b4cf99..3bb1535e 100644 --- a/axum/src/test_helpers/mod.rs +++ b/axum/src/test_helpers/mod.rs @@ -1,6 +1,6 @@ #![allow(clippy::disallowed_names)] -use crate::{body::HttpBody, BoxError}; +use crate::{extract::Request, response::Response, serve}; mod test_client; pub(crate) use self::test_client::*; diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index fa052a00..9e1858cb 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -1,11 +1,11 @@ -use super::{BoxError, HttpBody}; +use super::{serve, Request, Response}; use bytes::Bytes; use http::{ header::{HeaderName, HeaderValue}, - Request, StatusCode, + StatusCode, }; -use hyper::Server; -use std::net::{SocketAddr, TcpListener}; +use std::{convert::Infallible, net::SocketAddr}; +use tokio::net::TcpListener; use tower::make::Shared; use tower_service::Service; @@ -15,25 +15,22 @@ pub(crate) struct TestClient { } impl TestClient { - pub(crate) fn new(svc: S) -> Self + pub(crate) fn new(svc: S) -> Self where - S: Service, Response = http::Response> - + Clone - + Send - + 'static, - ResBody: HttpBody + Send + 'static, - ResBody::Data: Send, - ResBody::Error: Into, + S: Service + Clone + Send + 'static, S::Future: Send, - S::Error: Into, { - let listener = TcpListener::bind("127.0.0.1:0").expect("Could not bind ephemeral socket"); + let std_listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + std_listener.set_nonblocking(true).unwrap(); + let listener = TcpListener::from_std(std_listener).unwrap(); + let addr = listener.local_addr().unwrap(); println!("Listening on {addr}"); tokio::spawn(async move { - let server = Server::from_tcp(listener).unwrap().serve(Shared::new(svc)); - server.await.expect("server error"); + serve(listener, Shared::new(svc)) + .await + .expect("server error") }); let client = reqwest::Client::builder() diff --git a/axum/src/typed_header.rs b/axum/src/typed_header.rs index 72b3d7e7..229cd06d 100644 --- a/axum/src/typed_header.rs +++ b/axum/src/typed_header.rs @@ -27,9 +27,7 @@ use std::convert::Infallible; /// } /// /// let app = Router::new().route("/users/:user_id/team/:team_id", get(users_teams_show)); -/// # async { -/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -/// # }; +/// # let _: Router = app; /// ``` /// /// # As response diff --git a/deny.toml b/deny.toml index c543455c..9a3fd955 100644 --- a/deny.toml +++ b/deny.toml @@ -25,6 +25,8 @@ skip-tree = [ { name = "spin" }, # lots still pulls in syn 1.x { name = "syn" }, + # until 1.0 is out we're pulling in both 0.14 and 1.0-rc.x + { name = "hyper" }, ] [sources] diff --git a/examples/anyhow-error-response/src/main.rs b/examples/anyhow-error-response/src/main.rs index 68548313..b7a2416a 100644 --- a/examples/anyhow-error-response/src/main.rs +++ b/examples/anyhow-error-response/src/main.rs @@ -10,18 +10,16 @@ use axum::{ routing::get, Router, }; -use std::net::SocketAddr; #[tokio::main] async fn main() { let app = Router::new().route("/", get(handler)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler() -> Result<(), AppError> { diff --git a/examples/chat/src/main.rs b/examples/chat/src/main.rs index 3b49c3e8..f7298c34 100644 --- a/examples/chat/src/main.rs +++ b/examples/chat/src/main.rs @@ -18,7 +18,6 @@ use axum::{ use futures::{sink::SinkExt, stream::StreamExt}; use std::{ collections::HashSet, - net::SocketAddr, sync::{Arc, Mutex}, }; use tokio::sync::broadcast; @@ -53,12 +52,11 @@ async fn main() { .route("/websocket", get(websocket_handler)) .with_state(app_state); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn websocket_handler( diff --git a/examples/consume-body-in-extractor-or-middleware/src/main.rs b/examples/consume-body-in-extractor-or-middleware/src/main.rs index f1b2cbf7..fd63fcb1 100644 --- a/examples/consume-body-in-extractor-or-middleware/src/main.rs +++ b/examples/consume-body-in-extractor-or-middleware/src/main.rs @@ -14,7 +14,6 @@ use axum::{ routing::post, Router, }; -use std::net::SocketAddr; use tower::ServiceBuilder; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -32,12 +31,11 @@ async fn main() { .route("/", post(handler)) .layer(ServiceBuilder::new().layer(middleware::from_fn(print_request_body))); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } // middleware that shows how to consume the request body upfront diff --git a/examples/cors/src/main.rs b/examples/cors/src/main.rs index a0a5e8d8..bbd19206 100644 --- a/examples/cors/src/main.rs +++ b/examples/cors/src/main.rs @@ -40,10 +40,8 @@ async fn main() { async fn serve(app: Router, port: u16) { let addr = SocketAddr::from(([127, 0, 0, 1], port)); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app).await.unwrap(); } async fn html() -> impl IntoResponse { diff --git a/examples/customize-extractor-error/src/main.rs b/examples/customize-extractor-error/src/main.rs index 893fbd14..e8820326 100644 --- a/examples/customize-extractor-error/src/main.rs +++ b/examples/customize-extractor-error/src/main.rs @@ -9,7 +9,6 @@ mod derive_from_request; mod with_rejection; use axum::{routing::post, Router}; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -29,10 +28,9 @@ async fn main() { .route("/derive-from-request", post(derive_from_request::handler)); // Run our application - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } diff --git a/examples/customize-path-rejection/src/main.rs b/examples/customize-path-rejection/src/main.rs index 3bcd91d3..fa382e4b 100644 --- a/examples/customize-path-rejection/src/main.rs +++ b/examples/customize-path-rejection/src/main.rs @@ -13,7 +13,6 @@ use axum::{ Router, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -30,12 +29,11 @@ async fn main() { let app = Router::new().route("/users/:user_id/teams/:team_id", get(handler)); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler(Path(params): Path) -> impl IntoResponse { diff --git a/examples/error-handling-and-dependency-injection/src/main.rs b/examples/error-handling-and-dependency-injection/src/main.rs index f28ed4ff..a5bdad9e 100644 --- a/examples/error-handling-and-dependency-injection/src/main.rs +++ b/examples/error-handling-and-dependency-injection/src/main.rs @@ -17,7 +17,7 @@ use axum::{ }; use serde::{Deserialize, Serialize}; use serde_json::json; -use std::{net::SocketAddr, sync::Arc}; +use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use uuid::Uuid; @@ -42,12 +42,11 @@ async fn main() { .with_state(user_repo); // Run our application - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } /// Handler for `GET /users/:id`. diff --git a/examples/form/src/main.rs b/examples/form/src/main.rs index 93a5ae13..3f9ed095 100644 --- a/examples/form/src/main.rs +++ b/examples/form/src/main.rs @@ -6,7 +6,6 @@ use axum::{extract::Form, response::Html, routing::get, Router}; use serde::Deserialize; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -22,13 +21,12 @@ async fn main() { // build our application with some routes let app = Router::new().route("/", get(show_form).post(accept_form)); - // run it with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + // run it + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn show_form() -> Html<&'static str> { diff --git a/examples/global-404-handler/src/main.rs b/examples/global-404-handler/src/main.rs index 8eb14691..38b02943 100644 --- a/examples/global-404-handler/src/main.rs +++ b/examples/global-404-handler/src/main.rs @@ -10,7 +10,6 @@ use axum::{ routing::get, Router, }; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -30,12 +29,11 @@ async fn main() { let app = app.fallback(handler_404); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler() -> Html<&'static str> { diff --git a/examples/graceful-shutdown/Cargo.toml b/examples/graceful-shutdown/Cargo.toml index 19af3113..f287feeb 100644 --- a/examples/graceful-shutdown/Cargo.toml +++ b/examples/graceful-shutdown/Cargo.toml @@ -6,4 +6,5 @@ publish = false [dependencies] axum = { path = "../../axum" } +hyper = { version = "0.14", features = ["full"] } tokio = { version = "1.0", features = ["full"] } diff --git a/examples/graceful-shutdown/src/main.rs b/examples/graceful-shutdown/src/main.rs index 3704889a..dabfee16 100644 --- a/examples/graceful-shutdown/src/main.rs +++ b/examples/graceful-shutdown/src/main.rs @@ -17,7 +17,7 @@ async fn main() { // run it let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); println!("listening on {}", addr); - axum::Server::bind(&addr) + hyper::Server::bind(&addr) .serve(app.into_make_service()) .with_graceful_shutdown(shutdown_signal()) .await diff --git a/examples/handle-head-request/src/main.rs b/examples/handle-head-request/src/main.rs index 5b0db264..d49d88ef 100644 --- a/examples/handle-head-request/src/main.rs +++ b/examples/handle-head-request/src/main.rs @@ -6,7 +6,6 @@ use axum::response::{IntoResponse, Response}; use axum::{http, routing::get, Router}; -use std::net::SocketAddr; fn app() -> Router { Router::new().route("/get-head", get(get_head_handler)) @@ -14,12 +13,11 @@ fn app() -> Router { #[tokio::main] async fn main() { - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - - axum::Server::bind(&addr) - .serve(app().into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app()).await.unwrap(); } // GET routes will also be called for HEAD requests but will have the response body removed. diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index 466caceb..de018335 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -5,7 +5,6 @@ //! ``` use axum::{response::Html, routing::get, Router}; -use std::net::SocketAddr; #[tokio::main] async fn main() { @@ -13,12 +12,11 @@ async fn main() { let app = Router::new().route("/", get(handler)); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler() -> Html<&'static str> { diff --git a/examples/http-proxy/src/main.rs b/examples/http-proxy/src/main.rs index 08845ca9..1abf3bbf 100644 --- a/examples/http-proxy/src/main.rs +++ b/examples/http-proxy/src/main.rs @@ -52,7 +52,7 @@ async fn main() { let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) + hyper::Server::bind(&addr) .http1_preserve_header_case(true) .http1_title_case_headers(true) .serve(Shared::new(service)) diff --git a/examples/jwt/src/main.rs b/examples/jwt/src/main.rs index 4ee20d4b..a6f8bffa 100644 --- a/examples/jwt/src/main.rs +++ b/examples/jwt/src/main.rs @@ -19,7 +19,7 @@ use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation} use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use serde_json::json; -use std::{fmt::Display, net::SocketAddr}; +use std::fmt::Display; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; // Quick instructions @@ -67,13 +67,11 @@ async fn main() { .route("/protected", get(protected)) .route("/authorize", post(authorize)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn protected(claims: Claims) -> Result { diff --git a/examples/key-value-store/src/main.rs b/examples/key-value-store/src/main.rs index ccf21825..b22802cb 100644 --- a/examples/key-value-store/src/main.rs +++ b/examples/key-value-store/src/main.rs @@ -19,7 +19,6 @@ use axum::{ use std::{ borrow::Cow, collections::HashMap, - net::SocketAddr, sync::{Arc, RwLock}, time::Duration, }; @@ -74,12 +73,11 @@ async fn main() { .with_state(Arc::clone(&shared_state)); // Run our app with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } type SharedState = Arc>; diff --git a/examples/listen-multiple-addrs/src/main.rs b/examples/listen-multiple-addrs/src/main.rs index f292c1d6..fed70daa 100644 --- a/examples/listen-multiple-addrs/src/main.rs +++ b/examples/listen-multiple-addrs/src/main.rs @@ -28,7 +28,7 @@ async fn main() { b: incoming_v6, }; - axum::Server::builder(combined) + hyper::Server::builder(combined) .serve(app.into_make_service()) .await .unwrap(); diff --git a/examples/multipart-form/src/main.rs b/examples/multipart-form/src/main.rs index 31f2887e..f8c0d96b 100644 --- a/examples/multipart-form/src/main.rs +++ b/examples/multipart-form/src/main.rs @@ -10,7 +10,6 @@ use axum::{ routing::get, Router, }; -use std::net::SocketAddr; use tower_http::limit::RequestBodyLimitLayer; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -34,12 +33,11 @@ async fn main() { .layer(tower_http::trace::TraceLayer::new_for_http()); // run it with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn show_form() -> Html<&'static str> { diff --git a/examples/oauth/src/main.rs b/examples/oauth/src/main.rs index 1a01570f..40a039bc 100644 --- a/examples/oauth/src/main.rs +++ b/examples/oauth/src/main.rs @@ -25,7 +25,7 @@ use oauth2::{ ClientSecret, CsrfToken, RedirectUrl, Scope, TokenResponse, TokenUrl, }; use serde::{Deserialize, Serialize}; -use std::{env, net::SocketAddr}; +use std::env; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; static COOKIE_NAME: &str = "SESSION"; @@ -56,13 +56,11 @@ async fn main() { .route("/logout", get(logout)) .with_state(app_state); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } #[derive(Clone)] diff --git a/examples/parse-body-based-on-content-type/src/main.rs b/examples/parse-body-based-on-content-type/src/main.rs index 9ee26c6a..bae4ec1d 100644 --- a/examples/parse-body-based-on-content-type/src/main.rs +++ b/examples/parse-body-based-on-content-type/src/main.rs @@ -15,7 +15,6 @@ use axum::{ Form, Json, RequestExt, Router, }; use serde::{Deserialize, Serialize}; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -31,12 +30,11 @@ async fn main() { let app = Router::new().route("/", post(handler)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } #[derive(Debug, Serialize, Deserialize)] diff --git a/examples/print-request-response/src/main.rs b/examples/print-request-response/src/main.rs index 4703c058..1348d026 100644 --- a/examples/print-request-response/src/main.rs +++ b/examples/print-request-response/src/main.rs @@ -13,7 +13,6 @@ use axum::{ routing::post, Router, }; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -30,12 +29,11 @@ async fn main() { .route("/", post(|| async move { "Hello from `POST /`" })) .layer(middleware::from_fn(print_request_response)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn print_request_response( diff --git a/examples/prometheus-metrics/src/main.rs b/examples/prometheus-metrics/src/main.rs index 675310be..b90c384f 100644 --- a/examples/prometheus-metrics/src/main.rs +++ b/examples/prometheus-metrics/src/main.rs @@ -17,7 +17,6 @@ use axum::{ use metrics_exporter_prometheus::{Matcher, PrometheusBuilder, PrometheusHandle}; use std::{ future::ready, - net::SocketAddr, time::{Duration, Instant}, }; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -42,24 +41,22 @@ fn main_app() -> Router { async fn start_main_server() { let app = main_app(); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await - .unwrap() + .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn start_metrics_server() { let app = metrics_app(); // NOTE: expose metrics enpoint on a different port - let addr = SocketAddr::from(([127, 0, 0, 1], 3001)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3001") .await - .unwrap() + .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } #[tokio::main] diff --git a/examples/query-params-with-empty-strings/src/main.rs b/examples/query-params-with-empty-strings/src/main.rs index aa424643..19117c4e 100644 --- a/examples/query-params-with-empty-strings/src/main.rs +++ b/examples/query-params-with-empty-strings/src/main.rs @@ -10,10 +10,11 @@ use std::{fmt, str::FromStr}; #[tokio::main] async fn main() { - axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) - .serve(app().into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app()).await.unwrap(); } fn app() -> Router { diff --git a/examples/readme/src/main.rs b/examples/readme/src/main.rs index bdcb8945..cd2120cb 100644 --- a/examples/readme/src/main.rs +++ b/examples/readme/src/main.rs @@ -11,7 +11,6 @@ use axum::{ Json, Router, }; use serde::{Deserialize, Serialize}; -use std::net::SocketAddr; #[tokio::main] async fn main() { @@ -26,13 +25,11 @@ async fn main() { .route("/users", post(create_user)); // run our app with hyper - // `axum::Server` is a re-export of `hyper::Server` - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } // basic handler that responds with a static string diff --git a/examples/rest-grpc-multiplex/src/main.rs b/examples/rest-grpc-multiplex/src/main.rs index f804f6ae..b7f0ab02 100644 --- a/examples/rest-grpc-multiplex/src/main.rs +++ b/examples/rest-grpc-multiplex/src/main.rs @@ -75,7 +75,7 @@ async fn main() { let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) + hyper::Server::bind(&addr) .serve(tower::make::Shared::new(service)) .await .unwrap(); diff --git a/examples/reverse-proxy/src/main.rs b/examples/reverse-proxy/src/main.rs index a01947c6..875a3a5a 100644 --- a/examples/reverse-proxy/src/main.rs +++ b/examples/reverse-proxy/src/main.rs @@ -16,7 +16,6 @@ use axum::{ Router, }; use hyper::client::HttpConnector; -use std::net::SocketAddr; type Client = hyper::client::Client; @@ -28,12 +27,11 @@ async fn main() { let app = Router::new().route("/", get(handler)).with_state(client); - let addr = SocketAddr::from(([127, 0, 0, 1], 4000)); - println!("reverse proxy listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:4000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler(State(client): State, mut req: Request) -> Response { @@ -54,10 +52,9 @@ async fn handler(State(client): State, mut req: Request) -> Response { async fn server() { let app = Router::new().route("/", get(|| async { "Hello, world!" })); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("server listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } diff --git a/examples/routes-and-handlers-close-together/src/main.rs b/examples/routes-and-handlers-close-together/src/main.rs index 75320d09..50721e70 100644 --- a/examples/routes-and-handlers-close-together/src/main.rs +++ b/examples/routes-and-handlers-close-together/src/main.rs @@ -8,7 +8,6 @@ use axum::{ routing::{get, post, MethodRouter}, Router, }; -use std::net::SocketAddr; #[tokio::main] async fn main() { @@ -17,12 +16,11 @@ async fn main() { .merge(get_foo()) .merge(post_foo()); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } fn root() -> Router { diff --git a/examples/sessions/src/main.rs b/examples/sessions/src/main.rs index 9bea9c1b..fcac6c56 100644 --- a/examples/sessions/src/main.rs +++ b/examples/sessions/src/main.rs @@ -21,7 +21,6 @@ use axum::{ }; use serde::{Deserialize, Serialize}; use std::fmt::Debug; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use uuid::Uuid; @@ -42,12 +41,11 @@ async fn main() { let app = Router::new().route("/", get(handler)).with_state(store); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler(user_id: UserIdFromSession) -> impl IntoResponse { diff --git a/examples/sqlx-postgres/src/main.rs b/examples/sqlx-postgres/src/main.rs index 8e3353b9..e9553037 100644 --- a/examples/sqlx-postgres/src/main.rs +++ b/examples/sqlx-postgres/src/main.rs @@ -21,9 +21,10 @@ use axum::{ Router, }; use sqlx::postgres::{PgPool, PgPoolOptions}; +use tokio::net::TcpListener; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; -use std::{net::SocketAddr, time::Duration}; +use std::time::Duration; #[tokio::main] async fn main() { @@ -55,12 +56,9 @@ async fn main() { .with_state(pool); // run it with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } // we can extract the connection pool with `State` diff --git a/examples/sse/src/main.rs b/examples/sse/src/main.rs index dab5a565..52888133 100644 --- a/examples/sse/src/main.rs +++ b/examples/sse/src/main.rs @@ -11,7 +11,7 @@ use axum::{ Router, }; use futures::stream::{self, Stream}; -use std::{convert::Infallible, net::SocketAddr, path::PathBuf, time::Duration}; +use std::{convert::Infallible, path::PathBuf, time::Duration}; use tokio_stream::StreamExt as _; use tower_http::{services::ServeDir, trace::TraceLayer}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -37,12 +37,11 @@ async fn main() { .layer(TraceLayer::new_for_http()); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn sse_handler( diff --git a/examples/static-file-server/src/main.rs b/examples/static-file-server/src/main.rs index 3a3a2414..3aa7a9a8 100644 --- a/examples/static-file-server/src/main.rs +++ b/examples/static-file-server/src/main.rs @@ -103,9 +103,9 @@ fn calling_serve_dir_from_a_handler() -> Router { async fn serve(app: Router, port: u16) { let addr = SocketAddr::from(([127, 0, 0, 1], port)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.layer(TraceLayer::new_for_http()).into_make_service()) + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app.layer(TraceLayer::new_for_http())) .await .unwrap(); } diff --git a/examples/stream-to-file/src/main.rs b/examples/stream-to-file/src/main.rs index 2d514162..02164d4e 100644 --- a/examples/stream-to-file/src/main.rs +++ b/examples/stream-to-file/src/main.rs @@ -13,7 +13,7 @@ use axum::{ BoxError, Router, }; use futures::{Stream, TryStreamExt}; -use std::{io, net::SocketAddr}; +use std::io; use tokio::{fs::File, io::BufWriter}; use tokio_util::io::StreamReader; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -39,12 +39,11 @@ async fn main() { .route("/", get(show_form).post(accept_form)) .route("/file/:file_name", post(save_request_body)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } // Handler that streams the request body to a file. diff --git a/examples/templates/src/main.rs b/examples/templates/src/main.rs index d7e01a94..1abdb33e 100644 --- a/examples/templates/src/main.rs +++ b/examples/templates/src/main.rs @@ -12,7 +12,6 @@ use axum::{ routing::get, Router, }; -use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -29,12 +28,11 @@ async fn main() { let app = Router::new().route("/greet/:name", get(greet)); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn greet(extract::Path(name): extract::Path) -> impl IntoResponse { diff --git a/examples/testing-websockets/src/main.rs b/examples/testing-websockets/src/main.rs index b778115c..954168b1 100644 --- a/examples/testing-websockets/src/main.rs +++ b/examples/testing-websockets/src/main.rs @@ -14,16 +14,14 @@ use axum::{ Router, }; use futures::{Sink, SinkExt, Stream, StreamExt}; -use std::net::SocketAddr; #[tokio::main] async fn main() { - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {addr}"); - axum::Server::bind(&addr) - .serve(app().into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app()).await.unwrap(); } fn app() -> Router { @@ -94,17 +92,18 @@ where #[cfg(test)] mod tests { use super::*; - use std::net::Ipv4Addr; + use std::net::{Ipv4Addr, SocketAddr}; use tokio_tungstenite::tungstenite; // We can integration test one handler by running the server in a background task and // connecting to it like any other client would. #[tokio::test] async fn integration_test() { - let server = axum::Server::bind(&SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0))) - .serve(app().into_make_service()); - let addr = server.local_addr(); - tokio::spawn(server); + let listener = tokio::net::TcpListener::bind(SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0))) + .await + .unwrap(); + let addr = listener.local_addr().unwrap(); + tokio::spawn(axum::serve(listener, app())); let (mut socket, _response) = tokio_tungstenite::connect_async(format!("ws://{addr}/integration-testable")) diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 5f7dbc07..99cc0402 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -24,14 +24,11 @@ async fn main() { .with(tracing_subscriber::fmt::layer()) .init(); - let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000)); - - tracing::debug!("listening on {}", addr); - - axum::Server::bind(&addr) - .serve(app().into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app()).await.unwrap(); } /// Having a function that produces our app makes it easy to call it from tests @@ -63,7 +60,8 @@ mod tests { http::{self, Request, StatusCode}, }; use serde_json::{json, Value}; - use std::net::{SocketAddr, TcpListener}; + use std::net::SocketAddr; + use tokio::net::TcpListener; use tower::Service; // for `call` use tower::ServiceExt; // for `oneshot` and `ready` @@ -131,15 +129,11 @@ mod tests { // You can also spawn a server and talk to it like any other HTTP server: #[tokio::test] async fn the_real_deal() { - let listener = TcpListener::bind("0.0.0.0:0".parse::().unwrap()).unwrap(); + let listener = TcpListener::bind("0.0.0.0:0").await.unwrap(); let addr = listener.local_addr().unwrap(); tokio::spawn(async move { - axum::Server::from_tcp(listener) - .unwrap() - .serve(app().into_make_service()) - .await - .unwrap(); + axum::serve(listener, app()).await.unwrap(); }); let client = hyper::Client::new(); diff --git a/examples/tls-rustls/src/main.rs b/examples/tls-rustls/src/main.rs index 5034cf31..860f56b5 100644 --- a/examples/tls-rustls/src/main.rs +++ b/examples/tls-rustls/src/main.rs @@ -93,10 +93,9 @@ async fn redirect_http_to_https(ports: Ports) { }; let addr = SocketAddr::from(([127, 0, 0, 1], ports.http)); - tracing::debug!("http redirect listening on {}", addr); - - axum::Server::bind(&addr) - .serve(redirect.into_make_service()) + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, redirect.into_make_service()) .await .unwrap(); } diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 4463bfa4..9e08876d 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -24,7 +24,6 @@ use axum::{ use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, - net::SocketAddr, sync::{Arc, RwLock}, time::Duration, }; @@ -68,12 +67,11 @@ async fn main() { ) .with_state(db); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } // The query parameters for todos index diff --git a/examples/tokio-postgres/src/main.rs b/examples/tokio-postgres/src/main.rs index 77c4c112..effc0320 100644 --- a/examples/tokio-postgres/src/main.rs +++ b/examples/tokio-postgres/src/main.rs @@ -13,7 +13,6 @@ use axum::{ }; use bb8::{Pool, PooledConnection}; use bb8_postgres::PostgresConnectionManager; -use std::net::SocketAddr; use tokio_postgres::NoTls; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -41,13 +40,12 @@ async fn main() { ) .with_state(pool); - // run it with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + // run it + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } type ConnectionPool = Pool>; diff --git a/examples/tracing-aka-logging/src/main.rs b/examples/tracing-aka-logging/src/main.rs index 6b4524ac..74a2055a 100644 --- a/examples/tracing-aka-logging/src/main.rs +++ b/examples/tracing-aka-logging/src/main.rs @@ -12,7 +12,8 @@ use axum::{ routing::get, Router, }; -use std::{net::SocketAddr, time::Duration}; +use std::time::Duration; +use tokio::net::TcpListener; use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer}; use tracing::{info_span, Span}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -80,12 +81,9 @@ async fn main() { ); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler() -> Html<&'static str> { diff --git a/examples/unix-domain-socket/src/main.rs b/examples/unix-domain-socket/src/main.rs index 0a3d3f0d..ce350720 100644 --- a/examples/unix-domain-socket/src/main.rs +++ b/examples/unix-domain-socket/src/main.rs @@ -63,7 +63,7 @@ mod unix { tokio::spawn(async { let app = Router::new().route("/", get(handler)); - axum::Server::builder(ServerAccept { uds }) + hyper::Server::builder(ServerAccept { uds }) .serve(app.into_make_service_with_connect_info::()) .await .unwrap(); diff --git a/examples/validator/src/main.rs b/examples/validator/src/main.rs index 4f4f6239..a6a25b85 100644 --- a/examples/validator/src/main.rs +++ b/examples/validator/src/main.rs @@ -19,8 +19,8 @@ use axum::{ Router, }; use serde::{de::DeserializeOwned, Deserialize}; -use std::net::SocketAddr; use thiserror::Error; +use tokio::net::TcpListener; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use validator::Validate; @@ -38,13 +38,9 @@ async fn main() { let app = Router::new().route("/", get(handler)); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } #[derive(Debug, Deserialize, Validate)] diff --git a/examples/versioning/src/main.rs b/examples/versioning/src/main.rs index fd3f6668..b5324d69 100644 --- a/examples/versioning/src/main.rs +++ b/examples/versioning/src/main.rs @@ -12,7 +12,7 @@ use axum::{ routing::get, RequestPartsExt, Router, }; -use std::{collections::HashMap, net::SocketAddr}; +use std::collections::HashMap; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -29,12 +29,11 @@ async fn main() { let app = Router::new().route("/:version/foo", get(handler)); // run it - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve(listener, app).await.unwrap(); } async fn handler(version: Version) { diff --git a/examples/websockets/src/main.rs b/examples/websockets/src/main.rs index 73993c15..e13182bb 100644 --- a/examples/websockets/src/main.rs +++ b/examples/websockets/src/main.rs @@ -66,12 +66,16 @@ async fn main() { ); // run it with hyper - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service_with_connect_info::()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + axum::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await + .unwrap(); } /// The handler for the HTTP request (this gets called when the HTTP GET lands at the start