From 4558671a0be23793aefeb25f98060629e5dd8662 Mon Sep 17 00:00:00 2001 From: Sugih Jamin Date: Sat, 23 Jul 2022 04:23:40 -0400 Subject: [PATCH] Redirect HTTP to HTTPS in `tls-rustls` example (#1166) --- examples/tls-rustls/src/main.rs | 63 +++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/examples/tls-rustls/src/main.rs b/examples/tls-rustls/src/main.rs index b3110675..40008a96 100644 --- a/examples/tls-rustls/src/main.rs +++ b/examples/tls-rustls/src/main.rs @@ -4,11 +4,24 @@ //! cd examples && cargo run -p example-tls-rustls //! ``` -use axum::{routing::get, Router}; +use axum::{ + extract::Host, + handler::Handler, + http::{StatusCode, Uri}, + response::Redirect, + routing::get, + BoxError, Router, +}; use axum_server::tls_rustls::RustlsConfig; use std::{net::SocketAddr, path::PathBuf}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +#[derive(Clone, Copy)] +struct Ports { + http: u16, + https: u16, +} + #[tokio::main] async fn main() { tracing_subscriber::registry() @@ -18,6 +31,14 @@ async fn main() { .with(tracing_subscriber::fmt::layer()) .init(); + let ports = Ports { + http: 7878, + https: 3000, + }; + // optional: spawn a second server to redirect http requests to this server + tokio::spawn(redirect_http_to_https(ports)); + + // configure certificate and private key used by https let config = RustlsConfig::from_pem_file( PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("self_signed_certs") @@ -31,8 +52,9 @@ async fn main() { let app = Router::new().route("/", get(handler)); - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("listening on {}", addr); + // run https server + let addr = SocketAddr::from(([127, 0, 0, 1], ports.https)); + tracing::debug!("listening on {}", addr); axum_server::bind_rustls(addr, config) .serve(app.into_make_service()) .await @@ -42,3 +64,38 @@ async fn main() { async fn handler() -> &'static str { "Hello, World!" } + +async fn redirect_http_to_https(ports: Ports) { + fn make_https(host: String, uri: Uri, ports: Ports) -> Result { + let mut parts = uri.into_parts(); + + parts.scheme = Some(axum::http::uri::Scheme::HTTPS); + + if parts.path_and_query.is_none() { + parts.path_and_query = Some("/".parse().unwrap()); + } + + let https_host = host.replace(&ports.http.to_string(), &ports.https.to_string()); + parts.authority = Some(https_host.parse()?); + + Ok(Uri::from_parts(parts)?) + } + + let redirect = move |Host(host): Host, uri: Uri| async move { + match make_https(host, uri, ports) { + Ok(uri) => Ok(Redirect::permanent(&uri.to_string())), + Err(error) => { + tracing::warn!(%error, "failed to convert URI to HTTPS"); + Err(StatusCode::BAD_REQUEST) + } + } + }; + + let addr = SocketAddr::from(([127, 0, 0, 1], ports.http)); + tracing::debug!("http redirect listening on {}", addr); + + axum::Server::bind(&addr) + .serve(redirect.into_make_service()) + .await + .unwrap(); +}