-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tls-graceful-shutdown example (#2208)
- Loading branch information
Showing
4 changed files
with
202 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "example-tls-graceful-shutdown" | ||
version = "0.1.0" | ||
edition = "2021" | ||
publish = false | ||
|
||
[dependencies] | ||
axum = { path = "../../axum" } | ||
axum-server = { version = "0.3", features = ["tls-rustls"] } | ||
hyper = { version = "0.14", features = ["full"] } | ||
tokio = { version = "1", features = ["full"] } | ||
tracing = "0.1" | ||
tracing-subscriber = { version = "0.3", features = ["env-filter"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
-----BEGIN CERTIFICATE----- | ||
MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL | ||
BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM | ||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X | ||
DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR | ||
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 | ||
IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A | ||
MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 | ||
daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 | ||
kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq | ||
dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT | ||
bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 | ||
J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK | ||
NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 | ||
yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W | ||
ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU | ||
XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg | ||
+MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 | ||
Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 | ||
fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr | ||
AopvZ09uEQ== | ||
-----END CERTIFICATE----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
-----BEGIN PRIVATE KEY----- | ||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD | ||
BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS | ||
tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw | ||
RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l | ||
YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t | ||
HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV | ||
W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB | ||
12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI | ||
+2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw | ||
zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt | ||
fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty | ||
RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT | ||
ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 | ||
myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ | ||
XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY | ||
5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD | ||
ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD | ||
ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 | ||
k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 | ||
wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV | ||
5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg | ||
3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa | ||
Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C | ||
rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m | ||
y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW | ||
w37pCS7ykL+7gp7V0WShYsw= | ||
-----END PRIVATE KEY----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
//! Run with | ||
//! | ||
//! ```not_rust | ||
//! cargo run -p example-tls-graceful-shutdown | ||
//! ``` | ||
use axum::{ | ||
extract::Host, | ||
handler::HandlerWithoutStateExt, | ||
http::{StatusCode, Uri}, | ||
response::Redirect, | ||
routing::get, | ||
BoxError, Router, | ||
}; | ||
use axum_server::tls_rustls::RustlsConfig; | ||
use std::{future::Future, net::SocketAddr, path::PathBuf, time::Duration}; | ||
use tokio::signal; | ||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; | ||
|
||
#[derive(Clone, Copy)] | ||
struct Ports { | ||
http: u16, | ||
https: u16, | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
tracing_subscriber::registry() | ||
.with( | ||
tracing_subscriber::EnvFilter::try_from_default_env() | ||
.unwrap_or_else(|_| "example_tls_graceful_shutdown=debug".into()), | ||
) | ||
.with(tracing_subscriber::fmt::layer()) | ||
.init(); | ||
|
||
let ports = Ports { | ||
http: 7878, | ||
https: 3000, | ||
}; | ||
|
||
//Create a handle for our TLS server so the shutdown signal can all shutdown | ||
let handle = axum_server::Handle::new(); | ||
//save the future for easy shutting down of redirect server | ||
let shutdown_future = shutdown_signal(handle.clone()); | ||
|
||
// optional: spawn a second server to redirect http requests to this server | ||
tokio::spawn(redirect_http_to_https(ports, shutdown_future)); | ||
|
||
// configure certificate and private key used by https | ||
let config = RustlsConfig::from_pem_file( | ||
PathBuf::from(env!("CARGO_MANIFEST_DIR")) | ||
.join("self_signed_certs") | ||
.join("cert.pem"), | ||
PathBuf::from(env!("CARGO_MANIFEST_DIR")) | ||
.join("self_signed_certs") | ||
.join("key.pem"), | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
let app = Router::new().route("/", get(handler)); | ||
|
||
// run https server | ||
let addr = SocketAddr::from(([127, 0, 0, 1], ports.https)); | ||
tracing::debug!("listening on {}", addr); | ||
axum_server::bind_rustls(addr, config) | ||
.handle(handle) | ||
.serve(app.into_make_service()) | ||
.await | ||
.unwrap(); | ||
} | ||
|
||
async fn shutdown_signal(handle: axum_server::Handle) { | ||
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 => {}, | ||
} | ||
|
||
tracing::info!("Received termination signal shutting down"); | ||
handle.graceful_shutdown(Some(Duration::from_secs(10))); // 10 secs is how long docker will wait | ||
// to force shutdown | ||
} | ||
|
||
async fn handler() -> &'static str { | ||
"Hello, World!" | ||
} | ||
|
||
async fn redirect_http_to_https(ports: Ports, signal: impl Future<Output = ()>) { | ||
fn make_https(host: String, uri: Uri, ports: Ports) -> Result<Uri, BoxError> { | ||
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)); | ||
//let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); | ||
tracing::debug!("listening on {}", &addr); | ||
hyper::Server::bind(&addr) | ||
.serve(redirect.into_make_service()) | ||
.with_graceful_shutdown(signal) | ||
.await | ||
.unwrap(); | ||
} |