//! Run with //! //! ```not_rust //! cargo run -p example-serve-with-hyper //! ``` //! //! This example shows how to run axum using hyper's low level API. //! //! The [hyper-util] crate exists to provide high level utilities but its still in early stages of //! development. //! //! [hyper-util]: https://crates.io/crates/hyper-util use std::convert::Infallible; use std::net::SocketAddr; use axum::extract::ConnectInfo; use axum::{extract::Request, routing::get, Router}; use hyper::body::Incoming; use hyper_util::rt::{TokioExecutor, TokioIo}; use hyper_util::server; use tokio::net::TcpListener; use tower::{Service, ServiceExt}; #[tokio::main] async fn main() { tokio::join!(serve_plain(), serve_with_connect_info()); } async fn serve_plain() { // Create a regular axum app. let app = Router::new().route("/", get(|| async { "Hello!" })); // Create a `TcpListener` using tokio. let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap(); // Continuously accept new connections. loop { // In this example we discard the remote address. See `fn serve_with_connect_info` for how // to expose that. let (socket, _remote_addr) = listener.accept().await.unwrap(); // We don't need to call `poll_ready` because `Router` is always ready. let tower_service = app.clone(); // Spawn a task to handle the connection. That way we can multiple connections // concurrently. tokio::spawn(async move { // Hyper has its own `AsyncRead` and `AsyncWrite` traits and doesn't use tokio. // `TokioIo` converts between them. let socket = TokioIo::new(socket); // Hyper also has its own `Service` trait and doesn't use tower. We can use // `hyper::service::service_fn` to create a hyper `Service` that calls our app through // `tower::Service::call`. let hyper_service = hyper::service::service_fn(move |request: Request| { // We have to clone `tower_service` because hyper's `Service` uses `&self` whereas // tower's `Service` requires `&mut self`. // // We don't need to call `poll_ready` since `Router` is always ready. tower_service.clone().call(request) }); // `server::conn::auto::Builder` supports both http1 and http2. // // `TokioExecutor` tells hyper to use `tokio::spawn` to spawn tasks. if let Err(err) = server::conn::auto::Builder::new(TokioExecutor::new()) // `serve_connection_with_upgrades` is required for websockets. If you don't need // that you can use `serve_connection` instead. .serve_connection_with_upgrades(socket, hyper_service) .await { eprintln!("failed to serve connection: {err:#}"); } }); } } // Similar setup to `serve_plain` but captures the remote address and exposes it through the // `ConnectInfo` extractor async fn serve_with_connect_info() { let app = Router::new().route( "/", get( |ConnectInfo(remote_addr): ConnectInfo| async move { format!("Hello {remote_addr}") }, ), ); let mut make_service = app.into_make_service_with_connect_info::(); let listener = TcpListener::bind("0.0.0.0:3001").await.unwrap(); loop { let (socket, remote_addr) = listener.accept().await.unwrap(); // We don't need to call `poll_ready` because `IntoMakeServiceWithConnectInfo` is always // ready. let tower_service = unwrap_infallible(make_service.call(remote_addr).await); tokio::spawn(async move { let socket = TokioIo::new(socket); let hyper_service = hyper::service::service_fn(move |request: Request| { tower_service.clone().oneshot(request) }); if let Err(err) = server::conn::auto::Builder::new(TokioExecutor::new()) .serve_connection_with_upgrades(socket, hyper_service) .await { eprintln!("failed to serve connection: {err:#}"); } }); } } fn unwrap_infallible(result: Result) -> T { match result { Ok(value) => value, Err(err) => match err {}, } }