2222//! * [ndarray] provides [NumPy](https://numpy.orgq)-like n-dimensional arrays used in numerical
2323//! computation.
2424
25+ use std:: { net:: SocketAddr , process:: exit, str:: FromStr , time:: Duration } ;
26+
27+ use axum_server:: { tls_rustls:: RustlsConfig , Handle } ;
2528use clap:: Parser ;
29+ use expanduser:: expanduser;
2630use tokio:: signal;
2731use tracing_subscriber:: { layer:: SubscriberExt , util:: SubscriberInitExt } ;
2832
@@ -44,6 +48,26 @@ struct CommandLineArgs {
4448 /// The port to which the proxy should bind
4549 #[ arg( long, default_value_t = 8080 , env = "S3_ACTIVE_STORAGE_PORT" ) ]
4650 port : u16 ,
51+ /// Flag indicating whether HTTPS should be used
52+ #[ arg( long, default_value_t = false , env = "S3_ACTIVE_STORAGE_HTTPS" ) ]
53+ https : bool ,
54+ /// Path to the certificate file to be used for HTTPS encryption
55+ #[ arg(
56+ long,
57+ default_value = "~/.config/s3-active-storage/certs/cert.pem" ,
58+ env = "S3_ACTIVE_STORAGE_CERT_FILE"
59+ ) ]
60+ cert_file : String ,
61+ /// Path to the key file to be used for HTTPS encryption
62+ #[ arg(
63+ long,
64+ default_value = "~/.config/s3-active-storage/certs/key.pem" ,
65+ env = "S3_ACTIVE_STORAGE_KEY_FILE"
66+ ) ]
67+ key_file : String ,
68+ /// Maximum time in seconds to wait for operations to complete upon receiving `ctrl+c` signal.
69+ #[ arg( long, default_value_t = 60 , env = "S3_ACTIVE_STORAGE_SHUTDOWN_TIMEOUT" ) ]
70+ graceful_shutdown_timeout : u64 ,
4771}
4872
4973/// Application entry point
@@ -54,13 +78,59 @@ async fn main() {
5478 init_tracing ( ) ;
5579
5680 let router = app:: router ( ) ;
81+ let addr = SocketAddr :: from_str ( & format ! ( "{}:{}" , args. host, args. port) )
82+ . expect ( "invalid host name, IP address or port number" ) ;
83+
84+ // Catch ctrl+c and try to shutdown gracefully
85+ let handle = Handle :: new ( ) ;
86+ tokio:: spawn ( shutdown_signal (
87+ handle. clone ( ) ,
88+ args. graceful_shutdown_timeout ,
89+ ) ) ;
5790
58- // run it with hyper
59- axum:: Server :: bind ( & format ! ( "{}:{}" , args. host, args. port) . parse ( ) . unwrap ( ) )
60- . serve ( router. into_make_service ( ) )
61- . with_graceful_shutdown ( shutdown_signal ( ) )
62- . await
63- . unwrap ( ) ;
91+ if args. https {
92+ // Expand files
93+ let abs_cert_file = expanduser ( args. cert_file )
94+ . expect ( "Failed to expand ~ to user name. Please provide an absolute path instead." )
95+ . canonicalize ( )
96+ . expect ( "failed to determine absolute path to TLS cerficate file" ) ;
97+ let abs_key_file = expanduser ( args. key_file )
98+ . expect ( "Failed to expand ~ to user name. Please provide an absolute path instead." )
99+ . canonicalize ( )
100+ . expect ( "failed to determine absolute path to TLS key file" ) ;
101+ // Check files exist
102+ if !abs_cert_file. exists ( ) {
103+ println ! (
104+ "TLS certificate file expected at '{}' but not found." ,
105+ abs_cert_file. display( )
106+ ) ;
107+ exit ( 1 )
108+ }
109+ if !abs_key_file. exists ( ) {
110+ println ! (
111+ "TLS key file expected at '{}' but not found." ,
112+ abs_key_file. display( )
113+ ) ;
114+ exit ( 1 )
115+ }
116+ // Set up TLS config
117+ let tls_config = RustlsConfig :: from_pem_file ( abs_cert_file, abs_key_file)
118+ . await
119+ . expect ( "Failed to load TLS certificate files" ) ;
120+ // run HTTPS server with hyper
121+ axum_server:: bind_rustls ( addr, tls_config)
122+ . handle ( handle)
123+ . serve ( router. into_make_service ( ) )
124+ . await
125+ . unwrap ( ) ;
126+ } else {
127+ // run HTTP server with hyper
128+ axum_server:: bind ( addr)
129+ . handle ( handle)
130+ . serve ( router. into_make_service ( ) )
131+ . await
132+ . unwrap ( ) ;
133+ }
64134}
65135
66136/// Initlialise tracing (logging)
@@ -80,7 +150,7 @@ fn init_tracing() {
80150/// Graceful shutdown handler
81151///
82152/// Installs signal handlers to catch Ctrl-C or SIGTERM and trigger a graceful shutdown.
83- async fn shutdown_signal ( ) {
153+ async fn shutdown_signal ( handle : Handle , timeout : u64 ) {
84154 let ctrl_c = async {
85155 signal:: ctrl_c ( )
86156 . await
@@ -104,4 +174,6 @@ async fn shutdown_signal() {
104174 }
105175
106176 println ! ( "signal received, starting graceful shutdown" ) ;
177+ // Force shutdown if graceful shutdown takes longer than 10s
178+ handle. graceful_shutdown ( Some ( Duration :: from_secs ( timeout) ) ) ;
107179}
0 commit comments