1717
1818mod router;
1919
20- use std:: { net:: SocketAddr , time :: Duration } ;
20+ use std:: net:: SocketAddr ;
2121
2222use crate :: { args:: DftArgs , config:: AppConfig , execution:: AppExecution } ;
23- use axum:: { extract:: State , routing:: get, Router } ;
24- use color_eyre:: { eyre:: eyre, Result } ;
25- use datafusion:: arrow:: json:: { writer:: LineDelimited , Writer } ;
23+ use axum:: Router ;
24+ use color_eyre:: Result ;
2625use datafusion_app:: {
2726 config:: merge_configs, extensions:: DftSessionStateBuilder , local:: ExecutionContext ,
2827} ;
29- use log:: info;
30- use tokio:: { net:: TcpListener , sync:: oneshot, task:: JoinHandle } ;
31- use tokio_stream:: StreamExt ;
32- use tower_http:: validate_request:: ValidateRequestHeaderLayer ;
28+ use router:: create_router;
29+ use tokio:: { net:: TcpListener , signal} ;
30+ use tracing:: info;
3331
3432use super :: try_start_metrics_server;
3533
36- const DEFAULT_TIMEOUT_SECONDS : u64 = 60 ;
3734const DEFAULT_SERVER_ADDRESS : & str = "127.0.0.1:8080" ;
3835
39- pub fn create_router (
40- config : & AppConfig ,
41- // flightsql: FlightSqlServiceImpl,
42- listener : TcpListener ,
43- rx : oneshot:: Receiver < ( ) > ,
44- // shutdown_future: impl Future<Output = ()> + Send,
45- ) -> Router {
46- let server_timeout = Duration :: from_secs ( DEFAULT_TIMEOUT_SECONDS ) ;
47- // let mut server_builder = Server::builder().timeout(server_timeout);
48- let shutdown_future = async move {
49- rx. await . ok ( ) ;
36+ /// From https://github.com/tokio-rs/axum/blob/main/examples/graceful-shutdown/src/main.rs
37+ async fn shutdown_signal ( ) {
38+ let ctrl_c = async {
39+ signal:: ctrl_c ( )
40+ . await
41+ . expect ( "failed to install Ctrl+C handler" ) ;
42+ } ;
43+
44+ #[ cfg( unix) ]
45+ let terminate = async {
46+ signal:: unix:: signal ( signal:: unix:: SignalKind :: terminate ( ) )
47+ . expect ( "failed to install signal handler" )
48+ . recv ( )
49+ . await ;
5050 } ;
5151
52- Router :: new ( ) . route ( "/" , get ( || async { "Hello, World!" } ) )
53-
54- // axum::serve(listener, router).await.unwrap();
55-
56- // TODO: onlu include TrailersLayer for testing
57- // if cfg!(feature = "flightsql") {
58- // match (
59- // &config.flightsql_server.auth.basic_auth,
60- // &config.flightsql_server.auth.bearer_token,
61- // ) {
62- // (Some(_), Some(_)) => Err(eyre!("Only one auth type can be used at a time")),
63- // (Some(basic), None) => {
64- // let basic_auth_layer =
65- // ValidateRequestHeaderLayer::basic(&basic.username, &basic.password);
66- // let f = server_builder
67- // .layer(basic_auth_layer)
68- // .add_service(flightsql.service())
69- // .serve_with_incoming_shutdown(
70- // tokio_stream::wrappers::TcpListenerStream::new(listener),
71- // shutdown_future,
72- // );
73- // Ok(tokio::task::spawn(f))
74- // }
75- // (None, Some(token)) => {
76- // let bearer_auth_layer = ValidateRequestHeaderLayer::bearer(token);
77- // let f = server_builder
78- // .layer(bearer_auth_layer)
79- // .add_service(flightsql.service())
80- // .serve_with_incoming_shutdown(
81- // tokio_stream::wrappers::TcpListenerStream::new(listener),
82- // shutdown_future,
83- // );
84- // Ok(tokio::task::spawn(f))
85- // }
86- // (None, None) => {
87- // let f = server_builder
88- // .add_service(flightsql.service())
89- // .serve_with_incoming_shutdown(
90- // tokio_stream::wrappers::TcpListenerStream::new(listener),
91- // shutdown_future,
92- // );
93- // Ok(tokio::task::spawn(f))
94- // }
95- // }
96- // } else {
97- // let f = server_builder
98- // .add_service(flightsql.service())
99- // .serve_with_incoming_shutdown(
100- // tokio_stream::wrappers::TcpListenerStream::new(listener),
101- // shutdown_future,
102- // );
103- // Ok(tokio::task::spawn(f))
104- // }
52+ #[ cfg( not( unix) ) ]
53+ let terminate = std:: future:: pending :: < ( ) > ( ) ;
54+
55+ tokio:: select! {
56+ _ = ctrl_c => { } ,
57+ _ = terminate => { } ,
58+ }
10559}
10660
10761/// Creates and manages a running FlightSqlServer with a background task
10862pub struct HttpApp {
109- execution : AppExecution ,
110- /// channel to send shutdown command
111- shutdown : Option < tokio:: sync:: oneshot:: Sender < ( ) > > ,
112-
11363 /// Address the server is listening on
11464 listener : TcpListener ,
11565
@@ -124,60 +74,30 @@ impl HttpApp {
12474 let listener = TcpListener :: bind ( addr) . await . unwrap ( ) ;
12575
12676 // prepare the shutdown channel
127- let ( tx, rx) = tokio:: sync:: oneshot:: channel ( ) ;
12877 let state = execution. execution_ctx ( ) . clone ( ) ;
12978
130- let router = Router :: new ( )
131- . route (
132- "/" ,
133- get ( |State ( state) : State < ExecutionContext > | async { "Hello, World!" } ) ,
134- )
135- . route (
136- "/query" ,
137- get ( |State ( state) : State < ExecutionContext > | async move {
138- let r = state. execute_sql ( "SELECT 1" ) . await ;
139- match r {
140- Ok ( mut ba) => {
141- let mut buf = Vec :: new ( ) ;
142- let mut writer: Writer < & mut [ u8 ] , LineDelimited > =
143- datafusion:: arrow:: json:: LineDelimitedWriter :: new ( & mut buf) ;
144- while let Some ( b) = ba. next ( ) . await {
145- writer. write ( & b. unwrap ( ) ) . unwrap ( ) ;
146- }
147- writer. finish ( ) . unwrap ( ) ;
148- let r = String :: from_utf8 ( buf) . unwrap ( ) ;
149- r
150- }
151- Err ( e) => "Meep" . to_string ( ) ,
152- }
153- } ) ,
154- )
155- . with_state ( state) ;
79+ let router = create_router ( state) ;
15680
15781 let metrics_addr: SocketAddr = metrics_addr. parse ( ) ?;
15882 try_start_metrics_server ( metrics_addr) ?;
15983
160- let app = Self {
161- execution,
162- shutdown : Some ( tx) ,
163- listener,
164- router,
165- } ;
84+ let app = Self { listener, router } ;
16685 Ok ( app)
16786 }
16887
16988 pub async fn run ( self ) {
170- match axum:: serve ( self . listener , self . router ) . await {
171- Ok ( _) => { }
89+ match axum:: serve ( self . listener , self . router )
90+ . with_graceful_shutdown ( shutdown_signal ( ) )
91+ . await
92+ {
93+ Ok ( _) => {
94+ info ! ( "Shutting down app" )
95+ }
17296 Err ( _) => {
17397 panic ! ( "Error serving HTTP app" )
17498 }
17599 }
176100 }
177-
178- async fn root_handler ( ) -> String {
179- "Hi there" . to_string ( )
180- }
181101}
182102
183103pub async fn try_run ( cli : DftArgs , config : AppConfig ) -> Result < ( ) > {
0 commit comments