Skip to main content

liminal_server/health/
endpoint.rs

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/// Handle for a running health endpoint server.
22///
23/// W4 leg 2 (§4.2): the health accept worker BLOCKS in `accept` (kernel-parked,
24/// zero idle wakes) rather than spinning a non-blocking poll with a backoff
25/// sleep. Shutdown wakes the blocked `accept` with an explicit self-connect
26/// interrupt to `interrupt_target` (the bound address, loopback-normalised),
27/// mirroring the leg-1 listeners. Shutdown also interrupts an in-flight request
28/// read directly via `active_stream`, so a silent client parked in
29/// `handle_connection`'s read cannot defer shutdown by its admitted deadline.
30#[derive(Debug)]
31pub struct HealthServerHandle {
32    local_addr: SocketAddr,
33    /// Loopback-normalised self-connect target used to interrupt the blocking
34    /// `accept` at shutdown.
35    interrupt_target: SocketAddr,
36    shutdown: Arc<AtomicBool>,
37    /// Slot holding a `try_clone` of the request stream the worker is currently
38    /// reading, if any. The worker registers it under the lock (with a shutdown
39    /// recheck) before blocking on the request read and clears it on completion;
40    /// `stop_worker` takes it and `shutdown(Both)`s it to interrupt an in-flight
41    /// blocking read directly (TOLD) rather than waiting out the read's admitted
42    /// deadline. At most one stream exists at a time (the worker is serial), so
43    /// one slot suffices.
44    active_stream: Arc<Mutex<Option<TcpStream>>>,
45    worker: Option<JoinHandle<Result<(), ServerError>>>,
46    /// Count of `accept` calls issued by the worker (test observability for the
47    /// zero-idle-wakes oracle: on a silent listener this stays at the single
48    /// parked call). The worker always maintains the counter; only the host-side
49    /// handle for reading it is test-scoped.
50    #[cfg(test)]
51    accept_attempts: Arc<AtomicU64>,
52    /// Count of connections shed under fd exhaustion via the reserve descriptor
53    /// (test observability for the shed helper reused from leg 1).
54    #[cfg(test)]
55    shed_count: Arc<AtomicU64>,
56    /// Count of accepted requests the worker has entered (incremented just
57    /// before it blocks reading the request). Test observability so a race can
58    /// deterministically catch the worker mid-request rather than parked in
59    /// `accept`.
60    #[cfg(test)]
61    requests_entered: Arc<AtomicU64>,
62}
63
64impl HealthServerHandle {
65    /// Returns the bound address for the health endpoint server.
66    #[must_use]
67    pub const fn local_addr(&self) -> SocketAddr {
68        self.local_addr
69    }
70
71    /// Stops the health endpoint server and waits for its worker thread to exit.
72    ///
73    /// # Errors
74    ///
75    /// Returns [`ServerError::HealthEndpoint`] if the worker thread cannot be
76    /// joined cleanly or if the server loop recorded a serving error.
77    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        // Interrupt an in-flight request read directly (TOLD): the flag store
87        // above happens-before this lock acquire, so any stream the worker
88        // registered before observing the flag is taken here and shut down,
89        // waking its blocked read at once. `shutdown(Both)` on the clone reaches
90        // the same underlying socket the worker is reading (a dup'd fd). If the
91        // worker is instead parked in `accept`, the slot is empty and the
92        // self-connect below wakes it. The guard is released (statement end)
93        // before the socket call, so no lock is held across `shutdown`.
94        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        // Explicit cross-platform interrupt (mirrors the leg-1 listeners): a
103        // single self-connect wakes a blocked `accept`. The worker sees the
104        // shutdown flag it observed under the same ordering and sheds the woken
105        // (spurious) socket rather than serving it — at most one spurious accept
106        // per interrupt. If the worker already exited (a real accept then flag
107        // check), the listener is gone and this connect fails fast; the join then
108        // returns immediately.
109        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    /// `accept` calls issued by the worker (test observability, oracle 8).
119    #[cfg(test)]
120    fn accept_attempts(&self) -> u64 {
121        self.accept_attempts.load(Ordering::SeqCst)
122    }
123
124    /// Connections shed under fd exhaustion (test observability).
125    #[cfg(test)]
126    fn shed_count(&self) -> u64 {
127        self.shed_count.load(Ordering::SeqCst)
128    }
129
130    /// Accepted requests the worker has entered (test observability).
131    #[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
145/// Starts the health endpoint HTTP server on a distinct health bind address.
146///
147/// The returned server handle is independent from the main wire protocol
148/// listener. Binding the health endpoint does not mark the main listener ready.
149///
150/// # Errors
151///
152/// Returns [`ServerError::HealthEndpoint`] when the health listener cannot bind
153/// or cannot report its local address. The listener stays BLOCKING (W4 leg 2):
154/// the accept worker kernel-parks in `accept` with zero idle wakes; shutdown
155/// wakes it via the self-connect interrupt.
156pub 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    // The listener stays BLOCKING (W4 leg 2): the accept worker kernel-parks in
165    // `accept` with zero idle wakes; shutdown wakes it via the self-connect
166    // interrupt below.
167    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    // One reserve descriptor held for the shed-with-spare-fd EMFILE policy,
220    // reusing the leg-1 helper.
221    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                // Register the in-flight stream and re-check shutdown atomically
227                // under the slot lock. If shutdown already fired, shed without
228                // serving (no request slips past the broadcast); otherwise the
229                // registered clone lets `stop_worker` interrupt this request's
230                // blocking read directly (TOLD). Pairing the flag load here with
231                // `stop_worker`'s flag-store-then-lock means a registration can
232                // never be missed: either the worker sees the flag and sheds, or
233                // shutdown finds the clone in the slot.
234                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                // A per-connection error (e.g. a TCP probe that connects but sends no HTTP
249                // data within the read timeout) must NOT terminate the serve loop — otherwise
250                // a single port probe kills the health server for the process lifetime and
251                // subsequent liveness/readiness probes get connection-refused. Only fatal
252                // listener-level accept errors (below) terminate serving.
253                let result = handle_connection(stream, readiness);
254                // The request is done: clear the slot so a later shutdown finds
255                // nothing to interrupt (and never shuts down an unrelated stream).
256                // A no-op if `stop_worker` already took the clone.
257                *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
277/// EMFILE/ENFILE resource exhaustion is transient, exactly as the leg-1 accept
278/// loops treat it (mirrors the sibling WebSocket listener's local predicate).
279fn 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        // A clustered server starts with the cluster gate unmet: config loaded and
553        // listener bound, but membership not yet established (G2). /ready is 503.
554        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        // The cluster start's on_established hook flips exactly this flag; once set,
573        // /ready must transition to 200 with no unmet conditions.
574        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    /// Oracle 8 (W4 leg 2) — on a quiet health listener the blocking accept is
636    /// issued exactly once (the parked call) and never again: zero repeated
637    /// accepts, zero application wakes after arming, with route behaviour
638    /// unchanged (a real request is still served afterwards).
639    #[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        // Route behaviour unchanged: a real request is still served after silence.
667        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    /// Oracle 9 (W4 leg 2) — absence proof over this module's production source:
677    /// the retired non-blocking flip and its `WouldBlock` + sleep poll must not
678    /// appear in the health accept path.
679    #[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    /// Oracle 10 (W4 leg 2) — shutdown interrupts the blocking accept wait at
696    /// every race point: before the worker arms, after it parks, concurrent with
697    /// a pending connection, and after an accept returns. Each shutdown returns
698    /// promptly (no sleep-poll) and joins cleanly (no worker leak); the released
699    /// listener refuses further connects (no descriptor leak).
700    #[test]
701    fn health_shutdown_interrupts_accept_wait() -> Result<(), Box<dyn std::error::Error>> {
702        // (a) shutdown immediately after start — possibly before the worker arms.
703        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        // (b) shutdown after the worker has parked the blocking accept.
712        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        // No descriptor leak: the released listener refuses further connects.
730        assert!(
731            TcpStream::connect(parked_addr).is_err(),
732            "the listener descriptor was released; further connects are refused"
733        );
734
735        // (c) shutdown concurrent with accept readiness: a client is pending.
736        // Whether the worker is still parked in `accept` or has already accepted
737        // and is blocked reading the silent client, shutdown interrupts promptly
738        // (self-connect for the parked case, in-flight stream shutdown for the
739        // reading case) — deterministically under the 2s read deadline. The bound
740        // is tightened to 500 ms to reflect the TOLD interrupt: it must not
741        // approach the read window that the pre-fix bytes deferred to (see
742        // `shutdown_interrupts_in_flight_silent_request_read` for the dedicated
743        // mid-read regression).
744        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        // (d) shutdown after an accept returns and a request is served.
760        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    /// Regression for oracle 10 case (c) (W4 leg 2 — tear-seat BOUNCE): an
774    /// in-flight SILENT request must not defer shutdown by its read window. A
775    /// client that connects and sends nothing parks the worker inside
776    /// `handle_connection`'s blocking read (the admitted 2s slow-client
777    /// deadline). Shutdown must interrupt that read directly (TOLD stream
778    /// interrupt), NOT wait for the deadline to expire.
779    ///
780    /// The promptness bound is inherently a timing assertion: it is set to
781    /// 500 ms — comfortably under the 2s read deadline (so the pre-fix bytes,
782    /// where the self-connect interrupt merely queues behind the blocked read,
783    /// red deterministically) and comfortably over scheduler-wakeup noise even
784    /// under full-workspace parallel load (so the post-fix stream interrupt
785    /// greens deterministically). The worker's entry into the read is observed
786    /// via the `requests_entered` counter, not a sleep, so the race is caught
787    /// deterministically mid-request.
788    #[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        // Connect but send NOTHING and keep the socket open: the worker accepts
794        // and blocks in handle_connection's read on the silent stream. The named
795        // binding keeps the client alive (a bare `_` would drop it and end the
796        // read early with EOF).
797        let _silent = TcpStream::connect(server.local_addr())?;
798
799        // Observe the worker has entered the in-flight request read (counter, not
800        // a sleep) so shutdown is fired while it is genuinely blocked on the read.
801        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    /// Oracle 11 (W4 leg 2, idle-honesty both-sides) — an unrelated served
824    /// request grows the BUSY listener's accept-attempt counter while the silent
825    /// listener's accept-attempt counter stays FLAT during the workload. The
826    /// growing side proves the fixture cannot pass by hiding the workload (a
827    /// frozen harness would leave the busy counter flat and fail); the flat side
828    /// proves genuine silence rather than a global freeze.
829    #[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        // Unrelated served workload on the BUSY listener: each served request
846        // returns the parked accept and re-parks a fresh one, growing its counter.
847        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}