use std::future::Future;
use crate::application::Result;
use crate::application::lifecycle::Lifecycle;
use crate::application::shutdown;
use crate::application::startup;
use crate::application::ty::Application;
use crate::health;
use crate::pipeline::assemble::into_service;
impl<S> Application<S>
where
S: Clone + Send + Sync + 'static,
{
pub async fn serve_with_health<L, F, Sh>(
self,
listener: L,
state_fn: F,
shutdown_signal: Sh,
) -> Result<()>
where
L: crate::axum::serve::Listener,
L::Addr: std::fmt::Debug,
F: Fn(&crate::application::Resources, &Lifecycle) -> S + Send + Sync + 'static,
Sh: Future<Output = ()> + Send + 'static,
{
let lifecycle = Lifecycle::new();
let (started, resolved_app) = startup::startup(self).await?;
let state = state_fn(&started.resources, &lifecycle);
lifecycle.mark_ready();
let routes = resolved_app.routes.with_state(state);
let app = Application {
routes,
proxy: resolved_app.proxy,
bind_address: resolved_app.bind_address,
port: resolved_app.port,
#[cfg(feature = "inertia")]
inertia_config: resolved_app.inertia_config,
#[cfg(feature = "inertia")]
page_contracts: resolved_app.page_contracts,
#[cfg(feature = "pages")]
pages: resolved_app.pages,
#[cfg(feature = "pages")]
maintenance_guard: resolved_app.maintenance_guard,
#[cfg(feature = "db")]
database: None,
#[cfg(feature = "cache")]
cache_config: None,
#[cfg(feature = "storage")]
storage_config: None,
#[cfg(feature = "mail")]
mail_config: None,
#[cfg(feature = "jobs")]
jobs_registry: None,
#[cfg(feature = "jobs")]
worker_config: None,
#[cfg(feature = "dx")]
error_mapping: resolved_app.error_mapping,
#[cfg(feature = "dev-proxy")]
dev_proxy_endpoint: resolved_app.dev_proxy_endpoint,
};
let drain_lifecycle = lifecycle.clone();
let drain_signal = async move {
shutdown_signal.await;
drain_lifecycle.begin_drain();
};
let app_service = into_service(app);
let merged = crate::axum::Router::<()>::new()
.merge(health::router())
.fallback_service(app_service)
.layer(health::lifecycle_layer(lifecycle.clone()));
let serve_result = crate::axum::serve(listener, merged.into_make_service())
.with_graceful_shutdown(drain_signal)
.await
.map_err(|source| crate::EngineError::Serve { source });
let hook_errors = lifecycle.run_drain_hooks().await;
for hook_err in &hook_errors {
eprintln!("warning: drain hook failed: {hook_err}");
}
let shutdown_result = shutdown::shutdown(started).await;
lifecycle.mark_stopped();
match serve_result {
Ok(()) => shutdown_result,
Err(serve_err) => {
if let Err(ref shutdown_err) = shutdown_result {
eprintln!("warning: shutdown also failed after serve error: {shutdown_err}");
}
Err(serve_err)
}
}
}
}