@@ -12,9 +12,7 @@ pub use crate::common::{ConnectionError, RequestError, ServerError};
1212use crate :: connection:: HttpConnection ;
1313use crate :: request:: Request ;
1414use crate :: response:: { Response , StatusCode } ;
15- use vmm_sys_util:: sock_ctrl_msg:: ScmSocket ;
16-
17- use vmm_sys_util:: epoll;
15+ use vmm_sys_util:: { epoll, eventfd:: EventFd , sock_ctrl_msg:: ScmSocket } ;
1816
1917static SERVER_FULL_ERROR_MESSAGE : & [ u8 ] = b"HTTP/1.1 503\r \n \
2018 Server: Firecracker API\r \n \
@@ -27,6 +25,7 @@ pub(crate) const MAX_PAYLOAD_SIZE: usize = 51200;
2725type Result < T > = std:: result:: Result < T , ServerError > ;
2826
2927/// Wrapper over `Request` which adds an identification token.
28+ #[ derive( Debug ) ]
3029pub struct ServerRequest {
3130 /// Inner request.
3231 pub request : Request ,
@@ -255,6 +254,9 @@ pub struct HttpServer {
255254 socket : UnixListener ,
256255 /// Server's epoll instance.
257256 epoll : epoll:: Epoll ,
257+ /// Event requesting micro-http shutdown.
258+ /// Used to break out of inner `epoll_wait` and reports shutdown event.
259+ kill_switch : Option < EventFd > ,
258260 /// Holds the token-connection pairs of the server.
259261 /// Each connection has an associated identification token, which is
260262 /// the file descriptor of the underlying stream.
@@ -278,6 +280,7 @@ impl HttpServer {
278280 Ok ( Self {
279281 socket,
280282 epoll,
283+ kill_switch : None ,
281284 connections : HashMap :: new ( ) ,
282285 payload_max_size : MAX_PAYLOAD_SIZE ,
283286 } )
@@ -300,11 +303,21 @@ impl HttpServer {
300303 Ok ( HttpServer {
301304 socket,
302305 epoll,
306+ kill_switch : None ,
303307 connections : HashMap :: new ( ) ,
304308 payload_max_size : MAX_PAYLOAD_SIZE ,
305309 } )
306310 }
307311
312+ /// Adds a `kill_switch` event fd used to break out of inner `epoll_wait`
313+ /// and report a shutdown event.
314+ pub fn add_kill_switch ( & mut self , kill_switch : EventFd ) -> Result < ( ) > {
315+ // Add the kill switch to the `epoll` structure.
316+ let ret = Self :: epoll_add ( & self . epoll , kill_switch. as_raw_fd ( ) ) ;
317+ self . kill_switch = Some ( kill_switch) ;
318+ ret
319+ }
320+
308321 /// This function sets the limit for PUT/PATCH requests. It overwrites the
309322 /// default limit of 0.05MiB with the one allowed by server.
310323 pub fn set_payload_max_size ( & mut self , request_payload_max_size : usize ) {
@@ -337,25 +350,33 @@ impl HttpServer {
337350 /// on a connection on which it is not possible.
338351 pub fn requests ( & mut self ) -> Result < Vec < ServerRequest > > {
339352 let mut parsed_requests: Vec < ServerRequest > = vec ! [ ] ;
340- let mut events = vec ! [ epoll:: EpollEvent :: default ( ) ; MAX_CONNECTIONS ] ;
353+ // Possible events coming from FDs: 1 + 1 + MAX_CONNECTIONS:
354+ // exit-eventfd, sock-listen-fd, active-connections-fds.
355+ let mut events = [ epoll:: EpollEvent :: default ( ) ; MAX_CONNECTIONS + 2 ] ;
341356 // This is a wrapper over the syscall `epoll_wait` and it will block the
342357 // current thread until at least one event is received.
343358 // The received notifications will then populate the `events` array with
344- // `event_count` elements, where 1 <= event_count <= MAX_CONNECTIONS.
359+ // `event_count` elements, where 1 <= event_count <= MAX_CONNECTIONS + 2 .
345360 let event_count = match self . epoll . wait ( -1 , & mut events[ ..] ) {
346361 Ok ( event_count) => event_count,
347362 Err ( e) if e. raw_os_error ( ) == Some ( libc:: EINTR ) => 0 ,
348363 Err ( e) => return Err ( ServerError :: IOError ( e) ) ,
349364 } ;
350- // We use `take()` on the iterator over `events` as, even though only
351- // `events_count` events have been inserted into `events`, the size of
352- // the array is still `MAX_CONNECTIONS`, so we discard empty elements
365+
366+ // Getting the file descriptor for kill switch.
367+ // If there is no kill switch fd, we use value -1 as an invalid fd.
368+ let kill_fd = self . kill_switch . as_ref ( ) . map_or ( -1 , |ks| ks. as_raw_fd ( ) ) ;
369+
370+ // We only iterate over first `event_count` events and discard empty elements
353371 // at the end of the array.
354- for e in events. iter ( ) . take ( event_count) {
372+ for e in events[ .. event_count] . iter ( ) {
355373 // Check the file descriptor which produced the notification `e`.
356- // It could be that we have a new connection, or one of our open
357- // connections is ready to exchange data with a client.
358- if e. fd ( ) == self . socket . as_raw_fd ( ) {
374+ // It could be that we need to shutdown, or have a new connection, or
375+ // one of our open connections is ready to exchange data with a client.
376+ if e. fd ( ) == kill_fd {
377+ // Report that the kill switch was triggered.
378+ return Err ( ServerError :: ShutdownEvent ) ;
379+ } else if e. fd ( ) == self . socket . as_raw_fd ( ) {
359380 // We have received a notification on the listener socket, which
360381 // means we have a new connection to accept.
361382 match self . handle_new_connection ( ) {
@@ -646,9 +667,10 @@ mod tests {
646667 use std:: io:: { Read , Write } ;
647668 use std:: net:: Shutdown ;
648669 use std:: os:: unix:: net:: UnixStream ;
670+ use std:: sync:: { Arc , Mutex } ;
649671
650672 use crate :: common:: Body ;
651- use vmm_sys_util:: tempfile:: TempFile ;
673+ use vmm_sys_util:: { eventfd :: EFD_NONBLOCK , tempfile:: TempFile } ;
652674
653675 fn get_temp_socket_file ( ) -> TempFile {
654676 let mut path_to_socket = TempFile :: new ( ) . unwrap ( ) ;
@@ -1095,4 +1117,45 @@ mod tests {
10951117 second_socket. shutdown ( std:: net:: Shutdown :: Both ) . unwrap ( ) ;
10961118 assert ! ( server. requests( ) . is_ok( ) ) ;
10971119 }
1120+
1121+ #[ test]
1122+ fn test_kill_switch ( ) {
1123+ let path_to_socket = get_temp_socket_file ( ) ;
1124+
1125+ let mut server = HttpServer :: new ( path_to_socket. as_path ( ) ) . unwrap ( ) ;
1126+ let kill_switch = EventFd :: new ( EFD_NONBLOCK ) . unwrap ( ) ;
1127+ server
1128+ . add_kill_switch ( kill_switch. try_clone ( ) . unwrap ( ) )
1129+ . unwrap ( ) ;
1130+ server. start_server ( ) . unwrap ( ) ;
1131+
1132+ let request_result = Arc :: new ( Mutex :: new ( Ok ( vec ! [ ] ) ) ) ;
1133+ let res_clone = request_result. clone ( ) ;
1134+ // Start a thread running the server, expect it to report shutdown event.
1135+ let handler = std:: thread:: spawn ( move || {
1136+ * res_clone. lock ( ) . unwrap ( ) = server. requests ( ) ;
1137+ } ) ;
1138+
1139+ // Trigger kill switch.
1140+ kill_switch. write ( 1 ) . unwrap ( ) ;
1141+ // Then send request.
1142+ let mut socket = UnixStream :: connect ( path_to_socket. as_path ( ) ) . unwrap ( ) ;
1143+ socket
1144+ . write_all (
1145+ b"PATCH /machine-config HTTP/1.1\r \n \
1146+ Content-Length: 13\r \n \
1147+ Content-Type: application/json\r \n \r \n whatever body",
1148+ )
1149+ . unwrap ( ) ;
1150+ // Wait for server thread to handle events.
1151+ handler. join ( ) . unwrap ( ) ;
1152+
1153+ // Expect shutdown event instead of http request event.
1154+ match & * request_result. lock ( ) . unwrap ( ) {
1155+ Err ( ServerError :: ShutdownEvent ) => ( ) ,
1156+ v => {
1157+ panic ! ( "Expected shutdown event, instead got {:?}." , v)
1158+ }
1159+ } ;
1160+ }
10981161}
0 commit comments