@@ -12,9 +12,7 @@ pub use crate::common::{ConnectionError, RequestError, ServerError};
12
12
use crate :: connection:: HttpConnection ;
13
13
use crate :: request:: Request ;
14
14
use 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 } ;
18
16
19
17
static SERVER_FULL_ERROR_MESSAGE : & [ u8 ] = b"HTTP/1.1 503\r \n \
20
18
Server: Firecracker API\r \n \
@@ -27,6 +25,7 @@ pub(crate) const MAX_PAYLOAD_SIZE: usize = 51200;
27
25
type Result < T > = std:: result:: Result < T , ServerError > ;
28
26
29
27
/// Wrapper over `Request` which adds an identification token.
28
+ #[ derive( Debug ) ]
30
29
pub struct ServerRequest {
31
30
/// Inner request.
32
31
pub request : Request ,
@@ -255,6 +254,9 @@ pub struct HttpServer {
255
254
socket : UnixListener ,
256
255
/// Server's epoll instance.
257
256
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 > ,
258
260
/// Holds the token-connection pairs of the server.
259
261
/// Each connection has an associated identification token, which is
260
262
/// the file descriptor of the underlying stream.
@@ -278,6 +280,7 @@ impl HttpServer {
278
280
Ok ( Self {
279
281
socket,
280
282
epoll,
283
+ kill_switch : None ,
281
284
connections : HashMap :: new ( ) ,
282
285
payload_max_size : MAX_PAYLOAD_SIZE ,
283
286
} )
@@ -300,11 +303,21 @@ impl HttpServer {
300
303
Ok ( HttpServer {
301
304
socket,
302
305
epoll,
306
+ kill_switch : None ,
303
307
connections : HashMap :: new ( ) ,
304
308
payload_max_size : MAX_PAYLOAD_SIZE ,
305
309
} )
306
310
}
307
311
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
+
308
321
/// This function sets the limit for PUT/PATCH requests. It overwrites the
309
322
/// default limit of 0.05MiB with the one allowed by server.
310
323
pub fn set_payload_max_size ( & mut self , request_payload_max_size : usize ) {
@@ -337,25 +350,33 @@ impl HttpServer {
337
350
/// on a connection on which it is not possible.
338
351
pub fn requests ( & mut self ) -> Result < Vec < ServerRequest > > {
339
352
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 ] ;
341
356
// This is a wrapper over the syscall `epoll_wait` and it will block the
342
357
// current thread until at least one event is received.
343
358
// 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 .
345
360
let event_count = match self . epoll . wait ( -1 , & mut events[ ..] ) {
346
361
Ok ( event_count) => event_count,
347
362
Err ( e) if e. raw_os_error ( ) == Some ( libc:: EINTR ) => 0 ,
348
363
Err ( e) => return Err ( ServerError :: IOError ( e) ) ,
349
364
} ;
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
353
371
// at the end of the array.
354
- for e in events. iter ( ) . take ( event_count) {
372
+ for e in events[ .. event_count] . iter ( ) {
355
373
// 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 ( ) {
359
380
// We have received a notification on the listener socket, which
360
381
// means we have a new connection to accept.
361
382
match self . handle_new_connection ( ) {
@@ -646,9 +667,10 @@ mod tests {
646
667
use std:: io:: { Read , Write } ;
647
668
use std:: net:: Shutdown ;
648
669
use std:: os:: unix:: net:: UnixStream ;
670
+ use std:: sync:: { Arc , Mutex } ;
649
671
650
672
use crate :: common:: Body ;
651
- use vmm_sys_util:: tempfile:: TempFile ;
673
+ use vmm_sys_util:: { eventfd :: EFD_NONBLOCK , tempfile:: TempFile } ;
652
674
653
675
fn get_temp_socket_file ( ) -> TempFile {
654
676
let mut path_to_socket = TempFile :: new ( ) . unwrap ( ) ;
@@ -1095,4 +1117,45 @@ mod tests {
1095
1117
second_socket. shutdown ( std:: net:: Shutdown :: Both ) . unwrap ( ) ;
1096
1118
assert ! ( server. requests( ) . is_ok( ) ) ;
1097
1119
}
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
+ }
1098
1161
}
0 commit comments