Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 91 additions & 26 deletions crates/rustapi-core/src/app/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ impl RustApi {
}
}

pub(super) fn print_hot_reload_banner(&self, addr: &str) {
/// Returns `None` when hot-reload is disabled; otherwise whether a watcher was
/// already active before this call updated `RUSTAPI_HOT_RELOAD`.
pub(super) fn print_hot_reload_banner(&self, addr: &str) -> Option<bool> {
if !self.hot_reload {
return;
return None;
}

let is_under_watcher = std::env::var("RUSTAPI_HOT_RELOAD")
Expand All @@ -44,6 +46,13 @@ impl RustApi {
}

tracing::info!(" Listening on http://{addr}");
Some(is_under_watcher)
}

async fn run_shutdown_hooks(hooks: Vec<crate::events::LifecycleHook>) {
for hook in hooks {
hook().await;
}
}
pub(super) fn apply_status_page(&mut self) {
if let Some(config) = &self.status_config {
Expand Down Expand Up @@ -236,20 +245,11 @@ impl RustApi {
{
self.prepare_for_serve(addr.as_ref()).await;

// Wrap the shutdown signal to run on_shutdown hooks after signal fires
let shutdown_hooks = self.lifecycle_hooks.on_shutdown;
let wrapped_signal = async move {
signal.await;
// Run on_shutdown hooks after the shutdown signal fires
for hook in shutdown_hooks {
hook().await;
}
};

let shutdown_hooks = std::mem::take(&mut self.lifecycle_hooks.on_shutdown);
let server = Server::new(self.router, self.layers, self.interceptors);
server
.run_with_shutdown(addr.as_ref(), wrapped_signal)
.await
server.run_with_shutdown(addr.as_ref(), signal).await?;
Self::run_shutdown_hooks(shutdown_hooks).await;
Ok(())
}

/// Enable HTTP/3 support with TLS certificates
Expand All @@ -265,16 +265,22 @@ impl RustApi {
/// .run_http3("0.0.0.0:443", "cert.pem", "key.pem")
/// .await
/// ```
/// Run HTTP/3 with TLS certificates and a graceful shutdown signal.
#[cfg(feature = "http3")]
pub async fn run_http3(
pub async fn run_http3_with_shutdown<F>(
mut self,
config: crate::http3::Http3Config,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
signal: F,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
F: std::future::Future<Output = ()> + Send + 'static,
{
use std::sync::Arc;

let addr = config.socket_addr();
self.prepare_for_serve(&addr).await;

let shutdown_hooks = std::mem::take(&mut self.lifecycle_hooks.on_shutdown);
let server = crate::http3::Http3Server::new(
&config,
Arc::new(self.router.clone()),
Expand All @@ -283,7 +289,18 @@ impl RustApi {
)
.await?;

server.run().await
server.run_with_shutdown(signal).await?;
Self::run_shutdown_hooks(shutdown_hooks).await;
Ok(())
}

#[cfg(feature = "http3")]
pub async fn run_http3(
self,
config: crate::http3::Http3Config,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.run_http3_with_shutdown(config, std::future::pending())
.await
}

/// Run HTTP/3 server with self-signed certificate (development only)
Expand All @@ -299,15 +316,21 @@ impl RustApi {
/// .run_http3_dev("0.0.0.0:8443")
/// .await
/// ```
/// Run HTTP/3 (self-signed) with a graceful shutdown signal.
#[cfg(feature = "http3-dev")]
pub async fn run_http3_dev(
pub async fn run_http3_dev_with_shutdown<F>(
mut self,
addr: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
signal: F,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
F: std::future::Future<Output = ()> + Send + 'static,
{
use std::sync::Arc;

self.prepare_for_serve(addr).await;

let shutdown_hooks = std::mem::take(&mut self.lifecycle_hooks.on_shutdown);
let server = crate::http3::Http3Server::new_with_self_signed(
addr,
Arc::new(self.router.clone()),
Expand All @@ -316,7 +339,18 @@ impl RustApi {
)
.await?;

server.run().await
server.run_with_shutdown(signal).await?;
Self::run_shutdown_hooks(shutdown_hooks).await;
Ok(())
}

#[cfg(feature = "http3-dev")]
pub async fn run_http3_dev(
self,
addr: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.run_http3_dev_with_shutdown(addr, std::future::pending())
.await
}

/// Configure HTTP/3 support for `run_http3` and `run_dual_stack`.
Expand Down Expand Up @@ -349,11 +383,16 @@ impl RustApi {
/// .run_dual_stack("0.0.0.0:8080")
/// .await
/// ```
/// Run HTTP/1.1 and HTTP/3 together with a graceful shutdown signal.
#[cfg(feature = "http3")]
pub async fn run_dual_stack(
pub async fn run_dual_stack_with_shutdown<F>(
mut self,
http_addr: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
signal: F,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
F: std::future::Future<Output = ()> + Send + 'static,
{
use std::sync::Arc;

let mut config = self
Expand All @@ -372,6 +411,7 @@ impl RustApi {

self.prepare_for_serve(&http_addr).await;

let shutdown_hooks = std::mem::take(&mut self.lifecycle_hooks.on_shutdown);
let router = Arc::new(self.router);
let layers = Arc::new(self.layers);
let interceptors = Arc::new(self.interceptors);
Expand All @@ -387,11 +427,36 @@ impl RustApi {
"Starting dual-stack HTTP/1.1 + HTTP/3 servers"
);

let notify = std::sync::Arc::new(tokio::sync::Notify::new());
let notify_for_signal = notify.clone();
tokio::spawn(async move {
signal.await;
notify_for_signal.notify_waiters();
});
let wait_for_shutdown = {
let notify = notify.clone();
async move {
notify.notified().await;
}
};
let wait_for_shutdown_http3 = async move {
notify.notified().await;
};

tokio::try_join!(
http1_server.run_with_shutdown(&http_addr, std::future::pending::<()>()),
http3_server.run_with_shutdown(std::future::pending::<()>()),
http1_server.run_with_shutdown(&http_addr, wait_for_shutdown),
http3_server.run_with_shutdown(wait_for_shutdown_http3),
)?;

Self::run_shutdown_hooks(shutdown_hooks).await;
Ok(())
}

#[cfg(feature = "http3")]
pub async fn run_dual_stack(
self,
http_addr: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
self.run_dual_stack_with_shutdown(http_addr, std::future::pending())
.await
}
}
Loading
Loading