1use std::io::{Read, Write};
2use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream};
3use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
4use std::sync::{Arc, Mutex, PoisonError};
5use std::thread::{self, JoinHandle};
6use std::time::Duration;
7
8use crate::ServerError;
9use crate::server::listener::{loopback_interrupt_target, shed_on_fd_exhaustion};
10
11use super::checks::{SharedReadinessState, health_check, readiness_check};
12
13use super::metrics_route;
14
15const HEALTH_PATH: &str = "/health";
16const READY_PATH: &str = "/ready";
17const METRICS_PATH: &str = "/metrics";
18const APPLICATION_JSON: &str = "application/json";
19const READ_BUFFER_BYTES: usize = 2048;
20
21#[derive(Debug)]
31pub struct HealthServerHandle {
32 local_addr: SocketAddr,
33 interrupt_target: SocketAddr,
36 shutdown: Arc<AtomicBool>,
37 active_stream: Arc<Mutex<Option<TcpStream>>>,
45 worker: Option<JoinHandle<Result<(), ServerError>>>,
46 #[cfg(test)]
51 accept_attempts: Arc<AtomicU64>,
52 #[cfg(test)]
55 shed_count: Arc<AtomicU64>,
56 #[cfg(test)]
61 requests_entered: Arc<AtomicU64>,
62}
63
64impl HealthServerHandle {
65 #[must_use]
67 pub const fn local_addr(&self) -> SocketAddr {
68 self.local_addr
69 }
70
71 pub fn shutdown(mut self) -> Result<(), ServerError> {
78 self.stop_worker()
79 }
80
81 fn stop_worker(&mut self) -> Result<(), ServerError> {
82 self.shutdown.store(true, Ordering::SeqCst);
83 let Some(worker) = self.worker.take() else {
84 return Ok(());
85 };
86 let in_flight = self
95 .active_stream
96 .lock()
97 .unwrap_or_else(PoisonError::into_inner)
98 .take();
99 if let Some(stream) = in_flight {
100 let _ = stream.shutdown(Shutdown::Both);
101 }
102 if let Ok(waker) = TcpStream::connect(self.interrupt_target) {
110 drop(waker);
111 }
112
113 worker.join().map_err(|_| ServerError::HealthEndpoint {
114 message: "health endpoint worker thread terminated unexpectedly".to_owned(),
115 })?
116 }
117
118 #[cfg(test)]
120 fn accept_attempts(&self) -> u64 {
121 self.accept_attempts.load(Ordering::SeqCst)
122 }
123
124 #[cfg(test)]
126 fn shed_count(&self) -> u64 {
127 self.shed_count.load(Ordering::SeqCst)
128 }
129
130 #[cfg(test)]
132 fn requests_entered(&self) -> u64 {
133 self.requests_entered.load(Ordering::SeqCst)
134 }
135}
136
137impl Drop for HealthServerHandle {
138 fn drop(&mut self) {
139 if let Err(error) = self.stop_worker() {
140 tracing::debug!(%error, "health endpoint shutdown during drop failed");
141 }
142 }
143}
144
145pub fn start_health_server(
157 bind_address: SocketAddr,
158 readiness: SharedReadinessState,
159) -> Result<HealthServerHandle, ServerError> {
160 let listener =
161 TcpListener::bind(bind_address).map_err(|error| ServerError::HealthEndpoint {
162 message: format!("failed to bind health endpoint at {bind_address}: {error}"),
163 })?;
164 let local_addr = listener
168 .local_addr()
169 .map_err(|error| ServerError::HealthEndpoint {
170 message: format!("failed to inspect health endpoint listener address: {error}"),
171 })?;
172 let interrupt_target = loopback_interrupt_target(local_addr);
173 let shutdown = Arc::new(AtomicBool::new(false));
174 let active_stream = Arc::new(Mutex::new(None));
175 let accept_attempts = Arc::new(AtomicU64::new(0));
176 let shed_count = Arc::new(AtomicU64::new(0));
177 let requests_entered = Arc::new(AtomicU64::new(0));
178 let worker_shutdown = Arc::clone(&shutdown);
179 let worker_stream = Arc::clone(&active_stream);
180 let worker_attempts = Arc::clone(&accept_attempts);
181 let worker_shed = Arc::clone(&shed_count);
182 let worker_entered = Arc::clone(&requests_entered);
183 let worker = thread::spawn(move || {
184 serve(
185 &listener,
186 &readiness,
187 &worker_shutdown,
188 &worker_stream,
189 &worker_attempts,
190 &worker_shed,
191 &worker_entered,
192 )
193 });
194
195 Ok(HealthServerHandle {
196 local_addr,
197 interrupt_target,
198 shutdown,
199 active_stream,
200 worker: Some(worker),
201 #[cfg(test)]
202 accept_attempts,
203 #[cfg(test)]
204 shed_count,
205 #[cfg(test)]
206 requests_entered,
207 })
208}
209
210fn serve(
211 listener: &TcpListener,
212 readiness: &SharedReadinessState,
213 shutdown: &AtomicBool,
214 active_stream: &Mutex<Option<TcpStream>>,
215 accept_attempts: &AtomicU64,
216 shed_count: &AtomicU64,
217 requests_entered: &AtomicU64,
218) -> Result<(), ServerError> {
219 let mut reserve = listener.try_clone().ok();
222 while !shutdown.load(Ordering::SeqCst) {
223 accept_attempts.fetch_add(1, Ordering::SeqCst);
224 match listener.accept() {
225 Ok((stream, ..)) => {
226 let admitted = {
235 let mut slot = active_stream.lock().unwrap_or_else(PoisonError::into_inner);
236 if shutdown.load(Ordering::SeqCst) {
237 false
238 } else {
239 *slot = stream.try_clone().ok();
240 true
241 }
242 };
243 if !admitted {
244 drop(stream);
245 continue;
246 }
247 requests_entered.fetch_add(1, Ordering::SeqCst);
248 let result = handle_connection(stream, readiness);
254 *active_stream.lock().unwrap_or_else(PoisonError::into_inner) = None;
258 if let Err(error) = result {
259 tracing::debug!(%error, "health endpoint connection error");
260 }
261 }
262 Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
263 Err(error) if is_transient_accept_error(&error) => {
264 shed_on_fd_exhaustion(listener, &mut reserve, shed_count, &error);
265 }
266 Err(error) => {
267 return Err(ServerError::HealthEndpoint {
268 message: format!("health endpoint accept failed: {error}"),
269 });
270 }
271 }
272 }
273
274 Ok(())
275}
276
277fn is_transient_accept_error(error: &std::io::Error) -> bool {
280 matches!(error.raw_os_error(), Some(code) if code == 24 || code == 23)
281}
282
283fn handle_connection(
284 mut stream: TcpStream,
285 readiness: &SharedReadinessState,
286) -> Result<(), ServerError> {
287 stream
288 .set_nonblocking(false)
289 .map_err(|error| ServerError::HealthEndpoint {
290 message: format!("failed to configure health request stream: {error}"),
291 })?;
292 stream
293 .set_read_timeout(Some(Duration::from_secs(2)))
294 .map_err(|error| ServerError::HealthEndpoint {
295 message: format!("failed to set health request read timeout: {error}"),
296 })?;
297
298 let mut buffer = [0_u8; READ_BUFFER_BYTES];
299 let bytes_read = stream
300 .read(&mut buffer)
301 .map_err(|error| ServerError::HealthEndpoint {
302 message: format!("failed to read health request: {error}"),
303 })?;
304
305 if bytes_read == 0 {
306 return Ok(());
307 }
308
309 let response = response_for_request(&buffer[..bytes_read], readiness)?;
310 stream
311 .write_all(&response)
312 .map_err(|error| ServerError::HealthEndpoint {
313 message: format!("failed to write health response: {error}"),
314 })?;
315 stream.flush().map_err(|error| ServerError::HealthEndpoint {
316 message: format!("failed to flush health response: {error}"),
317 })
318}
319
320fn response_for_request(
321 request: &[u8],
322 readiness: &SharedReadinessState,
323) -> Result<Vec<u8>, ServerError> {
324 let Ok(request) = std::str::from_utf8(request) else {
325 return Ok(empty_response(StatusCode::BadRequest));
326 };
327 let Some((method, path)) = parse_request_line(request) else {
328 return Ok(empty_response(StatusCode::BadRequest));
329 };
330
331 match (method, path) {
332 ("GET", HEALTH_PATH) => json_response(StatusCode::Ok, &health_check()),
333 ("GET", READY_PATH) => {
334 let status = readiness_check(&readiness.snapshot());
335 let status_code = if status.ready {
336 StatusCode::Ok
337 } else {
338 StatusCode::ServiceUnavailable
339 };
340 json_response(status_code, &status)
341 }
342 ("GET", METRICS_PATH) => Ok(response(
343 StatusCode::Ok,
344 Some(metrics_route::CONTENT_TYPE),
345 metrics_route::render_body().as_bytes(),
346 )),
347 (_, HEALTH_PATH | READY_PATH | METRICS_PATH) => {
348 Ok(empty_response(StatusCode::MethodNotAllowed))
349 }
350 _ => Ok(empty_response(StatusCode::NotFound)),
351 }
352}
353
354fn parse_request_line(request: &str) -> Option<(&str, &str)> {
355 let request_line = request.lines().next()?;
356 let mut parts = request_line.split_whitespace();
357 let method = parts.next()?;
358 let path = parts.next()?;
359 parts.next()?;
360
361 Some((method, path))
362}
363
364fn json_response<T>(status: StatusCode, value: &T) -> Result<Vec<u8>, ServerError>
365where
366 T: serde::Serialize,
367{
368 let body = serde_json::to_vec(value).map_err(|error| ServerError::HealthEndpoint {
369 message: format!("failed to serialize health response: {error}"),
370 })?;
371 Ok(response(status, Some(APPLICATION_JSON), &body))
372}
373
374fn empty_response(status: StatusCode) -> Vec<u8> {
375 response(status, None, &[])
376}
377
378fn response(status: StatusCode, content_type: Option<&str>, body: &[u8]) -> Vec<u8> {
379 let mut response = Vec::new();
380 let status_line = format!("HTTP/1.1 {} {}\r\n", status.code(), status.reason());
381 response.extend_from_slice(status_line.as_bytes());
382 response.extend_from_slice(format!("Content-Length: {}\r\n", body.len()).as_bytes());
383 response.extend_from_slice(b"Connection: close\r\n");
384 if let Some(content_type) = content_type {
385 response.extend_from_slice(format!("Content-Type: {content_type}\r\n").as_bytes());
386 }
387 response.extend_from_slice(b"\r\n");
388 response.extend_from_slice(body);
389 response
390}
391
392#[derive(Debug, Clone, Copy, PartialEq, Eq)]
393enum StatusCode {
394 Ok,
395 BadRequest,
396 NotFound,
397 MethodNotAllowed,
398 ServiceUnavailable,
399}
400
401impl StatusCode {
402 const fn code(self) -> u16 {
403 match self {
404 Self::Ok => 200,
405 Self::BadRequest => 400,
406 Self::NotFound => 404,
407 Self::MethodNotAllowed => 405,
408 Self::ServiceUnavailable => 503,
409 }
410 }
411
412 const fn reason(self) -> &'static str {
413 match self {
414 Self::Ok => "OK",
415 Self::BadRequest => "Bad Request",
416 Self::NotFound => "Not Found",
417 Self::MethodNotAllowed => "Method Not Allowed",
418 Self::ServiceUnavailable => "Service Unavailable",
419 }
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use std::io::{Read, Write};
426 use std::net::{SocketAddr, TcpStream};
427 use std::thread;
428 use std::time::{Duration, Instant};
429
430 use serde_json::Value;
431
432 use super::{response_for_request, start_health_server};
433 use crate::health::checks::{
434 ClusterReadiness, ReadinessCondition, ReadinessState, SharedReadinessState,
435 };
436
437 fn loopback_ephemeral() -> Result<SocketAddr, Box<dyn std::error::Error>> {
438 Ok("127.0.0.1:0".parse()?)
439 }
440
441 fn get(address: SocketAddr, path: &str) -> Result<String, Box<dyn std::error::Error>> {
442 let mut stream = TcpStream::connect(address)?;
443 stream.set_read_timeout(Some(Duration::from_secs(2)))?;
444 let request = format!("GET {path} HTTP/1.1\r\nHost: localhost\r\n\r\n");
445 stream.write_all(request.as_bytes())?;
446
447 let mut response = String::new();
448 stream.read_to_string(&mut response)?;
449 Ok(response)
450 }
451
452 fn assert_status(response: &str, status: u16) {
453 let expected = format!("HTTP/1.1 {status} ");
454 assert!(
455 response.starts_with(&expected),
456 "response status did not start with {expected}: {response}"
457 );
458 }
459
460 fn body(response: &str) -> Result<&str, Box<dyn std::error::Error>> {
461 let Some((_headers, body)) = response.split_once("\r\n\r\n") else {
462 return Err("response did not contain a header/body separator".into());
463 };
464 Ok(body)
465 }
466
467 fn json_body(response: &str) -> Result<Value, Box<dyn std::error::Error>> {
468 Ok(serde_json::from_str(body(response)?)?)
469 }
470
471 #[test]
472 fn health_endpoint_returns_json_200_regardless_of_readiness()
473 -> Result<(), Box<dyn std::error::Error>> {
474 let readiness = SharedReadinessState::new(ReadinessState::default());
475 let server = start_health_server(loopback_ephemeral()?, readiness)?;
476
477 let response = get(server.local_addr(), "/health")?;
478 server.shutdown()?;
479
480 assert_status(&response, 200);
481 assert!(response.contains("Content-Type: application/json\r\n"));
482 let body = json_body(&response)?;
483 assert_eq!(body["status"], "healthy");
484
485 Ok(())
486 }
487
488 #[test]
489 fn ready_endpoint_returns_503_before_main_listener_binds()
490 -> Result<(), Box<dyn std::error::Error>> {
491 let readiness = SharedReadinessState::new(ReadinessState::new(
492 true,
493 false,
494 ClusterReadiness::NotConfigured,
495 ));
496 let server = start_health_server(loopback_ephemeral()?, readiness)?;
497
498 let response = get(server.local_addr(), "/ready")?;
499 server.shutdown()?;
500
501 assert_status(&response, 503);
502 assert!(response.contains("Content-Type: application/json\r\n"));
503 let body = json_body(&response)?;
504 assert_eq!(body["ready"], false);
505 assert_eq!(body["unmet_conditions"][0], "listener_bound");
506
507 Ok(())
508 }
509
510 #[test]
511 fn ready_endpoint_returns_200_after_all_startup_gates() -> Result<(), Box<dyn std::error::Error>>
512 {
513 let readiness = SharedReadinessState::new(ReadinessState::ready_without_cluster());
514 let server = start_health_server(loopback_ephemeral()?, readiness)?;
515
516 let response = get(server.local_addr(), "/ready")?;
517 server.shutdown()?;
518
519 assert_status(&response, 200);
520 let body = json_body(&response)?;
521 assert_eq!(body["ready"], true);
522 let Some(unmet_conditions) = body["unmet_conditions"].as_array() else {
523 return Err("unmet_conditions should be an array".into());
524 };
525 assert!(unmet_conditions.is_empty());
526
527 Ok(())
528 }
529
530 #[test]
531 fn ready_endpoint_updates_from_shared_readiness_state() -> Result<(), Box<dyn std::error::Error>>
532 {
533 let readiness = SharedReadinessState::new(ReadinessState::default());
534 let server = start_health_server(loopback_ephemeral()?, readiness.clone())?;
535
536 let response = get(server.local_addr(), "/ready")?;
537 assert_status(&response, 503);
538
539 readiness.set_config_loaded(true);
540 readiness.set_listener_bound(true);
541 let response = get(server.local_addr(), "/ready")?;
542 server.shutdown()?;
543
544 assert_status(&response, 200);
545
546 Ok(())
547 }
548
549 #[test]
550 fn clustered_ready_transitions_503_to_200_when_membership_established()
551 -> Result<(), Box<dyn std::error::Error>> {
552 let readiness = SharedReadinessState::new(ReadinessState::new(
555 true,
556 true,
557 ClusterReadiness::Configured {
558 membership_established: false,
559 },
560 ));
561 let server = start_health_server(loopback_ephemeral()?, readiness.clone())?;
562
563 let response = get(server.local_addr(), "/ready")?;
564 assert_status(&response, 503);
565 let body = json_body(&response)?;
566 assert_eq!(body["ready"], false);
567 assert_eq!(
568 body["unmet_conditions"][0],
569 serde_json::to_value(ReadinessCondition::ClusterMembershipEstablished)?
570 );
571
572 readiness.set_cluster_membership_established(true);
575 let response = get(server.local_addr(), "/ready")?;
576 server.shutdown()?;
577
578 assert_status(&response, 200);
579 let body = json_body(&response)?;
580 assert_eq!(body["ready"], true);
581 let Some(unmet_conditions) = body["unmet_conditions"].as_array() else {
582 return Err("unmet_conditions should be an array".into());
583 };
584 assert!(unmet_conditions.is_empty());
585
586 Ok(())
587 }
588
589 #[test]
590 fn cluster_readiness_is_listed_when_configured_but_not_joined()
591 -> Result<(), Box<dyn std::error::Error>> {
592 let readiness = SharedReadinessState::new(ReadinessState::new(
593 true,
594 true,
595 ClusterReadiness::Configured {
596 membership_established: false,
597 },
598 ));
599 let response = response_for_request(b"GET /ready HTTP/1.1\r\n\r\n", &readiness)?;
600 let response = String::from_utf8(response)?;
601
602 assert_status(&response, 503);
603 let body = json_body(&response)?;
604 assert_eq!(
605 body["unmet_conditions"][0],
606 serde_json::to_value(ReadinessCondition::ClusterMembershipEstablished)?
607 );
608
609 Ok(())
610 }
611
612 #[test]
613 fn unsupported_paths_are_not_served() -> Result<(), Box<dyn std::error::Error>> {
614 let readiness = SharedReadinessState::default();
615 let response = response_for_request(b"GET /unknown HTTP/1.1\r\n\r\n", &readiness)?;
616 let response = String::from_utf8(response)?;
617
618 assert_status(&response, 404);
619
620 Ok(())
621 }
622
623 #[test]
624 fn unsupported_methods_on_health_paths_are_rejected() -> Result<(), Box<dyn std::error::Error>>
625 {
626 let readiness = SharedReadinessState::default();
627 let response = response_for_request(b"POST /health HTTP/1.1\r\n\r\n", &readiness)?;
628 let response = String::from_utf8(response)?;
629
630 assert_status(&response, 405);
631
632 Ok(())
633 }
634
635 #[test]
640 fn silent_health_listener_has_zero_application_wakes() -> Result<(), Box<dyn std::error::Error>>
641 {
642 let server = start_health_server(loopback_ephemeral()?, SharedReadinessState::default())?;
643
644 let deadline = Instant::now() + Duration::from_secs(2);
645 while server.accept_attempts() < 1 && Instant::now() < deadline {
646 thread::sleep(Duration::from_millis(5));
647 }
648 let armed = server.accept_attempts();
649 assert_eq!(
650 armed, 1,
651 "the blocking accept is issued exactly once when parked"
652 );
653
654 thread::sleep(Duration::from_millis(200));
655 assert_eq!(
656 server.accept_attempts(),
657 armed,
658 "a silent health listener must not wake or re-accept"
659 );
660 assert_eq!(
661 server.shed_count(),
662 0,
663 "a silent health listener sheds nothing"
664 );
665
666 let response = get(server.local_addr(), "/health")?;
668 assert_status(&response, 200);
669 let body = json_body(&response)?;
670 assert_eq!(body["status"], "healthy");
671
672 server.shutdown()?;
673 Ok(())
674 }
675
676 #[test]
680 fn health_accept_source_has_no_wouldblock_sleep_poll() {
681 const SOURCE: &str = include_str!("endpoint.rs");
682 let production = SOURCE.split("mod tests").next().unwrap_or(SOURCE);
683 for forbidden in [
684 "set_nonblocking(true)",
685 "ErrorKind::WouldBlock",
686 "thread::sleep",
687 ] {
688 assert!(
689 !production.contains(forbidden),
690 "retired health accept-path source `{forbidden}` reappeared"
691 );
692 }
693 }
694
695 #[test]
701 fn health_shutdown_interrupts_accept_wait() -> Result<(), Box<dyn std::error::Error>> {
702 let server = start_health_server(loopback_ephemeral()?, SharedReadinessState::default())?;
704 let start = Instant::now();
705 server.shutdown()?;
706 assert!(
707 start.elapsed() < Duration::from_secs(2),
708 "shutdown before arming must interrupt promptly, not sleep-poll"
709 );
710
711 let server = start_health_server(loopback_ephemeral()?, SharedReadinessState::default())?;
713 let deadline = Instant::now() + Duration::from_secs(2);
714 while server.accept_attempts() < 1 && Instant::now() < deadline {
715 thread::sleep(Duration::from_millis(5));
716 }
717 assert_eq!(
718 server.accept_attempts(),
719 1,
720 "the worker parked before shutdown"
721 );
722 let parked_addr = server.local_addr();
723 let start = Instant::now();
724 server.shutdown()?;
725 assert!(
726 start.elapsed() < Duration::from_secs(2),
727 "shutdown of a parked accept must interrupt promptly"
728 );
729 assert!(
731 TcpStream::connect(parked_addr).is_err(),
732 "the listener descriptor was released; further connects are refused"
733 );
734
735 let server = start_health_server(loopback_ephemeral()?, SharedReadinessState::default())?;
745 let deadline = Instant::now() + Duration::from_secs(2);
746 while server.accept_attempts() < 1 && Instant::now() < deadline {
747 thread::sleep(Duration::from_millis(5));
748 }
749 let _pending = TcpStream::connect(server.local_addr())?;
750 let start = Instant::now();
751 server.shutdown()?;
752 assert!(
753 start.elapsed() < Duration::from_millis(500),
754 "shutdown concurrent with a pending accept must interrupt promptly (TOLD), \
755 not defer by a request read deadline: elapsed {:?}",
756 start.elapsed()
757 );
758
759 let server = start_health_server(loopback_ephemeral()?, SharedReadinessState::default())?;
761 let response = get(server.local_addr(), "/health")?;
762 assert_status(&response, 200);
763 let start = Instant::now();
764 server.shutdown()?;
765 assert!(
766 start.elapsed() < Duration::from_secs(2),
767 "shutdown after a served request must interrupt the next parked accept promptly"
768 );
769
770 Ok(())
771 }
772
773 #[test]
789 fn shutdown_interrupts_in_flight_silent_request_read() -> Result<(), Box<dyn std::error::Error>>
790 {
791 let server = start_health_server(loopback_ephemeral()?, SharedReadinessState::default())?;
792
793 let _silent = TcpStream::connect(server.local_addr())?;
798
799 let deadline = Instant::now() + Duration::from_secs(2);
802 while server.requests_entered() < 1 && Instant::now() < deadline {
803 thread::sleep(Duration::from_millis(5));
804 }
805 assert_eq!(
806 server.requests_entered(),
807 1,
808 "the worker entered the in-flight silent request read"
809 );
810
811 let start = Instant::now();
812 server.shutdown()?;
813 assert!(
814 start.elapsed() < Duration::from_millis(500),
815 "shutdown must interrupt an in-flight silent request read promptly (TOLD), \
816 not defer by the read's admitted 2s deadline: elapsed {:?}",
817 start.elapsed()
818 );
819
820 Ok(())
821 }
822
823 #[test]
830 fn health_idle_grows_unrelated_counters_while_accept_stays_flat()
831 -> Result<(), Box<dyn std::error::Error>> {
832 let idle = start_health_server(loopback_ephemeral()?, SharedReadinessState::default())?;
833 let busy = start_health_server(loopback_ephemeral()?, SharedReadinessState::default())?;
834
835 let deadline = Instant::now() + Duration::from_secs(2);
836 while (idle.accept_attempts() < 1 || busy.accept_attempts() < 1)
837 && Instant::now() < deadline
838 {
839 thread::sleep(Duration::from_millis(5));
840 }
841 let idle_armed = idle.accept_attempts();
842 assert_eq!(idle_armed, 1, "the idle listener parks exactly one accept");
843 let busy_before = busy.accept_attempts();
844
845 for _ in 0..5 {
848 let response = get(busy.local_addr(), "/health")?;
849 assert_status(&response, 200);
850 }
851 let deadline = Instant::now() + Duration::from_secs(2);
852 while busy.accept_attempts() <= busy_before && Instant::now() < deadline {
853 thread::sleep(Duration::from_millis(5));
854 }
855
856 assert!(
857 busy.accept_attempts() > busy_before,
858 "an unrelated served request grows the busy listener's accept counter"
859 );
860 assert_eq!(
861 idle.accept_attempts(),
862 idle_armed,
863 "the silent listener's accept counter stays flat during the workload"
864 );
865 assert_eq!(idle.shed_count(), 0, "the silent listener sheds nothing");
866
867 idle.shutdown()?;
868 busy.shutdown()?;
869 Ok(())
870 }
871}