@@ -14,6 +14,7 @@ use crate::handlers::Handler;
1414
1515const MAX_HEADER_BYTES : usize = 16 * 1_024 ;
1616const MAX_BODY_BYTES : usize = 16 * 1_024 * 1_024 ;
17+ const POLL_INTERVAL : core:: time:: Duration = core:: time:: Duration :: from_millis ( 100 ) ;
1718
1819/// Synchronous HTTP/1.1 JSON-RPC server.
1920pub struct RpcServer {
@@ -56,33 +57,67 @@ impl RpcServer {
5657 pub fn serve ( self ) -> io:: Result < ( ) > {
5758 let active = Arc :: new ( Mutex :: new ( 0_usize ) ) ;
5859 for stream in self . listener . incoming ( ) {
59- let mut stream = stream?;
60- let should_accept = {
61- let mut count = active. lock ( ) ;
62- if * count >= self . max_connections {
63- false
64- } else {
65- * count += 1 ;
66- true
60+ self . handle_accept ( & active, stream?) ?;
61+ }
62+ Ok ( ( ) )
63+ }
64+
65+ /// Runs the accept loop until `shutdown` is set to `true`.
66+ ///
67+ /// Polls non-blocking accept on a fixed cadence so the loop can observe
68+ /// shutdown without parking on an open socket. Each accepted connection
69+ /// is restored to blocking mode and handed to a bounded worker thread,
70+ /// preserving the configured `idle_timeout` per connection.
71+ #[ allow( clippy:: needless_pass_by_value) ]
72+ pub fn serve_with_shutdown (
73+ self ,
74+ shutdown : alloc:: sync:: Arc < core:: sync:: atomic:: AtomicBool > ,
75+ ) -> io:: Result < ( ) > {
76+ use core:: sync:: atomic:: Ordering ;
77+
78+ self . listener . set_nonblocking ( true ) ?;
79+ let active = Arc :: new ( Mutex :: new ( 0_usize ) ) ;
80+ while !shutdown. load ( Ordering :: Acquire ) {
81+ match self . listener . accept ( ) {
82+ Ok ( ( stream, _addr) ) => {
83+ stream. set_nonblocking ( false ) ?;
84+ self . handle_accept ( & active, stream) ?;
6785 }
68- } ;
69- if !should_accept {
70- write_status ( & mut stream , 503 , "Service Unavailable" , b"busy" , false ) ? ;
71- continue ;
86+ Err ( error ) if error . kind ( ) == io :: ErrorKind :: WouldBlock => {
87+ thread :: sleep ( POLL_INTERVAL ) ;
88+ }
89+ Err ( error ) => return Err ( error ) ,
7290 }
91+ }
92+ Ok ( ( ) )
93+ }
7394
74- let auth = Arc :: clone ( & self . auth ) ;
75- let handler = Arc :: clone ( & self . handler ) ;
76- let active = Arc :: clone ( & active) ;
77- let idle_timeout = self . idle_timeout ;
78- thread:: spawn ( move || {
79- if let Err ( error) = serve_connection ( stream, & auth, & handler, idle_timeout) {
80- debug ! ( %error, "rpc connection closed with error" ) ;
81- }
82- let mut count = active. lock ( ) ;
83- * count = count. saturating_sub ( 1 ) ;
84- } ) ;
95+ fn handle_accept ( & self , active : & Arc < Mutex < usize > > , mut stream : TcpStream ) -> io:: Result < ( ) > {
96+ let should_accept = {
97+ let mut count = active. lock ( ) ;
98+ if * count >= self . max_connections {
99+ false
100+ } else {
101+ * count += 1 ;
102+ true
103+ }
104+ } ;
105+ if !should_accept {
106+ write_status ( & mut stream, 503 , "Service Unavailable" , b"busy" , false ) ?;
107+ return Ok ( ( ) ) ;
85108 }
109+
110+ let auth = Arc :: clone ( & self . auth ) ;
111+ let handler = Arc :: clone ( & self . handler ) ;
112+ let active = Arc :: clone ( active) ;
113+ let idle_timeout = self . idle_timeout ;
114+ thread:: spawn ( move || {
115+ if let Err ( error) = serve_connection ( stream, & auth, & handler, idle_timeout) {
116+ debug ! ( %error, "rpc connection closed with error" ) ;
117+ }
118+ let mut count = active. lock ( ) ;
119+ * count = count. saturating_sub ( 1 ) ;
120+ } ) ;
86121 Ok ( ( ) )
87122 }
88123}
@@ -255,3 +290,31 @@ fn write_status(
255290 stream. write_all ( body) ?;
256291 stream. flush ( )
257292}
293+
294+ #[ cfg( test) ]
295+ mod tests {
296+ use super :: * ;
297+ use core:: sync:: atomic:: { AtomicBool , Ordering } ;
298+
299+ use crate :: context:: Context ;
300+
301+ #[ test]
302+ #[ allow( clippy:: expect_used) ]
303+ fn serve_with_shutdown_exits_on_signal ( ) -> std:: io:: Result < ( ) > {
304+ let auth = Arc :: new ( Auth :: basic ( "alice" , "secret" ) ) ;
305+ let handler = Arc :: new ( Handler :: new ( Arc :: new ( Context :: new ( ) ) ) ) ;
306+ let server = RpcServer :: bind (
307+ "127.0.0.1:0" ,
308+ auth,
309+ handler,
310+ 4 ,
311+ core:: time:: Duration :: from_millis ( 500 ) ,
312+ ) ?;
313+ let shutdown = Arc :: new ( AtomicBool :: new ( false ) ) ;
314+ let shutdown_clone = Arc :: clone ( & shutdown) ;
315+ let handle = std:: thread:: spawn ( move || server. serve_with_shutdown ( shutdown_clone) ) ;
316+ std:: thread:: sleep ( core:: time:: Duration :: from_millis ( 150 ) ) ;
317+ shutdown. store ( true , Ordering :: Release ) ;
318+ handle. join ( ) . expect ( "join serve thread" )
319+ }
320+ }
0 commit comments