diff --git a/Cargo.lock b/Cargo.lock index cf03e1d..f77b63c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,6 +97,12 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "async-compression" version = "0.4.22" @@ -237,6 +243,28 @@ dependencies = [ "syn", ] +[[package]] +name = "axum-server" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "495c05f60d6df0093e8fb6e74aa5846a0ad06abaf96d76166283720bf740f8ab" +dependencies = [ + "arc-swap", + "bytes", + "fs-err", + "http", + "http-body", + "hyper", + "hyper-util", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -342,6 +370,7 @@ dependencies = [ "anyhow", "axum", "axum-extra", + "axum-server", "bytes", "clap", "const_format", @@ -599,6 +628,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-err" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f89bda4c2a21204059a977ed3bfe746677dfd137b83c339e702b0ac91d482aa" +dependencies = [ + "autocfg", + "tokio", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -1321,6 +1360,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.11.0" @@ -1565,9 +1613,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", diff --git a/Cargo.toml b/Cargo.toml index c5b2a1f..b9342d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ edition = "2024" [dependencies] # core -tokio = { version = "1.44.2", features = ["full"] } +tokio = { version = "1.45.1", features = ["full"] } tokio-util = "0.7.15" tokio-rustls = "0.26.2" hyper = { version = "1.6.0", features = ["full"] } @@ -20,6 +20,7 @@ futures-util = "0.3.31" mimalloc = "0.1.46" axum = { version = "0.8.4", features = ["macros"] } axum-extra = { version = "0.10.1", features = ["typed-header"] } +axum-server = { version = "0.7.2", features = ["tls-rustls"] } tower = { version = "0.5.2", features = ["full"] } tower-http = { version = "0.6.4", features = ["full"] } # tools diff --git a/Makefile b/Makefile index d90d768..f023cfb 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ dev: CANDY_LOG=debug $(CARGO) watch -x run run: - $(CARGO) run + CANDY_LOG=debug $(CARGO) run test: $(CARGO) test diff --git a/src/http/mod.rs b/src/http/mod.rs index 27647ef..f065c2f 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -1,26 +1,13 @@ -use std::{ - path::Path, - sync::{Arc, LazyLock}, - time::Duration, -}; +use std::{net::SocketAddr, sync::LazyLock, time::Duration}; use anyhow::anyhow; -use axum::{Router, extract::Request, middleware, routing::get}; +use axum::{Router, middleware, routing::get}; +use axum_server::tls_rustls::RustlsConfig; use dashmap::DashMap; -use futures_util::pin_mut; -use hyper::body::Incoming; -use hyper_util::rt::{TokioExecutor, TokioIo}; use tokio::net::TcpListener; -use tokio_rustls::{ - TlsAcceptor, - rustls::{ - ServerConfig, - pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject}, - }, -}; -use tower::{Service, ServiceBuilder}; +use tower::ServiceBuilder; use tower_http::{compression::CompressionLayer, timeout::TimeoutLayer}; -use tracing::{debug, error, info, warn}; +use tracing::{debug, info, warn}; use crate::{ config::SettingHost, @@ -124,8 +111,6 @@ pub async fn make_server(host: SettingHost) -> anyhow::Result<()> { router = logging_route(router); let addr = format!("{}:{}", host.ip, host.port); - let listener = TcpListener::bind(&addr).await?; - info!("listening on {}", addr); // check ssl eanbled or not // if ssl enabled @@ -141,57 +126,15 @@ pub async fn make_server(host: SettingHost) -> anyhow::Result<()> { .as_ref() .ok_or(anyhow!("certificate_key not found"))?; debug!("certificate {} certificate_key {}", cert, key); - let rustls_config = rustls_server_config(key, cert)?; - let tls_acceptor = TlsAcceptor::from(rustls_config); - - pin_mut!(listener); - loop { - let tower_service = router.clone(); - let tls_acceptor = tls_acceptor.clone(); - - // Wait for new tcp connecttion - let (cnx, addr) = match listener.accept().await { - Ok((cnx, addr)) => (cnx, addr), - Err(err) => { - error!("TCP connection accept error: {:?}", err); - continue; - } - }; - - let tls_handler = async move { - // Wait for tls handshake to happen - let Ok(stream) = tls_acceptor.accept(cnx).await else { - error!("error during tls handshake connection from {}", addr); - return; - }; - - // Hyper has its own `AsyncRead` and `AsyncWrite` traits and doesn't use tokio. - // `TokioIo` converts between them. - let stream = TokioIo::new(stream); - - // Hyper also has its own `Service` trait and doesn't use tower. We can use - // `hyper::service::service_fn` to create a hyper `Service` that calls our app through - // `tower::Service::call`. - let hyper_service = - hyper::service::service_fn(move |request: Request| { - // We have to clone `tower_service` because hyper's `Service` uses `&self` whereas - // tower's `Service` requires `&mut self`. - // - // We don't need to call `poll_ready` since `Router` is always ready. - tower_service.clone().call(request) - }); - - let ret = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new()) - .serve_connection_with_upgrades(stream, hyper_service) - .await; - - if let Err(err) = ret { - warn!("error serving connection from {}: {}", addr, err); - } - }; - tokio::spawn(tls_handler); - } + let rustls_config = RustlsConfig::from_pem_file(cert, key).await?; + let addr: SocketAddr = addr.parse()?; + info!("listening on https://{}", addr); + axum_server::bind_rustls(addr, rustls_config) + .serve(router.into_make_service()) + .await?; } else { + let listener = TcpListener::bind(&addr).await?; + info!("listening on http://{}", addr); axum::serve(listener, router) .with_graceful_shutdown(shutdown_signal(shutdown)) .await?; @@ -199,43 +142,3 @@ pub async fn make_server(host: SettingHost) -> anyhow::Result<()> { Ok(()) } - -/// Creates a Rustls `ServerConfig` for TLS-enabled connections. -/// -/// # Arguments -/// - `key`: Path to the PEM-encoded private key file. -/// - `cert`: Path to the PEM-encoded certificate chain file. -/// -/// # Returns -/// - `Ok(Arc)`: A configured `ServerConfig` with: -/// - No client authentication. -/// - ALPN protocols `h2` and `http/1.1` for HTTP/2 and HTTP/1.1 support. -/// - The provided certificate and private key. -/// - `Err(anyhow::Error)`: If the key/cert files are missing, malformed, or invalid. -/// -/// # Errors -/// - Fails if: -/// - The private key or certificate files cannot be read or parsed. -/// - The key/cert pair is incompatible (e.g., mismatched algorithms). -/// - The certificate chain is empty or invalid. -/// -/// # Example -/// ```rust -/// let config = rustls_server_config("key.pem", "cert.pem")?; -fn rustls_server_config( - key: impl AsRef, - cert: impl AsRef, -) -> anyhow::Result> { - let key = PrivateKeyDer::from_pem_file(key)?; - - let certs = CertificateDer::pem_file_iter(cert)?.try_collect()?; - - let mut config = ServerConfig::builder() - .with_no_client_auth() - .with_single_cert(certs, key) - .expect("bad certificate/key"); - - config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - - Ok(Arc::new(config)) -} diff --git a/src/http/serve.rs b/src/http/serve.rs index bf56b85..323a79c 100644 --- a/src/http/serve.rs +++ b/src/http/serve.rs @@ -144,7 +144,6 @@ pub async fn serve( // check static file root configuration // if root is None, then return InternalError let Some(ref root) = host_route.root else { - // return Err(RouteError::InternalError()); return custom_not_found!(host_route, request).await; }; // try find index file first diff --git a/src/main.rs b/src/main.rs index cecb49b..c889934 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,10 +45,11 @@ async fn main() -> Result<()> { info!("server started"); while let Some(res) = servers.join_next().await { - if let Err(err) = res { - error!("server error: {}", err); - continue; - } + error!("server error: {:?}", res); + // if let Err(err) = res { + // error!("server error: {}", err); + // continue; + // } } Ok(()) diff --git a/src/utils/service.rs b/src/utils/service.rs index 711fd94..9a92673 100644 --- a/src/utils/service.rs +++ b/src/utils/service.rs @@ -59,12 +59,13 @@ where tokio::select! { _ = ctrl_c => { shutdown_cb() - // let _ = stop_core().map_err(log_err); }, _ = terminate => { shutdown_cb() }, } + + tracing::info!("Received termination signal shutting down"); } pub fn shutdown() {