use crate::cli;
use std::{net::SocketAddr, process::exit, str::FromStr, time::Duration};
use axum::ServiceExt;
use axum_server::{tls_rustls::RustlsConfig, Handle};
use expanduser::expanduser;
use tokio::signal;
pub async fn serve(args: &cli::CommandLineArgs, service: crate::app::Service) {
let addr = SocketAddr::from_str(&format!("{}:{}", args.host, args.port))
.expect("invalid host name, IP address or port number");
let handle = Handle::new();
tokio::spawn(shutdown_signal(
handle.clone(),
args.graceful_shutdown_timeout,
));
if args.https {
let abs_cert_file = expanduser(&args.cert_file)
.expect("Failed to expand ~ to user name. Please provide an absolute path instead.")
.canonicalize()
.expect("failed to determine absolute path to TLS cerficate file");
let abs_key_file = expanduser(&args.key_file)
.expect("Failed to expand ~ to user name. Please provide an absolute path instead.")
.canonicalize()
.expect("failed to determine absolute path to TLS key file");
if !abs_cert_file.exists() {
println!(
"TLS certificate file expected at '{}' but not found.",
abs_cert_file.display()
);
exit(1)
}
if !abs_key_file.exists() {
println!(
"TLS key file expected at '{}' but not found.",
abs_key_file.display()
);
exit(1)
}
let tls_config = RustlsConfig::from_pem_file(abs_cert_file, abs_key_file)
.await
.expect("Failed to load TLS certificate files");
axum_server::bind_rustls(addr, tls_config)
.handle(handle)
.serve(service.into_make_service())
.await
.unwrap();
} else {
axum_server::bind(addr)
.handle(handle)
.serve(service.into_make_service())
.await
.unwrap();
}
}
async fn shutdown_signal(handle: Handle, timeout: u64) {
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 => {},
}
println!("signal received, starting graceful shutdown");
handle.graceful_shutdown(Some(Duration::from_secs(timeout)));
}