diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea3ac5e1..653de509 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,8 @@ jobs: coverage: name: Coverage runs-on: ubuntu-latest + env: + RUSTFLAGS: --cfg hyper_unstable_tracing steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable diff --git a/Cargo.toml b/Cargo.toml index c41335ed..7fbd8840 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,6 @@ viz-tower = { version = "0.1.0", path = "viz-tower" } anyhow = "1.0" async-trait = "0.1" bytes = "1.5" -dyn-clone = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" @@ -67,7 +66,7 @@ sync_wrapper = "0.1.2" thiserror = "1.0" # router -path-tree = "0.7" +path-tree = "0.7.3" # http headers = "0.4" diff --git a/examples/compression/src/main.rs b/examples/compression/src/main.rs index fb1d86cb..839b3a3a 100644 --- a/examples/compression/src/main.rs +++ b/examples/compression/src/main.rs @@ -1,10 +1,9 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; -use viz::{get, middleware::compression, serve, Request, Result, Router, Tree}; +use viz::{get, middleware::compression, serve, Request, Result, Router}; async fn index(_req: Request) -> Result<&'static str> { Ok("Hello, World!") @@ -19,15 +18,10 @@ async fn main() -> Result<()> { let app = Router::new() .route("/", get(index)) .with(compression::Config); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/cors/src/main.rs b/examples/cors/src/main.rs index b2178cea..f62b84df 100644 --- a/examples/cors/src/main.rs +++ b/examples/cors/src/main.rs @@ -1,9 +1,8 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; -use viz::{get, middleware::cors, serve, Method, Request, Result, Router, Tree}; +use viz::{get, middleware::cors, serve, Method, Request, Result, Router}; async fn index(_req: Request) -> Result<&'static str> { Ok("Hello, World!") @@ -27,15 +26,10 @@ async fn main() -> Result<()> { .route("/", get(index).options(options)) // .with(cors::Config::default()); // Default CORS config .with(custom_cors); // Our custom CORS config - let tree = Arc::new(Tree::from(app)); - - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/csrf/src/main.rs b/examples/csrf/src/main.rs index c133c7a9..e6878b35 100644 --- a/examples/csrf/src/main.rs +++ b/examples/csrf/src/main.rs @@ -1,7 +1,6 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{net::SocketAddr, sync::Arc, time::Duration}; +use std::{net::SocketAddr, time::Duration}; use tokio::net::TcpListener; use viz::{ @@ -10,7 +9,7 @@ use viz::{ csrf::{self, CsrfToken}, helper::CookieOptions, }, - serve, Method, Request, RequestExt, Result, Router, Tree, + serve, Method, Request, RequestExt, Result, Router, }; async fn index(mut req: Request) -> Result { @@ -39,15 +38,10 @@ async fn main() -> Result<()> { csrf::verify, )) .with(cookie::Config::default()); - let tree = Arc::new(Tree::from(app)); - - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/databases/sea-orm/src/main.rs b/examples/databases/sea-orm/src/main.rs index 8fba74a0..ced916ca 100644 --- a/examples/databases/sea-orm/src/main.rs +++ b/examples/databases/sea-orm/src/main.rs @@ -1,11 +1,10 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] //! `SeaOrm` example for Viz framework. use sea_orm_example::{api, db::init_db}; -use std::{env, net::SocketAddr, path::PathBuf, sync::Arc}; +use std::{env, net::SocketAddr, path::PathBuf}; use tokio::net::TcpListener; -use viz::{handlers::serve, middleware, serve, types::State, Result, Router, Tree}; +use viz::{handlers::serve, middleware, serve, types::State, Result, Router}; #[tokio::main] async fn main() -> Result<(), Box> { @@ -26,11 +25,10 @@ async fn main() -> Result<(), Box> { .delete("/todos/:id", api::delete) .with(State::new(db)) .with(middleware::limits::Config::new()); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(serve(stream, tree, Some(addr))); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/forms/form/src/main.rs b/examples/forms/form/src/main.rs index d862287e..9624979f 100644 --- a/examples/forms/form/src/main.rs +++ b/examples/forms/form/src/main.rs @@ -1,12 +1,11 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] use serde::{Deserialize, Serialize}; -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; use viz::{ get, middleware::limits, serve, types::Form, IntoHandler, Request, Response, ResponseExt, - Result, Router, Tree, + Result, Router, }; #[derive(Deserialize, Serialize)] @@ -35,15 +34,10 @@ async fn main() -> Result<()> { .route("/", get(new).post(create.into_handler())) // limit body size .with(limits::Config::default()); - let tree = Arc::new(Tree::from(app)); - - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/forms/multipart/src/main.rs b/examples/forms/multipart/src/main.rs index dc31ea9e..83e661d0 100644 --- a/examples/forms/multipart/src/main.rs +++ b/examples/forms/multipart/src/main.rs @@ -1,15 +1,14 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] use futures_util::TryStreamExt; -use std::{fs::File, net::SocketAddr, sync::Arc}; +use std::{fs::File, net::SocketAddr}; use tempfile::tempdir; use tokio::net::TcpListener; use viz::{ middleware::limits, serve, types::{Multipart, PayloadError}, - IntoHandler, IntoResponse, Request, Response, ResponseExt, Result, Router, Tree, + IntoHandler, IntoResponse, Request, Response, ResponseExt, Result, Router, }; // HTML form for uploading photos @@ -54,15 +53,10 @@ async fn main() -> Result<()> { .post("/", upload.into_handler()) // limit body size .with(limits::Config::default()); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/graceful-shutdown/Cargo.toml b/examples/graceful-shutdown/Cargo.toml index 750df073..3f4e8af1 100644 --- a/examples/graceful-shutdown/Cargo.toml +++ b/examples/graceful-shutdown/Cargo.toml @@ -7,4 +7,6 @@ publish = false [dependencies] viz.workspace = true -tokio = { workspace = true, features = ["rt-multi-thread", "macros", "time"] } +tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal"] } +tracing.workspace = true +tracing-subscriber = { workspace = true, features = ["env-filter"] } diff --git a/examples/graceful-shutdown/src/main.rs b/examples/graceful-shutdown/src/main.rs index 6940c037..3a2b112a 100644 --- a/examples/graceful-shutdown/src/main.rs +++ b/examples/graceful-shutdown/src/main.rs @@ -1,13 +1,11 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -//! Graceful shutdown server. -//! -//! See - -use std::{net::SocketAddr, sync::Arc, time::Duration}; -use tokio::{net::TcpListener, pin}; -use viz::{server::conn::http1, Io, Request, Responder, Result, Router, Tree}; +use std::net::SocketAddr; +use tokio::net::TcpListener; +use tokio::signal; +use tracing::{error, info}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +use viz::{serve, Request, Result, Router}; async fn index(_: Request) -> Result<&'static str> { Ok("Hello, World!") @@ -15,55 +13,49 @@ async fn index(_: Request) -> Result<&'static str> { #[tokio::main] async fn main() -> Result<()> { + tracing_subscriber::registry() + .with( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| "tracing=debug,hyper=debug".into()), + ) + .with(tracing_subscriber::fmt::layer()) + .init(); + let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); let listener = TcpListener::bind(addr).await?; - println!("listening on {addr}"); + info!("listening on {addr}"); let app = Router::new().get("/", index); - let tree = Arc::new(Tree::from(app)); - - // Use a 5 second timeout for incoming connections to the server. - // If a request is in progress when the 5 second timeout elapses, - // use a 2 second timeout for processing the final request and graceful shutdown. - let connection_timeouts = vec![Duration::from_secs(5), Duration::from_secs(2)]; - loop { - // Clone the connection_timeouts so they can be passed to the new task. - let connection_timeouts_clone = connection_timeouts.clone(); + let server = serve(listener, app).signal(shutdown_signal()); - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); + if let Err(e) = server.await { + error!("{e}"); + } - tokio::task::spawn(async move { - // Pin the connection object so we can use tokio::select! below. - let conn = http1::Builder::new() - .serve_connection(Io::new(stream), Responder::new(tree, Some(addr))); - pin!(conn); + Ok(()) +} - // Iterate the timeouts. Use tokio::select! to wait on the - // result of polling the connection itself, - // and also on tokio::time::sleep for the current timeout duration. - for (iter, sleep_duration) in connection_timeouts_clone.iter().enumerate() { - println!("iter = {iter} sleep_duration = {sleep_duration:?}"); - tokio::select! { - res = conn.as_mut() => { - // Polling the connection returned a result. - // In this case print either the successful or error result for the connection - // and break out of the loop. - match res { - Ok(()) => println!("after polling conn, no error"), - Err(e) => println!("error serving connection: {e:?}"), - }; - break; - } - () = tokio::time::sleep(*sleep_duration) => { - // tokio::time::sleep returned a result. - // Call graceful_shutdown on the connection and continue the loop. - println!("iter = {iter} got timeout_interval, calling conn.graceful_shutdown"); - conn.as_mut().graceful_shutdown(); - } - } - } - }); +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, } } diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index 520de837..433134ae 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -1,26 +1,28 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{net::SocketAddr, sync::Arc}; +use std::{net::SocketAddr, str::FromStr}; use tokio::net::TcpListener; -use viz::{serve, Request, Result, Router, Tree}; +use viz::{serve, Request, Result, Router}; -async fn index(_: Request) -> Result<&'static str> { - Ok("Hello, World!") +async fn index(_: Request) -> Result { + Ok(String::from("Hello, World!")) } #[tokio::main] async fn main() -> Result<()> { - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); + let addr = SocketAddr::from_str("[::1]:3000").unwrap(); let listener = TcpListener::bind(addr).await?; println!("listening on http://{addr}"); - let app = Router::new().get("/", index); - let tree = Arc::new(Tree::from(app)); + let mut app = Router::new().get("/", |_| async { Ok("Hello, World!") }); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(serve(stream, tree, Some(addr))); + for n in 0..1000 { + app = app.get(&format!("/{}", n), index); } + + if let Err(e) = serve(listener, app).await { + println!("{e}"); + } + + Ok(()) } diff --git a/examples/htmlx/src/main.rs b/examples/htmlx/src/main.rs index 3dd61a45..1a032ce4 100644 --- a/examples/htmlx/src/main.rs +++ b/examples/htmlx/src/main.rs @@ -13,7 +13,7 @@ use std::{ use tokio::net::TcpListener; use viz::{ header::HeaderValue, middleware::limits, serve, types::State, Error, IntoResponse, Request, - RequestExt, Response, ResponseExt, Result, Router, StatusCode, Tree, + RequestExt, Response, ResponseExt, Result, Router, StatusCode, }; /// In-memory todo store @@ -100,15 +100,10 @@ async fn main() -> Result<()> { .any("/*", |_| async { Ok(Response::text("Welcome!")) }) .with(State::new(DB::default())) .with(limits::Config::default()); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/limits/src/main.rs b/examples/limits/src/main.rs index 4de0df46..0f2da782 100644 --- a/examples/limits/src/main.rs +++ b/examples/limits/src/main.rs @@ -1,7 +1,6 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; use viz::{ middleware::limits, @@ -12,7 +11,6 @@ use viz::{ RequestExt, Result, Router, - Tree, }; async fn echo(mut req: Request) -> Result { @@ -36,15 +34,10 @@ async fn main() -> Result<()> { .post("/", echo) // limit body size .with(limits::Config::default().limits(limits)); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/otel/metrics/src/main.rs b/examples/otel/metrics/src/main.rs index 97fe9b1e..cbec1ce4 100644 --- a/examples/otel/metrics/src/main.rs +++ b/examples/otel/metrics/src/main.rs @@ -1,7 +1,6 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; use opentelemetry::{global, KeyValue}; @@ -13,7 +12,7 @@ use opentelemetry_sdk::{ use viz::{ handlers::prometheus::{ExporterBuilder, Prometheus, Registry}, middleware::otel, - serve, Error, Request, Result, Router, Tree, + serve, Error, Request, Result, Router, }; async fn index(_: Request) -> Result<&'static str> { @@ -59,18 +58,13 @@ async fn main() -> Result<()> { .get("/:username", index) .get("/metrics", Prometheus::new(registry)) .with(otel::metrics::Config::new(&global::meter("otel"))); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + Ok(()) + // Ensure all spans have been reported // global::shutdown_tracer_provider(); // provider.shutdown(); diff --git a/examples/otel/tracing/src/main.rs b/examples/otel/tracing/src/main.rs index b53fd236..67e98116 100644 --- a/examples/otel/tracing/src/main.rs +++ b/examples/otel/tracing/src/main.rs @@ -6,9 +6,9 @@ use opentelemetry_sdk::{ runtime::TokioCurrentThread, {propagation::TraceContextPropagator, trace::Tracer}, }; -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; -use viz::{middleware::otel, serve, Request, Result, Router, Tree}; +use viz::{middleware::otel, serve, Request, Result, Router}; fn init_tracer() -> Tracer { global::set_text_map_propagator(TraceContextPropagator::new()); @@ -34,15 +34,10 @@ async fn main() -> Result<()> { .get("/", index) .get("/:username", index) .with(otel::tracing::Config::new(tracer)); - let tree = Arc::new(Tree::from(app)); - - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/routing/openapi/src/main.rs b/examples/routing/openapi/src/main.rs index f5b8a2eb..2a22f9b0 100644 --- a/examples/routing/openapi/src/main.rs +++ b/examples/routing/openapi/src/main.rs @@ -21,7 +21,7 @@ use viz::{ middleware, serve, types::{Json, Params, Query, State, StateError}, Error, HandlerExt, IntoResponse, Request, RequestExt, Response, ResponseExt, Result, Router, - StatusCode, Tree, + StatusCode, }; /// In-memory todo store @@ -329,15 +329,10 @@ async fn main() -> Result<(), Error> { "#, )) }); - let tree = Arc::new(Tree::from(app)); - - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/routing/todos/src/main.rs b/examples/routing/todos/src/main.rs index 37fdf81d..c0d2f1fc 100644 --- a/examples/routing/todos/src/main.rs +++ b/examples/routing/todos/src/main.rs @@ -11,7 +11,6 @@ use viz::{ middleware, serve, types::{Json, Params, Query, State}, Error, IntoResponse, Request, RequestExt, Response, ResponseExt, Result, Router, StatusCode, - Tree, }; /// In-memory todo store @@ -137,15 +136,10 @@ async fn main() -> Result<()> { .with(State::new(db)) // Set limits for the payload data of request .with(middleware::limits::Config::new()); - let tree = Arc::new(Tree::from(app)); - - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/rustls/src/main.rs b/examples/rustls/src/main.rs index 0afb6b6e..66f18741 100644 --- a/examples/rustls/src/main.rs +++ b/examples/rustls/src/main.rs @@ -1,9 +1,8 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] use std::{net::SocketAddr, sync::Arc}; use tokio::net::TcpListener; -use viz::{get, serve, tls, Request, Result, Router, Tree}; +use viz::{get, serve, tls, Request, Result, Router}; async fn index(_: Request) -> Result<&'static str> { Ok("Hello, World!") @@ -16,7 +15,6 @@ async fn main() -> Result<()> { println!("listening on http://{addr}"); let app = Router::new().route("/", get(index)); - let tree = Arc::new(Tree::from(app)); let listener = tls::Listener::<_, tls::rustls::TlsAcceptor>::new( listener, @@ -28,13 +26,9 @@ async fn main() -> Result<()> { .into(), ); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/session/src/main.rs b/examples/session/src/main.rs index a3465036..44f87c06 100644 --- a/examples/session/src/main.rs +++ b/examples/session/src/main.rs @@ -1,7 +1,6 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; use sessions::MemoryStorage; @@ -15,7 +14,7 @@ use viz::{ }, serve, types::CookieKey, - Request, RequestExt, Result, Router, Tree, + Request, RequestExt, Result, Router, }; async fn index(req: Request) -> Result<&'static str> { @@ -41,15 +40,10 @@ async fn main() -> Result<()> { CookieOptions::default(), )) .with(cookie::Config::with_key(CookieKey::generate())); - let tree = Arc::new(Tree::from(app)); - - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/sse/src/main.rs b/examples/sse/src/main.rs index 5eeb2984..1fc2ffcc 100644 --- a/examples/sse/src/main.rs +++ b/examples/sse/src/main.rs @@ -1,5 +1,4 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] use futures_util::StreamExt; use std::{net::SocketAddr, sync::Arc}; @@ -13,7 +12,7 @@ use viz::{ serve, types::{Event, Sse, State}, Error, HandlerExt, IntoResponse, Request, RequestExt, Response, ResponseExt, Result, Router, - StatusCode, Tree, + StatusCode, }; type ArcSystem = Arc; @@ -64,15 +63,10 @@ async fn main() -> Result<()> { let app = Router::new() .route("/", get(index)) .route("/stats", get(stats.with(State::new(sys)))); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/static-files/embed/src/main.rs b/examples/static-files/embed/src/main.rs index 8c7cdb58..e6d7283f 100644 --- a/examples/static-files/embed/src/main.rs +++ b/examples/static-files/embed/src/main.rs @@ -1,8 +1,8 @@ #![deny(warnings)] -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; -use viz::{handlers::embed, serve, Result, Router, StatusCode, Tree}; +use viz::{handlers::embed, serve, Result, Router, StatusCode}; #[derive(rust_embed::RustEmbed)] #[folder = "public"] @@ -21,15 +21,10 @@ async fn main() -> Result<()> { .get("/", embed::File::::new("index.html")) .get("/static/*", embed::Dir::::default()) .any("/*", |_| async { Ok(StatusCode::NOT_FOUND) }); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/static-files/include-dir/src/main.rs b/examples/static-files/include-dir/src/main.rs index 64218e76..0f330ee5 100644 --- a/examples/static-files/include-dir/src/main.rs +++ b/examples/static-files/include-dir/src/main.rs @@ -3,11 +3,10 @@ use http_body_util::Full; use include_dir::{include_dir, Dir}; -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; use viz::{ serve, IntoResponse, Request, RequestExt, Response, ResponseExt, Result, Router, StatusCode, - Tree, }; const ASSETS: Dir = include_dir!("examples/static-files/include-dir/html"); // frontend dir @@ -45,11 +44,10 @@ async fn main() -> Result<()> { .any("/*", |_| async { Ok(StatusCode::NOT_FOUND) }) .get("/", index) .get("/*", assets); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(serve(stream, tree, Some(addr))); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/static-files/serve/src/main.rs b/examples/static-files/serve/src/main.rs index e34d25ac..ff197a57 100644 --- a/examples/static-files/serve/src/main.rs +++ b/examples/static-files/serve/src/main.rs @@ -1,9 +1,8 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{env, net::SocketAddr, path::PathBuf, sync::Arc}; +use std::{env, net::SocketAddr, path::PathBuf}; use tokio::net::TcpListener; -use viz::{handlers::serve, serve, Request, Response, ResponseExt, Result, Router, Tree}; +use viz::{handlers::serve, serve, Request, Response, ResponseExt, Result, Router}; async fn index(_: Request) -> Result<&'static str> { Ok("Hello, World!") @@ -25,15 +24,10 @@ async fn main() -> Result<()> { serve::Dir::new(dir.join("../../../examples")).listing(), ) .any("/*", |_| async { Ok(Response::text("Welcome!")) }); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/static-routes/Cargo.toml b/examples/static-routes/Cargo.toml index 29f38645..b8a82479 100644 --- a/examples/static-routes/Cargo.toml +++ b/examples/static-routes/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] viz.workspace = true -hyper.workspace = true +hyper.workspace = true tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } once_cell = "1.19" diff --git a/examples/static-routes/src/main.rs b/examples/static-routes/src/main.rs index de7e0012..c60f9cdd 100644 --- a/examples/static-routes/src/main.rs +++ b/examples/static-routes/src/main.rs @@ -1,15 +1,15 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] +use hyper::server::conn::http1; use hyper::service::service_fn; use once_cell::sync::Lazy; use std::{net::SocketAddr, sync::Arc}; use tokio::net::TcpListener; + use viz::{ - server::conn::http1, types::{Params, RouteInfo}, - Body, IntoResponse, Io, Method, Request, RequestExt, Response, Result, Router, StatusCode, - Tree, + Body, Handler, IntoResponse, Io, Method, Request, RequestExt, Response, Result, Router, + StatusCode, Tree, }; /// Static Lazy Routes diff --git a/examples/templates/askama/src/main.rs b/examples/templates/askama/src/main.rs index 63cf62d8..75e3c39c 100644 --- a/examples/templates/askama/src/main.rs +++ b/examples/templates/askama/src/main.rs @@ -1,11 +1,10 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use askama::Template; use tokio::net::TcpListener; -use viz::{serve, BytesMut, Error, Request, Response, ResponseExt, Result, Router, Tree}; +use viz::{serve, BytesMut, Error, Request, Response, ResponseExt, Result, Router}; #[derive(Template)] #[template(path = "hello.html")] @@ -32,15 +31,10 @@ async fn main() -> Result<()> { println!("listening on http://{addr}"); let app = Router::new().get("/", index); - let tree = Arc::new(Tree::from(app)); - - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/templates/markup/src/main.rs b/examples/templates/markup/src/main.rs index 587343a2..9a71cf3c 100644 --- a/examples/templates/markup/src/main.rs +++ b/examples/templates/markup/src/main.rs @@ -1,11 +1,10 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] #![allow(clippy::must_use_candidate)] #![allow(clippy::inherent_to_string_shadow_display)] -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; -use viz::{serve, BytesMut, Request, Response, ResponseExt, Result, Router, Tree}; +use viz::{serve, BytesMut, Request, Response, ResponseExt, Result, Router}; pub struct Todo<'a> { id: u64, @@ -36,17 +35,12 @@ async fn main() -> Result<()> { println!("listening on http://{addr}"); let app = Router::new().get("/", index); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } markup::define! { diff --git a/examples/templates/maud/src/main.rs b/examples/templates/maud/src/main.rs index 16727200..36c4c69e 100644 --- a/examples/templates/maud/src/main.rs +++ b/examples/templates/maud/src/main.rs @@ -1,12 +1,11 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] #![allow(clippy::must_use_candidate)] #![allow(clippy::inherent_to_string_shadow_display)] use maud::{html, PreEscaped, DOCTYPE}; -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; -use viz::{serve, Request, Response, ResponseExt, Result, Router, Tree}; +use viz::{serve, Request, Response, ResponseExt, Result, Router}; pub struct Todo<'a> { id: u64, @@ -53,15 +52,10 @@ async fn main() -> Result<()> { println!("listening on http://{addr}"); let app = Router::new().get("/", index); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/templates/minijinja/src/main.rs b/examples/templates/minijinja/src/main.rs index 65d9467b..11e690a6 100644 --- a/examples/templates/minijinja/src/main.rs +++ b/examples/templates/minijinja/src/main.rs @@ -1,13 +1,13 @@ #![deny(warnings)] #![allow(clippy::unused_async)] -use std::{env, net::SocketAddr, path::PathBuf, sync::Arc}; +use std::{env, net::SocketAddr, path::PathBuf}; use minijinja::{context, path_loader, Environment}; use once_cell::sync::Lazy; use serde::Serialize; use tokio::net::TcpListener; -use viz::{serve, BytesMut, Error, Request, Response, ResponseExt, Result, Router, Tree}; +use viz::{serve, BytesMut, Error, Request, Response, ResponseExt, Result, Router}; static TPLS: Lazy = Lazy::new(|| { let dir = env::var("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap(); @@ -54,15 +54,10 @@ async fn main() -> Result<()> { println!("listening on http://{addr}"); let app = Router::new().get("/", index); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/templates/tera/src/main.rs b/examples/templates/tera/src/main.rs index dc74c56c..788a8849 100644 --- a/examples/templates/tera/src/main.rs +++ b/examples/templates/tera/src/main.rs @@ -1,13 +1,12 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use once_cell::sync::Lazy; use serde::Serialize; use tera::{Context, Tera}; use tokio::net::TcpListener; -use viz::{serve, BytesMut, Error, Request, Response, ResponseExt, Result, Router, Tree}; +use viz::{serve, BytesMut, Error, Request, Response, ResponseExt, Result, Router}; static TPLS: Lazy = Lazy::new(|| Tera::new("examples/templates/tera/templates/**/*").unwrap()); @@ -51,15 +50,10 @@ async fn main() -> Result<()> { println!("listening on http://{addr}"); let app = Router::new().get("/", index); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/tower/src/main.rs b/examples/tower/src/main.rs index 6371445a..d1ad350f 100644 --- a/examples/tower/src/main.rs +++ b/examples/tower/src/main.rs @@ -1,6 +1,6 @@ //! Viz + Tower services -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; use tower::{ service_fn, @@ -13,7 +13,7 @@ use tower_http::{ trace::TraceLayer, }; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; -use viz::{serve, Body, Error, IntoResponse, Request, Response, Result, Router, Tree}; +use viz::{serve, Body, Error, IntoResponse, Request, Response, Result, Router}; use viz_tower::{Layered, ServiceHandler}; async fn index(_: Request) -> Result { @@ -61,15 +61,14 @@ async fn main() -> Result<()> { .get("/about", about) .any("/*", any_handler) .with(Layered::new(layer)); - let tree = Arc::new(Tree::from(app)); let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); let listener = TcpListener::bind(addr).await?; println!("listening on http://{addr}"); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(serve(stream, tree, Some(addr))); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/examples/tracing/src/main.rs b/examples/tracing/src/main.rs index cdcbdc6c..36473542 100644 --- a/examples/tracing/src/main.rs +++ b/examples/tracing/src/main.rs @@ -1,11 +1,10 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; use tracing::{debug, error, info, instrument}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; -use viz::{serve, Request, RequestExt, Result, Router, Tree}; +use viz::{serve, Request, RequestExt, Result, Router}; #[instrument] async fn index(req: Request) -> Result<&'static str> { @@ -28,15 +27,10 @@ async fn main() -> Result<()> { info!("listening on http://{addr}"); let app = Router::new().get("/", index); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, Some(addr)).await { - error!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + error!("{e}"); } + + Ok(()) } diff --git a/examples/unix-socket/src/main.rs b/examples/unix-socket/src/main.rs index 76b88e81..1294a455 100644 --- a/examples/unix-socket/src/main.rs +++ b/examples/unix-socket/src/main.rs @@ -4,15 +4,12 @@ //! curl --unix-socket /tmp/viz.sock http://localhost/ //! ``` #![deny(warnings)] -#![allow(clippy::unused_async)] #[cfg(unix)] #[tokio::main] async fn main() -> viz::Result<()> { - use std::sync::Arc; - use tokio::net::UnixListener; - use viz::{get, serve, IntoHandler, Result, Router, Tree}; + use viz::{get, serve, IntoHandler, Result, Router}; async fn index() -> Result<&'static str> { Ok("Hello world!") @@ -24,17 +21,12 @@ async fn main() -> viz::Result<()> { let listener = UnixListener::bind(path)?; let app = Router::new().route("/", get(index.into_handler())); - let tree = Arc::new(Tree::from(app)); - - loop { - let (stream, _) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve(stream, tree, None).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } #[cfg(not(unix))] diff --git a/examples/websocket-chat/src/main.rs b/examples/websocket-chat/src/main.rs index 01ec3798..888fba6b 100644 --- a/examples/websocket-chat/src/main.rs +++ b/examples/websocket-chat/src/main.rs @@ -1,15 +1,14 @@ #![deny(warnings)] -#![allow(clippy::unused_async)] use futures_util::{SinkExt, StreamExt}; -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; use tokio::sync::broadcast::{channel, Sender}; use viz::{ - get, serve_with_upgrades, + get, serve, types::{Message, Params, State, WebSocket}, HandlerExt, IntoHandler, IntoResponse, Request, RequestExt, Response, ResponseExt, Result, - Router, Tree, + Router, }; async fn index() -> Result { @@ -61,15 +60,10 @@ async fn main() -> Result<()> { let app = Router::new() .route("/", get(index.into_handler())) .route("/ws/:name", get(ws.with(State::new(channel.0)))); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(async move { - if let Err(err) = serve_with_upgrades(stream, tree, Some(addr)).await { - eprintln!("Error while serving HTTP connection: {err}"); - } - }); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } diff --git a/viz-core/Cargo.toml b/viz-core/Cargo.toml index 364a42e2..fd778af5 100644 --- a/viz-core/Cargo.toml +++ b/viz-core/Cargo.toml @@ -25,7 +25,7 @@ default = [ "websocket", "cookie", "session", - "fs" + "fs", ] state = [] @@ -57,7 +57,6 @@ otel-metrics = ["otel", "opentelemetry?/metrics"] [dependencies] async-trait.workspace = true -dyn-clone.workspace = true futures-util.workspace = true sync_wrapper.workspace = true thiserror.workspace = true diff --git a/viz-core/src/from_request.rs b/viz-core/src/from_request.rs index 32f26c48..130e48bc 100644 --- a/viz-core/src/from_request.rs +++ b/viz-core/src/from_request.rs @@ -1,9 +1,9 @@ //! Extracts data from the [`Request`] by types. -use crate::{async_trait, IntoResponse, Request}; +use crate::{IntoResponse, Request}; /// An interface for extracting data from the HTTP [`Request`]. -#[async_trait] +#[crate::async_trait] pub trait FromRequest: Sized { /// The type returned in the event of a conversion error. type Error: IntoResponse; @@ -12,7 +12,7 @@ pub trait FromRequest: Sized { async fn extract(req: &mut Request) -> Result; } -#[async_trait] +#[crate::async_trait] impl FromRequest for Option where T: FromRequest, @@ -24,7 +24,7 @@ where } } -#[async_trait] +#[crate::async_trait] impl FromRequest for Result where T: FromRequest, diff --git a/viz-core/src/handler.rs b/viz-core/src/handler.rs index a312d4d9..5bff45dc 100644 --- a/viz-core/src/handler.rs +++ b/viz-core/src/handler.rs @@ -1,6 +1,6 @@ //! Traits and types for handling an HTTP. -use crate::{async_trait, Future}; +mod cloneable; mod after; pub use after::After; @@ -56,8 +56,8 @@ pub use service::ServiceHandler; /// A simplified asynchronous interface for handling input and output. /// /// Composable request handlers. -#[async_trait] -pub trait Handler: dyn_clone::DynClone + Send + Sync + 'static { +#[crate::async_trait] +pub trait Handler: Send + Sync + 'static { /// The returned type after the call operator is used. type Output; @@ -65,12 +65,12 @@ pub trait Handler: dyn_clone::DynClone + Send + Sync + 'static { async fn call(&self, input: Input) -> Self::Output; } -#[async_trait] +#[crate::async_trait] impl Handler for F where I: Send + 'static, F: Fn(I) -> Fut + ?Sized + Clone + Send + Sync + 'static, - Fut: Future + Send, + Fut: ::core::future::Future + Send + 'static, { type Output = Fut::Output; @@ -168,7 +168,7 @@ pub trait HandlerExt: Handler { } /// Catches rejected error while calling the handler. - fn catch_error(self, f: F) -> CatchError + fn catch_error(self, f: F) -> CatchError where Self: Sized, { @@ -186,9 +186,9 @@ pub trait HandlerExt: Handler { /// Converts this Handler into a [`BoxHandler`]. fn boxed(self) -> BoxHandler where - Self: Sized, + Self: Sized + Clone, { - Box::new(self) + BoxHandler::new(self) } /// Returns a new [`Handler`] that wrapping the `Self` and a type implementing [`Transform`]. diff --git a/viz-core/src/handler/after.rs b/viz-core/src/handler/after.rs index 95e95204..4b452389 100644 --- a/viz-core/src/handler/after.rs +++ b/viz-core/src/handler/after.rs @@ -1,4 +1,4 @@ -use crate::{async_trait, Handler, Result}; +use crate::{Handler, Result}; /// Maps the output `Result` after the handler called. #[derive(Debug, Clone)] @@ -15,13 +15,12 @@ impl After { } } -#[async_trait] +#[crate::async_trait] impl Handler for After where I: Send + 'static, - H: Handler> + Clone, - O: Send + 'static, - F: Handler + Clone, + H: Handler>, + F: Handler, { type Output = F::Output; diff --git a/viz-core/src/handler/and_then.rs b/viz-core/src/handler/and_then.rs index aa32dc24..515388dd 100644 --- a/viz-core/src/handler/and_then.rs +++ b/viz-core/src/handler/and_then.rs @@ -1,4 +1,4 @@ -use crate::{async_trait, Handler, Result}; +use crate::{Handler, Result}; /// Calls `op` if the output is `Ok`, otherwise returns the `Err` value of the output. #[derive(Debug, Clone)] @@ -15,13 +15,13 @@ impl AndThen { } } -#[async_trait] +#[crate::async_trait] impl Handler for AndThen where I: Send + 'static, - H: Handler> + Clone, + H: Handler>, + F: Handler, O: Send, - F: Handler + Clone, { type Output = F::Output; diff --git a/viz-core/src/handler/around.rs b/viz-core/src/handler/around.rs index 342efafe..2e401964 100644 --- a/viz-core/src/handler/around.rs +++ b/viz-core/src/handler/around.rs @@ -1,4 +1,4 @@ -use crate::{async_trait, Handler, Result}; +use crate::{Handler, Result}; /// Represents a middleware parameter, which is a tuple that includes Requset and `BoxHandler`. pub type Next = (I, H); @@ -18,12 +18,12 @@ impl Around { } } -#[async_trait] +#[crate::async_trait] impl Handler for Around where I: Send + 'static, H: Handler> + Clone, - F: Handler, Output = H::Output> + Clone, + F: Handler, Output = H::Output>, { type Output = F::Output; diff --git a/viz-core/src/handler/before.rs b/viz-core/src/handler/before.rs index 309fa7ec..6eeb901b 100644 --- a/viz-core/src/handler/before.rs +++ b/viz-core/src/handler/before.rs @@ -1,4 +1,4 @@ -use crate::{async_trait, Handler, Result}; +use crate::{Handler, Result}; /// Maps the input before the handler calls. #[derive(Debug, Clone)] @@ -15,12 +15,12 @@ impl Before { } } -#[async_trait] +#[crate::async_trait] impl Handler for Before where I: Send + 'static, - F: Handler> + Clone, - H: Handler> + Clone, + F: Handler>, + H: Handler>, { type Output = H::Output; diff --git a/viz-core/src/handler/boxed.rs b/viz-core/src/handler/boxed.rs index b81b34d0..43ea69aa 100644 --- a/viz-core/src/handler/boxed.rs +++ b/viz-core/src/handler/boxed.rs @@ -1,19 +1,52 @@ -use crate::{async_trait, Handler, Request, Response, Result}; +use std::fmt; -/// Alias the boxed Handler. -pub type BoxHandler> = Box>; +use super::cloneable::BoxCloneable; +use crate::{Handler, Request, Response, Result}; -impl Clone for BoxHandler { +/// A [`Clone`] + [`Send`] boxed [`Handler`]. +pub struct BoxHandler>(BoxCloneable); + +impl BoxHandler { + /// Creates a new `BoxHandler`. + pub fn new(h: H) -> Self + where + H: Handler + Send + Sync + Clone + 'static, + { + Self(Box::new(h)) + } +} + +impl Clone for BoxHandler +where + I: Send + 'static, + O: 'static, +{ fn clone(&self) -> Self { - dyn_clone::clone_box(&**self) + Self(self.0.clone_box()) + } +} + +#[crate::async_trait] +impl Handler for BoxHandler +where + I: Send + 'static, + O: 'static, +{ + type Output = O; + + async fn call(&self, i: I) -> Self::Output { + self.0.call(i).await } } -#[async_trait] -impl Handler for BoxHandler { - type Output = Result; +impl From> for BoxHandler { + fn from(value: BoxCloneable) -> Self { + Self(value) + } +} - async fn call(&self, req: Request) -> Self::Output { - self.as_ref().call(req).await +impl fmt::Debug for BoxHandler { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BoxHandler").finish() } } diff --git a/viz-core/src/handler/catch_error.rs b/viz-core/src/handler/catch_error.rs index 692f31ce..9dc49f68 100644 --- a/viz-core/src/handler/catch_error.rs +++ b/viz-core/src/handler/catch_error.rs @@ -1,16 +1,16 @@ use std::marker::PhantomData; -use crate::{async_trait, Handler, IntoResponse, Response, Result}; +use crate::{Handler, IntoResponse, Response, Result}; /// Catches rejected error while calling the handler. #[derive(Debug)] -pub struct CatchError { +pub struct CatchError { h: H, f: F, _marker: PhantomData R>, } -impl Clone for CatchError +impl Clone for CatchError where H: Clone, F: Clone, @@ -24,7 +24,7 @@ where } } -impl CatchError { +impl CatchError { /// Creates a [`CatchError`] handler. #[inline] pub fn new(h: H, f: F) -> Self { @@ -36,14 +36,14 @@ impl CatchError { } } -#[async_trait] -impl Handler for CatchError +#[crate::async_trait] +impl Handler for CatchError where I: Send + 'static, - H: Handler> + Clone, + H: Handler>, O: IntoResponse + Send, E: std::error::Error + Send + 'static, - F: Handler + Clone, + F: Handler, R: IntoResponse + 'static, { type Output = Result; diff --git a/viz-core/src/handler/catch_unwind.rs b/viz-core/src/handler/catch_unwind.rs index 81fe847d..6ef08f3f 100644 --- a/viz-core/src/handler/catch_unwind.rs +++ b/viz-core/src/handler/catch_unwind.rs @@ -1,8 +1,4 @@ -use std::{any::Any, panic::AssertUnwindSafe}; - -use futures_util::FutureExt; - -use crate::{async_trait, Handler, IntoResponse, Response, Result}; +use crate::{future::FutureExt, Handler, IntoResponse, Response, Result}; /// Catches unwinding panics while calling the handler. #[derive(Debug, Clone)] @@ -19,19 +15,22 @@ impl CatchUnwind { } } -#[async_trait] +#[crate::async_trait] impl Handler for CatchUnwind where I: Send + 'static, - H: Handler> + Clone, + H: Handler>, O: IntoResponse + Send, - F: Handler, Output = R> + Clone, + F: Handler, Output = R>, R: IntoResponse, { type Output = Result; async fn call(&self, i: I) -> Self::Output { - match AssertUnwindSafe(self.h.call(i)).catch_unwind().await { + match ::core::panic::AssertUnwindSafe(self.h.call(i)) + .catch_unwind() + .await + { Ok(r) => r.map(IntoResponse::into_response), Err(e) => Ok(self.f.call(e).await.into_response()), } diff --git a/viz-core/src/handler/cloneable.rs b/viz-core/src/handler/cloneable.rs new file mode 100644 index 00000000..75351455 --- /dev/null +++ b/viz-core/src/handler/cloneable.rs @@ -0,0 +1,16 @@ +use super::Handler; + +pub(crate) type BoxCloneable = Box>; + +pub(crate) trait Cloneable: Handler { + fn clone_box(&self) -> BoxCloneable; +} + +impl Cloneable for T +where + T: Handler + Clone, +{ + fn clone_box(&self) -> BoxCloneable { + Box::new(self.clone()) + } +} diff --git a/viz-core/src/handler/either.rs b/viz-core/src/handler/either.rs index 7acbee8f..ae161bec 100644 --- a/viz-core/src/handler/either.rs +++ b/viz-core/src/handler/either.rs @@ -1,4 +1,4 @@ -use crate::{async_trait, Handler}; +use crate::Handler; /// Combines two different handlers having the same associated types into a single type. #[derive(Debug, Clone)] @@ -9,12 +9,12 @@ pub enum Either { Right(R), } -#[async_trait] +#[crate::async_trait] impl Handler for Either where I: Send + 'static, - L: Handler + Clone, - R: Handler + Clone, + L: Handler, + R: Handler, { type Output = O; diff --git a/viz-core/src/handler/fn_ext.rs b/viz-core/src/handler/fn_ext.rs index a0677874..a69022dc 100644 --- a/viz-core/src/handler/fn_ext.rs +++ b/viz-core/src/handler/fn_ext.rs @@ -1,11 +1,9 @@ -use crate::{async_trait, Request}; - /// A handler with extractors. -#[async_trait] -pub trait FnExt: Clone + Send + Sync + 'static { +#[crate::async_trait] +pub trait FnExt: Send + Sync + 'static { /// The returned type after the call operator is used. type Output; /// Performs the call operation. - async fn call(&self, req: Request) -> Self::Output; + async fn call(&self, i: I) -> Self::Output; } diff --git a/viz-core/src/handler/fn_ext_hanlder.rs b/viz-core/src/handler/fn_ext_hanlder.rs index 665f9bb9..08a431e8 100644 --- a/viz-core/src/handler/fn_ext_hanlder.rs +++ b/viz-core/src/handler/fn_ext_hanlder.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use crate::{async_trait, FnExt, FromRequest, Handler, IntoResponse, Request, Result}; +use crate::{FnExt, FromRequest, Handler, IntoResponse, Result}; /// A wrapper of the extractors handler. #[derive(Debug)] @@ -22,17 +22,18 @@ impl FnExtHandler { } } -#[async_trait] -impl Handler for FnExtHandler +#[crate::async_trait] +impl Handler for FnExtHandler where + I: Send + 'static, E: FromRequest + 'static, - E::Error: IntoResponse + Send, - H: FnExt>, + E::Error: IntoResponse, + H: FnExt>, O: 'static, { type Output = H::Output; - async fn call(&self, req: Request) -> Self::Output { - self.0.call(req).await.map_err(IntoResponse::into_error) + async fn call(&self, i: I) -> Self::Output { + self.0.call(i).await.map_err(IntoResponse::into_error) } } diff --git a/viz-core/src/handler/into_handler.rs b/viz-core/src/handler/into_handler.rs index 5ca17b8d..179dbc9d 100644 --- a/viz-core/src/handler/into_handler.rs +++ b/viz-core/src/handler/into_handler.rs @@ -1,9 +1,7 @@ -use crate::{FromRequest, IntoResponse, Request, Result}; - -use super::{FnExt, FnExtHandler, Handler}; +use crate::{handler::FnExtHandler, FnExt, FromRequest, Handler, IntoResponse, Result}; /// The trait implemented by types that can be converted to a [`Handler`]. -pub trait IntoHandler { +pub trait IntoHandler { /// The target handler. type Handler: Handler; @@ -12,11 +10,12 @@ pub trait IntoHandler { fn into_handler(self) -> Self::Handler; } -impl IntoHandler for H +impl IntoHandler for H where + I: Send + 'static, E: FromRequest + 'static, - E::Error: IntoResponse + Send, - H: FnExt>, + E::Error: IntoResponse, + H: FnExt>, O: 'static, { type Handler = FnExtHandler; diff --git a/viz-core/src/handler/map.rs b/viz-core/src/handler/map.rs index 2a5a0503..bbb8b45c 100644 --- a/viz-core/src/handler/map.rs +++ b/viz-core/src/handler/map.rs @@ -1,4 +1,4 @@ -use crate::{async_trait, Handler, Result}; +use crate::{Handler, Result}; /// Maps the `Ok` value of the output if after the handler called. #[derive(Debug, Clone)] @@ -15,17 +15,16 @@ impl Map { } } -#[async_trait] -impl Handler for Map +#[crate::async_trait] +impl Handler for Map where I: Send + 'static, - H: Handler> + Clone, - O: Send, - F: Handler + Clone, + H: Handler>, + F: FnOnce(O) -> T + Send + Sync + Copy + 'static, { - type Output = H::Output; + type Output = Result; async fn call(&self, i: I) -> Self::Output { - Ok(self.f.call(self.h.call(i).await?).await) + self.h.call(i).await.map(self.f) } } diff --git a/viz-core/src/handler/map_err.rs b/viz-core/src/handler/map_err.rs index 54bdcdd4..54ea3f07 100644 --- a/viz-core/src/handler/map_err.rs +++ b/viz-core/src/handler/map_err.rs @@ -1,4 +1,4 @@ -use crate::{async_trait, Error, Handler, Result}; +use crate::{Error, Handler, Result}; /// Maps the `Err` value of the output if after the handler called. #[derive(Debug, Clone)] @@ -15,20 +15,16 @@ impl MapErr { } } -#[async_trait] -impl Handler for MapErr +#[crate::async_trait] +impl Handler for MapErr where I: Send + 'static, - H: Handler> + Clone, - O: Send, - F: Handler + Clone, + H: Handler>, + F: FnOnce(E) -> Error + Send + Sync + Copy + 'static, { - type Output = H::Output; + type Output = Result; async fn call(&self, i: I) -> Self::Output { - match self.h.call(i).await { - Ok(o) => Ok(o), - Err(e) => Err(self.f.call(e).await), - } + self.h.call(i).await.map_err(self.f) } } diff --git a/viz-core/src/handler/map_into_response.rs b/viz-core/src/handler/map_into_response.rs index 4718990d..1e871dd7 100644 --- a/viz-core/src/handler/map_into_response.rs +++ b/viz-core/src/handler/map_into_response.rs @@ -1,4 +1,4 @@ -use crate::{async_trait, Handler, IntoResponse, Response, Result}; +use crate::{Handler, IntoResponse, Response, Result}; /// Maps the handler's output type to the [`Response`]. #[derive(Debug, Clone)] @@ -12,12 +12,12 @@ impl MapInToResponse { } } -#[async_trait] +#[crate::async_trait] impl Handler for MapInToResponse where I: Send + 'static, - H: Handler> + Clone, - O: IntoResponse + Send, + H: Handler>, + O: IntoResponse, { type Output = Result; diff --git a/viz-core/src/handler/or_else.rs b/viz-core/src/handler/or_else.rs index 5b8c71b4..942d6f76 100644 --- a/viz-core/src/handler/or_else.rs +++ b/viz-core/src/handler/or_else.rs @@ -1,4 +1,4 @@ -use crate::{async_trait, Error, Handler, Result}; +use crate::{Error, Handler, Result}; /// Calls `op` if the output is `Err`, otherwise returns the `Ok` value of the output. #[derive(Debug, Clone)] @@ -15,13 +15,13 @@ impl OrElse { } } -#[async_trait] +#[crate::async_trait] impl Handler for OrElse where I: Send + 'static, - H: Handler> + Clone, + H: Handler>, + F: Handler, O: Send, - F: Handler + Clone, { type Output = F::Output; diff --git a/viz-core/src/handler/service.rs b/viz-core/src/handler/service.rs index 56c7fa43..23bf038e 100644 --- a/viz-core/src/handler/service.rs +++ b/viz-core/src/handler/service.rs @@ -1,8 +1,6 @@ use hyper::service::Service; -use crate::{ - async_trait, Body, BoxError, Bytes, Error, Handler, HttpBody, Request, Response, Result, -}; +use crate::{Body, BoxError, Bytes, Error, Handler, HttpBody, Request, Response, Result}; /// Converts a hyper [`Service`] to a viz [`Handler`]. #[derive(Debug, Clone)] @@ -15,14 +13,14 @@ impl ServiceHandler { } } -#[async_trait] +#[crate::async_trait] impl Handler> for ServiceHandler where I: HttpBody + Send + 'static, O: HttpBody + Send + 'static, O::Data: Into, O::Error: Into, - S: Service, Response = Response> + Send + Sync + Clone + 'static, + S: Service, Response = Response> + Send + Sync + 'static, S::Future: Send, S::Error: Into, { @@ -32,7 +30,7 @@ where self.0 .call(req) .await - .map(|resp| resp.map(Body::wrap)) .map_err(Error::boxed) + .map(|resp| resp.map(Body::wrap)) } } diff --git a/viz-core/src/lib.rs b/viz-core/src/lib.rs index 201e84a4..b6d7d289 100644 --- a/viz-core/src/lib.rs +++ b/viz-core/src/lib.rs @@ -43,21 +43,22 @@ pub type Request = http::Request; /// Represents an HTTP Response. pub type Response = http::Response; /// Represents either success (Ok) or failure (Err). -pub type Result = core::result::Result; +pub type Result = ::core::result::Result; pub use async_trait::async_trait; pub use bytes::{Bytes, BytesMut}; +pub use core::future::Future; +pub use futures_util::future; #[doc(inline)] pub use headers; pub use http::{header, Method, StatusCode}; pub use hyper::body::{Body as HttpBody, Incoming}; pub use hyper_util::rt::TokioIo as Io; -pub use std::future::Future; pub use thiserror::Error as ThisError; #[doc(hidden)] mod tuples { - use super::{async_trait, Error, FnExt, FromRequest, Future, IntoResponse, Request, Result}; + use super::{Error, FnExt, FromRequest, Future, IntoResponse, Request, Result}; tuple_impls!(A B C D E F G H I J K L); } diff --git a/viz-core/src/macros.rs b/viz-core/src/macros.rs index 729eef7d..577e20e2 100644 --- a/viz-core/src/macros.rs +++ b/viz-core/src/macros.rs @@ -8,7 +8,7 @@ macro_rules! tuple_impls { }; // "Private" internal implementation (@impl $( $T:ident )*) => { - #[async_trait] + #[crate::async_trait] impl<$($T,)*> FromRequest for ($($T,)*) where $($T: FromRequest + Send,)* @@ -22,12 +22,12 @@ macro_rules! tuple_impls { } } - #[async_trait] - impl<$($T,)* Fun, Fut, Out> FnExt<($($T,)*)> for Fun + #[crate::async_trait] + impl<$($T,)* Fun, Fut, Out> FnExt for Fun where $($T: FromRequest + Send,)* $($T::Error: IntoResponse + Send,)* - Fun: Fn($($T,)*) -> Fut + Clone + Send + Sync + 'static, + Fun: Fn($($T,)*) -> Fut + Send + Sync + 'static, Fut: Future> + Send, { type Output = Fut::Output; diff --git a/viz-core/src/middleware/compression.rs b/viz-core/src/middleware/compression.rs index 11b15921..b34a9d03 100644 --- a/viz-core/src/middleware/compression.rs +++ b/viz-core/src/middleware/compression.rs @@ -6,7 +6,6 @@ use async_compression::tokio::bufread; use tokio_util::io::{ReaderStream, StreamReader}; use crate::{ - async_trait, header::{HeaderValue, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH}, Body, Handler, IntoResponse, Request, Response, Result, Transform, }; @@ -32,11 +31,11 @@ pub struct CompressionMiddleware { h: H, } -#[async_trait] +#[crate::async_trait] impl Handler for CompressionMiddleware where + H: Handler>, O: IntoResponse, - H: Handler> + Clone, { type Output = Result; diff --git a/viz-core/src/middleware/cookie.rs b/viz-core/src/middleware/cookie.rs index e8ac8b64..a75489a6 100644 --- a/viz-core/src/middleware/cookie.rs +++ b/viz-core/src/middleware/cookie.rs @@ -3,7 +3,6 @@ use std::fmt; use crate::{ - async_trait, header::{HeaderValue, COOKIE, SET_COOKIE}, types::{Cookie, CookieJar, CookieKey, Cookies}, Handler, IntoResponse, Request, Response, Result, Transform, @@ -80,10 +79,10 @@ impl fmt::Debug for CookieMiddleware { } } -#[async_trait] +#[crate::async_trait] impl Handler for CookieMiddleware where - H: Handler> + Clone, + H: Handler>, O: IntoResponse, { type Output = Result; diff --git a/viz-core/src/middleware/cors.rs b/viz-core/src/middleware/cors.rs index d0327899..8c14bed5 100644 --- a/viz-core/src/middleware/cors.rs +++ b/viz-core/src/middleware/cors.rs @@ -3,7 +3,6 @@ use std::{collections::HashSet, fmt, sync::Arc}; use crate::{ - async_trait, header::{ HeaderMap, HeaderName, HeaderValue, ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_HEADERS, @@ -203,10 +202,10 @@ pub struct CorsMiddleware { aceh: AccessControlExposeHeaders, } -#[async_trait] +#[crate::async_trait] impl Handler for CorsMiddleware where - H: Handler> + Clone, + H: Handler>, O: IntoResponse, { type Output = Result; diff --git a/viz-core/src/middleware/csrf.rs b/viz-core/src/middleware/csrf.rs index 321e8241..4fd62c32 100644 --- a/viz-core/src/middleware/csrf.rs +++ b/viz-core/src/middleware/csrf.rs @@ -5,7 +5,6 @@ use std::{collections::HashSet, fmt, sync::Arc}; use base64::Engine as _; use crate::{ - async_trait, header::{HeaderName, HeaderValue, VARY}, middleware::helper::{CookieOptions, Cookieable}, Error, FromRequest, Handler, IntoResponse, Method, Request, RequestExt, Response, Result, @@ -37,7 +36,7 @@ pub enum Store { #[derive(Debug, Clone)] pub struct CsrfToken(pub String); -#[async_trait] +#[crate::async_trait] impl FromRequest for CsrfToken { type Error = Error; @@ -186,10 +185,10 @@ where } } -#[async_trait] +#[crate::async_trait] impl Handler for CsrfMiddleware where - H: Handler> + Clone, + H: Handler>, O: IntoResponse, S: Fn() -> Result> + Send + Sync + 'static, G: Fn(&[u8], Vec) -> Vec + Send + Sync + 'static, @@ -199,6 +198,7 @@ where async fn call(&self, mut req: Request) -> Self::Output { let mut secret = self.config.get(&req)?; + let config = self.config.as_ref(); if !config.ignored_methods.contains(req.method()) { @@ -212,11 +212,11 @@ where return Err((StatusCode::FORBIDDEN, "Invalid csrf token").into_error()); } } - let otp = (config.secret)()?; let secret = (config.secret)()?; let token = base64::engine::general_purpose::URL_SAFE_NO_PAD .encode((config.generate)(&secret, otp)); + req.extensions_mut().insert(CsrfToken(token.to_string())); self.config.set(&req, token, secret)?; diff --git a/viz-core/src/middleware/limits.rs b/viz-core/src/middleware/limits.rs index 9b4a7441..9abab877 100644 --- a/viz-core/src/middleware/limits.rs +++ b/viz-core/src/middleware/limits.rs @@ -3,7 +3,7 @@ #[cfg(feature = "multipart")] use std::sync::Arc; -use crate::{async_trait, types, Handler, IntoResponse, Request, Response, Result, Transform}; +use crate::{types, Handler, IntoResponse, Request, Response, Result, Transform}; /// A configuration for [`LimitsMiddleware`]. #[derive(Debug, Clone)] @@ -67,10 +67,10 @@ pub struct LimitsMiddleware { config: Config, } -#[async_trait] +#[crate::async_trait] impl Handler for LimitsMiddleware where - H: Handler> + Clone, + H: Handler>, O: IntoResponse, { type Output = Result; diff --git a/viz-core/src/middleware/otel/metrics.rs b/viz-core/src/middleware/otel/metrics.rs index 16b3c51b..a981b8c4 100644 --- a/viz-core/src/middleware/otel/metrics.rs +++ b/viz-core/src/middleware/otel/metrics.rs @@ -14,10 +14,7 @@ use opentelemetry_semantic_conventions::trace::{ HTTP_ROUTE, NETWORK_PROTOCOL_VERSION, SERVER_ADDRESS, SERVER_PORT, URL_SCHEME, }; -use crate::{ - async_trait, Handler, IntoResponse, Request, RequestExt, Response, ResponseExt, Result, - Transform, -}; +use crate::{Handler, IntoResponse, Request, RequestExt, Response, ResponseExt, Result, Transform}; const HTTP_SERVER_ACTIVE_REQUESTS: &str = "http.server.active_requests"; const HTTP_SERVER_DURATION: &str = "http.server.duration"; @@ -96,40 +93,45 @@ pub struct MetricsMiddleware { response_size: Histogram, } -#[async_trait] +#[crate::async_trait] impl Handler for MetricsMiddleware where - H: Handler> + Clone, + H: Handler>, O: IntoResponse, { type Output = Result; async fn call(&self, req: Request) -> Self::Output { + let Self { + active_requests, + duration, + request_size, + response_size, + h, + } = self; + let timer = SystemTime::now(); let mut attributes = build_attributes(&req, req.route_info().pattern.as_str()); - self.active_requests.add(1, &attributes); + active_requests.add(1, &attributes); - self.request_size - .record(req.content_length().unwrap_or(0), &attributes); + request_size.record(req.content_length().unwrap_or(0), &attributes); - let resp = self - .h + let resp = h .call(req) .await .map(IntoResponse::into_response) .map(|resp| { - self.active_requests.add(-1, &attributes); + active_requests.add(-1, &attributes); attributes.push(HTTP_RESPONSE_STATUS_CODE.i64(i64::from(resp.status().as_u16()))); - self.response_size - .record(resp.content_length().unwrap_or(0), &attributes); + response_size.record(resp.content_length().unwrap_or(0), &attributes); resp }); - self.duration.record( + duration.record( timer.elapsed().map(|t| t.as_secs_f64()).unwrap_or_default(), &attributes, ); diff --git a/viz-core/src/middleware/otel/tracing.rs b/viz-core/src/middleware/otel/tracing.rs index a6c0d977..9de8f394 100644 --- a/viz-core/src/middleware/otel/tracing.rs +++ b/viz-core/src/middleware/otel/tracing.rs @@ -19,7 +19,6 @@ use opentelemetry_semantic_conventions::trace::{ }; use crate::{ - async_trait, header::{HeaderMap, HeaderName}, headers::UserAgent, Handler, IntoResponse, Request, RequestExt, Response, ResponseExt, Result, Transform, @@ -58,10 +57,10 @@ pub struct TracingMiddleware { tracer: Arc, } -#[async_trait] +#[crate::async_trait] impl Handler for TracingMiddleware where - H: Handler> + Clone, + H: Handler>, O: IntoResponse, T: Tracer + Send + Sync + Clone + 'static, T::Span: Send + Sync + 'static, diff --git a/viz-core/src/middleware/session/config.rs b/viz-core/src/middleware/session/config.rs index c1fdec8c..21cd72dd 100644 --- a/viz-core/src/middleware/session/config.rs +++ b/viz-core/src/middleware/session/config.rs @@ -5,7 +5,6 @@ use std::{ }; use crate::{ - async_trait, middleware::helper::{CookieOptions, Cookieable}, types::{Cookie, Session}, Error, Handler, IntoResponse, Request, RequestExt, Response, Result, StatusCode, Transform, @@ -89,10 +88,10 @@ where } } -#[async_trait] +#[crate::async_trait] impl Handler for SessionMiddleware where - H: Handler> + Clone, + H: Handler>, O: IntoResponse, S: Storage + 'static, G: Fn() -> String + Send + Sync + 'static, @@ -101,12 +100,14 @@ where type Output = Result; async fn call(&self, mut req: Request) -> Self::Output { + let Self { h, config } = self; + let cookies = req.cookies().map_err(Error::from)?; - let cookie = self.config.get_cookie(&cookies); + let cookie = config.get_cookie(&cookies); let mut session_id = cookie.as_ref().map(Cookie::value).map(ToString::to_string); let data = match &session_id { - Some(sid) if (self.config.store().verify)(sid) => self.config.store().get(sid).await?, + Some(sid) if (config.store().verify)(sid) => config.store().get(sid).await?, _ => None, }; if data.is_none() && session_id.is_some() { @@ -115,7 +116,7 @@ where let session = Session::new(data.unwrap_or_default()); req.extensions_mut().insert(session.clone()); - let resp = self.h.call(req).await.map(IntoResponse::into_response); + let resp = h.call(req).await.map(IntoResponse::into_response); let status = session.status().load(Ordering::Acquire); @@ -125,8 +126,8 @@ where if status == PURGED { if let Some(sid) = &session_id { - self.config.store().remove(sid).await.map_err(Error::from)?; - self.config.remove_cookie(&cookies); + config.store().remove(sid).await.map_err(Error::from)?; + config.remove_cookie(&cookies); } return resp; @@ -134,23 +135,19 @@ where if status == RENEWED { if let Some(sid) = &session_id.take() { - self.config.store().remove(sid).await.map_err(Error::from)?; + config.store().remove(sid).await.map_err(Error::from)?; } } let sid = session_id.unwrap_or_else(|| { - let sid = (self.config.store().generate)(); - self.config.set_cookie(&cookies, &sid); + let sid = (config.store().generate)(); + config.set_cookie(&cookies, &sid); sid }); - self.config + config .store() - .set( - &sid, - session.data()?, - &self.config.ttl().unwrap_or_else(max_age), - ) + .set(&sid, session.data()?, &config.ttl().unwrap_or_else(max_age)) .await .map_err(Error::from)?; diff --git a/viz-core/src/request.rs b/viz-core/src/request.rs index 84ae12ba..70aae7e9 100644 --- a/viz-core/src/request.rs +++ b/viz-core/src/request.rs @@ -1,5 +1,5 @@ use crate::{ - async_trait, header, + header, types::{PayloadError, RealIp}, Body, BodyState, Bytes, FromRequest, Request, Result, }; @@ -36,7 +36,7 @@ use crate::types::Session; use crate::types::{ParamsError, PathDeserializer, RouteInfo}; /// The [`Request`] Extension. -#[async_trait] +#[crate::async_trait] pub trait RequestExt: private::Sealed + Sized { /// Get URL's schema of this request. fn schema(&self) -> Option<&http::uri::Scheme>; @@ -193,7 +193,7 @@ pub trait RequestExt: private::Sealed + Sized { fn realip(&self) -> Option; } -#[async_trait] +#[crate::async_trait] impl RequestExt for Request { fn schema(&self) -> Option<&http::uri::Scheme> { self.uri().scheme() diff --git a/viz-core/src/response.rs b/viz-core/src/response.rs index bdd5743c..18ce6a1e 100644 --- a/viz-core/src/response.rs +++ b/viz-core/src/response.rs @@ -1,10 +1,10 @@ use futures_util::Stream; use http_body_util::Full; -use crate::{async_trait, header, Body, BoxError, Bytes, Error, Response, Result, StatusCode}; +use crate::{header, Body, BoxError, Bytes, Error, Response, Result, StatusCode}; /// The [`Response`] Extension. -#[async_trait] +#[crate::async_trait] pub trait ResponseExt: private::Sealed + Sized { /// Get the size of this response's body. fn content_length(&self) -> Option; @@ -186,7 +186,7 @@ pub trait ResponseExt: private::Sealed + Sized { } } -#[async_trait] +#[crate::async_trait] impl ResponseExt for Response { fn content_length(&self) -> Option { self.headers() diff --git a/viz-core/src/types/cookie.rs b/viz-core/src/types/cookie.rs index d59fd7d6..5199ed34 100644 --- a/viz-core/src/types/cookie.rs +++ b/viz-core/src/types/cookie.rs @@ -6,8 +6,7 @@ use std::{ }; use crate::{ - async_trait, Error, FromRequest, IntoResponse, Request, RequestExt, Response, StatusCode, - ThisError, + Error, FromRequest, IntoResponse, Request, RequestExt, Response, StatusCode, ThisError, }; pub use ::cookie::{Cookie, CookieJar, SameSite}; @@ -193,7 +192,7 @@ impl Cookies { } } -#[async_trait] +#[crate::async_trait] impl FromRequest for Cookies { type Error = CookiesError; diff --git a/viz-core/src/types/form.rs b/viz-core/src/types/form.rs index dbaa2bcf..9eb14ed9 100644 --- a/viz-core/src/types/form.rs +++ b/viz-core/src/types/form.rs @@ -7,7 +7,7 @@ use std::{ use serde::de::DeserializeOwned; -use crate::{async_trait, FromRequest, Request, RequestExt, Result}; +use crate::{FromRequest, Request, RequestExt, Result}; use super::{Payload, PayloadError}; @@ -81,7 +81,7 @@ impl Payload for Form { } } -#[async_trait] +#[crate::async_trait] impl FromRequest for Form where T: DeserializeOwned, diff --git a/viz-core/src/types/header.rs b/viz-core/src/types/header.rs index be81c3b8..27aa4694 100644 --- a/viz-core/src/types/header.rs +++ b/viz-core/src/types/header.rs @@ -6,7 +6,7 @@ use std::{ }; use crate::{ - async_trait, header, + header, headers::{self, HeaderMapExt}, Error, FromRequest, IntoResponse, Request, Response, Result, StatusCode, ThisError, }; @@ -66,7 +66,7 @@ where } } -#[async_trait] +#[crate::async_trait] impl FromRequest for Header where T: headers::Header, diff --git a/viz-core/src/types/json.rs b/viz-core/src/types/json.rs index 4ca05130..96a2083a 100644 --- a/viz-core/src/types/json.rs +++ b/viz-core/src/types/json.rs @@ -5,9 +5,7 @@ use std::{ ops::{Deref, DerefMut}, }; -use crate::{ - async_trait, FromRequest, IntoResponse, Request, RequestExt, Response, ResponseExt, Result, -}; +use crate::{FromRequest, IntoResponse, Request, RequestExt, Response, ResponseExt, Result}; use super::{Payload, PayloadError}; @@ -82,7 +80,7 @@ impl Payload for Json { } } -#[async_trait] +#[crate::async_trait] impl FromRequest for Json where T: serde::de::DeserializeOwned, diff --git a/viz-core/src/types/limits.rs b/viz-core/src/types/limits.rs index 96e5a753..821fb524 100644 --- a/viz-core/src/types/limits.rs +++ b/viz-core/src/types/limits.rs @@ -1,6 +1,6 @@ use std::{convert::Infallible, sync::Arc}; -use crate::{async_trait, FromRequest, Request, RequestExt}; +use crate::{FromRequest, Request, RequestExt}; #[cfg(feature = "form")] use super::Form; @@ -81,7 +81,7 @@ impl Limits { } } -#[async_trait] +#[crate::async_trait] impl FromRequest for Limits { type Error = Infallible; diff --git a/viz-core/src/types/multipart.rs b/viz-core/src/types/multipart.rs index 99d9147d..209e05e6 100644 --- a/viz-core/src/types/multipart.rs +++ b/viz-core/src/types/multipart.rs @@ -2,9 +2,7 @@ use form_data::FormData; -use crate::{ - async_trait, Body, Error, FromRequest, IntoResponse, Request, RequestExt, Response, StatusCode, -}; +use crate::{Body, Error, FromRequest, IntoResponse, Request, RequestExt, Response, StatusCode}; use super::{Payload, PayloadError}; @@ -28,7 +26,7 @@ impl Payload for Multipart { } } -#[async_trait] +#[crate::async_trait] impl FromRequest for Multipart { type Error = PayloadError; diff --git a/viz-core/src/types/params.rs b/viz-core/src/types/params.rs index 5eedc294..e5daf16a 100644 --- a/viz-core/src/types/params.rs +++ b/viz-core/src/types/params.rs @@ -11,8 +11,7 @@ use std::{ use serde::de::DeserializeOwned; use crate::{ - async_trait, Error, FromRequest, IntoResponse, Request, RequestExt, Response, StatusCode, - ThisError, + Error, FromRequest, IntoResponse, Request, RequestExt, Response, StatusCode, ThisError, }; pub(crate) use de::PathDeserializer; @@ -77,7 +76,7 @@ impl Params { } } -#[async_trait] +#[crate::async_trait] impl FromRequest for Params where T: DeserializeOwned, diff --git a/viz-core/src/types/query.rs b/viz-core/src/types/query.rs index 9984e099..3026e06c 100644 --- a/viz-core/src/types/query.rs +++ b/viz-core/src/types/query.rs @@ -7,7 +7,7 @@ use std::{ use serde::de::DeserializeOwned; -use crate::{async_trait, types::PayloadError, FromRequest, Request, RequestExt, Result}; +use crate::{types::PayloadError, FromRequest, Request, RequestExt, Result}; /// Extracts the data from the query string of a URL. pub struct Query(pub T); @@ -64,7 +64,7 @@ where } } -#[async_trait] +#[crate::async_trait] impl FromRequest for Query where T: DeserializeOwned, diff --git a/viz-core/src/types/session.rs b/viz-core/src/types/session.rs index 0a6109a1..10d8d1be 100644 --- a/viz-core/src/types/session.rs +++ b/viz-core/src/types/session.rs @@ -14,7 +14,7 @@ use serde_json::{from_value, to_value, Value}; use sessions_core::{Data, State, CHANGED, PURGED, RENEWED, UNCHANGED}; -use crate::{async_trait, Error, FromRequest, IntoResponse, Request, RequestExt, StatusCode}; +use crate::{Error, FromRequest, IntoResponse, Request, RequestExt, StatusCode}; /// A session for the current request. #[derive(Clone)] @@ -170,7 +170,7 @@ impl fmt::Debug for Session { } } -#[async_trait] +#[crate::async_trait] impl FromRequest for Session { type Error = Infallible; diff --git a/viz-core/src/types/state.rs b/viz-core/src/types/state.rs index c72e4681..b4ee6020 100644 --- a/viz-core/src/types/state.rs +++ b/viz-core/src/types/state.rs @@ -6,8 +6,8 @@ use std::{ }; use crate::{ - async_trait, handler::Transform, Error, FromRequest, Handler, IntoResponse, Request, - RequestExt, Response, Result, StatusCode, ThisError, + handler::Transform, Error, FromRequest, Handler, IntoResponse, Request, RequestExt, Response, + Result, StatusCode, ThisError, }; /// Extracts state from the extensions of a request. @@ -49,7 +49,7 @@ impl DerefMut for State { } } -#[async_trait] +#[crate::async_trait] impl FromRequest for State where T: Clone + Send + Sync + 'static, @@ -72,11 +72,11 @@ where } } -#[async_trait] +#[crate::async_trait] impl Handler for State<(T, H)> where T: Clone + Send + Sync + 'static, - H: Handler> + Clone, + H: Handler>, O: IntoResponse, { type Output = Result; diff --git a/viz-core/src/types/websocket.rs b/viz-core/src/types/websocket.rs index 8461a345..675446df 100644 --- a/viz-core/src/types/websocket.rs +++ b/viz-core/src/types/websocket.rs @@ -6,7 +6,6 @@ use hyper::upgrade::{OnUpgrade, Upgraded}; use tokio_tungstenite::tungstenite::protocol::Role; use crate::{ - async_trait, header::{SEC_WEBSOCKET_PROTOCOL, UPGRADE}, headers::{ Connection, HeaderMapExt, HeaderValue, SecWebsocketAccept, SecWebsocketKey, @@ -99,7 +98,7 @@ impl WebSocket { } } -#[async_trait] +#[crate::async_trait] impl FromRequest for WebSocket { type Error = WebSocketError; diff --git a/viz-core/tests/handler.rs b/viz-core/tests/handler.rs index 2c8d6545..7311b3d4 100644 --- a/viz-core/tests/handler.rs +++ b/viz-core/tests/handler.rs @@ -200,11 +200,11 @@ async fn handler() -> Result<()> { } } - async fn map(res: Response) -> Response { + fn map(res: Response) -> Response { res } - async fn map_err(err: Error) -> Error { + fn map_err(err: Error) -> Error { err } @@ -232,7 +232,9 @@ async fn handler() -> Result<()> { name: "round 1".to_string(), }) .map(map) - .catch_error(|_: CustomError2| async move { "Custom Error 2" }) + .catch_error::<_, CustomError2, &'static str>(|_: CustomError2| async move { + "Custom Error 2" + }) .catch_unwind( |_: Box| async move { panic!("Custom Error 2") }, ); @@ -255,12 +257,14 @@ async fn handler() -> Result<()> { .map_err(map_err) .or_else(or_else); let rhb = b.map_into_response(); - let rhc = c - .map_into_response() - .catch_error(|_: CustomError2| async move { "Custom Error 2" }) - .catch_error2(|e: std::io::Error| async move { - (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()) - }); + let rhc = + c.map_into_response() + .catch_error(|_: CustomError2| async move { "Custom Error 2" }) + .catch_error2::<_, std::io::Error, (StatusCode, String)>( + |e: std::io::Error| async move { + (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()) + }, + ); let rhd = d .map_into_response() .map(map) @@ -289,25 +293,25 @@ async fn handler() -> Result<()> { assert!(rhb.call(Request::default()).await.is_err()); - let brha: BoxHandler = rha.boxed(); - let brhb: BoxHandler = Box::new(rhb) + let brha: BoxHandler<_, _> = rha.boxed(); + let brhb: BoxHandler<_, _> = Box::new(rhb) .around(MyAround { name: "MyRound 3".to_string(), }) .boxed(); - let brhc: BoxHandler = Box::new(rhc); - let brhd: BoxHandler = Box::new(rhd); - let brhe: BoxHandler = rhe.boxed(); - let brhf: BoxHandler = Box::new(rhf); - let brhg: BoxHandler = Box::new(rhg); - let brhh: BoxHandler = Box::new(rhh); - let brhi: BoxHandler = Box::new(rhi); - let brhj: BoxHandler = Box::new(rhj); - let brhk: BoxHandler = rhk.boxed(); - let brhl: BoxHandler = Box::new(rhl); - let brhm: BoxHandler = rhm.boxed(); - - let v: Vec = vec![ + let brhc: BoxHandler<_, _> = rhc.boxed(); + let brhd: BoxHandler<_, _> = rhd.boxed(); + let brhe: BoxHandler<_, _> = rhe.boxed(); + let brhf: BoxHandler<_, _> = rhf.boxed(); + let brhg: BoxHandler<_, _> = rhg.boxed(); + let brhh: BoxHandler<_, _> = rhh.boxed(); + let brhi: BoxHandler<_, _> = rhi.boxed(); + let brhj: BoxHandler<_, _> = rhj.boxed(); + let brhk: BoxHandler<_, _> = rhk.boxed(); + let brhl: BoxHandler<_, _> = rhl.boxed(); + let brhm: BoxHandler<_, _> = rhm.boxed(); + + let v: Vec> = vec![ brha, brhb, brhc, brhd, brhe, brhf, brhg, brhh, brhi, brhj, brhk, brhl, brhm, ]; diff --git a/viz-handlers/src/embed.rs b/viz-handlers/src/embed.rs index 86191ddd..1ea13e71 100644 --- a/viz-handlers/src/embed.rs +++ b/viz-handlers/src/embed.rs @@ -5,8 +5,7 @@ use std::{borrow::Cow, marker::PhantomData}; use http_body_util::Full; use rust_embed::{EmbeddedFile, RustEmbed}; use viz_core::{ - async_trait, - header::{HeaderMap, CONTENT_TYPE, ETAG, IF_NONE_MATCH}, + header::{CONTENT_TYPE, ETAG, IF_NONE_MATCH}, Handler, IntoResponse, Method, Request, RequestExt, Response, Result, StatusCode, }; @@ -28,7 +27,7 @@ impl File { } } -#[async_trait] +#[viz_core::async_trait] impl Handler for File where E: RustEmbed + Send + Sync + 'static, @@ -36,7 +35,7 @@ where type Output = Result; async fn call(&self, req: Request) -> Self::Output { - serve::(&self.0, req.method(), req.headers()) + serve::(&self.0, &req) } } @@ -56,7 +55,7 @@ impl Default for Dir { } } -#[async_trait] +#[viz_core::async_trait] impl Handler for Dir where E: RustEmbed + Send + Sync + 'static, @@ -64,20 +63,21 @@ where type Output = Result; async fn call(&self, req: Request) -> Self::Output { - let path = match req.route_info().params.first().map(|(_, v)| v) { - Some(p) => p, - None => "index.html", - }; - - serve::(path, req.method(), req.headers()) + serve::( + match req.route_info().params.first().map(|(_, v)| v) { + Some(p) => p, + None => "index.html", + }, + &req, + ) } } -fn serve(path: &str, method: &Method, headers: &HeaderMap) -> Result +fn serve(path: &str, req: &Request) -> Result where E: RustEmbed + Send + Sync + 'static, { - if method != Method::GET { + if Method::GET != req.method() { Err(StatusCode::METHOD_NOT_ALLOWED.into_error())?; } @@ -85,7 +85,8 @@ where Some(EmbeddedFile { data, metadata }) => { let hash = hex::encode(metadata.sha256_hash()); - if headers + if req + .headers() .get(IF_NONE_MATCH) .map_or(false, |etag| etag.to_str().unwrap_or("000000").eq(&hash)) { diff --git a/viz-handlers/src/prometheus.rs b/viz-handlers/src/prometheus.rs index 1326281c..53dfd7d3 100644 --- a/viz-handlers/src/prometheus.rs +++ b/viz-handlers/src/prometheus.rs @@ -7,7 +7,6 @@ use opentelemetry::{global::handle_error, metrics::MetricsError}; use prometheus::{Encoder, TextEncoder}; use viz_core::{ - async_trait, header::{HeaderValue, CONTENT_TYPE}, Handler, IntoResponse, Request, Response, Result, StatusCode, }; @@ -31,7 +30,7 @@ impl Prometheus { } } -#[async_trait] +#[viz_core::async_trait] impl Handler for Prometheus { type Output = Result; diff --git a/viz-handlers/src/serve.rs b/viz-handlers/src/serve.rs index 09280c90..bbf19671 100644 --- a/viz-handlers/src/serve.rs +++ b/viz-handlers/src/serve.rs @@ -11,7 +11,6 @@ use tokio::io::AsyncReadExt; use tokio_util::io::ReaderStream; use viz_core::{ - async_trait, headers::{ AcceptRanges, ContentLength, ContentRange, ContentType, ETag, HeaderMap, HeaderMapExt, IfMatch, IfModifiedSince, IfNoneMatch, IfUnmodifiedSince, LastModified, Range, @@ -47,7 +46,7 @@ impl File { } } -#[async_trait] +#[viz_core::async_trait] impl Handler for File { type Output = Result; @@ -98,7 +97,7 @@ impl Dir { } } -#[async_trait] +#[viz_core::async_trait] impl Handler for Dir { type Output = Result; diff --git a/viz-router/src/resources.rs b/viz-router/src/resources.rs index 42c4b89a..7fac8634 100644 --- a/viz-router/src/resources.rs +++ b/viz-router/src/resources.rs @@ -72,7 +72,7 @@ impl Resources { pub(crate) fn on(mut self, kind: Kind, method: Method, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { match self .routes @@ -95,7 +95,7 @@ impl Resources { pub fn index(self, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { self.on(Kind::Empty, Method::GET, handler) } @@ -105,7 +105,7 @@ impl Resources { pub fn new(self, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { self.on(Kind::New, Method::GET, handler) } @@ -115,7 +115,7 @@ impl Resources { pub fn create(self, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { self.on(Kind::Empty, Method::POST, handler) } @@ -125,7 +125,7 @@ impl Resources { pub fn show(self, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { self.on(Kind::Id, Method::GET, handler) } @@ -135,7 +135,7 @@ impl Resources { pub fn edit(self, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { self.on(Kind::Edit, Method::GET, handler) } @@ -145,7 +145,7 @@ impl Resources { pub fn update(self, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { self.on(Kind::Id, Method::PUT, handler) } @@ -155,7 +155,7 @@ impl Resources { pub fn update_with_patch(self, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { self.on(Kind::Id, Method::PATCH, handler) } @@ -165,7 +165,7 @@ impl Resources { pub fn destroy(self, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { self.on(Kind::Id, Method::DELETE, handler) } @@ -200,7 +200,7 @@ impl Resources { pub fn with(self, t: T) -> Self where T: Transform, - T::Output: Handler>, + T::Output: Handler> + Clone, { self.map_handler(|handler| t.transform(handler).boxed()) } @@ -288,7 +288,7 @@ mod tests { #[async_trait] impl Handler for LoggerHandler where - H: Handler + Clone, + H: Handler, { type Output = H::Output; @@ -307,8 +307,8 @@ mod tests { async fn around((req, handler): Next) -> Result where - H: Handler> + Clone, - O: IntoResponse + Send + 'static, + H: Handler>, + O: IntoResponse, { handler.call(req).await.map(IntoResponse::into_response) } diff --git a/viz-router/src/route.rs b/viz-router/src/route.rs index c86334d7..94089c7e 100644 --- a/viz-router/src/route.rs +++ b/viz-router/src/route.rs @@ -14,7 +14,7 @@ macro_rules! export_internal_verb { pub fn $name(self, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { self.on(Method::$verb, handler) } @@ -28,7 +28,7 @@ macro_rules! export_verb { pub fn $name(handler: H) -> Route where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { Route::new().$name(handler) } @@ -71,7 +71,7 @@ impl Route { pub fn on(self, method: Method, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { self.push(method, handler.map_into_response().boxed()) } @@ -81,7 +81,7 @@ impl Route { pub fn any(self, handler: H) -> Self where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { [ Method::GET, @@ -127,7 +127,7 @@ impl Route { pub fn with(self, t: T) -> Self where T: Transform, - T::Output: Handler>, + T::Output: Handler> + Clone, { self.map_handler(|handler| t.transform(handler).boxed()) } @@ -167,7 +167,7 @@ impl FromIterator<(Method, BoxHandler)> for Route { pub fn on(method: Method, handler: H) -> Route where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { Route::new().on(method, handler) } @@ -189,7 +189,7 @@ repeat!( pub fn any(handler: H) -> Route where H: Handler> + Clone, - O: IntoResponse + Send + 'static, + O: IntoResponse, { Route::new().any(handler) } @@ -253,7 +253,7 @@ mod tests { #[async_trait] impl Handler for LoggerHandler where - H: Handler + Clone, + H: Handler, { type Output = H::Output; @@ -272,23 +272,23 @@ mod tests { async fn around((req, handler): Next) -> Result where - H: Handler> + Clone, - O: IntoResponse + Send + 'static, + H: Handler>, + O: IntoResponse, { handler.call(req).await.map(IntoResponse::into_response) } async fn around_1((req, handler): Next) -> Result where - H: Handler> + Clone, - O: IntoResponse + Send + 'static, + H: Handler>, + O: IntoResponse, { handler.call(req).await.map(IntoResponse::into_response) } async fn around_2((req, handler): Next) -> Result where - H: Handler> + Clone, + H: Handler>, { handler.call(req).await } @@ -302,7 +302,7 @@ mod tests { impl Handler> for Around2 where I: Send + 'static, - H: Handler> + Clone, + H: Handler>, { type Output = H::Output; diff --git a/viz-router/src/router.rs b/viz-router/src/router.rs index 4aa48cba..53e2b265 100644 --- a/viz-router/src/router.rs +++ b/viz-router/src/router.rs @@ -140,7 +140,7 @@ impl Router { #[must_use] pub fn map_handler(self, f: F) -> Self where - F: Fn(BoxHandler) -> BoxHandler, + F: Fn(BoxHandler>) -> BoxHandler>, { Self { routes: self.routes.map(|routes| { @@ -164,8 +164,8 @@ impl Router { #[must_use] pub fn with(self, t: T) -> Self where - T: Transform, - T::Output: Handler>, + T: Transform>>, + T::Output: Handler> + Clone, { self.map_handler(|handler| t.transform(handler).boxed()) } @@ -174,7 +174,8 @@ impl Router { #[must_use] pub fn with_handler(self, f: H) -> Self where - H: Handler, Output = Result> + Clone, + H: Handler>>, Output = Result> + + Clone, { self.map_handler(|handler| handler.around(f.clone()).boxed()) } @@ -217,7 +218,7 @@ mod tests { #[async_trait] impl Handler for LoggerHandler where - H: Handler + Clone, + H: Handler, { type Output = H::Output; @@ -280,7 +281,7 @@ mod tests { async fn middle((req, h): Next) -> Result where - H: Handler> + Clone, + H: Handler>, { h.call(req).await } diff --git a/viz-router/src/tree.rs b/viz-router/src/tree.rs index c49235fe..df2fe635 100644 --- a/viz-router/src/tree.rs +++ b/viz-router/src/tree.rs @@ -7,7 +7,7 @@ use viz_core::{BoxHandler, Method}; use crate::{Route, Router}; /// Store all final routes. -#[derive(Default)] +#[derive(Clone, Default)] pub struct Tree(Vec<(Method, PathTree)>); impl Tree { diff --git a/viz-test/src/lib.rs b/viz-test/src/lib.rs index b48e7b10..20764f3f 100644 --- a/viz-test/src/lib.rs +++ b/viz-test/src/lib.rs @@ -1,7 +1,7 @@ use reqwest::Client; -use std::{net::SocketAddr, sync::Arc}; +use std::{future::IntoFuture, net::SocketAddr}; use tokio::net::TcpListener; -use viz::{serve, Error, Result, Router, Tree}; +use viz::{serve, Error, Result, Router}; pub use http; pub use nano_id; @@ -22,14 +22,13 @@ impl TestServer { /// Will return `Err` if the server fails to start. pub async fn new(router: Router) -> Result { let listener = TcpListener::bind("127.0.0.1:0").await?; - let tree = Arc::new(Tree::from(router)); let addr = listener.local_addr()?; let client = reqwest::Client::builder() .redirect(reqwest::redirect::Policy::none()) .build() .map_err(Error::boxed)?; - tokio::spawn(run(listener, tree)); + tokio::spawn(serve(listener, router).into_future()); Ok(Self { addr, client }) } @@ -59,11 +58,3 @@ impl TestServer { self.client.put(self.path(url)) } } - -async fn run(listener: TcpListener, tree: Arc) -> Result<()> { - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(serve(stream, tree, Some(addr))); - } -} diff --git a/viz-tower/src/layer.rs b/viz-tower/src/layer.rs index ab3e826f..fa3978e8 100644 --- a/viz-tower/src/layer.rs +++ b/viz-tower/src/layer.rs @@ -1,5 +1,3 @@ -use viz_core::Transform; - use crate::Middleware; /// Transforms a Tower layer into Viz Middleware. @@ -13,7 +11,7 @@ impl Layered { } } -impl Transform for Layered +impl viz_core::Transform for Layered where L: Clone, { diff --git a/viz-tower/src/lib.rs b/viz-tower/src/lib.rs index 535690e8..94e45b26 100644 --- a/viz-tower/src/lib.rs +++ b/viz-tower/src/lib.rs @@ -1,9 +1,7 @@ //! An adapter that makes a tower [`Service`] into a [`Handler`]. use tower::{Service, ServiceExt}; -use viz_core::{ - async_trait, Body, BoxError, Bytes, Error, Handler, HttpBody, Request, Response, Result, -}; +use viz_core::{Body, BoxError, Bytes, Error, Handler, HttpBody, Request, Response, Result}; mod service; pub use service::HandlerService; @@ -25,7 +23,7 @@ impl ServiceHandler { } } -#[async_trait] +#[viz_core::async_trait] impl Handler for ServiceHandler where O: HttpBody + Send + 'static, @@ -42,8 +40,8 @@ where .clone() .oneshot(req) .await - .map(|resp| resp.map(Body::wrap)) .map_err(Error::boxed) + .map(|resp| resp.map(Body::wrap)) } } diff --git a/viz-tower/src/middleware.rs b/viz-tower/src/middleware.rs index 45d9f5b0..1f868f44 100644 --- a/viz-tower/src/middleware.rs +++ b/viz-tower/src/middleware.rs @@ -1,7 +1,5 @@ use tower::{Layer, Service, ServiceExt}; -use viz_core::{ - async_trait, Body, BoxError, Bytes, Error, Handler, HttpBody, Request, Response, Result, -}; +use viz_core::{Body, BoxError, Bytes, Error, Handler, HttpBody, Request, Response, Result}; use crate::HandlerService; @@ -19,15 +17,15 @@ impl Middleware { } } -#[async_trait] +#[viz_core::async_trait] impl Handler for Middleware where - L: Layer> + Send + Sync + Clone + 'static, - H: Handler> + Send + Sync + Clone + 'static, + L: Layer> + Send + Sync + 'static, + H: Handler> + Clone, O: HttpBody + Send + 'static, O::Data: Into, O::Error: Into, - L::Service: Service> + Send + Sync + Clone + 'static, + L::Service: Service> + Send + Sync + 'static, >::Future: Send, >::Error: Into, { @@ -35,11 +33,10 @@ where async fn call(&self, req: Request) -> Self::Output { self.l - .clone() .layer(HandlerService::new(self.h.clone())) .oneshot(req) .await - .map(|resp| resp.map(Body::wrap)) .map_err(Error::boxed) + .map(|resp| resp.map(Body::wrap)) } } diff --git a/viz-tower/src/service.rs b/viz-tower/src/service.rs index 6247618b..875e6e14 100644 --- a/viz-tower/src/service.rs +++ b/viz-tower/src/service.rs @@ -1,10 +1,12 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; -use tower::Service; -use viz_core::{Error, Future, Handler, Request, Response, Result}; +use viz_core::{Error, Handler, Request, Response, Result}; -/// An adapter that makes a [`Handler`] into a [`Service`]. +/// An adapter that makes a [`Handler`] into a [`Service`](tower::Service). #[derive(Debug)] pub struct HandlerService(H); @@ -24,13 +26,13 @@ where } } -impl Service for HandlerService +impl tower::Service for HandlerService where - H: Handler> + Send + Clone + 'static, + H: Handler> + Clone, { type Response = Response; type Error = Error; - type Future = Pin> + Send + 'static>>; + type Future = Pin + Send>>; #[inline] fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { @@ -38,7 +40,7 @@ where } fn call(&mut self, req: Request) -> Self::Future { - let handler = self.0.clone(); - Box::pin(async move { handler.call(req).await }) + let h = self.0.clone(); + Box::pin(async move { h.call(req).await }) } } diff --git a/viz/Cargo.toml b/viz/Cargo.toml index b341bea8..768d3ef2 100644 --- a/viz/Cargo.toml +++ b/viz/Cargo.toml @@ -76,12 +76,15 @@ viz-macros = { workspace = true, optional = true } hyper.workspace = true hyper-util.workspace = true +futures-util = { workspace = true, optional = true } + rustls-pemfile = { workspace = true, optional = true } -futures-util = { workspace = true, optional = true } tokio-native-tls = { workspace = true, optional = true } tokio-rustls = { workspace = true, optional = true } -tokio.workspace = true +tokio = { workspace = true, features = ["macros"] } +tokio-util = { workspace = true, features = ["net"] } +tracing.workspace = true [dev-dependencies] tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread"] } diff --git a/viz/README.md b/viz/README.md index f8ff5042..c86a1f87 100644 --- a/viz/README.md +++ b/viz/README.md @@ -49,14 +49,14 @@ - Simple + Flexible `Handler` & `Middleware` -- Supports: Tower `Service` +- Supports Tower `Service` ## Hello Viz ```rust -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; use tokio::net::TcpListener; -use viz::{serve, Request, Result, Router, Tree}; +use viz::{serve, Request, Result, Router}; async fn index(_: Request) -> Result<&'static str> { Ok("Hello, Viz!") @@ -69,13 +69,12 @@ async fn main() -> Result<()> { println!("listening on http://{addr}"); let app = Router::new().get("/", index); - let tree = Arc::new(Tree::from(app)); - loop { - let (stream, addr) = listener.accept().await?; - let tree = tree.clone(); - tokio::task::spawn(serve(stream, tree, Some(addr))); + if let Err(e) = serve(listener, app).await { + println!("{e}"); } + + Ok(()) } ``` diff --git a/viz/src/lib.rs b/viz/src/lib.rs index 354cc9f7..ca4c2206 100644 --- a/viz/src/lib.rs +++ b/viz/src/lib.rs @@ -14,12 +14,14 @@ //! //! * Robust [`Routing`](#routing) //! +//! * Supports Tower [`Service`] +//! //! # Hello Viz //! //! ```no_run -//! use std::{net::SocketAddr, sync::Arc}; +//! use std::net::SocketAddr; //! use tokio::net::TcpListener; -//! use viz::{serve, Request, Result, Router, Tree}; +//! use viz::{serve, Request, Result, Router}; //! //! async fn index(_: Request) -> Result<&'static str> { //! Ok("Hello, Viz!") @@ -32,13 +34,12 @@ //! println!("listening on http://{addr}"); //! //! let app = Router::new().get("/", index); -//! let tree = Arc::new(Tree::from(app)); //! -//! loop { -//! let (stream, addr) = listener.accept().await?; -//! let tree = tree.clone(); -//! tokio::task::spawn(serve(stream, tree, Some(addr))); +//! if let Err(e) = serve(listener, app).await { +//! println!("{e}"); //! } +//! +//! Ok(()) //! } //! ``` //! @@ -80,9 +81,9 @@ //! //! #[async_trait] //! impl Handler for MyHandler { -//! type Output = Result; +//! type Output = Result; //! -//! async fn call(&self, req: Request) -> Self::Output { +//! async fn call(&self, req: Request) -> Self::Output { //! let path = req.path(); //! let method = req.method().clone(); //! let code = self.code.fetch_add(1, Ordering::SeqCst); @@ -188,7 +189,7 @@ //! //! async fn around((req, handler): Next) -> Result //! where -//! H: Handler> + Clone, +//! H: Handler>, //! { //! // before ... //! let result = handler.call(req).await; @@ -221,7 +222,7 @@ //! # use std::time::Duration; //! # use viz::{ //! # async_trait, get, types::Params, Transform, HandlerExt, IntoResponse, IntoHandler, -//! # Request, Response, ResponseExt, Result, Router, StatusCode, Next, Handler +//! # Request, Response, ResponseExt, Result, Router, StatusCode, Next, Handler, //! # }; //! async fn index(_: Request) -> Result { //! Ok(StatusCode::OK.into_response()) @@ -288,7 +289,7 @@ //! #[async_trait] //! impl Handler for TimeoutMiddleware //! where -//! H: Handler + Clone, +//! H: Handler, //! { //! type Output = H::Output; //! @@ -512,6 +513,7 @@ //! //! [`FutureExt`]: https://docs.rs/futures/latest/futures/future/trait.FutureExt.html //! [`StreamExt`]: https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html +//! [`Service`]: https://docs.rs/tower-service/latest/tower_service/trait.Service.html #![doc(html_logo_url = "https://viz.rs/logo.svg")] #![doc(html_favicon_url = "https://viz.rs/logo.svg")] @@ -525,26 +527,24 @@ mod responder; #[cfg(any(feature = "http1", feature = "http2"))] pub use responder::Responder; - #[cfg(any(feature = "http1", feature = "http2"))] -mod serve; +mod server; #[cfg(any(feature = "http1", feature = "http2"))] -pub use serve::{serve, serve_with_upgrades}; +pub use server::{serve, Server}; /// TLS +#[cfg(any(feature = "native_tls", feature = "rustls"))] pub mod tls; -pub use viz_core::*; -pub use viz_router::*; #[cfg(feature = "handlers")] #[cfg_attr(docsrs, doc(cfg(feature = "handlers")))] #[doc(inline)] pub use viz_handlers as handlers; -#[cfg(any(feature = "http1", feature = "http2"))] -pub use hyper::server; - #[cfg(feature = "macros")] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] #[doc(inline)] pub use viz_macros::handler; + +pub use viz_core::*; +pub use viz_router::*; diff --git a/viz/src/responder.rs b/viz/src/responder.rs index 70ae3e59..624933cc 100644 --- a/viz/src/responder.rs +++ b/viz/src/responder.rs @@ -1,4 +1,4 @@ -use std::{convert::Infallible, future::Future, net::SocketAddr, pin::Pin, sync::Arc}; +use std::{convert::Infallible, future::Future, pin::Pin, sync::Arc}; use crate::{ types::RouteInfo, Body, Handler, Incoming, IntoResponse, Method, Request, Response, StatusCode, @@ -7,58 +7,58 @@ use crate::{ /// Handles the HTTP [`Request`] and retures the HTTP [`Response`]. #[derive(Debug)] -pub struct Responder { +pub struct Responder { tree: Arc, - addr: Option, + remote_addr: Option, } -impl Responder { +impl Responder +where + A: Clone + Send + Sync + 'static, +{ /// Creates a Responder for handling the [`Request`]. #[must_use] - pub fn new(tree: Arc, addr: Option) -> Self { - Self { tree, addr } + pub fn new(tree: Arc, remote_addr: Option) -> Self { + Self { tree, remote_addr } } +} + +impl hyper::service::Service> for Responder +where + A: Clone + Send + Sync + 'static, +{ + type Response = Response; + type Error = Infallible; + type Future = Pin> + Send>>; - /// Serves a request and returns a response. - async fn serve( - mut req: Request, - tree: Arc, - addr: Option, - ) -> Result { + fn call(&self, mut req: Request) -> Self::Future { let method = req.method().clone(); let path = req.uri().path().to_owned(); - let Some((handler, route)) = tree.find(&method, &path).or_else(|| { + let Some((handler, route)) = self.tree.find(&method, &path).or_else(|| { if method == Method::HEAD { - tree.find(&Method::GET, &path) + self.tree.find(&Method::GET, &path) } else { None } }) else { - return Ok(StatusCode::NOT_FOUND.into_response()); + return Box::pin(async move { Ok(StatusCode::NOT_FOUND.into_response()) }); }; - req.extensions_mut().insert(addr); + req.extensions_mut().insert(self.remote_addr.clone()); req.extensions_mut().insert(Arc::from(RouteInfo { id: *route.id, pattern: route.pattern(), params: route.params().into(), })); - // req.set_state(tree.clone()); - Ok(handler - .call(req.map(Body::Incoming)) - .await - .unwrap_or_else(IntoResponse::into_response)) - } -} -impl hyper::service::Service> for Responder { - type Response = Response; - type Error = Infallible; - type Future = Pin> + Send>>; + let handler = handler.clone(); - #[inline] - fn call(&self, req: Request) -> Self::Future { - Box::pin(Self::serve(req, self.tree.clone(), self.addr)) + Box::pin(async move { + Ok(handler + .call(req.map(Body::Incoming)) + .await + .unwrap_or_else(IntoResponse::into_response)) + }) } } diff --git a/viz/src/serve.rs b/viz/src/serve.rs deleted file mode 100644 index 0d9af2d3..00000000 --- a/viz/src/serve.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::{net::SocketAddr, sync::Arc}; - -use hyper_util::{rt::TokioExecutor, server::conn::auto::Builder}; -use tokio::io::{AsyncRead, AsyncWrite}; - -use viz_core::{Io, Result}; -use viz_router::Tree; - -use crate::Responder; - -/// Serve the connections. -/// -/// # Errors -/// -/// Will return `Err` if the connection does not be served. -pub async fn serve(stream: I, tree: Arc, addr: Option) -> Result<()> -where - I: AsyncRead + AsyncWrite + Unpin + Send + 'static, -{ - Builder::new(TokioExecutor::new()) - .serve_connection(Io::new(stream), Responder::new(tree, addr)) - .await - .map_err(Into::into) -} - -/// Serve the connections with upgrades. -/// -/// # Errors -/// -/// Will return `Err` if the connection does not be served. -pub async fn serve_with_upgrades( - stream: I, - tree: Arc, - addr: Option, -) -> Result<()> -where - I: AsyncRead + AsyncWrite + Unpin + Send + 'static, -{ - Builder::new(TokioExecutor::new()) - .serve_connection_with_upgrades(Io::new(stream), Responder::new(tree, addr)) - .await - .map_err(Into::into) -} diff --git a/viz/src/server.rs b/viz/src/server.rs new file mode 100644 index 00000000..3845bb3b --- /dev/null +++ b/viz/src/server.rs @@ -0,0 +1,180 @@ +use std::{ + fmt::Debug, + future::{pending, Future, IntoFuture, Pending}, + io, + pin::Pin, + sync::Arc, +}; + +use hyper_util::{ + rt::{TokioExecutor, TokioIo}, + server::conn::auto::Builder, +}; +use tokio::{pin, select, sync::watch}; +use tokio_util::net::Listener; + +use crate::{future::FutureExt, Responder, Router, Tree}; + +/// Starts a server and serves the connections. +pub fn serve(listener: L, router: Router) -> Server +where + L: Listener + Send + 'static, + L::Io: Send + Unpin, + L::Addr: Send + Sync + Debug, +{ + Server::::new(listener, router) +} + +/// A listening HTTP server that accepts connections. +#[derive(Debug)] +pub struct Server> { + signal: F, + tree: Tree, + listener: L, + builder: Builder, +} + +impl Server { + /// Starts a [`Server`] with a listener and a [`Tree`]. + pub fn new(listener: L, router: Router) -> Server { + Server { + listener, + signal: pending(), + tree: router.into(), + builder: Builder::new(TokioExecutor::new()), + } + } + + /// Changes the signal for graceful shutdown. + pub fn signal(self, signal: T) -> Server { + Server { + signal, + tree: self.tree, + builder: self.builder, + listener: self.listener, + } + } + + /// Returns the HTTP1 or HTTP2 connection builder. + pub fn builder(&mut self) -> &mut Builder { + &mut self.builder + } +} + +/// Copied from Axum. Thanks. +impl IntoFuture for Server +where + L: Listener + Send + 'static, + L::Io: Send + Unpin, + L::Addr: Send + Sync + Debug, + F: Future + Send + 'static, +{ + type Output = io::Result<()>; + type IntoFuture = Pin + Send>>; + + fn into_future(self) -> Self::IntoFuture { + let Self { + tree, + signal, + builder, + mut listener, + } = self; + + let (shutdown_tx, shutdown_rx) = watch::channel(()); + let shutdown_tx = Arc::new(shutdown_tx); + + tokio::spawn(async move { + signal.await; + tracing::trace!("received graceful shutdown signal"); + drop(shutdown_rx); + }); + + let (close_tx, close_rx) = watch::channel(()); + + let tree = Arc::new(tree); + + Box::pin(async move { + loop { + let (stream, remote_addr) = select! { + res = listener.accept() => { + match res { + Ok(conn) => conn, + Err(e) => { + if !is_connection_error(&e) { + // [From `hyper::Server` in 0.14](https://github.com/hyperium/hyper/blob/v0.14.27/src/server/tcp.rs#L186) + tracing::error!("listener accept error: {e}"); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + continue + } + } + } + () = shutdown_tx.closed() => { + tracing::trace!("server is closing"); + break; + } + }; + + tracing::trace!("connection {:?} accepted", remote_addr); + + let io = TokioIo::new(stream); + let remote_addr = Arc::new(remote_addr); + let builder = builder.clone(); + let responder = + Responder::>::new(tree.clone(), Some(remote_addr.clone())); + + let shutdown_tx = Arc::clone(&shutdown_tx); + let close_rx = close_rx.clone(); + + tokio::spawn(async move { + let conn = builder.serve_connection_with_upgrades(io, responder); + pin!(conn); + + let shutdown = shutdown_tx.closed().fuse(); + pin!(shutdown); + + loop { + select! { + res = conn.as_mut() => { + if let Err(e) = res { + tracing::error!("connection failed: {e}"); + } + break; + } + () = &mut shutdown => { + tracing::trace!("connection is starting to graceful shutdown"); + conn.as_mut().graceful_shutdown(); + } + } + } + + tracing::trace!("connection {:?} closed", remote_addr); + + drop(close_rx); + }); + } + + drop(close_rx); + drop(listener); + + tracing::trace!( + "waiting for {} task(s) to finish", + close_tx.receiver_count() + ); + close_tx.closed().await; + + tracing::trace!("server shutdown complete"); + + Ok(()) + }) + } +} + +fn is_connection_error(e: &io::Error) -> bool { + matches!( + e.kind(), + io::ErrorKind::ConnectionRefused + | io::ErrorKind::ConnectionAborted + | io::ErrorKind::ConnectionReset + ) +} diff --git a/viz/src/tls/listener.rs b/viz/src/tls/listener.rs index c3d36c1c..7227d7c2 100644 --- a/viz/src/tls/listener.rs +++ b/viz/src/tls/listener.rs @@ -1,5 +1,4 @@ /// Unified TLS listener type. -#[allow(dead_code)] #[derive(Debug)] pub struct Listener { pub(crate) inner: T, diff --git a/viz/src/tls/native_tls.rs b/viz/src/tls/native_tls.rs index 73b4d796..bb5180ab 100644 --- a/viz/src/tls/native_tls.rs +++ b/viz/src/tls/native_tls.rs @@ -1,5 +1,11 @@ -use std::{fmt, net::SocketAddr}; +use std::{ + fmt, + io::{Error as IoError, ErrorKind, Result as IoResult}, + net::SocketAddr, + task::{Context, Poll}, +}; +use futures_util::FutureExt; use tokio::net::{TcpListener, TcpStream}; use tokio_native_tls::{native_tls::TlsAcceptor as TlsAcceptorWrapper, TlsStream}; @@ -38,15 +44,21 @@ impl Config { } } -impl Listener { - /// A [`TlsStream`] and [`SocketAddr`] part for accepting TLS. - /// - /// # Errors - /// - /// Will return `Err` if accepting the stream fails. - pub async fn accept(&self) -> Result<(TlsStream, SocketAddr)> { - let (stream, addr) = self.inner.accept().await?; - let tls_stream = self.acceptor.accept(stream).await.map_err(Error::boxed)?; - Ok((tls_stream, addr)) +impl tokio_util::net::Listener for Listener { + type Io = TlsStream; + type Addr = SocketAddr; + + fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll> { + let Poll::Ready((stream, addr)) = self.inner.poll_accept(cx)? else { + return Poll::Pending; + }; + Box::pin(self.acceptor.accept(stream)) + .poll_unpin(cx) + .map_ok(|stream| (stream, addr)) + .map_err(|e| IoError::new(ErrorKind::Other, e)) + } + + fn local_addr(&self) -> IoResult { + self.inner.local_addr() } } diff --git a/viz/src/tls/rustls.rs b/viz/src/tls/rustls.rs index 7540e7c5..e9dd4c1c 100644 --- a/viz/src/tls/rustls.rs +++ b/viz/src/tls/rustls.rs @@ -1,8 +1,10 @@ use std::{ - io::{Error as IoError, ErrorKind}, + io::{Error as IoError, ErrorKind, Result as IoResult}, net::SocketAddr, + task::{Context, Poll}, }; +use futures_util::FutureExt; use tokio::net::{TcpListener, TcpStream}; use tokio_rustls::{ rustls::{ @@ -149,17 +151,21 @@ impl Config { } } -impl Listener { - /// Accepts a new incoming connection from this listener. - /// - /// Returns a [`TlsStream`] and [`SocketAddr`] part. - /// - /// # Errors - /// - /// This function throws if it is not accepted from the listener. - pub async fn accept(&self) -> Result<(TlsStream, SocketAddr)> { - let (stream, addr) = self.inner.accept().await?; - let tls_stream = self.acceptor.accept(stream).await?; - Ok((tls_stream, addr)) +impl tokio_util::net::Listener for Listener { + type Io = TlsStream; + type Addr = SocketAddr; + + fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll> { + let Poll::Ready((stream, addr)) = self.inner.poll_accept(cx)? else { + return Poll::Pending; + }; + self.acceptor + .accept(stream) + .poll_unpin(cx) + .map_ok(|stream| (stream, addr)) + } + + fn local_addr(&self) -> IoResult { + self.inner.local_addr() } }