use std::net::SocketAddr;
use std::sync::Arc;
use crate::BoxError;
use crate::plugin::{GasketApp, Plugin};
pub async fn run(app: Arc<GasketApp>) -> Result<(), BoxError> {
let addr = SocketAddr::new(app.config.server.host.parse()?, app.config.server.port);
let router = app.build_router();
tracing::info!(%addr, name = %app.config.name, env = %app.config.env, "Starting server");
let listener = tokio::net::TcpListener::bind(addr).await?;
let local_addr = listener.local_addr()?;
app.ready(local_addr).await?;
tracing::info!(%local_addr, "Server listening");
axum::serve(
listener,
router.into_make_service_with_connect_info::<SocketAddr>(),
)
.with_graceful_shutdown(shutdown_signal())
.await?;
app.shutdown().await;
tracing::info!("Server shutdown complete");
Ok(())
}
async fn shutdown_signal() {
let ctrl_c = async {
if let Err(e) = tokio::signal::ctrl_c().await {
tracing::error!(error = %e, "Ctrl+C handler failed; falling back to SIGTERM-only shutdown");
std::future::pending::<()>().await;
}
};
#[cfg(unix)]
let terminate = async {
match tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {
Ok(mut sig) => {
sig.recv().await;
}
Err(e) => {
tracing::error!(error = %e, "SIGTERM handler failed; falling back to Ctrl+C-only shutdown");
std::future::pending::<()>().await;
}
}
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
() = ctrl_c => {},
() = terminate => {},
}
tracing::info!("Shutdown signal received");
}
#[derive(Debug, Default)]
pub struct ServerPlugin;
impl Plugin for ServerPlugin {
fn name(&self) -> &'static str {
"gasket:server"
}
fn ordering(&self) -> crate::plugin::PluginOrdering {
crate::plugin::PluginOrdering::new().last()
}
}
impl ServerPlugin {
pub async fn run(app: Arc<GasketApp>) -> Result<(), BoxError> {
run(app).await
}
}