@@ -7,9 +7,10 @@ use axum::{
77 extract:: { ConnectInfo , Request } ,
88 middleware:: AddExtension ,
99} ;
10+ use futures_util:: FutureExt as _;
1011use hyper:: { body:: Incoming , service:: service_fn} ;
1112use hyper_util:: rt:: { TokioExecutor , TokioIo } ;
12- use opentelemetry:: trace:: { FutureExt , SpanKind } ;
13+ use opentelemetry:: trace:: { FutureExt as _ , SpanKind } ;
1314use opentelemetry_semantic_conventions as semconv;
1415use snafu:: { OptionExt , ResultExt , Snafu } ;
1516use stackable_shared:: time:: Duration ;
@@ -138,17 +139,27 @@ impl TlsServer {
138139 /// router.
139140 ///
140141 /// It also starts a background task to rotate the certificate as needed.
141- pub async fn run ( self ) -> Result < ( ) > {
142+ ///
143+ /// The `shutdown_signal` can be used to notify the [`TlsServer`] to
144+ /// gracefully shutdown.
145+ pub async fn run < F > ( self , shutdown_signal : F ) -> Result < ( ) >
146+ where
147+ F : Future < Output = ( ) > ,
148+ {
149+ let Self {
150+ cert_resolver,
151+ socket_addr,
152+ config,
153+ router,
154+ } = self ;
155+
142156 let start = tokio:: time:: Instant :: now ( ) + * WEBHOOK_CERTIFICATE_ROTATION_INTERVAL ;
143157 let mut interval = tokio:: time:: interval_at ( start, * WEBHOOK_CERTIFICATE_ROTATION_INTERVAL ) ;
144158
145- let tls_acceptor = TlsAcceptor :: from ( Arc :: new ( self . config ) ) ;
146- let tcp_listener =
147- TcpListener :: bind ( self . socket_addr )
148- . await
149- . context ( BindTcpListenerSnafu {
150- socket_addr : self . socket_addr ,
151- } ) ?;
159+ let tls_acceptor = TlsAcceptor :: from ( Arc :: new ( config) ) ;
160+ let tcp_listener = TcpListener :: bind ( socket_addr)
161+ . await
162+ . context ( BindTcpListenerSnafu { socket_addr } ) ?;
152163
153164 // To be able to extract the connect info from incoming requests, it is
154165 // required to turn the router into a Tower service which is capable of
@@ -161,24 +172,35 @@ impl TlsServer {
161172 // - https://github.com/tokio-rs/axum/discussions/2397
162173 // - https://github.com/tokio-rs/axum/blob/b02ce307371a973039018a13fa012af14775948c/examples/serve-with-hyper/src/main.rs#L98
163174
164- let mut router = self
165- . router
166- . into_make_service_with_connect_info :: < SocketAddr > ( ) ;
175+ let mut router = router. into_make_service_with_connect_info :: < SocketAddr > ( ) ;
176+
177+ // Fuse the future so that it only yields `Poll::Ready` once. The future
178+ // additionally needs to be pinned to be able to be used in the select!
179+ // macro below.
180+ let shutdown_signal = shutdown_signal. fuse ( ) ;
181+ tokio:: pin!( shutdown_signal) ;
167182
168183 loop {
169184 let tls_acceptor = tls_acceptor. clone ( ) ;
170185
171186 // Wait for either a new TCP connection or the certificate rotation interval tick
172187 tokio:: select! {
173- // We opt for a biased execution of arms to make sure we always check if the
174- // certificate needs rotation based on the interval. This ensures, we always use
175- // a valid certificate for the TLS connection.
188+ // We opt for a biased execution of arms to make sure we always check if a
189+ // shutdown signal was received or the certificate needs rotation based on the
190+ // interval. This ensures, we always use a valid certificate for the TLS connection.
176191 biased;
177192
193+ // As soon as this future because `Poll::Ready`, break out of the loop which cancels
194+ // the certification rotation interval and stops accepting new TCP connections.
195+ _ = & mut shutdown_signal => {
196+ tracing:: trace!( "received shutdown signal" ) ;
197+ break ;
198+ }
199+
178200 // This is cancellation-safe. If this branch is cancelled, the tick is NOT consumed.
179201 // As such, we will not miss rotating the certificate.
180202 _ = interval. tick( ) => {
181- self . cert_resolver
203+ cert_resolver
182204 . rotate_certificate( )
183205 . await
184206 . context( RotateCertificateSnafu ) ?
@@ -210,6 +232,8 @@ impl TlsServer {
210232 }
211233 } ;
212234 }
235+
236+ Ok ( ( ) )
213237 }
214238
215239 async fn handle_request (
0 commit comments