@@ -193,6 +193,11 @@ impl<T: Read + Write> ClientConnection<T> {
193
193
Ok ( ( ) )
194
194
}
195
195
196
+ /// Discards all pending writes from the inner connection.
197
+ fn clear_write_buffer ( & mut self ) {
198
+ self . connection . clear_write_buffer ( ) ;
199
+ }
200
+
196
201
// Returns `true` if the connection is closed and safe to drop.
197
202
fn is_done ( & self ) -> bool {
198
203
self . state == ClientConnectionState :: Closed
@@ -343,6 +348,22 @@ impl HttpServer {
343
348
// We have a notification on one of our open connections.
344
349
let fd = e. fd ( ) ;
345
350
let client_connection = self . connections . get_mut ( & fd) . unwrap ( ) ;
351
+
352
+ // If we receive a hang up on a connection, we clear the write buffer and set
353
+ // the connection state to closed to mark it ready for removal from the
354
+ // connections map, which will gracefully close the socket.
355
+ // The connection is also marked for removal when encountering `EPOLLERR`,
356
+ // since this is an "error condition happened on the associated file
357
+ // descriptor", according to the `epoll_ctl` man page.
358
+ if e. event_set ( ) . contains ( epoll:: EventSet :: ERROR )
359
+ || e. event_set ( ) . contains ( epoll:: EventSet :: HANG_UP )
360
+ || e. event_set ( ) . contains ( epoll:: EventSet :: READ_HANG_UP )
361
+ {
362
+ client_connection. clear_write_buffer ( ) ;
363
+ client_connection. state = ClientConnectionState :: Closed ;
364
+ continue ;
365
+ }
366
+
346
367
if e. event_set ( ) . contains ( epoll:: EventSet :: IN ) {
347
368
// We have bytes to read from this connection.
348
369
// If our `read` yields `Request` objects, we wrap them with an ID before
@@ -358,7 +379,11 @@ impl HttpServer {
358
379
// either an error message or an `expect` response, we change its `epoll`
359
380
// event set to notify us when the stream is ready for writing.
360
381
if client_connection. state == ClientConnectionState :: AwaitingOutgoing {
361
- Self :: epoll_mod ( & self . epoll , fd, epoll:: EventSet :: OUT ) ?;
382
+ Self :: epoll_mod (
383
+ & self . epoll ,
384
+ fd,
385
+ epoll:: EventSet :: OUT | epoll:: EventSet :: READ_HANG_UP ,
386
+ ) ?;
362
387
}
363
388
} else if e. event_set ( ) . contains ( epoll:: EventSet :: OUT ) {
364
389
// We have bytes to write on this connection.
@@ -367,7 +392,11 @@ impl HttpServer {
367
392
// and we don't have any more responses to write, we change the `epoll`
368
393
// event set to notify us when we have bytes to read from the stream.
369
394
if client_connection. state == ClientConnectionState :: AwaitingIncoming {
370
- Self :: epoll_mod ( & self . epoll , fd, epoll:: EventSet :: IN ) ?;
395
+ Self :: epoll_mod (
396
+ & self . epoll ,
397
+ fd,
398
+ epoll:: EventSet :: IN | epoll:: EventSet :: READ_HANG_UP ,
399
+ ) ?;
371
400
}
372
401
}
373
402
}
@@ -492,7 +521,11 @@ impl HttpServer {
492
521
// `epoll` event set to notify us when the stream is ready for writing.
493
522
if let ClientConnectionState :: AwaitingIncoming = client_connection. state {
494
523
client_connection. state = ClientConnectionState :: AwaitingOutgoing ;
495
- Self :: epoll_mod ( & self . epoll , response. id as RawFd , epoll:: EventSet :: OUT ) ?;
524
+ Self :: epoll_mod (
525
+ & self . epoll ,
526
+ response. id as RawFd ,
527
+ epoll:: EventSet :: OUT | epoll:: EventSet :: READ_HANG_UP ,
528
+ ) ?;
496
529
}
497
530
client_connection. enqueue_response ( response. response ) ?;
498
531
}
@@ -554,7 +587,10 @@ impl HttpServer {
554
587
. ctl (
555
588
epoll:: ControlOperation :: Add ,
556
589
stream_fd,
557
- epoll:: EpollEvent :: new ( epoll:: EventSet :: IN , stream_fd as u64 ) ,
590
+ epoll:: EpollEvent :: new (
591
+ epoll:: EventSet :: IN | epoll:: EventSet :: READ_HANG_UP ,
592
+ stream_fd as u64 ,
593
+ ) ,
558
594
)
559
595
. map_err ( ServerError :: IOError )
560
596
}
@@ -575,6 +611,7 @@ impl HttpServer {
575
611
mod tests {
576
612
use super :: * ;
577
613
use std:: io:: { Read , Write } ;
614
+ use std:: net:: Shutdown ;
578
615
use std:: os:: unix:: net:: UnixStream ;
579
616
580
617
use crate :: common:: Body ;
@@ -750,7 +787,7 @@ mod tests {
750
787
let mut server = HttpServer :: new ( path_to_socket. as_path ( ) ) . unwrap ( ) ;
751
788
server. start_server ( ) . unwrap ( ) ;
752
789
753
- let mut sockets: Vec < UnixStream > = Vec :: with_capacity ( 11 ) ;
790
+ let mut sockets: Vec < UnixStream > = Vec :: with_capacity ( MAX_CONNECTIONS + 1 ) ;
754
791
for _ in 0 ..MAX_CONNECTIONS {
755
792
sockets. push ( UnixStream :: connect ( path_to_socket. as_path ( ) ) . unwrap ( ) ) ;
756
793
assert ! ( server. requests( ) . unwrap( ) . is_empty( ) ) ;
@@ -761,6 +798,38 @@ mod tests {
761
798
let mut buf: [ u8 ; 120 ] = [ 0 ; 120 ] ;
762
799
sockets[ MAX_CONNECTIONS ] . read_exact ( & mut buf) . unwrap ( ) ;
763
800
assert_eq ! ( & buf[ ..] , SERVER_FULL_ERROR_MESSAGE ) ;
801
+ assert_eq ! ( server. connections. len( ) , 10 ) ;
802
+ {
803
+ // Drop this stream.
804
+ let _refused_stream = sockets. pop ( ) . unwrap ( ) ;
805
+ }
806
+ assert_eq ! ( server. connections. len( ) , 10 ) ;
807
+
808
+ // Check that the server detects a connection shutdown.
809
+ let sock: & UnixStream = sockets. get ( 0 ) . unwrap ( ) ;
810
+ sock. shutdown ( Shutdown :: Both ) . unwrap ( ) ;
811
+ assert ! ( server. requests( ) . unwrap( ) . is_empty( ) ) ;
812
+ // Server should drop a closed connection.
813
+ assert_eq ! ( server. connections. len( ) , 9 ) ;
814
+
815
+ // Close the backing FD of this connection by dropping
816
+ // it out of scope.
817
+ {
818
+ // Enforce the drop call on the stream
819
+ let _sock = sockets. pop ( ) . unwrap ( ) ;
820
+ }
821
+ assert ! ( server. requests( ) . unwrap( ) . is_empty( ) ) ;
822
+ // Server should drop a closed connection.
823
+ assert_eq ! ( server. connections. len( ) , 8 ) ;
824
+
825
+ let sock: & UnixStream = sockets. get ( 1 ) . unwrap ( ) ;
826
+ // Close both the read and write sides of the socket
827
+ // separately and check that the server detects it.
828
+ sock. shutdown ( Shutdown :: Read ) . unwrap ( ) ;
829
+ sock. shutdown ( Shutdown :: Write ) . unwrap ( ) ;
830
+ assert ! ( server. requests( ) . unwrap( ) . is_empty( ) ) ;
831
+ // Server should drop a closed connection.
832
+ assert_eq ! ( server. connections. len( ) , 7 ) ;
764
833
}
765
834
766
835
#[ test]
0 commit comments