@@ -23,9 +23,11 @@ impl RustApi {
2323 }
2424 }
2525
26- pub ( super ) fn print_hot_reload_banner ( & self , addr : & str ) {
26+ /// Returns `None` when hot-reload is disabled; otherwise whether a watcher was
27+ /// already active before this call updated `RUSTAPI_HOT_RELOAD`.
28+ pub ( super ) fn print_hot_reload_banner ( & self , addr : & str ) -> Option < bool > {
2729 if !self . hot_reload {
28- return ;
30+ return None ;
2931 }
3032
3133 let is_under_watcher = std:: env:: var ( "RUSTAPI_HOT_RELOAD" )
@@ -44,6 +46,13 @@ impl RustApi {
4446 }
4547
4648 tracing:: info!( " Listening on http://{addr}" ) ;
49+ Some ( is_under_watcher)
50+ }
51+
52+ async fn run_shutdown_hooks ( hooks : Vec < crate :: events:: LifecycleHook > ) {
53+ for hook in hooks {
54+ hook ( ) . await ;
55+ }
4756 }
4857 pub ( super ) fn apply_status_page ( & mut self ) {
4958 if let Some ( config) = & self . status_config {
@@ -236,20 +245,11 @@ impl RustApi {
236245 {
237246 self . prepare_for_serve ( addr. as_ref ( ) ) . await ;
238247
239- // Wrap the shutdown signal to run on_shutdown hooks after signal fires
240- let shutdown_hooks = self . lifecycle_hooks . on_shutdown ;
241- let wrapped_signal = async move {
242- signal. await ;
243- // Run on_shutdown hooks after the shutdown signal fires
244- for hook in shutdown_hooks {
245- hook ( ) . await ;
246- }
247- } ;
248-
248+ let shutdown_hooks = std:: mem:: take ( & mut self . lifecycle_hooks . on_shutdown ) ;
249249 let server = Server :: new ( self . router , self . layers , self . interceptors ) ;
250- server
251- . run_with_shutdown ( addr . as_ref ( ) , wrapped_signal )
252- . await
250+ server. run_with_shutdown ( addr . as_ref ( ) , signal ) . await ? ;
251+ Self :: run_shutdown_hooks ( shutdown_hooks ) . await ;
252+ Ok ( ( ) )
253253 }
254254
255255 /// Enable HTTP/3 support with TLS certificates
@@ -265,16 +265,22 @@ impl RustApi {
265265 /// .run_http3("0.0.0.0:443", "cert.pem", "key.pem")
266266 /// .await
267267 /// ```
268+ /// Run HTTP/3 with TLS certificates and a graceful shutdown signal.
268269 #[ cfg( feature = "http3" ) ]
269- pub async fn run_http3 (
270+ pub async fn run_http3_with_shutdown < F > (
270271 mut self ,
271272 config : crate :: http3:: Http3Config ,
272- ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > > {
273+ signal : F ,
274+ ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > >
275+ where
276+ F : std:: future:: Future < Output = ( ) > + Send + ' static ,
277+ {
273278 use std:: sync:: Arc ;
274279
275280 let addr = config. socket_addr ( ) ;
276281 self . prepare_for_serve ( & addr) . await ;
277282
283+ let shutdown_hooks = std:: mem:: take ( & mut self . lifecycle_hooks . on_shutdown ) ;
278284 let server = crate :: http3:: Http3Server :: new (
279285 & config,
280286 Arc :: new ( self . router . clone ( ) ) ,
@@ -283,7 +289,18 @@ impl RustApi {
283289 )
284290 . await ?;
285291
286- server. run ( ) . await
292+ server. run_with_shutdown ( signal) . await ?;
293+ Self :: run_shutdown_hooks ( shutdown_hooks) . await ;
294+ Ok ( ( ) )
295+ }
296+
297+ #[ cfg( feature = "http3" ) ]
298+ pub async fn run_http3 (
299+ self ,
300+ config : crate :: http3:: Http3Config ,
301+ ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > > {
302+ self . run_http3_with_shutdown ( config, std:: future:: pending ( ) )
303+ . await
287304 }
288305
289306 /// Run HTTP/3 server with self-signed certificate (development only)
@@ -299,15 +316,21 @@ impl RustApi {
299316 /// .run_http3_dev("0.0.0.0:8443")
300317 /// .await
301318 /// ```
319+ /// Run HTTP/3 (self-signed) with a graceful shutdown signal.
302320 #[ cfg( feature = "http3-dev" ) ]
303- pub async fn run_http3_dev (
321+ pub async fn run_http3_dev_with_shutdown < F > (
304322 mut self ,
305323 addr : & str ,
306- ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > > {
324+ signal : F ,
325+ ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > >
326+ where
327+ F : std:: future:: Future < Output = ( ) > + Send + ' static ,
328+ {
307329 use std:: sync:: Arc ;
308330
309331 self . prepare_for_serve ( addr) . await ;
310332
333+ let shutdown_hooks = std:: mem:: take ( & mut self . lifecycle_hooks . on_shutdown ) ;
311334 let server = crate :: http3:: Http3Server :: new_with_self_signed (
312335 addr,
313336 Arc :: new ( self . router . clone ( ) ) ,
@@ -316,7 +339,18 @@ impl RustApi {
316339 )
317340 . await ?;
318341
319- server. run ( ) . await
342+ server. run_with_shutdown ( signal) . await ?;
343+ Self :: run_shutdown_hooks ( shutdown_hooks) . await ;
344+ Ok ( ( ) )
345+ }
346+
347+ #[ cfg( feature = "http3-dev" ) ]
348+ pub async fn run_http3_dev (
349+ self ,
350+ addr : & str ,
351+ ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > > {
352+ self . run_http3_dev_with_shutdown ( addr, std:: future:: pending ( ) )
353+ . await
320354 }
321355
322356 /// Configure HTTP/3 support for `run_http3` and `run_dual_stack`.
@@ -349,11 +383,16 @@ impl RustApi {
349383 /// .run_dual_stack("0.0.0.0:8080")
350384 /// .await
351385 /// ```
386+ /// Run HTTP/1.1 and HTTP/3 together with a graceful shutdown signal.
352387 #[ cfg( feature = "http3" ) ]
353- pub async fn run_dual_stack (
388+ pub async fn run_dual_stack_with_shutdown < F > (
354389 mut self ,
355390 http_addr : & str ,
356- ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > > {
391+ signal : F ,
392+ ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > >
393+ where
394+ F : std:: future:: Future < Output = ( ) > + Send + ' static ,
395+ {
357396 use std:: sync:: Arc ;
358397
359398 let mut config = self
@@ -372,6 +411,7 @@ impl RustApi {
372411
373412 self . prepare_for_serve ( & http_addr) . await ;
374413
414+ let shutdown_hooks = std:: mem:: take ( & mut self . lifecycle_hooks . on_shutdown ) ;
375415 let router = Arc :: new ( self . router ) ;
376416 let layers = Arc :: new ( self . layers ) ;
377417 let interceptors = Arc :: new ( self . interceptors ) ;
@@ -387,11 +427,36 @@ impl RustApi {
387427 "Starting dual-stack HTTP/1.1 + HTTP/3 servers"
388428 ) ;
389429
430+ let notify = std:: sync:: Arc :: new ( tokio:: sync:: Notify :: new ( ) ) ;
431+ let notify_for_signal = notify. clone ( ) ;
432+ tokio:: spawn ( async move {
433+ signal. await ;
434+ notify_for_signal. notify_waiters ( ) ;
435+ } ) ;
436+ let wait_for_shutdown = {
437+ let notify = notify. clone ( ) ;
438+ async move {
439+ notify. notified ( ) . await ;
440+ }
441+ } ;
442+ let wait_for_shutdown_http3 = async move {
443+ notify. notified ( ) . await ;
444+ } ;
445+
390446 tokio:: try_join!(
391- http1_server. run_with_shutdown( & http_addr, std :: future :: pending :: < ( ) > ( ) ) ,
392- http3_server. run_with_shutdown( std :: future :: pending :: < ( ) > ( ) ) ,
447+ http1_server. run_with_shutdown( & http_addr, wait_for_shutdown ) ,
448+ http3_server. run_with_shutdown( wait_for_shutdown_http3 ) ,
393449 ) ?;
394-
450+ Self :: run_shutdown_hooks ( shutdown_hooks ) . await ;
395451 Ok ( ( ) )
396452 }
453+
454+ #[ cfg( feature = "http3" ) ]
455+ pub async fn run_dual_stack (
456+ self ,
457+ http_addr : & str ,
458+ ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > > {
459+ self . run_dual_stack_with_shutdown ( http_addr, std:: future:: pending ( ) )
460+ . await
461+ }
397462}
0 commit comments