diff --git a/examples/low-level-openssl/Cargo.toml b/examples/low-level-openssl/Cargo.toml index 6e8a756c..c5247dec 100644 --- a/examples/low-level-openssl/Cargo.toml +++ b/examples/low-level-openssl/Cargo.toml @@ -8,6 +8,7 @@ publish = false axum = { path = "../../axum" } futures-util = { version = "0.3", default-features = false, features = ["alloc"] } hyper = { version = "1.0.0", features = ["full"] } +hyper-util = { version = "0.1" } openssl = "0.10" tokio = { version = "1", features = ["full"] } tokio-openssl = "0.6" diff --git a/examples/low-level-openssl/src/main.rs b/examples/low-level-openssl/src/main.rs index 3cc99a83..1b473756 100644 --- a/examples/low-level-openssl/src/main.rs +++ b/examples/low-level-openssl/src/main.rs @@ -1,91 +1,100 @@ -// TODO -fn main() { - eprint!("this example has not yet been updated to hyper 1.0"); +use axum::{http::Request, routing::get, Router}; +use futures_util::pin_mut; +use hyper::body::Incoming; +use hyper_util::rt::{TokioExecutor, TokioIo}; +use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod}; +use std::{path::PathBuf, pin::Pin}; +use tokio::net::TcpListener; +use tokio_openssl::SslStream; +use tower::Service; +use tracing::{error, info, warn}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +#[tokio::main] +async fn main() { + tracing_subscriber::registry() + .with( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| "example_low_level_openssl=debug".into()), + ) + .with(tracing_subscriber::fmt::layer()) + .init(); + + let mut tls_builder = SslAcceptor::mozilla_modern_v5(SslMethod::tls()).unwrap(); + + tls_builder + .set_certificate_file( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("self_signed_certs") + .join("cert.pem"), + SslFiletype::PEM, + ) + .unwrap(); + + tls_builder + .set_private_key_file( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("self_signed_certs") + .join("key.pem"), + SslFiletype::PEM, + ) + .unwrap(); + + tls_builder.check_private_key().unwrap(); + + let tls_acceptor = tls_builder.build(); + + let bind = "[::1]:3000"; + let tcp_listener = TcpListener::bind(bind).await.unwrap(); + info!("HTTPS server listening on {bind}. To contact curl -k https://localhost:3000"); + let app = Router::new().route("/", get(handler)); + + pin_mut!(tcp_listener); + + loop { + let tower_service = app.clone(); + let tls_acceptor = tls_acceptor.clone(); + + // Wait for new tcp connection + let (cnx, addr) = tcp_listener.accept().await.unwrap(); + + tokio::spawn(async move { + let ssl = Ssl::new(tls_acceptor.context()).unwrap(); + let mut tls_stream = SslStream::new(ssl, cnx).unwrap(); + if let Err(err) = SslStream::accept(Pin::new(&mut tls_stream)).await { + error!( + "error during tls handshake connection from {}: {}", + addr, err + ); + return; + } + + // Hyper has its own `AsyncRead` and `AsyncWrite` traits and doesn't use tokio. + // `TokioIo` converts between them. + let stream = TokioIo::new(tls_stream); + + // Hyper has also 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); + } + }); + } } -// use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod}; -// use tokio_openssl::SslStream; - -// use axum::{body::Body, http::Request, routing::get, Router}; -// use futures_util::future::poll_fn; -// use hyper::server::{ -// accept::Accept, -// conn::{AddrIncoming, Http}, -// }; -// use std::{path::PathBuf, pin::Pin, sync::Arc}; -// use tokio::net::TcpListener; -// use tower::MakeService; - -// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - -// #[tokio::main] -// async fn main() { -// tracing_subscriber::registry() -// .with( -// tracing_subscriber::EnvFilter::try_from_default_env() -// .unwrap_or_else(|_| "example_low_level_openssl=debug".into()), -// ) -// .with(tracing_subscriber::fmt::layer()) -// .init(); - -// let mut tls_builder = SslAcceptor::mozilla_modern_v5(SslMethod::tls()).unwrap(); - -// tls_builder -// .set_certificate_file( -// PathBuf::from(env!("CARGO_MANIFEST_DIR")) -// .join("self_signed_certs") -// .join("cert.pem"), -// SslFiletype::PEM, -// ) -// .unwrap(); - -// tls_builder -// .set_private_key_file( -// PathBuf::from(env!("CARGO_MANIFEST_DIR")) -// .join("self_signed_certs") -// .join("key.pem"), -// SslFiletype::PEM, -// ) -// .unwrap(); - -// tls_builder.check_private_key().unwrap(); - -// let acceptor = tls_builder.build(); - -// let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); -// let mut listener = AddrIncoming::from_listener(listener).unwrap(); - -// let protocol = Arc::new(Http::new()); - -// let mut app = Router::new().route("/", get(handler)).into_make_service(); - -// tracing::info!("listening on https://localhost:3000"); - -// loop { -// let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx)) -// .await -// .unwrap() -// .unwrap(); - -// let acceptor = acceptor.clone(); - -// let protocol = protocol.clone(); - -// let svc = MakeService::<_, Request>::make_service(&mut app, &stream); - -// tokio::spawn(async move { -// let ssl = Ssl::new(acceptor.context()).unwrap(); -// let mut tls_stream = SslStream::new(ssl, stream).unwrap(); - -// SslStream::accept(Pin::new(&mut tls_stream)).await.unwrap(); - -// let _ = protocol -// .serve_connection(tls_stream, svc.await.unwrap()) -// .await; -// }); -// } -// } - -// async fn handler() -> &'static str { -// "Hello, World!" -// } +async fn handler() -> &'static str { + "Hello, World!" +}