Skip to main content

fastapi_http/
server.rs

1//! HTTP server with asupersync integration.
2//!
3//! This module provides a TCP server that uses asupersync for structured
4//! concurrency and cancel-correct request handling.
5//!
6// NOTE: This server implementation is used by `serve`/`serve_with_config` and is
7// intentionally asupersync-only (no tokio). Some ancillary types are still
8// evolving as the runtime's I/O surface matures.
9#![allow(dead_code)]
10//!
11//! # Architecture
12//!
13//! The server creates a region hierarchy:
14//!
15//! ```text
16//! Server Region (root)
17//! ├── Connection Region 1
18//! │   ├── Request Task 1 (with Cx, Budget)
19//! │   ├── Request Task 2 (with Cx, Budget)
20//! │   └── ...
21//! ├── Connection Region 2
22//! │   └── ...
23//! └── ...
24//! ```
25//!
26//! Each request runs with its own [`RequestContext`](fastapi_core::RequestContext)
27//! that wraps the asupersync [`Cx`](asupersync::Cx), providing:
28//!
29//! - Cancel-correct request handling via checkpoints
30//! - Budget-based request timeouts
31//! - Structured concurrency for background work
32//!
33//! # Example
34//!
35//! ```ignore
36//! use fastapi_http::TcpServer;
37//! use fastapi_core::{RequestContext, Request, Response};
38//!
39//! async fn handler(ctx: &RequestContext, req: Request) -> Response {
40//!     Response::ok().body("Hello, World!")
41//! }
42//!
43//! let config = ServerConfig::new("127.0.0.1:8080");
44//! let server = TcpServer::new(config);
45//! server.serve(handler).await?;
46//! ```
47
48use crate::connection::should_keep_alive;
49use crate::expect::{
50    CONTINUE_RESPONSE, ExpectHandler, ExpectResult, PreBodyValidator, PreBodyValidators,
51};
52use crate::http2;
53use crate::parser::{ParseError, ParseLimits, ParseStatus, Parser, StatefulParser};
54use crate::response::{ResponseWrite, ResponseWriter};
55use asupersync::io::{AsyncRead, AsyncWrite, ReadBuf};
56use asupersync::net::{TcpListener, TcpStream};
57use asupersync::runtime::{JoinHandle, Runtime, RuntimeHandle, SpawnError};
58use asupersync::signal::{GracefulOutcome, ShutdownController, ShutdownReceiver};
59use asupersync::stream::Stream;
60use asupersync::time::{timeout, timeout_at};
61use asupersync::{Budget, Cx, Time};
62use fastapi_core::app::App;
63use fastapi_core::{Method, Request, RequestContext, Response, StatusCode};
64use std::future::Future;
65use std::io;
66use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
67use std::pin::Pin;
68use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
69use std::sync::{Arc, Mutex, OnceLock};
70use std::task::Poll;
71use std::time::{Duration, Instant};
72
73/// Global start time for computing asupersync Time values.
74/// This is lazily initialized on first use.
75static START_TIME: OnceLock<Instant> = OnceLock::new();
76
77/// Returns the current time as an asupersync Time value.
78///
79/// This uses wall clock time relative to a lazily-initialized start point,
80/// which is compatible with asupersync's standalone timer mechanism.
81fn current_time() -> Time {
82    let start = START_TIME.get_or_init(Instant::now);
83    let now = Instant::now();
84    if now < *start {
85        Time::ZERO
86    } else {
87        let elapsed = now.duration_since(*start);
88        Time::from_nanos(elapsed.as_nanos() as u64)
89    }
90}
91
92fn request_deadline_at(now: Time, request_timeout: Time) -> Time {
93    Time::from_nanos(now.as_nanos().saturating_add(request_timeout.as_nanos()))
94}
95
96/// Computes an absolute request deadline from a configured timeout duration.
97///
98/// `ServerConfig::request_timeout` stores a timeout magnitude as `Time`. Apply
99/// it relative to the current runtime clock; using it directly as a deadline
100/// makes every request expire once process uptime exceeds the timeout.
101fn request_deadline(request_timeout: Time) -> Time {
102    request_deadline_at(current_time(), request_timeout)
103}
104
105fn request_cx_from_parent(parent: &Cx, _budget: Budget) -> Cx {
106    // asupersync 0.3.4 moved ambient constructors behind test-internals; production
107    // request contexts must inherit the runtime-bound server context, and no public
108    // API attaches a budget to an inherited Cx. The request deadline is therefore
109    // enforced at the call sites instead: the handler future races a
110    // `timeout_at(deadline, ..)` sleep (so an over-budget or stuck handler is
111    // dropped and answered with 504 at the deadline, not after it completes) and
112    // the same deadline is published on `RequestContext::deadline` for middleware.
113    // Dropping the raced future is how cancellation is delivered; this shared-state
114    // clone must never be cancel-marked, or the connection itself would be poisoned.
115    parent.clone()
116}
117
118/// Default request timeout in seconds.
119pub const DEFAULT_REQUEST_TIMEOUT_SECS: u64 = 30;
120
121/// Default read buffer size in bytes.
122pub const DEFAULT_READ_BUFFER_SIZE: usize = 8192;
123
124/// Default maximum connections (0 = unlimited).
125pub const DEFAULT_MAX_CONNECTIONS: usize = 0;
126
127/// Default keep-alive timeout in seconds (time to wait for next request).
128pub const DEFAULT_KEEP_ALIVE_TIMEOUT_SECS: u64 = 75;
129
130/// Default max requests per connection (0 = unlimited).
131pub const DEFAULT_MAX_REQUESTS_PER_CONNECTION: usize = 100;
132
133/// Default drain timeout in seconds (time to wait for in-flight requests on shutdown).
134pub const DEFAULT_DRAIN_TIMEOUT_SECS: u64 = 30;
135
136struct CatchUnwind<F>(Pin<Box<F>>);
137
138impl<F: Future> CatchUnwind<F> {
139    fn new(future: F) -> Self {
140        Self(Box::pin(future))
141    }
142}
143
144impl<F: Future> Future for CatchUnwind<F> {
145    type Output = std::thread::Result<F::Output>;
146
147    fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
148        let inner = self.0.as_mut();
149        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| inner.poll(cx)));
150        match result {
151            Ok(Poll::Pending) => Poll::Pending,
152            Ok(Poll::Ready(output)) => Poll::Ready(Ok(output)),
153            Err(payload) => Poll::Ready(Err(payload)),
154        }
155    }
156}
157
158fn panic_payload_message(payload: &(dyn std::any::Any + Send)) -> String {
159    if let Some(message) = payload.downcast_ref::<&'static str>() {
160        (*message).to_string()
161    } else if let Some(message) = payload.downcast_ref::<String>() {
162        message.clone()
163    } else {
164        "non-string panic payload".to_string()
165    }
166}
167
168/// Server configuration for the HTTP/1.1 server.
169///
170/// Controls bind address, timeouts, connection limits, and HTTP parsing behavior.
171/// All timeouts use sensible defaults suitable for production use.
172///
173/// # Defaults
174///
175/// | Setting | Default |
176/// |---------|---------|
177/// | `request_timeout` | 30s |
178/// | `max_connections` | 0 (unlimited) |
179/// | `read_buffer_size` | 8192 bytes |
180/// | `tcp_nodelay` | `true` |
181/// | `keep_alive_timeout` | 75s |
182/// | `max_requests_per_connection` | 100 |
183/// | `drain_timeout` | 30s |
184///
185/// # Example
186///
187/// ```ignore
188/// use fastapi_http::{ServerConfig, serve_with_config};
189///
190/// let config = ServerConfig::new("0.0.0.0:8000")
191///     .with_request_timeout_secs(60)
192///     .with_max_connections(1000)
193///     .with_keep_alive_timeout_secs(120);
194/// ```
195#[derive(Debug, Clone)]
196pub struct ServerConfig {
197    /// Address to bind to.
198    pub bind_addr: String,
199    /// Default request timeout.
200    pub request_timeout: Time,
201    /// Maximum connections (0 = unlimited).
202    pub max_connections: usize,
203    /// Read buffer size.
204    pub read_buffer_size: usize,
205    /// HTTP parse limits.
206    pub parse_limits: ParseLimits,
207    /// Allowed hostnames for Host header validation (empty = allow all).
208    pub allowed_hosts: Vec<String>,
209    /// Whether to trust X-Forwarded-Host for host validation.
210    pub trust_x_forwarded_host: bool,
211    /// Enable TCP_NODELAY.
212    pub tcp_nodelay: bool,
213    /// Keep-alive timeout (time to wait for next request on a connection).
214    /// Set to 0 to disable keep-alive timeout.
215    pub keep_alive_timeout: Duration,
216    /// Maximum requests per connection (0 = unlimited).
217    pub max_requests_per_connection: usize,
218    /// Drain timeout (time to wait for in-flight requests on shutdown).
219    /// After this timeout, the server stops waiting for lingering connection
220    /// tasks and returns to the caller.
221    pub drain_timeout: Duration,
222    /// Pre-body validation hooks (run after parsing headers but before any body is read).
223    ///
224    /// This is used to gate `Expect: 100-continue` and to reject requests early based on
225    /// headers alone (auth/content-type/content-length/etc).
226    pub pre_body_validators: PreBodyValidators,
227}
228
229impl ServerConfig {
230    /// Creates a new server configuration with the given bind address.
231    #[must_use]
232    pub fn new(bind_addr: impl Into<String>) -> Self {
233        Self {
234            bind_addr: bind_addr.into(),
235            request_timeout: Time::from_secs(DEFAULT_REQUEST_TIMEOUT_SECS),
236            max_connections: DEFAULT_MAX_CONNECTIONS,
237            read_buffer_size: DEFAULT_READ_BUFFER_SIZE,
238            parse_limits: ParseLimits::default(),
239            allowed_hosts: Vec::new(),
240            trust_x_forwarded_host: false,
241            tcp_nodelay: true,
242            keep_alive_timeout: Duration::from_secs(DEFAULT_KEEP_ALIVE_TIMEOUT_SECS),
243            max_requests_per_connection: DEFAULT_MAX_REQUESTS_PER_CONNECTION,
244            drain_timeout: Duration::from_secs(DEFAULT_DRAIN_TIMEOUT_SECS),
245            pre_body_validators: PreBodyValidators::new(),
246        }
247    }
248
249    /// Sets the request timeout.
250    #[must_use]
251    pub fn with_request_timeout(mut self, timeout: Time) -> Self {
252        self.request_timeout = timeout;
253        self
254    }
255
256    /// Sets the request timeout in seconds.
257    #[must_use]
258    pub fn with_request_timeout_secs(mut self, secs: u64) -> Self {
259        self.request_timeout = Time::from_secs(secs);
260        self
261    }
262
263    /// Sets the maximum number of connections.
264    #[must_use]
265    pub fn with_max_connections(mut self, max: usize) -> Self {
266        self.max_connections = max;
267        self
268    }
269
270    /// Sets the read buffer size.
271    #[must_use]
272    pub fn with_read_buffer_size(mut self, size: usize) -> Self {
273        self.read_buffer_size = size;
274        self
275    }
276
277    /// Sets the HTTP parse limits.
278    #[must_use]
279    pub fn with_parse_limits(mut self, limits: ParseLimits) -> Self {
280        self.parse_limits = limits;
281        self
282    }
283
284    /// Sets allowed hosts for Host header validation.
285    ///
286    /// An empty list means "allow any host".
287    /// Patterns are normalized to lowercase for case-insensitive matching.
288    #[must_use]
289    pub fn with_allowed_hosts<I, S>(mut self, hosts: I) -> Self
290    where
291        I: IntoIterator<Item = S>,
292        S: Into<String>,
293    {
294        // Pre-lowercase patterns to avoid allocation during matching
295        self.allowed_hosts = hosts
296            .into_iter()
297            .map(|s| s.into().to_ascii_lowercase())
298            .collect();
299        self
300    }
301
302    /// Adds a single allowed host.
303    ///
304    /// The pattern is normalized to lowercase for case-insensitive matching.
305    #[must_use]
306    pub fn allow_host(mut self, host: impl Into<String>) -> Self {
307        // Pre-lowercase pattern to avoid allocation during matching
308        self.allowed_hosts.push(host.into().to_ascii_lowercase());
309        self
310    }
311
312    /// Enables or disables trust of X-Forwarded-Host.
313    #[must_use]
314    pub fn with_trust_x_forwarded_host(mut self, trust: bool) -> Self {
315        self.trust_x_forwarded_host = trust;
316        self
317    }
318
319    /// Enables or disables TCP_NODELAY.
320    #[must_use]
321    pub fn with_tcp_nodelay(mut self, enabled: bool) -> Self {
322        self.tcp_nodelay = enabled;
323        self
324    }
325
326    /// Replace all configured pre-body validators.
327    #[must_use]
328    pub fn with_pre_body_validators(mut self, validators: PreBodyValidators) -> Self {
329        self.pre_body_validators = validators;
330        self
331    }
332
333    /// Add a pre-body validator.
334    #[must_use]
335    pub fn with_pre_body_validator<V: PreBodyValidator + 'static>(mut self, validator: V) -> Self {
336        self.pre_body_validators.add(validator);
337        self
338    }
339
340    /// Sets the keep-alive timeout.
341    ///
342    /// This is the time to wait for another request on a keep-alive connection
343    /// before closing it. Set to Duration::ZERO to disable keep-alive timeout.
344    #[must_use]
345    pub fn with_keep_alive_timeout(mut self, timeout: Duration) -> Self {
346        self.keep_alive_timeout = timeout;
347        self
348    }
349
350    /// Sets the keep-alive timeout in seconds.
351    #[must_use]
352    pub fn with_keep_alive_timeout_secs(mut self, secs: u64) -> Self {
353        self.keep_alive_timeout = Duration::from_secs(secs);
354        self
355    }
356
357    /// Sets the maximum requests per connection.
358    ///
359    /// Set to 0 for unlimited requests per connection.
360    #[must_use]
361    pub fn with_max_requests_per_connection(mut self, max: usize) -> Self {
362        self.max_requests_per_connection = max;
363        self
364    }
365
366    /// Sets the drain timeout.
367    ///
368    /// This is the time to wait for in-flight requests to complete during
369    /// shutdown. After this timeout, the server stops waiting for lingering
370    /// connection tasks and returns.
371    #[must_use]
372    pub fn with_drain_timeout(mut self, timeout: Duration) -> Self {
373        self.drain_timeout = timeout;
374        self
375    }
376
377    /// Sets the drain timeout in seconds.
378    #[must_use]
379    pub fn with_drain_timeout_secs(mut self, secs: u64) -> Self {
380        self.drain_timeout = Duration::from_secs(secs);
381        self
382    }
383}
384
385impl Default for ServerConfig {
386    fn default() -> Self {
387        Self::new("127.0.0.1:8080")
388    }
389}
390
391/// HTTP server error.
392#[derive(Debug)]
393pub enum ServerError {
394    /// IO error.
395    Io(io::Error),
396    /// Parse error.
397    Parse(ParseError),
398    /// HTTP/2 error.
399    Http2(http2::Http2Error),
400    /// Server was shut down.
401    Shutdown,
402    /// Connection limit reached.
403    ConnectionLimitReached,
404    /// Keep-alive timeout expired (idle connection).
405    KeepAliveTimeout,
406}
407
408impl std::fmt::Display for ServerError {
409    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410        match self {
411            Self::Io(e) => write!(f, "IO error: {e}"),
412            Self::Parse(e) => write!(f, "Parse error: {e}"),
413            Self::Http2(e) => write!(f, "HTTP/2 error: {e}"),
414            Self::Shutdown => write!(f, "Server shutdown"),
415            Self::ConnectionLimitReached => write!(f, "Connection limit reached"),
416            Self::KeepAliveTimeout => write!(f, "Keep-alive timeout"),
417        }
418    }
419}
420
421impl std::error::Error for ServerError {
422    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
423        match self {
424            Self::Io(e) => Some(e),
425            Self::Parse(e) => Some(e),
426            Self::Http2(e) => Some(e),
427            _ => None,
428        }
429    }
430}
431
432// ============================================================================
433// Host Header Validation
434// ============================================================================
435
436#[derive(Debug, Clone, PartialEq, Eq)]
437enum HostValidationErrorKind {
438    Missing,
439    Invalid,
440    NotAllowed,
441}
442
443#[derive(Debug, Clone)]
444struct HostValidationError {
445    kind: HostValidationErrorKind,
446    detail: String,
447}
448
449impl HostValidationError {
450    fn missing() -> Self {
451        Self {
452            kind: HostValidationErrorKind::Missing,
453            detail: "missing Host header".to_string(),
454        }
455    }
456
457    fn invalid(detail: impl Into<String>) -> Self {
458        Self {
459            kind: HostValidationErrorKind::Invalid,
460            detail: detail.into(),
461        }
462    }
463
464    fn not_allowed(detail: impl Into<String>) -> Self {
465        Self {
466            kind: HostValidationErrorKind::NotAllowed,
467            detail: detail.into(),
468        }
469    }
470
471    fn response(&self) -> Response {
472        let message = match self.kind {
473            HostValidationErrorKind::Missing => "Bad Request: Host header required",
474            HostValidationErrorKind::Invalid => "Bad Request: invalid Host header",
475            HostValidationErrorKind::NotAllowed => "Bad Request: Host not allowed",
476        };
477        Response::with_status(StatusCode::BAD_REQUEST).body(fastapi_core::ResponseBody::Bytes(
478            message.as_bytes().to_vec(),
479        ))
480    }
481}
482
483#[derive(Debug, Clone, PartialEq, Eq)]
484struct HostHeader {
485    host: String,
486    port: Option<u16>,
487}
488
489fn validate_host_header(
490    request: &Request,
491    config: &ServerConfig,
492) -> Result<HostHeader, HostValidationError> {
493    let raw = extract_effective_host(request, config)?;
494    let parsed = parse_host_header(&raw)
495        .ok_or_else(|| HostValidationError::invalid(format!("invalid host value: {raw}")))?;
496
497    if !is_allowed_host(&parsed, &config.allowed_hosts) {
498        return Err(HostValidationError::not_allowed(format!(
499            "host not allowed: {}",
500            parsed.host
501        )));
502    }
503
504    Ok(parsed)
505}
506
507fn extract_effective_host(
508    request: &Request,
509    config: &ServerConfig,
510) -> Result<String, HostValidationError> {
511    if config.trust_x_forwarded_host {
512        if let Some(value) = header_value(request, "x-forwarded-host")? {
513            let forwarded = extract_first_list_value(&value)
514                .ok_or_else(|| HostValidationError::invalid("empty X-Forwarded-Host value"))?;
515            return Ok(forwarded.to_string());
516        }
517    }
518
519    match header_value(request, "host")? {
520        Some(value) => Ok(value),
521        None => Err(HostValidationError::missing()),
522    }
523}
524
525fn header_value(request: &Request, name: &str) -> Result<Option<String>, HostValidationError> {
526    request
527        .headers()
528        .get(name)
529        .map(|bytes| {
530            std::str::from_utf8(bytes)
531                .map(|s| s.trim().to_string())
532                .map_err(|_| {
533                    HostValidationError::invalid(format!("invalid UTF-8 in {name} header"))
534                })
535        })
536        .transpose()
537}
538
539fn extract_first_list_value(value: &str) -> Option<&str> {
540    value.split(',').map(str::trim).find(|v| !v.is_empty())
541}
542
543fn parse_host_header(value: &str) -> Option<HostHeader> {
544    let value = value.trim();
545    if value.is_empty() {
546        return None;
547    }
548    if value.chars().any(|c| c.is_control() || c.is_whitespace()) {
549        return None;
550    }
551
552    if value.starts_with('[') {
553        let end = value.find(']')?;
554        let host = &value[1..end];
555        if host.is_empty() {
556            return None;
557        }
558        if host.parse::<Ipv6Addr>().is_err() {
559            return None;
560        }
561        let rest = &value[end + 1..];
562        let port = if rest.is_empty() {
563            None
564        } else {
565            let port_str = rest.strip_prefix(':')?;
566            parse_port(port_str)
567        };
568        return Some(HostHeader {
569            host: host.to_ascii_lowercase(),
570            port,
571        });
572    }
573
574    let mut parts = value.split(':');
575    let host = parts.next().unwrap_or("");
576    let port_part = parts.next();
577    if parts.next().is_some() {
578        // Multiple colons without brackets (likely IPv6) are invalid
579        return None;
580    }
581    if host.is_empty() {
582        return None;
583    }
584
585    let port = match port_part {
586        Some(p) => parse_port(p),
587        None => None,
588    };
589
590    if host.parse::<Ipv4Addr>().is_ok() || is_valid_hostname(host) {
591        Some(HostHeader {
592            host: host.to_ascii_lowercase(),
593            port,
594        })
595    } else {
596        None
597    }
598}
599
600fn parse_port(port: &str) -> Option<u16> {
601    if port.is_empty() || !port.chars().all(|c| c.is_ascii_digit()) {
602        return None;
603    }
604    let value = port.parse::<u16>().ok()?;
605    if value == 0 { None } else { Some(value) }
606}
607
608fn is_valid_hostname(host: &str) -> bool {
609    // Note: str::len() returns byte length (RFC 1035 specifies 253 octets)
610    if host.len() > 253 {
611        return false;
612    }
613    for label in host.split('.') {
614        if label.is_empty() || label.len() > 63 {
615            return false;
616        }
617        let bytes = label.as_bytes();
618        if bytes.first() == Some(&b'-') || bytes.last() == Some(&b'-') {
619            return false;
620        }
621        if !label.chars().all(|c| c.is_ascii_alphanumeric() || c == '-') {
622            return false;
623        }
624    }
625    true
626}
627
628fn is_allowed_host(host: &HostHeader, allowed_hosts: &[String]) -> bool {
629    if allowed_hosts.is_empty() {
630        return true;
631    }
632
633    allowed_hosts
634        .iter()
635        .any(|pattern| host_matches_pattern(host, pattern))
636}
637
638fn host_matches_pattern(host: &HostHeader, pattern: &str) -> bool {
639    // Note: patterns are pre-lowercased at config time, so no allocation needed here
640    let pattern = pattern.trim();
641    if pattern.is_empty() {
642        return false;
643    }
644    if pattern == "*" {
645        return true;
646    }
647    if let Some(suffix) = pattern.strip_prefix("*.") {
648        // suffix is already lowercase (pre-processed at config time)
649        if host.host == suffix {
650            return false;
651        }
652        return host.host.len() > suffix.len() + 1
653            && host.host.ends_with(suffix)
654            && host.host.as_bytes()[host.host.len() - suffix.len() - 1] == b'.';
655    }
656
657    if let Some(parsed) = parse_host_header(pattern) {
658        if parsed.host != host.host {
659            return false;
660        }
661        if let Some(port) = parsed.port {
662            return host.port == Some(port);
663        }
664        return true;
665    }
666
667    false
668}
669
670fn header_str<'a>(req: &'a Request, name: &str) -> Option<&'a str> {
671    req.headers()
672        .get(name)
673        .and_then(|v| std::str::from_utf8(v).ok())
674        .map(str::trim)
675}
676
677fn header_has_token(req: &Request, name: &str, token: &str) -> bool {
678    let Some(v) = header_str(req, name) else {
679        return false;
680    };
681    v.split(',')
682        .map(str::trim)
683        .any(|t| t.eq_ignore_ascii_case(token))
684}
685
686fn connection_has_token(req: &Request, token: &str) -> bool {
687    header_has_token(req, "connection", token)
688}
689
690fn is_websocket_upgrade_request(req: &Request) -> bool {
691    if req.method() != Method::Get {
692        return false;
693    }
694    if !header_has_token(req, "upgrade", "websocket") {
695        return false;
696    }
697    connection_has_token(req, "upgrade")
698}
699
700fn has_request_body_headers(req: &Request) -> bool {
701    if req.headers().contains("transfer-encoding") {
702        return true;
703    }
704    if let Some(v) = header_str(req, "content-length") {
705        if v.is_empty() {
706            return true;
707        }
708        match v.parse::<usize>() {
709            Ok(0) => false,
710            Ok(_) => true,
711            Err(_) => true,
712        }
713    } else {
714        false
715    }
716}
717
718impl From<io::Error> for ServerError {
719    fn from(e: io::Error) -> Self {
720        Self::Io(e)
721    }
722}
723
724impl From<ParseError> for ServerError {
725    fn from(e: ParseError) -> Self {
726        Self::Parse(e)
727    }
728}
729
730impl From<http2::Http2Error> for ServerError {
731    fn from(e: http2::Http2Error) -> Self {
732        Self::Http2(e)
733    }
734}
735
736/// Processes a connection with the given handler.
737///
738/// This is the unified connection handling logic used by all server modes.
739/// It runs the parse-dispatch-write loop for HTTP/1.1 (and delegates to the
740/// internal HTTP/2 handler when h2c prior-knowledge is detected).
741///
742/// The function is public so that embedders can build custom accept loops
743/// while reusing the core per-connection logic.
744pub async fn process_connection<H, Fut>(
745    cx: &Cx,
746    request_counter: &AtomicU64,
747    mut stream: TcpStream,
748    _peer_addr: SocketAddr,
749    config: &ServerConfig,
750    handler: H,
751) -> Result<(), ServerError>
752where
753    H: Fn(RequestContext, &mut Request) -> Fut,
754    Fut: Future<Output = Response>,
755{
756    let (proto, buffered) = sniff_protocol(&mut stream, config.keep_alive_timeout).await?;
757    if proto == SniffedProtocol::Http2PriorKnowledge {
758        return process_connection_http2(cx, request_counter, stream, config, handler).await;
759    }
760
761    let mut parser = StatefulParser::new().with_limits(config.parse_limits.clone());
762    if !buffered.is_empty() {
763        parser.feed(&buffered)?;
764    }
765    let mut read_buffer = vec![0u8; config.read_buffer_size];
766    let mut response_writer = ResponseWriter::new();
767    let mut requests_on_connection: usize = 0;
768    let max_requests = config.max_requests_per_connection;
769
770    loop {
771        // Check for cancellation
772        if cx.is_cancel_requested() {
773            return Ok(());
774        }
775
776        // Try to parse a complete request from buffered data first
777        let parse_result = parser.feed(&[])?;
778
779        let mut request = match parse_result {
780            ParseStatus::Complete { request, .. } => request,
781            ParseStatus::Incomplete => {
782                let keep_alive_timeout = config.keep_alive_timeout;
783
784                let bytes_read = if keep_alive_timeout.is_zero() {
785                    read_into_buffer(&mut stream, &mut read_buffer).await?
786                } else {
787                    match read_with_timeout(&mut stream, &mut read_buffer, keep_alive_timeout).await
788                    {
789                        Ok(0) => return Ok(()),
790                        Ok(n) => n,
791                        Err(e) if e.kind() == io::ErrorKind::TimedOut => {
792                            cx.trace(&format!(
793                                "Keep-alive timeout ({:?}) - closing idle connection",
794                                keep_alive_timeout
795                            ));
796                            return Err(ServerError::KeepAliveTimeout);
797                        }
798                        Err(e) => return Err(ServerError::Io(e)),
799                    }
800                };
801
802                if bytes_read == 0 {
803                    return Ok(());
804                }
805
806                match parser.feed(&read_buffer[..bytes_read])? {
807                    ParseStatus::Complete { request, .. } => request,
808                    ParseStatus::Incomplete => continue,
809                }
810            }
811        };
812
813        requests_on_connection += 1;
814
815        // Generate unique request ID for this request with timeout budget
816        let request_id = request_counter.fetch_add(1, Ordering::Relaxed);
817        let deadline = request_deadline_at(cx.now(), config.request_timeout);
818        let request_budget = Budget::new().with_deadline(deadline);
819        let request_cx = request_cx_from_parent(cx, request_budget);
820        let ctx = RequestContext::new(request_cx, request_id).with_deadline(deadline);
821
822        // Validate Host header
823        if let Err(err) = validate_host_header(&request, config) {
824            ctx.trace(&format!("Rejecting request: {}", err.detail));
825            let response = err.response().header("connection", b"close".to_vec());
826            let response_write = response_writer.write(response);
827            write_response(&mut stream, response_write).await?;
828            return Ok(());
829        }
830
831        // Run header-only validators before honoring Expect: 100-continue or reading any body bytes.
832        if let Err(response) = config.pre_body_validators.validate_all(&request) {
833            let response = response.header("connection", b"close".to_vec());
834            let response_write = response_writer.write(response);
835            write_response(&mut stream, response_write).await?;
836            return Ok(());
837        }
838
839        // Handle Expect: 100-continue
840        // RFC 7231 Section 5.1.1: If the server receives a request with Expect: 100-continue,
841        // it should either send 100 Continue (to proceed) or a final status code (to reject).
842        match ExpectHandler::check_expect(&request) {
843            ExpectResult::NoExpectation => {
844                // No Expect header - proceed normally
845            }
846            ExpectResult::ExpectsContinue => {
847                // Expect: 100-continue present
848                // Send 100 Continue to tell client to proceed with body
849                ctx.trace("Sending 100 Continue for Expect: 100-continue");
850                write_raw_response(&mut stream, CONTINUE_RESPONSE).await?;
851            }
852            ExpectResult::UnknownExpectation(value) => {
853                // Unknown expectation - return 417 Expectation Failed
854                ctx.trace(&format!("Rejecting unknown Expect value: {}", value));
855                let response =
856                    ExpectHandler::expectation_failed(format!("Unsupported Expect value: {value}"));
857                let response_write = response_writer.write(response);
858                write_response(&mut stream, response_write).await?;
859                return Ok(());
860            }
861        }
862
863        let client_wants_keep_alive = should_keep_alive(&request);
864        let at_max_requests = max_requests > 0 && requests_on_connection >= max_requests;
865        let mut server_will_keep_alive = client_wants_keep_alive && !at_max_requests;
866
867        // Race the handler (including its middleware chain) against the request
868        // deadline. Losing the race drops the handler future, so no late
869        // response can be produced, published by middleware, or observed
870        // anywhere after the client has been told 504.
871        // Losing the race drops the handler future, which is how cancellation
872        // is delivered; the request Cx must NOT be cancel-marked here because
873        // it shares cancel state with this connection's Cx, and the 504 still
874        // has to be written on this connection.
875        let mut response = match timeout_at(deadline, handler(ctx, &mut request)).await {
876            Ok(response) => response,
877            Err(_elapsed) => {
878                // The abandoned handler may not have consumed the request
879                // body, so the connection cannot be reused safely.
880                server_will_keep_alive = false;
881                Response::with_status(StatusCode::GATEWAY_TIMEOUT).body(
882                    fastapi_core::ResponseBody::Bytes(
883                        b"Gateway Timeout: request processing exceeded time limit".to_vec(),
884                    ),
885                )
886            }
887        };
888
889        response = if server_will_keep_alive {
890            response.header("connection", b"keep-alive".to_vec())
891        } else {
892            response.header("connection", b"close".to_vec())
893        };
894
895        let response_write = response_writer.write(response);
896        write_response(&mut stream, response_write).await?;
897
898        if let Some(tasks) = App::take_background_tasks(&mut request) {
899            tasks.execute_all().await;
900        }
901
902        if !server_will_keep_alive {
903            return Ok(());
904        }
905    }
906}
907
908async fn process_connection_http2<H, Fut>(
909    cx: &Cx,
910    request_counter: &AtomicU64,
911    stream: TcpStream,
912    config: &ServerConfig,
913    handler: H,
914) -> Result<(), ServerError>
915where
916    H: Fn(RequestContext, &mut Request) -> Fut,
917    Fut: Future<Output = Response>,
918{
919    const FLAG_END_HEADERS: u8 = 0x4;
920    const FLAG_ACK: u8 = 0x1;
921
922    let mut framed = http2::FramedH2::new(stream, Vec::new());
923    let mut hpack = http2::HpackDecoder::new();
924    let recv_max_frame_size: u32 = 16 * 1024;
925    let mut peer_max_frame_size: u32 = 16 * 1024;
926    let mut flow_control = http2::H2FlowControl::new();
927
928    let first = framed.read_frame(recv_max_frame_size).await?;
929    if first.header.frame_type() != http2::FrameType::Settings
930        || first.header.stream_id != 0
931        || (first.header.flags & FLAG_ACK) != 0
932    {
933        return Err(http2::Http2Error::Protocol("expected client SETTINGS after preface").into());
934    }
935    apply_http2_settings_with_fc(
936        &mut hpack,
937        &mut peer_max_frame_size,
938        Some(&mut flow_control),
939        &first.payload,
940    )?;
941
942    framed
943        .write_frame(http2::FrameType::Settings, 0, 0, SERVER_SETTINGS_PAYLOAD)
944        .await?;
945    framed
946        .write_frame(http2::FrameType::Settings, FLAG_ACK, 0, &[])
947        .await?;
948
949    let default_body_limit = config.parse_limits.max_request_size;
950    let mut last_stream_id: u32 = 0;
951
952    loop {
953        if cx.is_cancel_requested() {
954            let _ = send_goaway(&mut framed, last_stream_id, h2_error_code::NO_ERROR).await;
955            return Ok(());
956        }
957
958        let frame = framed.read_frame(recv_max_frame_size).await?;
959        match frame.header.frame_type() {
960            http2::FrameType::Settings => {
961                let is_ack = validate_settings_frame(
962                    frame.header.stream_id,
963                    frame.header.flags,
964                    &frame.payload,
965                )?;
966                if is_ack {
967                    continue;
968                }
969                apply_http2_settings_with_fc(
970                    &mut hpack,
971                    &mut peer_max_frame_size,
972                    Some(&mut flow_control),
973                    &frame.payload,
974                )?;
975                framed
976                    .write_frame(http2::FrameType::Settings, FLAG_ACK, 0, &[])
977                    .await?;
978            }
979            http2::FrameType::Ping => {
980                if frame.header.stream_id != 0 || frame.payload.len() != 8 {
981                    return Err(http2::Http2Error::Protocol("invalid PING frame").into());
982                }
983                if (frame.header.flags & FLAG_ACK) == 0 {
984                    framed
985                        .write_frame(http2::FrameType::Ping, FLAG_ACK, 0, &frame.payload)
986                        .await?;
987                }
988            }
989            http2::FrameType::Goaway => {
990                validate_goaway_payload(&frame.payload)?;
991                return Ok(());
992            }
993            http2::FrameType::PushPromise => {
994                return Err(
995                    http2::Http2Error::Protocol("PUSH_PROMISE not supported by server").into(),
996                );
997            }
998            http2::FrameType::Headers => {
999                let stream_id = frame.header.stream_id;
1000                if stream_id == 0 {
1001                    return Err(
1002                        http2::Http2Error::Protocol("HEADERS must not be on stream 0").into(),
1003                    );
1004                }
1005                if stream_id % 2 == 0 {
1006                    return Err(http2::Http2Error::Protocol(
1007                        "client-initiated stream ID must be odd",
1008                    )
1009                    .into());
1010                }
1011                if stream_id <= last_stream_id {
1012                    return Err(http2::Http2Error::Protocol(
1013                        "stream ID must be greater than previous",
1014                    )
1015                    .into());
1016                }
1017                last_stream_id = stream_id;
1018                let (end_stream, mut header_block) =
1019                    extract_header_block_fragment(frame.header.flags, &frame.payload)?;
1020
1021                if (frame.header.flags & FLAG_END_HEADERS) == 0 {
1022                    loop {
1023                        let cont = framed.read_frame(recv_max_frame_size).await?;
1024                        if cont.header.frame_type() != http2::FrameType::Continuation
1025                            || cont.header.stream_id != stream_id
1026                        {
1027                            return Err(http2::Http2Error::Protocol(
1028                                "expected CONTINUATION for header block",
1029                            )
1030                            .into());
1031                        }
1032                        header_block.extend_from_slice(&cont.payload);
1033                        if header_block.len() > MAX_HEADER_BLOCK_SIZE {
1034                            return Err(http2::Http2Error::Protocol(
1035                                "header block exceeds maximum size",
1036                            )
1037                            .into());
1038                        }
1039                        if (cont.header.flags & FLAG_END_HEADERS) != 0 {
1040                            break;
1041                        }
1042                    }
1043                }
1044
1045                let headers = hpack
1046                    .decode(&header_block)
1047                    .map_err(http2::Http2Error::from)?;
1048                let mut request = request_from_h2_headers(headers)?;
1049
1050                if !end_stream {
1051                    let mut body = Vec::new();
1052                    let mut stream_reset = false;
1053                    let mut stream_received: u32 = 0;
1054                    loop {
1055                        let f = framed.read_frame(recv_max_frame_size).await?;
1056                        match f.header.frame_type() {
1057                            http2::FrameType::Data if f.header.stream_id == 0 => {
1058                                return Err(http2::Http2Error::Protocol(
1059                                    "DATA must not be on stream 0",
1060                                )
1061                                .into());
1062                            }
1063                            http2::FrameType::Data if f.header.stream_id == stream_id => {
1064                                let (data, data_end_stream) =
1065                                    extract_data_payload(f.header.flags, &f.payload)?;
1066                                if body.len().saturating_add(data.len()) > default_body_limit {
1067                                    return Err(http2::Http2Error::Protocol(
1068                                        "request body exceeds configured limit",
1069                                    )
1070                                    .into());
1071                                }
1072                                body.extend_from_slice(data);
1073
1074                                // Flow control: track received data and send
1075                                // WINDOW_UPDATEs to prevent sender stalling.
1076                                let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
1077                                stream_received += data_len;
1078                                let conn_inc = flow_control.data_received_connection(data_len);
1079                                let stream_inc = flow_control.stream_window_update(stream_received);
1080                                if stream_inc > 0 {
1081                                    stream_received = 0;
1082                                }
1083                                send_window_updates(&mut framed, conn_inc, stream_id, stream_inc)
1084                                    .await?;
1085
1086                                if data_end_stream {
1087                                    break;
1088                                }
1089                            }
1090                            http2::FrameType::RstStream => {
1091                                validate_rst_stream_payload(f.header.stream_id, &f.payload)?;
1092                                if f.header.stream_id == stream_id {
1093                                    stream_reset = true;
1094                                    break;
1095                                }
1096                            }
1097                            http2::FrameType::PushPromise => {
1098                                return Err(http2::Http2Error::Protocol(
1099                                    "PUSH_PROMISE not supported by server",
1100                                )
1101                                .into());
1102                            }
1103                            http2::FrameType::Settings
1104                            | http2::FrameType::Ping
1105                            | http2::FrameType::Goaway
1106                            | http2::FrameType::WindowUpdate
1107                            | http2::FrameType::Priority
1108                            | http2::FrameType::Unknown => {
1109                                if f.header.frame_type() == http2::FrameType::Goaway {
1110                                    validate_goaway_payload(&f.payload)?;
1111                                    return Ok(());
1112                                }
1113                                if f.header.frame_type() == http2::FrameType::Priority {
1114                                    validate_priority_payload(f.header.stream_id, &f.payload)?;
1115                                }
1116                                if f.header.frame_type() == http2::FrameType::WindowUpdate {
1117                                    validate_window_update_payload(&f.payload)?;
1118                                    let increment = u32::from_be_bytes([
1119                                        f.payload[0],
1120                                        f.payload[1],
1121                                        f.payload[2],
1122                                        f.payload[3],
1123                                    ]) & 0x7FFF_FFFF;
1124                                    if f.header.stream_id == 0 {
1125                                        apply_send_conn_window_update(
1126                                            &mut flow_control,
1127                                            increment,
1128                                        )?;
1129                                    }
1130                                }
1131                                if f.header.frame_type() == http2::FrameType::Ping {
1132                                    if f.header.stream_id != 0 || f.payload.len() != 8 {
1133                                        return Err(http2::Http2Error::Protocol(
1134                                            "invalid PING frame",
1135                                        )
1136                                        .into());
1137                                    }
1138                                    if (f.header.flags & FLAG_ACK) == 0 {
1139                                        framed
1140                                            .write_frame(
1141                                                http2::FrameType::Ping,
1142                                                FLAG_ACK,
1143                                                0,
1144                                                &f.payload,
1145                                            )
1146                                            .await?;
1147                                    }
1148                                }
1149                                if f.header.frame_type() == http2::FrameType::Settings {
1150                                    let is_ack = validate_settings_frame(
1151                                        f.header.stream_id,
1152                                        f.header.flags,
1153                                        &f.payload,
1154                                    )?;
1155                                    if !is_ack {
1156                                        apply_http2_settings_with_fc(
1157                                            &mut hpack,
1158                                            &mut peer_max_frame_size,
1159                                            Some(&mut flow_control),
1160                                            &f.payload,
1161                                        )?;
1162                                        framed
1163                                            .write_frame(
1164                                                http2::FrameType::Settings,
1165                                                FLAG_ACK,
1166                                                0,
1167                                                &[],
1168                                            )
1169                                            .await?;
1170                                    }
1171                                }
1172                            }
1173                            _ => {
1174                                return Err(http2::Http2Error::Protocol(
1175                                    "unsupported frame while reading request body",
1176                                )
1177                                .into());
1178                            }
1179                        }
1180                    }
1181                    if stream_reset {
1182                        continue;
1183                    }
1184                    request.set_body(fastapi_core::Body::Bytes(body));
1185                }
1186
1187                let request_id = request_counter.fetch_add(1, Ordering::Relaxed);
1188                let request_budget =
1189                    Budget::new().with_deadline(request_deadline(config.request_timeout));
1190                let request_cx = request_cx_from_parent(cx, request_budget);
1191                let ctx = RequestContext::new(request_cx, request_id);
1192
1193                if let Err(err) = validate_host_header(&request, config) {
1194                    let response = err.response();
1195                    process_connection_http2_write_response(
1196                        &mut framed,
1197                        response,
1198                        stream_id,
1199                        peer_max_frame_size,
1200                        recv_max_frame_size,
1201                        Some(&mut flow_control),
1202                    )
1203                    .await?;
1204                    continue;
1205                }
1206
1207                if let Err(response) = config.pre_body_validators.validate_all(&request) {
1208                    process_connection_http2_write_response(
1209                        &mut framed,
1210                        response,
1211                        stream_id,
1212                        peer_max_frame_size,
1213                        recv_max_frame_size,
1214                        Some(&mut flow_control),
1215                    )
1216                    .await?;
1217                    continue;
1218                }
1219
1220                let response = handler(ctx, &mut request).await;
1221                process_connection_http2_write_response(
1222                    &mut framed,
1223                    response,
1224                    stream_id,
1225                    peer_max_frame_size,
1226                    recv_max_frame_size,
1227                    Some(&mut flow_control),
1228                )
1229                .await?;
1230
1231                if let Some(tasks) = App::take_background_tasks(&mut request) {
1232                    tasks.execute_all().await;
1233                }
1234            }
1235            http2::FrameType::WindowUpdate => {
1236                validate_window_update_payload(&frame.payload)?;
1237                let increment = u32::from_be_bytes([
1238                    frame.payload[0],
1239                    frame.payload[1],
1240                    frame.payload[2],
1241                    frame.payload[3],
1242                ]) & 0x7FFF_FFFF;
1243                if frame.header.stream_id == 0 {
1244                    apply_send_conn_window_update(&mut flow_control, increment)?;
1245                }
1246            }
1247            _ => {
1248                handle_h2_idle_frame(&frame)?;
1249            }
1250        }
1251    }
1252}
1253
1254async fn process_connection_http2_write_response(
1255    framed: &mut http2::FramedH2,
1256    response: Response,
1257    stream_id: u32,
1258    mut peer_max_frame_size: u32,
1259    recv_max_frame_size: u32,
1260    mut flow_control: Option<&mut http2::H2FlowControl>,
1261) -> Result<(), ServerError> {
1262    use std::future::poll_fn;
1263
1264    const FLAG_END_STREAM: u8 = 0x1;
1265    const FLAG_END_HEADERS: u8 = 0x4;
1266
1267    let (status, mut headers, mut body) = response.into_parts();
1268    if !status.allows_body() {
1269        body = fastapi_core::ResponseBody::Empty;
1270    }
1271
1272    let mut add_content_length = matches!(body, fastapi_core::ResponseBody::Bytes(_));
1273    for (name, _) in &headers {
1274        if name.eq_ignore_ascii_case("content-length") {
1275            add_content_length = false;
1276            break;
1277        }
1278    }
1279    if add_content_length {
1280        headers.push((
1281            "content-length".to_string(),
1282            body.len().to_string().into_bytes(),
1283        ));
1284    }
1285
1286    let mut block: Vec<u8> = Vec::new();
1287    let status_bytes = status.as_u16().to_string().into_bytes();
1288    http2::hpack_encode_literal_without_indexing(&mut block, b":status", &status_bytes);
1289    for (name, value) in &headers {
1290        if is_h2_forbidden_header_name(name) {
1291            continue;
1292        }
1293        let n = name.to_ascii_lowercase();
1294        http2::hpack_encode_literal_without_indexing(&mut block, n.as_bytes(), value);
1295    }
1296
1297    let max = usize::try_from(peer_max_frame_size).unwrap_or(16 * 1024);
1298    let mut headers_flags = FLAG_END_HEADERS;
1299    if body.is_empty() {
1300        headers_flags |= FLAG_END_STREAM;
1301    }
1302
1303    if block.len() <= max {
1304        framed
1305            .write_frame(http2::FrameType::Headers, headers_flags, stream_id, &block)
1306            .await?;
1307    } else {
1308        // Split into HEADERS + CONTINUATION.
1309        let mut first_flags = 0u8;
1310        if body.is_empty() {
1311            first_flags |= FLAG_END_STREAM;
1312        }
1313        let (first, rest) = block.split_at(max);
1314        framed
1315            .write_frame(http2::FrameType::Headers, first_flags, stream_id, first)
1316            .await?;
1317        let mut remaining = rest;
1318        while remaining.len() > max {
1319            let (chunk, r) = remaining.split_at(max);
1320            framed
1321                .write_frame(http2::FrameType::Continuation, 0, stream_id, chunk)
1322                .await?;
1323            remaining = r;
1324        }
1325        framed
1326            .write_frame(
1327                http2::FrameType::Continuation,
1328                FLAG_END_HEADERS,
1329                stream_id,
1330                remaining,
1331            )
1332            .await?;
1333    }
1334
1335    // Track per-stream send window (peer's receive window for this stream).
1336    let mut stream_send_window: i64 = flow_control
1337        .as_ref()
1338        .map_or(i64::MAX, |fc| i64::from(fc.peer_initial_window_size()));
1339
1340    match body {
1341        fastapi_core::ResponseBody::Empty => Ok(()),
1342        fastapi_core::ResponseBody::Bytes(bytes) => {
1343            if bytes.is_empty() {
1344                return Ok(());
1345            }
1346            let mut remaining = bytes.as_slice();
1347            while !remaining.is_empty() {
1348                let max = usize::try_from(peer_max_frame_size).unwrap_or(16 * 1024);
1349                let send_len = remaining.len().min(max);
1350
1351                let send_len = h2_fc_clamp_send(
1352                    framed,
1353                    &mut flow_control,
1354                    &mut stream_send_window,
1355                    stream_id,
1356                    send_len,
1357                    &mut peer_max_frame_size,
1358                    recv_max_frame_size,
1359                )
1360                .await?;
1361
1362                let (chunk, r) = remaining.split_at(send_len);
1363                let flags = if r.is_empty() { FLAG_END_STREAM } else { 0 };
1364                framed
1365                    .write_frame(http2::FrameType::Data, flags, stream_id, chunk)
1366                    .await?;
1367                remaining = r;
1368            }
1369            Ok(())
1370        }
1371        fastapi_core::ResponseBody::Stream(mut s) => {
1372            loop {
1373                let next = poll_fn(|cx| Pin::new(&mut s).poll_next(cx)).await;
1374                match next {
1375                    Some(chunk) => {
1376                        let mut remaining = chunk.as_slice();
1377                        while !remaining.is_empty() {
1378                            let max = usize::try_from(peer_max_frame_size).unwrap_or(16 * 1024);
1379                            let send_len = remaining.len().min(max);
1380                            let send_len = h2_fc_clamp_send(
1381                                framed,
1382                                &mut flow_control,
1383                                &mut stream_send_window,
1384                                stream_id,
1385                                send_len,
1386                                &mut peer_max_frame_size,
1387                                recv_max_frame_size,
1388                            )
1389                            .await?;
1390
1391                            let (c, r) = remaining.split_at(send_len);
1392                            framed
1393                                .write_frame(http2::FrameType::Data, 0, stream_id, c)
1394                                .await?;
1395                            remaining = r;
1396                        }
1397                    }
1398                    None => {
1399                        framed
1400                            .write_frame(http2::FrameType::Data, FLAG_END_STREAM, stream_id, &[])
1401                            .await?;
1402                        break;
1403                    }
1404                }
1405            }
1406            Ok(())
1407        }
1408    }
1409}
1410
1411/// Clamp `desired` bytes against the send-side flow control windows. If windows
1412/// are exhausted, reads frames from the peer (draining WINDOW_UPDATEs, handling
1413/// PING/SETTINGS) until enough window is available. Returns the number of bytes
1414/// that can be sent now (always > 0 on success).
1415async fn h2_fc_clamp_send(
1416    framed: &mut http2::FramedH2,
1417    flow_control: &mut Option<&mut http2::H2FlowControl>,
1418    stream_send_window: &mut i64,
1419    stream_id: u32,
1420    desired: usize,
1421    peer_max_frame_size: &mut u32,
1422    recv_max_frame_size: u32,
1423) -> Result<usize, ServerError> {
1424    let fc = match flow_control.as_mut() {
1425        Some(fc) => fc,
1426        None => return Ok(desired),
1427    };
1428
1429    loop {
1430        let conn_avail = usize::try_from(fc.send_conn_window().max(0)).unwrap_or(0);
1431        let stream_avail = usize::try_from((*stream_send_window).max(0)).unwrap_or(0);
1432        let peer_max = usize::try_from(*peer_max_frame_size).unwrap_or(16 * 1024);
1433        let allowed = desired.min(conn_avail).min(stream_avail).min(peer_max);
1434
1435        if allowed > 0 {
1436            let send = allowed;
1437            fc.consume_send_conn_window(u32::try_from(send).unwrap_or(u32::MAX));
1438            *stream_send_window -= i64::try_from(send).unwrap_or(i64::MAX);
1439            return Ok(send);
1440        }
1441
1442        // Window exhausted -- read peer frames until we get a WINDOW_UPDATE.
1443        let frame = framed.read_frame(recv_max_frame_size).await?;
1444        match frame.header.frame_type() {
1445            http2::FrameType::WindowUpdate => {
1446                apply_peer_window_update_for_send(
1447                    fc,
1448                    stream_send_window,
1449                    stream_id,
1450                    frame.header.stream_id,
1451                    &frame.payload,
1452                )?;
1453            }
1454            http2::FrameType::Ping => {
1455                if frame.header.stream_id != 0 || frame.payload.len() != 8 {
1456                    return Err(ServerError::Http2(http2::Http2Error::Protocol(
1457                        "invalid PING frame",
1458                    )));
1459                }
1460                if frame.header.flags & 0x1 == 0 {
1461                    framed
1462                        .write_frame(http2::FrameType::Ping, 0x1, 0, &frame.payload)
1463                        .await?;
1464                }
1465            }
1466            http2::FrameType::Settings => {
1467                let is_ack = validate_settings_frame(
1468                    frame.header.stream_id,
1469                    frame.header.flags,
1470                    &frame.payload,
1471                )?;
1472                if !is_ack {
1473                    apply_peer_settings_for_send(
1474                        fc,
1475                        stream_send_window,
1476                        peer_max_frame_size,
1477                        &frame.payload,
1478                    )?;
1479                    // ACK the peer's SETTINGS.
1480                    framed
1481                        .write_frame(http2::FrameType::Settings, 0x1, 0, &[])
1482                        .await?;
1483                }
1484            }
1485            http2::FrameType::Goaway => {
1486                validate_goaway_payload(&frame.payload)?;
1487                return Err(ServerError::Http2(http2::Http2Error::Protocol(
1488                    "received GOAWAY while writing response",
1489                )));
1490            }
1491            http2::FrameType::RstStream => {
1492                validate_rst_stream_payload(frame.header.stream_id, &frame.payload)?;
1493                if frame.header.stream_id == stream_id {
1494                    return Err(ServerError::Http2(http2::Http2Error::Protocol(
1495                        "stream reset by peer during response",
1496                    )));
1497                }
1498            }
1499            _ => { /* ignore unknown/irrelevant frames */ }
1500        }
1501    }
1502}
1503
1504/// TCP server with asupersync integration.
1505///
1506/// This server manages the lifecycle of connections and requests using
1507/// asupersync's structured concurrency primitives. Each connection runs
1508/// in its own region, and each request gets its own task with a budget.
1509pub struct TcpServer {
1510    config: ServerConfig,
1511    request_counter: Arc<AtomicU64>,
1512    /// Current number of active connections (wrapped in Arc for concurrent feature).
1513    connection_counter: Arc<AtomicU64>,
1514    /// Whether the server is draining (shutting down gracefully).
1515    draining: Arc<AtomicBool>,
1516    /// Handles to spawned connection tasks for graceful shutdown.
1517    connection_handles: Mutex<Vec<JoinHandle<()>>>,
1518    /// Shutdown controller for coordinated graceful shutdown.
1519    shutdown_controller: Arc<ShutdownController>,
1520    /// Connection pool metrics counters.
1521    metrics_counters: Arc<MetricsCounters>,
1522}
1523
1524struct ConnectionSlotGuard {
1525    counter: Arc<AtomicU64>,
1526}
1527
1528impl ConnectionSlotGuard {
1529    fn new(counter: Arc<AtomicU64>) -> Self {
1530        Self { counter }
1531    }
1532}
1533
1534impl Drop for ConnectionSlotGuard {
1535    fn drop(&mut self) {
1536        self.counter.fetch_sub(1, Ordering::Relaxed);
1537    }
1538}
1539
1540impl std::fmt::Debug for TcpServer {
1541    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1542        f.debug_struct("TcpServer")
1543            .field("config", &self.config)
1544            .field("request_counter", &self.request_counter)
1545            .field("connection_counter", &self.connection_counter)
1546            .field("draining", &self.draining)
1547            .field(
1548                "connection_handles",
1549                &self.connection_handles.lock().map_or(0, |h| h.len()),
1550            )
1551            .field("shutdown_controller", &self.shutdown_controller)
1552            .field("metrics_counters", &self.metrics_counters)
1553            .finish()
1554    }
1555}
1556
1557impl TcpServer {
1558    /// Creates a new TCP server with the given configuration.
1559    #[must_use]
1560    pub fn new(config: ServerConfig) -> Self {
1561        Self {
1562            config,
1563            request_counter: Arc::new(AtomicU64::new(0)),
1564            connection_counter: Arc::new(AtomicU64::new(0)),
1565            draining: Arc::new(AtomicBool::new(false)),
1566            connection_handles: Mutex::new(Vec::new()),
1567            shutdown_controller: Arc::new(ShutdownController::new()),
1568            metrics_counters: Arc::new(MetricsCounters::new()),
1569        }
1570    }
1571
1572    fn clone_for_connection_task(&self) -> Self {
1573        Self {
1574            config: self.config.clone(),
1575            request_counter: Arc::clone(&self.request_counter),
1576            connection_counter: Arc::clone(&self.connection_counter),
1577            draining: Arc::clone(&self.draining),
1578            connection_handles: Mutex::new(Vec::new()),
1579            shutdown_controller: Arc::clone(&self.shutdown_controller),
1580            metrics_counters: Arc::clone(&self.metrics_counters),
1581        }
1582    }
1583
1584    /// Returns the server configuration.
1585    #[must_use]
1586    pub fn config(&self) -> &ServerConfig {
1587        &self.config
1588    }
1589
1590    /// Generates a unique request ID.
1591    fn next_request_id(&self) -> u64 {
1592        self.request_counter.fetch_add(1, Ordering::Relaxed)
1593    }
1594
1595    /// Returns the current number of active connections.
1596    #[must_use]
1597    pub fn current_connections(&self) -> u64 {
1598        self.connection_counter.load(Ordering::Relaxed)
1599    }
1600
1601    /// Returns a snapshot of the server's connection pool metrics.
1602    #[must_use]
1603    pub fn metrics(&self) -> ServerMetrics {
1604        ServerMetrics {
1605            active_connections: self.connection_counter.load(Ordering::Relaxed),
1606            total_accepted: self.metrics_counters.total_accepted.load(Ordering::Relaxed),
1607            total_rejected: self.metrics_counters.total_rejected.load(Ordering::Relaxed),
1608            total_timed_out: self
1609                .metrics_counters
1610                .total_timed_out
1611                .load(Ordering::Relaxed),
1612            total_requests: self.request_counter.load(Ordering::Relaxed),
1613            bytes_in: self.metrics_counters.bytes_in.load(Ordering::Relaxed),
1614            bytes_out: self.metrics_counters.bytes_out.load(Ordering::Relaxed),
1615        }
1616    }
1617
1618    /// Records bytes read from a client.
1619    fn record_bytes_in(&self, n: u64) {
1620        self.metrics_counters
1621            .bytes_in
1622            .fetch_add(n, Ordering::Relaxed);
1623    }
1624
1625    /// Records bytes written to a client.
1626    fn record_bytes_out(&self, n: u64) {
1627        self.metrics_counters
1628            .bytes_out
1629            .fetch_add(n, Ordering::Relaxed);
1630    }
1631
1632    /// Attempts to acquire a connection slot.
1633    ///
1634    /// Returns true if a slot was acquired, false if the connection limit
1635    /// has been reached. If max_connections is 0 (unlimited), always returns true.
1636    fn try_acquire_connection(&self) -> bool {
1637        let max = self.config.max_connections;
1638        if max == 0 {
1639            // Unlimited connections
1640            self.connection_counter.fetch_add(1, Ordering::Relaxed);
1641            self.metrics_counters
1642                .total_accepted
1643                .fetch_add(1, Ordering::Relaxed);
1644            return true;
1645        }
1646
1647        // Try to increment if under limit
1648        let mut current = self.connection_counter.load(Ordering::Relaxed);
1649        loop {
1650            if current >= max as u64 {
1651                self.metrics_counters
1652                    .total_rejected
1653                    .fetch_add(1, Ordering::Relaxed);
1654                return false;
1655            }
1656            match self.connection_counter.compare_exchange_weak(
1657                current,
1658                current + 1,
1659                Ordering::AcqRel,
1660                Ordering::Relaxed,
1661            ) {
1662                Ok(_) => {
1663                    self.metrics_counters
1664                        .total_accepted
1665                        .fetch_add(1, Ordering::Relaxed);
1666                    return true;
1667                }
1668                Err(actual) => current = actual,
1669            }
1670        }
1671    }
1672
1673    /// Releases a connection slot.
1674    fn release_connection(&self) {
1675        self.connection_counter.fetch_sub(1, Ordering::Relaxed);
1676    }
1677
1678    /// Returns true if the server is draining (shutting down gracefully).
1679    #[must_use]
1680    pub fn is_draining(&self) -> bool {
1681        self.draining.load(Ordering::Acquire)
1682    }
1683
1684    /// Starts the drain process for graceful shutdown.
1685    ///
1686    /// This sets the draining flag, which causes the server to:
1687    /// - Stop accepting new connections
1688    /// - Return 503 to new connection attempts
1689    /// - Allow in-flight requests to complete
1690    pub fn start_drain(&self) {
1691        self.draining.store(true, Ordering::Release);
1692    }
1693
1694    /// Waits for all in-flight connections to drain, with a timeout.
1695    ///
1696    /// Returns `true` if all connections drained successfully,
1697    /// `false` if the timeout was reached with connections still active.
1698    ///
1699    /// # Arguments
1700    ///
1701    /// * `timeout` - Maximum time to wait for connections to drain
1702    /// * `poll_interval` - How often to check connection count (default 10ms)
1703    pub async fn wait_for_drain(&self, timeout: Duration, poll_interval: Option<Duration>) -> bool {
1704        let start = Instant::now();
1705        let poll_interval = poll_interval.unwrap_or(Duration::from_millis(10));
1706
1707        while self.current_connections() > 0 {
1708            if start.elapsed() >= timeout {
1709                return false;
1710            }
1711            // NOTE: We use blocking sleep here intentionally:
1712            // 1. This is only called during graceful shutdown (not a hot path)
1713            // 2. The default poll interval is 10ms (minimal CPU impact)
1714            // 3. During shutdown, blocking briefly is acceptable
1715            // 4. Using async sleep requires threading Time (or Cx) through this API
1716            //
1717            // If this becomes a bottleneck, consider:
1718            // - Using asupersync::runtime::yield_now() in a tighter loop
1719            // - Adding a Cx parameter to access async sleep
1720            std::thread::sleep(poll_interval);
1721        }
1722        true
1723    }
1724
1725    /// Initiates graceful shutdown and waits for connections to drain.
1726    ///
1727    /// This is a convenience method that combines `start_drain()` and
1728    /// `wait_for_drain()` using the configured drain timeout.
1729    ///
1730    /// Returns the number of connections still active after the wait completes
1731    /// (0 if all drained within the timeout).
1732    pub async fn drain(&self) -> u64 {
1733        self.start_drain();
1734        let drained = self.wait_for_drain(self.config.drain_timeout, None).await;
1735        if drained {
1736            0
1737        } else {
1738            self.current_connections()
1739        }
1740    }
1741
1742    /// Returns a reference to the server's shutdown controller.
1743    ///
1744    /// This can be used to coordinate shutdown from external code,
1745    /// such as signal handlers or health check endpoints.
1746    #[must_use]
1747    pub fn shutdown_controller(&self) -> &Arc<ShutdownController> {
1748        &self.shutdown_controller
1749    }
1750
1751    /// Returns a receiver for shutdown notifications.
1752    ///
1753    /// Use this to receive shutdown signals in other parts of your application.
1754    /// Multiple receivers can be created and they will all be notified.
1755    #[must_use]
1756    pub fn subscribe_shutdown(&self) -> ShutdownReceiver {
1757        self.shutdown_controller.subscribe()
1758    }
1759
1760    /// Initiates server shutdown.
1761    ///
1762    /// This triggers the shutdown process:
1763    /// 1. Sets the draining flag to stop accepting new connections
1764    /// 2. Notifies all shutdown receivers
1765    /// 3. The server's accept loop will exit and drain connections
1766    ///
1767    /// This method is safe to call multiple times - subsequent calls are no-ops.
1768    pub fn shutdown(&self) {
1769        self.start_drain();
1770        self.shutdown_controller.shutdown();
1771    }
1772
1773    /// Checks if shutdown has been initiated.
1774    #[must_use]
1775    pub fn is_shutting_down(&self) -> bool {
1776        self.shutdown_controller.is_shutting_down() || self.is_draining()
1777    }
1778
1779    /// Runs the server with graceful shutdown support.
1780    ///
1781    /// The server will run until either:
1782    /// - The provided shutdown receiver signals shutdown
1783    /// - The server Cx is cancelled
1784    /// - An unrecoverable error occurs
1785    ///
1786    /// When shutdown is signaled, the server will:
1787    /// 1. Stop accepting new connections
1788    /// 2. Wait for existing connections to complete (up to drain_timeout)
1789    /// 3. Return gracefully
1790    ///
1791    /// # Example
1792    ///
1793    /// ```ignore
1794    /// use asupersync::signal::ShutdownController;
1795    /// use fastapi_http::{TcpServer, ServerConfig};
1796    ///
1797    /// let controller = ShutdownController::new();
1798    /// let server = TcpServer::new(ServerConfig::new("127.0.0.1:8080"));
1799    ///
1800    /// // Get a shutdown receiver
1801    /// let shutdown = controller.subscribe();
1802    ///
1803    /// // In another task, you can trigger shutdown:
1804    /// // controller.shutdown();
1805    ///
1806    /// server.serve_with_shutdown(&cx, shutdown, handler).await?;
1807    /// ```
1808    pub async fn serve_with_shutdown<H, Fut>(
1809        &self,
1810        cx: &Cx,
1811        mut shutdown: ShutdownReceiver,
1812        handler: H,
1813    ) -> Result<GracefulOutcome<()>, ServerError>
1814    where
1815        H: Fn(RequestContext, &mut Request) -> Fut + Send + Sync + 'static,
1816        Fut: Future<Output = Response> + Send + 'static,
1817    {
1818        let bind_addr = self.config.bind_addr.clone();
1819        let listener = TcpListener::bind(bind_addr).await?;
1820        let local_addr = listener.local_addr()?;
1821
1822        cx.trace(&format!(
1823            "Server listening on {local_addr} (with graceful shutdown)"
1824        ));
1825
1826        // Run the accept loop with shutdown racing
1827        let result = self
1828            .accept_loop_with_shutdown(cx, listener, handler, &mut shutdown)
1829            .await;
1830
1831        match result {
1832            Ok(outcome) => {
1833                if outcome.is_shutdown() {
1834                    cx.trace("Shutdown signal received, draining connections");
1835                    self.start_drain();
1836                    self.drain_connection_tasks(cx).await;
1837                }
1838                Ok(outcome)
1839            }
1840            Err(e) => Err(e),
1841        }
1842    }
1843
1844    /// Accept loop that checks for shutdown signals.
1845    async fn accept_loop_with_shutdown<H, Fut>(
1846        &self,
1847        cx: &Cx,
1848        listener: TcpListener,
1849        handler: H,
1850        shutdown: &mut ShutdownReceiver,
1851    ) -> Result<GracefulOutcome<()>, ServerError>
1852    where
1853        H: Fn(RequestContext, &mut Request) -> Fut + Send + Sync + 'static,
1854        Fut: Future<Output = Response> + Send + 'static,
1855    {
1856        let handler = Arc::new(handler);
1857
1858        loop {
1859            // Check for shutdown or cancellation first
1860            if shutdown.is_shutting_down() {
1861                return Ok(GracefulOutcome::ShutdownSignaled);
1862            }
1863            if cx.is_cancel_requested() || self.is_draining() {
1864                return Ok(GracefulOutcome::ShutdownSignaled);
1865            }
1866
1867            // Accept a connection
1868            let (mut stream, peer_addr) = match listener.accept().await {
1869                Ok(conn) => conn,
1870                Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
1871                    continue;
1872                }
1873                Err(e) => {
1874                    cx.trace(&format!("Accept error: {e}"));
1875                    if is_fatal_accept_error(&e) {
1876                        self.drain_connection_tasks(cx).await;
1877                        return Err(ServerError::Io(e));
1878                    }
1879                    continue;
1880                }
1881            };
1882
1883            // Check connection limit before processing
1884            if !self.try_acquire_connection() {
1885                cx.trace(&format!(
1886                    "Connection limit reached ({}), rejecting {peer_addr}",
1887                    self.config.max_connections
1888                ));
1889
1890                let response = Response::with_status(StatusCode::SERVICE_UNAVAILABLE)
1891                    .header("connection", b"close".to_vec())
1892                    .body(fastapi_core::ResponseBody::Bytes(
1893                        b"503 Service Unavailable: connection limit reached".to_vec(),
1894                    ));
1895                let mut writer = crate::response::ResponseWriter::new();
1896                let response_bytes = writer.write(response);
1897                let _ = write_response(&mut stream, response_bytes).await;
1898                continue;
1899            }
1900
1901            // Configure the connection
1902            if self.config.tcp_nodelay {
1903                let _ = stream.set_nodelay(true);
1904            }
1905
1906            cx.trace(&format!(
1907                "Accepted connection from {peer_addr} ({}/{})",
1908                self.current_connections(),
1909                if self.config.max_connections == 0 {
1910                    "∞".to_string()
1911                } else {
1912                    self.config.max_connections.to_string()
1913                }
1914            ));
1915
1916            let request_id = self.next_request_id();
1917            let request_budget =
1918                Budget::new().with_deadline(request_deadline(self.config.request_timeout));
1919            let request_cx = request_cx_from_parent(cx, request_budget);
1920            let ctx = RequestContext::new(request_cx, request_id);
1921
1922            // Handle connection inline (single-threaded mode)
1923            let result = self
1924                .handle_connection(&ctx, stream, peer_addr, &*handler)
1925                .await;
1926
1927            self.release_connection();
1928
1929            if let Err(e) = result {
1930                cx.trace(&format!("Connection error from {peer_addr}: {e}"));
1931            }
1932        }
1933    }
1934
1935    /// Runs the server, accepting connections and handling requests.
1936    ///
1937    /// This method will run until the server Cx is cancelled or an
1938    /// unrecoverable error occurs.
1939    ///
1940    /// # Arguments
1941    ///
1942    /// * `cx` - The capability context for the server region
1943    /// * `handler` - The request handler function
1944    ///
1945    /// # Errors
1946    ///
1947    /// Returns an error if binding fails or an unrecoverable IO error occurs.
1948    pub async fn serve<H, Fut>(&self, cx: &Cx, handler: H) -> Result<(), ServerError>
1949    where
1950        H: Fn(RequestContext, &mut Request) -> Fut + Send + Sync + 'static,
1951        Fut: Future<Output = Response> + Send + 'static,
1952    {
1953        let bind_addr = self.config.bind_addr.clone();
1954        let listener = TcpListener::bind(bind_addr).await?;
1955        let local_addr = listener.local_addr()?;
1956
1957        cx.trace(&format!("Server listening on {local_addr}"));
1958
1959        self.accept_loop(cx, listener, handler).await
1960    }
1961
1962    /// Runs the server on a specific listener.
1963    ///
1964    /// This is useful when you already have a bound listener.
1965    pub async fn serve_on<H, Fut>(
1966        &self,
1967        cx: &Cx,
1968        listener: TcpListener,
1969        handler: H,
1970    ) -> Result<(), ServerError>
1971    where
1972        H: Fn(RequestContext, &mut Request) -> Fut + Send + Sync + 'static,
1973        Fut: Future<Output = Response> + Send + 'static,
1974    {
1975        self.accept_loop(cx, listener, handler).await
1976    }
1977
1978    /// Runs the server with a Handler trait object.
1979    ///
1980    /// This is the recommended way to serve an application that implements
1981    /// the `Handler` trait (like `App`).
1982    ///
1983    /// # Example
1984    ///
1985    /// ```ignore
1986    /// use fastapi_http::TcpServer;
1987    /// use fastapi_core::{App, Handler};
1988    /// use std::sync::Arc;
1989    ///
1990    /// let app = App::builder()
1991    ///     .get("/", handler_fn)
1992    ///     .build();
1993    ///
1994    /// let server = TcpServer::new(ServerConfig::new("127.0.0.1:8080"));
1995    /// let cx = Cx::for_testing();
1996    /// server.serve_handler(&cx, Arc::new(app)).await?;
1997    /// ```
1998    pub async fn serve_handler(
1999        &self,
2000        cx: &Cx,
2001        handler: Arc<dyn fastapi_core::Handler>,
2002    ) -> Result<(), ServerError> {
2003        let bind_addr = self.config.bind_addr.clone();
2004        let listener = TcpListener::bind(bind_addr).await?;
2005        let local_addr = listener.local_addr()?;
2006
2007        cx.trace(&format!("Server listening on {local_addr}"));
2008
2009        self.accept_loop_handler(cx, listener, handler).await
2010    }
2011
2012    /// Runs the server for a concrete [`App`].
2013    ///
2014    /// This enables protocol-aware features that require connection ownership,
2015    /// such as WebSocket upgrades.
2016    pub async fn serve_app(&self, cx: &Cx, app: Arc<App>) -> Result<(), ServerError> {
2017        let bind_addr = self.config.bind_addr.clone();
2018        let listener = TcpListener::bind(bind_addr).await?;
2019        let local_addr = listener.local_addr()?;
2020
2021        cx.trace(&format!("Server listening on {local_addr}"));
2022        self.accept_loop_app(cx, listener, app).await
2023    }
2024
2025    /// Runs the server for a concrete [`App`] with concurrent connection handling.
2026    ///
2027    /// This keeps the same app-aware request path as [`Self::serve_app`], but each accepted
2028    /// connection is spawned on the current asupersync runtime instead of being handled inline by
2029    /// the accept loop.
2030    ///
2031    /// # Panics
2032    ///
2033    /// Panics if called outside of an asupersync runtime context (i.e. when
2034    /// [`Runtime::current_handle()`] returns `None`).
2035    pub async fn serve_app_concurrent(&self, cx: &Cx, app: Arc<App>) -> Result<(), ServerError> {
2036        let bind_addr = self.config.bind_addr.clone();
2037        let listener = TcpListener::bind(bind_addr).await?;
2038        let local_addr = listener.local_addr()?;
2039
2040        cx.trace(&format!(
2041            "Server listening on {local_addr} (concurrent app mode)"
2042        ));
2043        self.accept_loop_app_concurrent(cx, listener, app).await
2044    }
2045
2046    /// Runs the server on a specific listener with a Handler trait object.
2047    pub async fn serve_on_handler(
2048        &self,
2049        cx: &Cx,
2050        listener: TcpListener,
2051        handler: Arc<dyn fastapi_core::Handler>,
2052    ) -> Result<(), ServerError> {
2053        self.accept_loop_handler(cx, listener, handler).await
2054    }
2055
2056    /// Runs the server on a specific listener for a concrete [`App`].
2057    ///
2058    /// This enables protocol-aware features that require connection ownership,
2059    /// such as WebSocket upgrades, while allowing callers (tests/embedders) to
2060    /// control the bind step and observe the selected local address.
2061    pub async fn serve_on_app(
2062        &self,
2063        cx: &Cx,
2064        listener: TcpListener,
2065        app: Arc<App>,
2066    ) -> Result<(), ServerError> {
2067        self.accept_loop_app(cx, listener, app).await
2068    }
2069
2070    /// Runs the server on a specific listener for a concrete [`App`] with concurrent connection
2071    /// handling.
2072    ///
2073    /// # Panics
2074    ///
2075    /// Panics if called outside of an asupersync runtime context (i.e. when
2076    /// [`Runtime::current_handle()`] returns `None`).
2077    pub async fn serve_on_app_concurrent(
2078        &self,
2079        cx: &Cx,
2080        listener: TcpListener,
2081        app: Arc<App>,
2082    ) -> Result<(), ServerError> {
2083        self.accept_loop_app_concurrent(cx, listener, app).await
2084    }
2085
2086    async fn accept_loop_app(
2087        &self,
2088        cx: &Cx,
2089        listener: TcpListener,
2090        app: Arc<App>,
2091    ) -> Result<(), ServerError> {
2092        loop {
2093            if cx.is_cancel_requested() {
2094                cx.trace("Server shutdown requested");
2095                return Ok(());
2096            }
2097            if self.is_draining() {
2098                cx.trace("Server draining, stopping accept loop");
2099                return Err(ServerError::Shutdown);
2100            }
2101
2102            let (mut stream, peer_addr) = match listener.accept().await {
2103                Ok(conn) => conn,
2104                Err(e) if e.kind() == io::ErrorKind::WouldBlock => continue,
2105                Err(e) => {
2106                    cx.trace(&format!("Accept error: {e}"));
2107                    if is_fatal_accept_error(&e) {
2108                        return Err(ServerError::Io(e));
2109                    }
2110                    continue;
2111                }
2112            };
2113
2114            if !self.try_acquire_connection() {
2115                cx.trace(&format!(
2116                    "Connection limit reached ({}), rejecting {peer_addr}",
2117                    self.config.max_connections
2118                ));
2119
2120                let response = Response::with_status(StatusCode::SERVICE_UNAVAILABLE)
2121                    .header("connection", b"close".to_vec())
2122                    .body(fastapi_core::ResponseBody::Bytes(
2123                        b"503 Service Unavailable: connection limit reached".to_vec(),
2124                    ));
2125                let mut writer = crate::response::ResponseWriter::new();
2126                let response_bytes = writer.write(response);
2127                let _ = write_response(&mut stream, response_bytes).await;
2128                continue;
2129            }
2130
2131            if self.config.tcp_nodelay {
2132                let _ = stream.set_nodelay(true);
2133            }
2134
2135            cx.trace(&format!(
2136                "Accepted connection from {peer_addr} ({}/{})",
2137                self.current_connections(),
2138                if self.config.max_connections == 0 {
2139                    "∞".to_string()
2140                } else {
2141                    self.config.max_connections.to_string()
2142                }
2143            ));
2144
2145            let result = self
2146                .handle_connection_app(cx, stream, peer_addr, app.as_ref())
2147                .await;
2148
2149            self.release_connection();
2150
2151            if let Err(e) = result {
2152                cx.trace(&format!("Connection error from {peer_addr}: {e}"));
2153            }
2154        }
2155    }
2156
2157    async fn accept_loop_app_concurrent(
2158        &self,
2159        cx: &Cx,
2160        listener: TcpListener,
2161        app: Arc<App>,
2162    ) -> Result<(), ServerError> {
2163        let runtime_handle = Runtime::current_handle()
2164            .expect("serve_app_concurrent must be called inside an asupersync runtime");
2165        let accept_poll_interval = Duration::from_millis(50);
2166
2167        loop {
2168            self.cleanup_completed_handles(cx).await;
2169
2170            if cx.is_cancel_requested() || self.is_draining() {
2171                cx.trace("Server shutting down, draining app connections");
2172                self.drain_connection_tasks(cx).await;
2173                return Ok(());
2174            }
2175
2176            let accept_future = Box::pin(listener.accept());
2177            let (mut stream, peer_addr) =
2178                match timeout(current_time(), accept_poll_interval, accept_future).await {
2179                    Ok(Ok(conn)) => conn,
2180                    Ok(Err(e)) if e.kind() == io::ErrorKind::WouldBlock => continue,
2181                    Ok(Err(e)) => {
2182                        cx.trace(&format!("Accept error: {e}"));
2183                        if is_fatal_accept_error(&e) {
2184                            return Err(ServerError::Io(e));
2185                        }
2186                        continue;
2187                    }
2188                    Err(_elapsed) => continue,
2189                };
2190
2191            if !self.try_acquire_connection() {
2192                cx.trace(&format!(
2193                    "Connection limit reached ({}), rejecting {peer_addr}",
2194                    self.config.max_connections
2195                ));
2196
2197                let response = Response::with_status(StatusCode::SERVICE_UNAVAILABLE)
2198                    .header("connection", b"close".to_vec())
2199                    .body(fastapi_core::ResponseBody::Bytes(
2200                        b"503 Service Unavailable: connection limit reached".to_vec(),
2201                    ));
2202                let mut writer = crate::response::ResponseWriter::new();
2203                let response_bytes = writer.write(response);
2204                let _ = write_response(&mut stream, response_bytes).await;
2205                continue;
2206            }
2207
2208            if self.config.tcp_nodelay {
2209                let _ = stream.set_nodelay(true);
2210            }
2211
2212            cx.trace(&format!(
2213                "Accepted connection from {peer_addr} ({}/{})",
2214                self.current_connections(),
2215                if self.config.max_connections == 0 {
2216                    "∞".to_string()
2217                } else {
2218                    self.config.max_connections.to_string()
2219                }
2220            ));
2221
2222            match self.spawn_connection_app_task(
2223                &runtime_handle,
2224                cx,
2225                stream,
2226                peer_addr,
2227                Arc::clone(&app),
2228            ) {
2229                Ok(handle) => {
2230                    if let Ok(mut handles) = self.connection_handles.lock() {
2231                        handles.push(handle);
2232                    }
2233                    self.cleanup_completed_handles(cx).await;
2234                }
2235                Err(e) => {
2236                    cx.trace(&format!("Failed to spawn app connection task: {e:?}"));
2237                }
2238            }
2239        }
2240    }
2241
2242    /// Accept loop for Handler trait objects.
2243    async fn accept_loop_handler(
2244        &self,
2245        cx: &Cx,
2246        listener: TcpListener,
2247        handler: Arc<dyn fastapi_core::Handler>,
2248    ) -> Result<(), ServerError> {
2249        loop {
2250            // Check for cancellation at each iteration.
2251            if cx.is_cancel_requested() {
2252                cx.trace("Server shutdown requested");
2253                return Ok(());
2254            }
2255
2256            // Check if draining (graceful shutdown)
2257            if self.is_draining() {
2258                cx.trace("Server draining, stopping accept loop");
2259                return Err(ServerError::Shutdown);
2260            }
2261
2262            // Accept a connection.
2263            let (mut stream, peer_addr) = match listener.accept().await {
2264                Ok(conn) => conn,
2265                Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
2266                    continue;
2267                }
2268                Err(e) => {
2269                    cx.trace(&format!("Accept error: {e}"));
2270                    if is_fatal_accept_error(&e) {
2271                        return Err(ServerError::Io(e));
2272                    }
2273                    continue;
2274                }
2275            };
2276
2277            // Check connection limit before processing
2278            if !self.try_acquire_connection() {
2279                cx.trace(&format!(
2280                    "Connection limit reached ({}), rejecting {peer_addr}",
2281                    self.config.max_connections
2282                ));
2283
2284                let response = Response::with_status(StatusCode::SERVICE_UNAVAILABLE)
2285                    .header("connection", b"close".to_vec())
2286                    .body(fastapi_core::ResponseBody::Bytes(
2287                        b"503 Service Unavailable: connection limit reached".to_vec(),
2288                    ));
2289                let mut writer = crate::response::ResponseWriter::new();
2290                let response_bytes = writer.write(response);
2291                let _ = write_response(&mut stream, response_bytes).await;
2292                continue;
2293            }
2294
2295            // Configure the connection.
2296            if self.config.tcp_nodelay {
2297                let _ = stream.set_nodelay(true);
2298            }
2299
2300            cx.trace(&format!(
2301                "Accepted connection from {peer_addr} ({}/{})",
2302                self.current_connections(),
2303                if self.config.max_connections == 0 {
2304                    "∞".to_string()
2305                } else {
2306                    self.config.max_connections.to_string()
2307                }
2308            ));
2309
2310            // Handle the connection with the Handler trait object
2311            let result = self
2312                .handle_connection_handler(cx, stream, peer_addr, &*handler)
2313                .await;
2314
2315            self.release_connection();
2316
2317            if let Err(e) = result {
2318                cx.trace(&format!("Connection error from {peer_addr}: {e}"));
2319            }
2320        }
2321    }
2322
2323    /// Serves HTTP requests with concurrent connection handling using `RuntimeHandle::spawn`.
2324    ///
2325    /// Each accepted connection is spawned as an independent task on the current
2326    /// asupersync runtime via [`RuntimeHandle`]. Task handles are tracked internally
2327    /// so they can be drained during graceful shutdown.
2328    ///
2329    /// # Arguments
2330    ///
2331    /// * `cx` - The asupersync context for cancellation and tracing
2332    /// * `handler` - The request handler
2333    ///
2334    /// # Panics
2335    ///
2336    /// Panics if called outside of an asupersync runtime context (i.e. when
2337    /// [`Runtime::current_handle()`] returns `None`).
2338    #[allow(clippy::too_many_lines)]
2339    pub async fn serve_concurrent<H, Fut>(&self, cx: &Cx, handler: H) -> Result<(), ServerError>
2340    where
2341        H: Fn(RequestContext, &mut Request) -> Fut + Send + Sync + 'static,
2342        Fut: Future<Output = Response> + Send + 'static,
2343    {
2344        let bind_addr = self.config.bind_addr.clone();
2345        let listener = TcpListener::bind(bind_addr).await?;
2346        let local_addr = listener.local_addr()?;
2347
2348        cx.trace(&format!(
2349            "Server listening on {local_addr} (concurrent mode)"
2350        ));
2351
2352        let handler = Arc::new(handler);
2353
2354        self.accept_loop_concurrent(cx, listener, handler).await
2355    }
2356
2357    /// Accept loop that spawns connection handlers concurrently using `RuntimeHandle`.
2358    async fn accept_loop_concurrent<H, Fut>(
2359        &self,
2360        cx: &Cx,
2361        listener: TcpListener,
2362        handler: Arc<H>,
2363    ) -> Result<(), ServerError>
2364    where
2365        H: Fn(RequestContext, &mut Request) -> Fut + Send + Sync + 'static,
2366        Fut: Future<Output = Response> + Send + 'static,
2367    {
2368        let runtime_handle = Runtime::current_handle()
2369            .expect("serve_concurrent must be called inside an asupersync runtime");
2370        let accept_poll_interval = Duration::from_millis(50);
2371
2372        loop {
2373            self.cleanup_completed_handles(cx).await;
2374
2375            // Check for cancellation or drain
2376            if cx.is_cancel_requested() || self.is_draining() {
2377                cx.trace("Server shutting down, draining connections");
2378                self.drain_connection_tasks(cx).await;
2379                return Ok(());
2380            }
2381
2382            // Poll accept periodically so shutdown is observed promptly even
2383            // when the listener is otherwise idle.
2384            let accept_future = Box::pin(listener.accept());
2385            let (mut stream, peer_addr) =
2386                match timeout(current_time(), accept_poll_interval, accept_future).await {
2387                    Ok(Ok(conn)) => conn,
2388                    Ok(Err(e)) if e.kind() == io::ErrorKind::WouldBlock => {
2389                        continue;
2390                    }
2391                    Ok(Err(e)) => {
2392                        cx.trace(&format!("Accept error: {e}"));
2393                        if is_fatal_accept_error(&e) {
2394                            return Err(ServerError::Io(e));
2395                        }
2396                        continue;
2397                    }
2398                    Err(_elapsed) => continue,
2399                };
2400
2401            // Check connection limit before processing
2402            if !self.try_acquire_connection() {
2403                cx.trace(&format!(
2404                    "Connection limit reached ({}), rejecting {peer_addr}",
2405                    self.config.max_connections
2406                ));
2407
2408                let response = Response::with_status(StatusCode::SERVICE_UNAVAILABLE)
2409                    .header("connection", b"close".to_vec())
2410                    .body(fastapi_core::ResponseBody::Bytes(
2411                        b"503 Service Unavailable: connection limit reached".to_vec(),
2412                    ));
2413                let mut writer = crate::response::ResponseWriter::new();
2414                let response_bytes = writer.write(response);
2415                let _ = write_response(&mut stream, response_bytes).await;
2416                continue;
2417            }
2418
2419            // Configure the connection
2420            if self.config.tcp_nodelay {
2421                let _ = stream.set_nodelay(true);
2422            }
2423
2424            cx.trace(&format!(
2425                "Accepted connection from {peer_addr} ({}/{})",
2426                self.current_connections(),
2427                if self.config.max_connections == 0 {
2428                    "∞".to_string()
2429                } else {
2430                    self.config.max_connections.to_string()
2431                }
2432            ));
2433
2434            // Spawn connection task using RuntimeHandle
2435            match self.spawn_connection_task(
2436                &runtime_handle,
2437                cx,
2438                stream,
2439                peer_addr,
2440                Arc::clone(&handler),
2441            ) {
2442                Ok(handle) => {
2443                    // Store handle for draining
2444                    if let Ok(mut handles) = self.connection_handles.lock() {
2445                        handles.push(handle);
2446                    }
2447                    // Periodically clean up completed handles
2448                    self.cleanup_completed_handles(cx).await;
2449                }
2450                Err(e) => {
2451                    cx.trace(&format!("Failed to spawn connection task: {e:?}"));
2452                }
2453            }
2454        }
2455    }
2456
2457    /// Spawns a connection handler task using [`RuntimeHandle::try_spawn`].
2458    ///
2459    /// The parent server [`Cx`] is cloned into the task so shutdown and
2460    /// cancellation propagate into per-connection I/O.
2461    fn spawn_connection_task<H, Fut>(
2462        &self,
2463        handle: &RuntimeHandle,
2464        cx: &Cx,
2465        stream: TcpStream,
2466        peer_addr: SocketAddr,
2467        handler: Arc<H>,
2468    ) -> Result<JoinHandle<()>, SpawnError>
2469    where
2470        H: Fn(RequestContext, &mut Request) -> Fut + Send + Sync + 'static,
2471        Fut: Future<Output = Response> + Send + 'static,
2472    {
2473        let config = self.config.clone();
2474        let connection_cx = cx.clone();
2475        let request_counter = Arc::clone(&self.request_counter);
2476        let connection_counter = Arc::clone(&self.connection_counter);
2477        let connection_slot = ConnectionSlotGuard::new(connection_counter);
2478
2479        handle.try_spawn(async move {
2480            let _connection_slot = connection_slot;
2481            let result = process_connection(
2482                &connection_cx,
2483                &request_counter,
2484                stream,
2485                peer_addr,
2486                &config,
2487                |ctx, req| handler(ctx, req),
2488            )
2489            .await;
2490
2491            if let Err(e) = result {
2492                // Current default: print to stderr. A structured sink can be wired via fastapi-core::logging.
2493                eprintln!("Connection error from {peer_addr}: {e}");
2494            }
2495        })
2496    }
2497
2498    fn spawn_connection_app_task(
2499        &self,
2500        handle: &RuntimeHandle,
2501        cx: &Cx,
2502        stream: TcpStream,
2503        peer_addr: SocketAddr,
2504        app: Arc<App>,
2505    ) -> Result<JoinHandle<()>, SpawnError> {
2506        let server = self.clone_for_connection_task();
2507        let connection_cx = cx.clone();
2508        let connection_counter = Arc::clone(&self.connection_counter);
2509        let connection_slot = ConnectionSlotGuard::new(connection_counter);
2510
2511        handle.try_spawn(async move {
2512            let _connection_slot = connection_slot;
2513            let result = server
2514                .handle_connection_app(&connection_cx, stream, peer_addr, app.as_ref())
2515                .await;
2516
2517            if let Err(e) = result {
2518                eprintln!("Connection error from {peer_addr}: {e}");
2519            }
2520        })
2521    }
2522
2523    fn take_finished_connection_handles(&self) -> Vec<JoinHandle<()>> {
2524        if let Ok(mut handles) = self.connection_handles.lock() {
2525            let mut finished = Vec::new();
2526            let mut idx = 0;
2527            while idx < handles.len() {
2528                if handles[idx].is_finished() {
2529                    finished.push(handles.swap_remove(idx));
2530                } else {
2531                    idx += 1;
2532                }
2533            }
2534            finished
2535        } else {
2536            Vec::new()
2537        }
2538    }
2539
2540    /// Removes completed task handles from the tracking vector and reports any panics.
2541    async fn cleanup_completed_handles(&self, cx: &Cx) {
2542        for handle in self.take_finished_connection_handles() {
2543            if let Err(payload) = CatchUnwind::new(handle).await {
2544                let message = panic_payload_message(payload.as_ref());
2545                cx.trace(&format!("Connection task panicked: {message}"));
2546                eprintln!("Connection task panicked: {message}");
2547            }
2548        }
2549    }
2550
2551    /// Drains all connection tasks during shutdown.
2552    async fn drain_connection_tasks(&self, cx: &Cx) {
2553        let drain_timeout = self.config.drain_timeout;
2554        let start = Instant::now();
2555
2556        cx.trace(&format!(
2557            "Draining {} connection tasks (timeout: {:?})",
2558            self.connection_handles.lock().map_or(0, |h| h.len()),
2559            drain_timeout
2560        ));
2561
2562        // Wait for all tasks to complete or timeout
2563        while start.elapsed() < drain_timeout {
2564            self.cleanup_completed_handles(cx).await;
2565
2566            let remaining = self
2567                .connection_handles
2568                .lock()
2569                .map_or(0, |h| h.iter().filter(|t| !t.is_finished()).count());
2570
2571            if remaining == 0 {
2572                self.cleanup_completed_handles(cx).await;
2573                cx.trace("All connection tasks drained successfully");
2574                return;
2575            }
2576
2577            // Yield to allow tasks to make progress
2578            asupersync::runtime::yield_now().await;
2579        }
2580
2581        self.cleanup_completed_handles(cx).await;
2582        cx.trace(&format!(
2583            "Drain timeout reached with {} tasks still running; lingering connection tasks will continue in the background",
2584            self.connection_handles
2585                .lock()
2586                .map_or(0, |h| h.iter().filter(|t| !t.is_finished()).count())
2587        ));
2588    }
2589
2590    async fn handle_connection_app(
2591        &self,
2592        cx: &Cx,
2593        mut stream: TcpStream,
2594        peer_addr: SocketAddr,
2595        app: &App,
2596    ) -> Result<(), ServerError> {
2597        let (proto, buffered) = sniff_protocol(&mut stream, self.config.keep_alive_timeout).await?;
2598        if !buffered.is_empty() {
2599            self.record_bytes_in(buffered.len() as u64);
2600        }
2601
2602        if proto == SniffedProtocol::Http2PriorKnowledge {
2603            return self
2604                .handle_connection_app_http2(cx, stream, peer_addr, app)
2605                .await;
2606        }
2607
2608        let mut parser = StatefulParser::new().with_limits(self.config.parse_limits.clone());
2609        if !buffered.is_empty() {
2610            parser.feed(&buffered)?;
2611        }
2612        let mut read_buffer = vec![0u8; self.config.read_buffer_size];
2613        let mut response_writer = ResponseWriter::new();
2614        let mut requests_on_connection: usize = 0;
2615        let max_requests = self.config.max_requests_per_connection;
2616
2617        loop {
2618            if cx.is_cancel_requested() {
2619                return Ok(());
2620            }
2621
2622            let parse_result = parser.feed(&[])?;
2623            let mut request = match parse_result {
2624                ParseStatus::Complete { request, .. } => request,
2625                ParseStatus::Incomplete => {
2626                    let keep_alive_timeout = self.config.keep_alive_timeout;
2627                    let bytes_read = if keep_alive_timeout.is_zero() {
2628                        read_into_buffer(&mut stream, &mut read_buffer).await?
2629                    } else {
2630                        match read_with_timeout(&mut stream, &mut read_buffer, keep_alive_timeout)
2631                            .await
2632                        {
2633                            Ok(0) => return Ok(()),
2634                            Ok(n) => n,
2635                            Err(e) if e.kind() == io::ErrorKind::TimedOut => {
2636                                self.metrics_counters
2637                                    .total_timed_out
2638                                    .fetch_add(1, Ordering::Relaxed);
2639                                return Err(ServerError::KeepAliveTimeout);
2640                            }
2641                            Err(e) => return Err(ServerError::Io(e)),
2642                        }
2643                    };
2644
2645                    if bytes_read == 0 {
2646                        return Ok(());
2647                    }
2648
2649                    self.record_bytes_in(bytes_read as u64);
2650
2651                    match parser.feed(&read_buffer[..bytes_read])? {
2652                        ParseStatus::Complete { request, .. } => request,
2653                        ParseStatus::Incomplete => continue,
2654                    }
2655                }
2656            };
2657
2658            requests_on_connection += 1;
2659
2660            let request_id = self.request_counter.fetch_add(1, Ordering::Relaxed);
2661
2662            // Per-request budget for HTTP requests.
2663            let deadline = request_deadline_at(cx.now(), self.config.request_timeout);
2664            let request_budget = Budget::new().with_deadline(deadline);
2665            let request_cx = request_cx_from_parent(cx, request_budget);
2666            let overrides = app.dependency_overrides();
2667            let ctx = RequestContext::with_overrides_and_body_limit(
2668                request_cx,
2669                request_id,
2670                overrides,
2671                app.config().max_body_size,
2672            )
2673            .with_deadline(deadline);
2674
2675            // Validate Host header
2676            if let Err(err) = validate_host_header(&request, &self.config) {
2677                ctx.trace(&format!(
2678                    "Rejecting request from {peer_addr}: {}",
2679                    err.detail
2680                ));
2681                let response = err.response().header("connection", b"close".to_vec());
2682                let response_write = response_writer.write(response);
2683                write_response(&mut stream, response_write).await?;
2684                return Ok(());
2685            }
2686
2687            // Header-only validators before any body reads / 100-continue.
2688            if let Err(response) = self.config.pre_body_validators.validate_all(&request) {
2689                let response = response.header("connection", b"close".to_vec());
2690                let response_write = response_writer.write(response);
2691                write_response(&mut stream, response_write).await?;
2692                return Ok(());
2693            }
2694
2695            // WebSocket upgrade: only attempt when request looks like a WS handshake.
2696            //
2697            // NOTE: This consumes the connection: after a successful 101 upgrade, we hand the
2698            // TcpStream to the websocket handler and stop HTTP keep-alive processing.
2699            if is_websocket_upgrade_request(&request)
2700                && app.websocket_route_count() > 0
2701                && app.has_websocket_route(request.path())
2702            {
2703                // WebSocket handshake must not have a request body.
2704                if has_request_body_headers(&request) {
2705                    let response = Response::with_status(StatusCode::BAD_REQUEST)
2706                        .header("connection", b"close".to_vec())
2707                        .body(fastapi_core::ResponseBody::Bytes(
2708                            b"Bad Request: websocket handshake must not include a body".to_vec(),
2709                        ));
2710                    let response_write = response_writer.write(response);
2711                    write_response(&mut stream, response_write).await?;
2712                    return Ok(());
2713                }
2714
2715                let Some(key) = header_str(&request, "sec-websocket-key") else {
2716                    let response = Response::with_status(StatusCode::BAD_REQUEST)
2717                        .header("connection", b"close".to_vec())
2718                        .body(fastapi_core::ResponseBody::Bytes(
2719                            b"Bad Request: missing Sec-WebSocket-Key".to_vec(),
2720                        ));
2721                    let response_write = response_writer.write(response);
2722                    write_response(&mut stream, response_write).await?;
2723                    return Ok(());
2724                };
2725                let accept = match fastapi_core::websocket_accept_from_key(key) {
2726                    Ok(v) => v,
2727                    Err(_) => {
2728                        let response = Response::with_status(StatusCode::BAD_REQUEST)
2729                            .header("connection", b"close".to_vec())
2730                            .body(fastapi_core::ResponseBody::Bytes(
2731                                b"Bad Request: invalid Sec-WebSocket-Key".to_vec(),
2732                            ));
2733                        let response_write = response_writer.write(response);
2734                        write_response(&mut stream, response_write).await?;
2735                        return Ok(());
2736                    }
2737                };
2738
2739                if header_str(&request, "sec-websocket-version") != Some("13") {
2740                    let response = Response::with_status(StatusCode::BAD_REQUEST)
2741                        .header("sec-websocket-version", b"13".to_vec())
2742                        .header("connection", b"close".to_vec())
2743                        .body(fastapi_core::ResponseBody::Bytes(
2744                            b"Bad Request: unsupported Sec-WebSocket-Version".to_vec(),
2745                        ));
2746                    let response_write = response_writer.write(response);
2747                    write_response(&mut stream, response_write).await?;
2748                    return Ok(());
2749                }
2750
2751                let response = Response::with_status(StatusCode::SWITCHING_PROTOCOLS)
2752                    .header("upgrade", b"websocket".to_vec())
2753                    .header("connection", b"Upgrade".to_vec())
2754                    .header("sec-websocket-accept", accept.into_bytes());
2755                let response_write = response_writer.write(response);
2756                if let ResponseWrite::Full(ref bytes) = response_write {
2757                    self.record_bytes_out(bytes.len() as u64);
2758                }
2759                write_response(&mut stream, response_write).await?;
2760
2761                // Hand off any already-read bytes to the websocket layer.
2762                let buffered = parser.take_buffered();
2763
2764                // WebSocket connections are long-lived; do not inherit the per-request deadline.
2765                let ws_root_cx = request_cx_from_parent(cx, Budget::new());
2766                let ws_ctx = RequestContext::with_overrides_and_body_limit(
2767                    ws_root_cx,
2768                    request_id,
2769                    app.dependency_overrides(),
2770                    app.config().max_body_size,
2771                );
2772
2773                let ws = fastapi_core::WebSocket::new(stream, buffered);
2774                let _ = app.handle_websocket(&ws_ctx, &mut request, ws).await;
2775                return Ok(());
2776            }
2777
2778            // Handle Expect: 100-continue
2779            match ExpectHandler::check_expect(&request) {
2780                ExpectResult::NoExpectation => {}
2781                ExpectResult::ExpectsContinue => {
2782                    ctx.trace("Sending 100 Continue for Expect: 100-continue");
2783                    write_raw_response(&mut stream, CONTINUE_RESPONSE).await?;
2784                }
2785                ExpectResult::UnknownExpectation(value) => {
2786                    ctx.trace(&format!("Rejecting unknown Expect value: {}", value));
2787                    let response = ExpectHandler::expectation_failed(format!(
2788                        "Unsupported Expect value: {value}"
2789                    ));
2790                    let response_write = response_writer.write(response);
2791                    write_response(&mut stream, response_write).await?;
2792                    return Ok(());
2793                }
2794            }
2795
2796            let client_wants_keep_alive = should_keep_alive(&request);
2797            let mut server_will_keep_alive = client_wants_keep_alive
2798                && (max_requests == 0 || requests_on_connection < max_requests);
2799
2800            // Race the handler (including its middleware chain) against the
2801            // request deadline. Losing the race drops the handler future, so
2802            // no late response can be produced, published by middleware (e.g.
2803            // into a coalescing replay cache), or observed anywhere after the
2804            // client has been told 504.
2805            // Losing the race drops the handler future, which is how
2806            // cancellation is delivered; the request Cx must NOT be
2807            // cancel-marked here because it shares cancel state with this
2808            // connection's Cx, and the 504 still has to be written on this
2809            // connection.
2810            let mut response = match timeout_at(deadline, app.handle(&ctx, &mut request)).await {
2811                Ok(response) => response,
2812                Err(_elapsed) => {
2813                    // The abandoned handler may not have consumed the
2814                    // request body, so the connection cannot be reused.
2815                    server_will_keep_alive = false;
2816                    Response::with_status(StatusCode::GATEWAY_TIMEOUT).body(
2817                        fastapi_core::ResponseBody::Bytes(
2818                            b"Gateway Timeout: request processing exceeded time limit".to_vec(),
2819                        ),
2820                    )
2821                }
2822            };
2823
2824            response = if server_will_keep_alive {
2825                response.header("connection", b"keep-alive".to_vec())
2826            } else {
2827                response.header("connection", b"close".to_vec())
2828            };
2829
2830            let response_write = response_writer.write(response);
2831            if let ResponseWrite::Full(ref bytes) = response_write {
2832                self.record_bytes_out(bytes.len() as u64);
2833            }
2834            write_response(&mut stream, response_write).await?;
2835
2836            if let Some(tasks) = App::take_background_tasks(&mut request) {
2837                tasks.execute_all().await;
2838            }
2839
2840            if !server_will_keep_alive {
2841                return Ok(());
2842            }
2843        }
2844    }
2845
2846    async fn handle_connection_app_http2(
2847        &self,
2848        cx: &Cx,
2849        stream: TcpStream,
2850        _peer_addr: SocketAddr,
2851        app: &App,
2852    ) -> Result<(), ServerError> {
2853        const FLAG_END_STREAM: u8 = 0x1;
2854        const FLAG_END_HEADERS: u8 = 0x4;
2855        const FLAG_ACK: u8 = 0x1;
2856
2857        let mut framed = http2::FramedH2::new(stream, Vec::new());
2858        let mut hpack = http2::HpackDecoder::new();
2859        let recv_max_frame_size: u32 = 16 * 1024; // RFC 7540 default receive limit.
2860        let mut peer_max_frame_size: u32 = 16 * 1024;
2861        let mut flow_control = http2::H2FlowControl::new();
2862
2863        let first = framed.read_frame(recv_max_frame_size).await?;
2864        self.record_bytes_in((http2::FrameHeader::LEN + first.payload.len()) as u64);
2865
2866        if first.header.frame_type() != http2::FrameType::Settings
2867            || first.header.stream_id != 0
2868            || (first.header.flags & FLAG_ACK) != 0
2869        {
2870            return Err(
2871                http2::Http2Error::Protocol("expected client SETTINGS after preface").into(),
2872            );
2873        }
2874
2875        apply_http2_settings_with_fc(
2876            &mut hpack,
2877            &mut peer_max_frame_size,
2878            Some(&mut flow_control),
2879            &first.payload,
2880        )?;
2881
2882        // Send server SETTINGS (empty for now) and ACK the client's SETTINGS.
2883        framed
2884            .write_frame(http2::FrameType::Settings, 0, 0, SERVER_SETTINGS_PAYLOAD)
2885            .await?;
2886        self.record_bytes_out(http2::FrameHeader::LEN as u64);
2887
2888        framed
2889            .write_frame(http2::FrameType::Settings, FLAG_ACK, 0, &[])
2890            .await?;
2891        self.record_bytes_out(http2::FrameHeader::LEN as u64);
2892        let mut last_stream_id: u32 = 0;
2893
2894        loop {
2895            if cx.is_cancel_requested() {
2896                let _ = send_goaway(&mut framed, last_stream_id, h2_error_code::NO_ERROR).await;
2897                return Ok(());
2898            }
2899
2900            let frame = framed.read_frame(recv_max_frame_size).await?;
2901            self.record_bytes_in((http2::FrameHeader::LEN + frame.payload.len()) as u64);
2902
2903            match frame.header.frame_type() {
2904                http2::FrameType::Settings => {
2905                    let is_ack = validate_settings_frame(
2906                        frame.header.stream_id,
2907                        frame.header.flags,
2908                        &frame.payload,
2909                    )?;
2910                    if is_ack {
2911                        // ACK for our SETTINGS.
2912                        continue;
2913                    }
2914                    apply_http2_settings_with_fc(
2915                        &mut hpack,
2916                        &mut peer_max_frame_size,
2917                        Some(&mut flow_control),
2918                        &frame.payload,
2919                    )?;
2920                    // ACK peer SETTINGS.
2921                    framed
2922                        .write_frame(http2::FrameType::Settings, FLAG_ACK, 0, &[])
2923                        .await?;
2924                    self.record_bytes_out(http2::FrameHeader::LEN as u64);
2925                }
2926                http2::FrameType::Ping => {
2927                    // Respond to pings to avoid clients stalling.
2928                    if frame.header.stream_id != 0 || frame.payload.len() != 8 {
2929                        return Err(http2::Http2Error::Protocol("invalid PING frame").into());
2930                    }
2931                    if (frame.header.flags & FLAG_ACK) == 0 {
2932                        framed
2933                            .write_frame(http2::FrameType::Ping, FLAG_ACK, 0, &frame.payload)
2934                            .await?;
2935                        self.record_bytes_out((http2::FrameHeader::LEN + 8) as u64);
2936                    }
2937                }
2938                http2::FrameType::Goaway => {
2939                    validate_goaway_payload(&frame.payload)?;
2940                    return Ok(());
2941                }
2942                http2::FrameType::PushPromise => {
2943                    return Err(http2::Http2Error::Protocol(
2944                        "PUSH_PROMISE not supported by server",
2945                    )
2946                    .into());
2947                }
2948                http2::FrameType::Headers => {
2949                    let stream_id = frame.header.stream_id;
2950                    if stream_id == 0 {
2951                        return Err(
2952                            http2::Http2Error::Protocol("HEADERS must not be on stream 0").into(),
2953                        );
2954                    }
2955                    if stream_id % 2 == 0 {
2956                        return Err(http2::Http2Error::Protocol(
2957                            "client-initiated stream ID must be odd",
2958                        )
2959                        .into());
2960                    }
2961                    if stream_id <= last_stream_id {
2962                        return Err(http2::Http2Error::Protocol(
2963                            "stream ID must be greater than previous",
2964                        )
2965                        .into());
2966                    }
2967                    last_stream_id = stream_id;
2968                    let (end_stream, mut header_block) =
2969                        extract_header_block_fragment(frame.header.flags, &frame.payload)?;
2970
2971                    // CONTINUATION frames until END_HEADERS.
2972                    if (frame.header.flags & FLAG_END_HEADERS) == 0 {
2973                        loop {
2974                            let cont = framed.read_frame(recv_max_frame_size).await?;
2975                            self.record_bytes_in(
2976                                (http2::FrameHeader::LEN + cont.payload.len()) as u64,
2977                            );
2978                            if cont.header.frame_type() != http2::FrameType::Continuation
2979                                || cont.header.stream_id != stream_id
2980                            {
2981                                return Err(http2::Http2Error::Protocol(
2982                                    "expected CONTINUATION for header block",
2983                                )
2984                                .into());
2985                            }
2986                            header_block.extend_from_slice(&cont.payload);
2987                            if header_block.len() > MAX_HEADER_BLOCK_SIZE {
2988                                return Err(http2::Http2Error::Protocol(
2989                                    "header block exceeds maximum size",
2990                                )
2991                                .into());
2992                            }
2993                            if (cont.header.flags & FLAG_END_HEADERS) != 0 {
2994                                break;
2995                            }
2996                        }
2997                    }
2998
2999                    let headers = hpack
3000                        .decode(&header_block)
3001                        .map_err(http2::Http2Error::from)?;
3002                    let mut request = request_from_h2_headers(headers)?;
3003                    request.set_version(fastapi_core::HttpVersion::Http2);
3004
3005                    // If there is a body, read DATA frames until END_STREAM.
3006                    if !end_stream {
3007                        let max = app.config().max_body_size;
3008                        let mut body = Vec::new();
3009                        let mut stream_reset = false;
3010                        let mut stream_received: u32 = 0;
3011                        loop {
3012                            let f = framed.read_frame(recv_max_frame_size).await?;
3013                            self.record_bytes_in(
3014                                (http2::FrameHeader::LEN + f.payload.len()) as u64,
3015                            );
3016                            match f.header.frame_type() {
3017                                http2::FrameType::Data if f.header.stream_id == 0 => {
3018                                    return Err(http2::Http2Error::Protocol(
3019                                        "DATA must not be on stream 0",
3020                                    )
3021                                    .into());
3022                                }
3023                                http2::FrameType::Data if f.header.stream_id == stream_id => {
3024                                    let (data, data_end_stream) =
3025                                        extract_data_payload(f.header.flags, &f.payload)?;
3026                                    if body.len().saturating_add(data.len()) > max {
3027                                        return Err(http2::Http2Error::Protocol(
3028                                            "request body exceeds configured max_body_size",
3029                                        )
3030                                        .into());
3031                                    }
3032                                    body.extend_from_slice(data);
3033
3034                                    // Flow control: track received data and send
3035                                    // WINDOW_UPDATEs to prevent sender stalling.
3036                                    let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
3037                                    stream_received += data_len;
3038                                    let conn_inc = flow_control.data_received_connection(data_len);
3039                                    let stream_inc =
3040                                        flow_control.stream_window_update(stream_received);
3041                                    if stream_inc > 0 {
3042                                        stream_received = 0;
3043                                    }
3044                                    send_window_updates(
3045                                        &mut framed,
3046                                        conn_inc,
3047                                        stream_id,
3048                                        stream_inc,
3049                                    )
3050                                    .await?;
3051
3052                                    if data_end_stream {
3053                                        break;
3054                                    }
3055                                }
3056                                http2::FrameType::RstStream => {
3057                                    validate_rst_stream_payload(f.header.stream_id, &f.payload)?;
3058                                    if f.header.stream_id == stream_id {
3059                                        stream_reset = true;
3060                                        break;
3061                                    }
3062                                }
3063                                http2::FrameType::PushPromise => {
3064                                    return Err(http2::Http2Error::Protocol(
3065                                        "PUSH_PROMISE not supported by server",
3066                                    )
3067                                    .into());
3068                                }
3069                                http2::FrameType::Settings
3070                                | http2::FrameType::Ping
3071                                | http2::FrameType::Goaway
3072                                | http2::FrameType::WindowUpdate
3073                                | http2::FrameType::Priority
3074                                | http2::FrameType::Unknown => {
3075                                    if f.header.frame_type() == http2::FrameType::Goaway {
3076                                        validate_goaway_payload(&f.payload)?;
3077                                        return Ok(());
3078                                    }
3079                                    if f.header.frame_type() == http2::FrameType::Priority {
3080                                        validate_priority_payload(f.header.stream_id, &f.payload)?;
3081                                    }
3082                                    if f.header.frame_type() == http2::FrameType::WindowUpdate {
3083                                        validate_window_update_payload(&f.payload)?;
3084                                        let increment = u32::from_be_bytes([
3085                                            f.payload[0],
3086                                            f.payload[1],
3087                                            f.payload[2],
3088                                            f.payload[3],
3089                                        ]) & 0x7FFF_FFFF;
3090                                        if f.header.stream_id == 0 {
3091                                            apply_send_conn_window_update(
3092                                                &mut flow_control,
3093                                                increment,
3094                                            )?;
3095                                        }
3096                                    }
3097                                    if f.header.frame_type() == http2::FrameType::Ping {
3098                                        if f.header.stream_id != 0 || f.payload.len() != 8 {
3099                                            return Err(http2::Http2Error::Protocol(
3100                                                "invalid PING frame",
3101                                            )
3102                                            .into());
3103                                        }
3104                                        if (f.header.flags & FLAG_ACK) == 0 {
3105                                            framed
3106                                                .write_frame(
3107                                                    http2::FrameType::Ping,
3108                                                    FLAG_ACK,
3109                                                    0,
3110                                                    &f.payload,
3111                                                )
3112                                                .await?;
3113                                            self.record_bytes_out(
3114                                                (http2::FrameHeader::LEN + 8) as u64,
3115                                            );
3116                                        }
3117                                    }
3118                                    if f.header.frame_type() == http2::FrameType::Settings {
3119                                        let is_ack = validate_settings_frame(
3120                                            f.header.stream_id,
3121                                            f.header.flags,
3122                                            &f.payload,
3123                                        )?;
3124                                        if !is_ack {
3125                                            apply_http2_settings_with_fc(
3126                                                &mut hpack,
3127                                                &mut peer_max_frame_size,
3128                                                Some(&mut flow_control),
3129                                                &f.payload,
3130                                            )?;
3131                                            framed
3132                                                .write_frame(
3133                                                    http2::FrameType::Settings,
3134                                                    FLAG_ACK,
3135                                                    0,
3136                                                    &[],
3137                                                )
3138                                                .await?;
3139                                            self.record_bytes_out(http2::FrameHeader::LEN as u64);
3140                                        }
3141                                    }
3142                                }
3143                                _ => {
3144                                    return Err(http2::Http2Error::Protocol(
3145                                        "unsupported frame while reading request body",
3146                                    )
3147                                    .into());
3148                                }
3149                            }
3150                        }
3151                        if stream_reset {
3152                            continue;
3153                        }
3154                        request.set_body(fastapi_core::Body::Bytes(body));
3155                    }
3156
3157                    let request_id = self.request_counter.fetch_add(1, Ordering::Relaxed);
3158                    let request_budget =
3159                        Budget::new().with_deadline(request_deadline(self.config.request_timeout));
3160                    let request_cx = request_cx_from_parent(cx, request_budget);
3161                    let overrides = app.dependency_overrides();
3162                    let ctx = RequestContext::with_overrides_and_body_limit(
3163                        request_cx,
3164                        request_id,
3165                        overrides,
3166                        app.config().max_body_size,
3167                    );
3168
3169                    if let Err(err) = validate_host_header(&request, &self.config) {
3170                        ctx.trace(&format!("Rejecting HTTP/2 request: {}", err.detail));
3171                        let response = err.response();
3172                        self.write_h2_response(
3173                            &mut framed,
3174                            response,
3175                            stream_id,
3176                            peer_max_frame_size,
3177                            recv_max_frame_size,
3178                            Some(&mut flow_control),
3179                        )
3180                        .await?;
3181                        continue;
3182                    }
3183
3184                    if let Err(response) = self.config.pre_body_validators.validate_all(&request) {
3185                        self.write_h2_response(
3186                            &mut framed,
3187                            response,
3188                            stream_id,
3189                            peer_max_frame_size,
3190                            recv_max_frame_size,
3191                            Some(&mut flow_control),
3192                        )
3193                        .await?;
3194                        continue;
3195                    }
3196
3197                    let response = app.handle(&ctx, &mut request).await;
3198
3199                    // Send response on the same stream.
3200                    self.write_h2_response(
3201                        &mut framed,
3202                        response,
3203                        stream_id,
3204                        peer_max_frame_size,
3205                        recv_max_frame_size,
3206                        Some(&mut flow_control),
3207                    )
3208                    .await?;
3209
3210                    if let Some(tasks) = App::take_background_tasks(&mut request) {
3211                        tasks.execute_all().await;
3212                    }
3213
3214                    // Yield to keep cancellation responsive.
3215                    asupersync::runtime::yield_now().await;
3216                }
3217                http2::FrameType::WindowUpdate => {
3218                    validate_window_update_payload(&frame.payload)?;
3219                    let increment = u32::from_be_bytes([
3220                        frame.payload[0],
3221                        frame.payload[1],
3222                        frame.payload[2],
3223                        frame.payload[3],
3224                    ]) & 0x7FFF_FFFF;
3225                    if frame.header.stream_id == 0 {
3226                        apply_send_conn_window_update(&mut flow_control, increment)?;
3227                    }
3228                }
3229                _ => {
3230                    handle_h2_idle_frame(&frame)?;
3231                }
3232            }
3233        }
3234    }
3235
3236    async fn write_h2_response(
3237        &self,
3238        framed: &mut http2::FramedH2,
3239        response: Response,
3240        stream_id: u32,
3241        mut peer_max_frame_size: u32,
3242        recv_max_frame_size: u32,
3243        mut flow_control: Option<&mut http2::H2FlowControl>,
3244    ) -> Result<(), ServerError> {
3245        use std::future::poll_fn;
3246
3247        const FLAG_END_STREAM: u8 = 0x1;
3248        const FLAG_END_HEADERS: u8 = 0x4;
3249
3250        let (status, mut headers, mut body) = response.into_parts();
3251        if !status.allows_body() {
3252            body = fastapi_core::ResponseBody::Empty;
3253        }
3254
3255        let mut add_content_length = matches!(body, fastapi_core::ResponseBody::Bytes(_));
3256        for (name, _) in &headers {
3257            if name.eq_ignore_ascii_case("content-length") {
3258                add_content_length = false;
3259                break;
3260            }
3261        }
3262
3263        if add_content_length {
3264            let len = body.len();
3265            headers.push(("content-length".to_string(), len.to_string().into_bytes()));
3266        }
3267
3268        // Encode headers: :status + response headers (filtered for HTTP/2).
3269        let mut block: Vec<u8> = Vec::new();
3270        let status_bytes = status.as_u16().to_string().into_bytes();
3271        http2::hpack_encode_literal_without_indexing(&mut block, b":status", &status_bytes);
3272
3273        for (name, value) in &headers {
3274            if is_h2_forbidden_header_name(name) {
3275                continue;
3276            }
3277            let n = name.to_ascii_lowercase();
3278            http2::hpack_encode_literal_without_indexing(&mut block, n.as_bytes(), value);
3279        }
3280
3281        // Write HEADERS + CONTINUATION if needed.
3282        let max = usize::try_from(peer_max_frame_size).unwrap_or(16 * 1024);
3283        if block.len() <= max {
3284            let mut flags = FLAG_END_HEADERS;
3285            if body.is_empty() {
3286                flags |= FLAG_END_STREAM;
3287            }
3288            framed
3289                .write_frame(http2::FrameType::Headers, flags, stream_id, &block)
3290                .await?;
3291            self.record_bytes_out((http2::FrameHeader::LEN + block.len()) as u64);
3292        } else {
3293            let mut flags = 0u8;
3294            if body.is_empty() {
3295                flags |= FLAG_END_STREAM;
3296            }
3297            let (first, rest) = block.split_at(max);
3298            framed
3299                .write_frame(http2::FrameType::Headers, flags, stream_id, first)
3300                .await?;
3301            self.record_bytes_out((http2::FrameHeader::LEN + first.len()) as u64);
3302
3303            let mut remaining = rest;
3304            while remaining.len() > max {
3305                let (chunk, r) = remaining.split_at(max);
3306                framed
3307                    .write_frame(http2::FrameType::Continuation, 0, stream_id, chunk)
3308                    .await?;
3309                self.record_bytes_out((http2::FrameHeader::LEN + chunk.len()) as u64);
3310                remaining = r;
3311            }
3312            framed
3313                .write_frame(
3314                    http2::FrameType::Continuation,
3315                    FLAG_END_HEADERS,
3316                    stream_id,
3317                    remaining,
3318                )
3319                .await?;
3320            self.record_bytes_out((http2::FrameHeader::LEN + remaining.len()) as u64);
3321        }
3322
3323        // Track per-stream send window (peer's receive window for this stream).
3324        let mut stream_send_window: i64 = flow_control
3325            .as_ref()
3326            .map_or(i64::MAX, |fc| i64::from(fc.peer_initial_window_size()));
3327
3328        // Write body with send-side flow control.
3329        match body {
3330            fastapi_core::ResponseBody::Empty => Ok(()),
3331            fastapi_core::ResponseBody::Bytes(bytes) => {
3332                if bytes.is_empty() {
3333                    return Ok(());
3334                }
3335                let mut remaining = bytes.as_slice();
3336                while !remaining.is_empty() {
3337                    let max = usize::try_from(peer_max_frame_size).unwrap_or(16 * 1024);
3338                    let send_len = remaining.len().min(max);
3339                    let send_len = h2_fc_clamp_send(
3340                        framed,
3341                        &mut flow_control,
3342                        &mut stream_send_window,
3343                        stream_id,
3344                        send_len,
3345                        &mut peer_max_frame_size,
3346                        recv_max_frame_size,
3347                    )
3348                    .await?;
3349
3350                    let (chunk, r) = remaining.split_at(send_len);
3351                    let flags = if r.is_empty() { FLAG_END_STREAM } else { 0 };
3352                    framed
3353                        .write_frame(http2::FrameType::Data, flags, stream_id, chunk)
3354                        .await?;
3355                    self.record_bytes_out((http2::FrameHeader::LEN + chunk.len()) as u64);
3356                    remaining = r;
3357                }
3358                Ok(())
3359            }
3360            fastapi_core::ResponseBody::Stream(mut s) => {
3361                loop {
3362                    let next = poll_fn(|cx| Pin::new(&mut s).poll_next(cx)).await;
3363                    match next {
3364                        Some(chunk) => {
3365                            let mut remaining = chunk.as_slice();
3366                            while !remaining.is_empty() {
3367                                let max = usize::try_from(peer_max_frame_size).unwrap_or(16 * 1024);
3368                                let send_len = remaining.len().min(max);
3369                                let send_len = h2_fc_clamp_send(
3370                                    framed,
3371                                    &mut flow_control,
3372                                    &mut stream_send_window,
3373                                    stream_id,
3374                                    send_len,
3375                                    &mut peer_max_frame_size,
3376                                    recv_max_frame_size,
3377                                )
3378                                .await?;
3379
3380                                let (c, r) = remaining.split_at(send_len);
3381                                framed
3382                                    .write_frame(http2::FrameType::Data, 0, stream_id, c)
3383                                    .await?;
3384                                self.record_bytes_out((http2::FrameHeader::LEN + c.len()) as u64);
3385                                remaining = r;
3386                            }
3387                        }
3388                        None => {
3389                            framed
3390                                .write_frame(
3391                                    http2::FrameType::Data,
3392                                    FLAG_END_STREAM,
3393                                    stream_id,
3394                                    &[],
3395                                )
3396                                .await?;
3397                            self.record_bytes_out(http2::FrameHeader::LEN as u64);
3398                            break;
3399                        }
3400                    }
3401                }
3402                Ok(())
3403            }
3404        }
3405    }
3406
3407    async fn handle_connection_handler_http2(
3408        &self,
3409        cx: &Cx,
3410        stream: TcpStream,
3411        handler: &dyn fastapi_core::Handler,
3412    ) -> Result<(), ServerError> {
3413        const FLAG_END_HEADERS: u8 = 0x4;
3414        const FLAG_ACK: u8 = 0x1;
3415
3416        let mut framed = http2::FramedH2::new(stream, Vec::new());
3417        let mut hpack = http2::HpackDecoder::new();
3418        let recv_max_frame_size: u32 = 16 * 1024;
3419        let mut peer_max_frame_size: u32 = 16 * 1024;
3420        let mut flow_control = http2::H2FlowControl::new();
3421
3422        let first = framed.read_frame(recv_max_frame_size).await?;
3423        self.record_bytes_in((http2::FrameHeader::LEN + first.payload.len()) as u64);
3424
3425        if first.header.frame_type() != http2::FrameType::Settings
3426            || first.header.stream_id != 0
3427            || (first.header.flags & FLAG_ACK) != 0
3428        {
3429            return Err(
3430                http2::Http2Error::Protocol("expected client SETTINGS after preface").into(),
3431            );
3432        }
3433
3434        apply_http2_settings_with_fc(
3435            &mut hpack,
3436            &mut peer_max_frame_size,
3437            Some(&mut flow_control),
3438            &first.payload,
3439        )?;
3440
3441        framed
3442            .write_frame(http2::FrameType::Settings, 0, 0, SERVER_SETTINGS_PAYLOAD)
3443            .await?;
3444        self.record_bytes_out(http2::FrameHeader::LEN as u64);
3445
3446        framed
3447            .write_frame(http2::FrameType::Settings, FLAG_ACK, 0, &[])
3448            .await?;
3449        self.record_bytes_out(http2::FrameHeader::LEN as u64);
3450
3451        let default_body_limit = self.config.parse_limits.max_request_size;
3452        let mut last_stream_id: u32 = 0;
3453
3454        loop {
3455            if cx.is_cancel_requested() {
3456                let _ = send_goaway(&mut framed, last_stream_id, h2_error_code::NO_ERROR).await;
3457                return Ok(());
3458            }
3459
3460            let frame = framed.read_frame(recv_max_frame_size).await?;
3461            self.record_bytes_in((http2::FrameHeader::LEN + frame.payload.len()) as u64);
3462
3463            match frame.header.frame_type() {
3464                http2::FrameType::Settings => {
3465                    let is_ack = validate_settings_frame(
3466                        frame.header.stream_id,
3467                        frame.header.flags,
3468                        &frame.payload,
3469                    )?;
3470                    if is_ack {
3471                        continue;
3472                    }
3473                    apply_http2_settings_with_fc(
3474                        &mut hpack,
3475                        &mut peer_max_frame_size,
3476                        Some(&mut flow_control),
3477                        &frame.payload,
3478                    )?;
3479                    framed
3480                        .write_frame(http2::FrameType::Settings, FLAG_ACK, 0, &[])
3481                        .await?;
3482                    self.record_bytes_out(http2::FrameHeader::LEN as u64);
3483                }
3484                http2::FrameType::Ping => {
3485                    if frame.header.stream_id != 0 || frame.payload.len() != 8 {
3486                        return Err(http2::Http2Error::Protocol("invalid PING frame").into());
3487                    }
3488                    if (frame.header.flags & FLAG_ACK) == 0 {
3489                        framed
3490                            .write_frame(http2::FrameType::Ping, FLAG_ACK, 0, &frame.payload)
3491                            .await?;
3492                        self.record_bytes_out((http2::FrameHeader::LEN + 8) as u64);
3493                    }
3494                }
3495                http2::FrameType::Goaway => {
3496                    validate_goaway_payload(&frame.payload)?;
3497                    return Ok(());
3498                }
3499                http2::FrameType::PushPromise => {
3500                    return Err(http2::Http2Error::Protocol(
3501                        "PUSH_PROMISE not supported by server",
3502                    )
3503                    .into());
3504                }
3505                http2::FrameType::Headers => {
3506                    let stream_id = frame.header.stream_id;
3507                    if stream_id == 0 {
3508                        return Err(
3509                            http2::Http2Error::Protocol("HEADERS must not be on stream 0").into(),
3510                        );
3511                    }
3512                    if stream_id % 2 == 0 {
3513                        return Err(http2::Http2Error::Protocol(
3514                            "client-initiated stream ID must be odd",
3515                        )
3516                        .into());
3517                    }
3518                    if stream_id <= last_stream_id {
3519                        return Err(http2::Http2Error::Protocol(
3520                            "stream ID must be greater than previous",
3521                        )
3522                        .into());
3523                    }
3524                    last_stream_id = stream_id;
3525                    let (end_stream, mut header_block) =
3526                        extract_header_block_fragment(frame.header.flags, &frame.payload)?;
3527
3528                    if (frame.header.flags & FLAG_END_HEADERS) == 0 {
3529                        loop {
3530                            let cont = framed.read_frame(recv_max_frame_size).await?;
3531                            self.record_bytes_in(
3532                                (http2::FrameHeader::LEN + cont.payload.len()) as u64,
3533                            );
3534                            if cont.header.frame_type() != http2::FrameType::Continuation
3535                                || cont.header.stream_id != stream_id
3536                            {
3537                                return Err(http2::Http2Error::Protocol(
3538                                    "expected CONTINUATION for header block",
3539                                )
3540                                .into());
3541                            }
3542                            header_block.extend_from_slice(&cont.payload);
3543                            if header_block.len() > MAX_HEADER_BLOCK_SIZE {
3544                                return Err(http2::Http2Error::Protocol(
3545                                    "header block exceeds maximum size",
3546                                )
3547                                .into());
3548                            }
3549                            if (cont.header.flags & FLAG_END_HEADERS) != 0 {
3550                                break;
3551                            }
3552                        }
3553                    }
3554
3555                    let headers = hpack
3556                        .decode(&header_block)
3557                        .map_err(http2::Http2Error::from)?;
3558                    let mut request = request_from_h2_headers(headers)?;
3559
3560                    if !end_stream {
3561                        let mut body = Vec::new();
3562                        let mut stream_reset = false;
3563                        let mut stream_received: u32 = 0;
3564                        loop {
3565                            let f = framed.read_frame(recv_max_frame_size).await?;
3566                            self.record_bytes_in(
3567                                (http2::FrameHeader::LEN + f.payload.len()) as u64,
3568                            );
3569                            match f.header.frame_type() {
3570                                http2::FrameType::Data if f.header.stream_id == 0 => {
3571                                    return Err(http2::Http2Error::Protocol(
3572                                        "DATA must not be on stream 0",
3573                                    )
3574                                    .into());
3575                                }
3576                                http2::FrameType::Data if f.header.stream_id == stream_id => {
3577                                    let (data, data_end_stream) =
3578                                        extract_data_payload(f.header.flags, &f.payload)?;
3579                                    if body.len().saturating_add(data.len()) > default_body_limit {
3580                                        return Err(http2::Http2Error::Protocol(
3581                                            "request body exceeds configured limit",
3582                                        )
3583                                        .into());
3584                                    }
3585                                    body.extend_from_slice(data);
3586
3587                                    // Flow control: track received data and send
3588                                    // WINDOW_UPDATEs to prevent sender stalling.
3589                                    let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
3590                                    stream_received += data_len;
3591                                    let conn_inc = flow_control.data_received_connection(data_len);
3592                                    let stream_inc =
3593                                        flow_control.stream_window_update(stream_received);
3594                                    if stream_inc > 0 {
3595                                        stream_received = 0;
3596                                    }
3597                                    send_window_updates(
3598                                        &mut framed,
3599                                        conn_inc,
3600                                        stream_id,
3601                                        stream_inc,
3602                                    )
3603                                    .await?;
3604
3605                                    if data_end_stream {
3606                                        break;
3607                                    }
3608                                }
3609                                http2::FrameType::RstStream => {
3610                                    validate_rst_stream_payload(f.header.stream_id, &f.payload)?;
3611                                    if f.header.stream_id == stream_id {
3612                                        stream_reset = true;
3613                                        break;
3614                                    }
3615                                }
3616                                http2::FrameType::PushPromise => {
3617                                    return Err(http2::Http2Error::Protocol(
3618                                        "PUSH_PROMISE not supported by server",
3619                                    )
3620                                    .into());
3621                                }
3622                                http2::FrameType::Settings
3623                                | http2::FrameType::Ping
3624                                | http2::FrameType::Goaway
3625                                | http2::FrameType::WindowUpdate
3626                                | http2::FrameType::Priority
3627                                | http2::FrameType::Unknown => {
3628                                    if f.header.frame_type() == http2::FrameType::Goaway {
3629                                        validate_goaway_payload(&f.payload)?;
3630                                        return Ok(());
3631                                    }
3632                                    if f.header.frame_type() == http2::FrameType::Priority {
3633                                        validate_priority_payload(f.header.stream_id, &f.payload)?;
3634                                    }
3635                                    if f.header.frame_type() == http2::FrameType::WindowUpdate {
3636                                        validate_window_update_payload(&f.payload)?;
3637                                        let increment = u32::from_be_bytes([
3638                                            f.payload[0],
3639                                            f.payload[1],
3640                                            f.payload[2],
3641                                            f.payload[3],
3642                                        ]) & 0x7FFF_FFFF;
3643                                        if f.header.stream_id == 0 {
3644                                            apply_send_conn_window_update(
3645                                                &mut flow_control,
3646                                                increment,
3647                                            )?;
3648                                        }
3649                                    }
3650                                    if f.header.frame_type() == http2::FrameType::Ping {
3651                                        if f.header.stream_id != 0 || f.payload.len() != 8 {
3652                                            return Err(http2::Http2Error::Protocol(
3653                                                "invalid PING frame",
3654                                            )
3655                                            .into());
3656                                        }
3657                                        if (f.header.flags & FLAG_ACK) == 0 {
3658                                            framed
3659                                                .write_frame(
3660                                                    http2::FrameType::Ping,
3661                                                    FLAG_ACK,
3662                                                    0,
3663                                                    &f.payload,
3664                                                )
3665                                                .await?;
3666                                            self.record_bytes_out(
3667                                                (http2::FrameHeader::LEN + 8) as u64,
3668                                            );
3669                                        }
3670                                    }
3671                                    if f.header.frame_type() == http2::FrameType::Settings {
3672                                        let is_ack = validate_settings_frame(
3673                                            f.header.stream_id,
3674                                            f.header.flags,
3675                                            &f.payload,
3676                                        )?;
3677                                        if !is_ack {
3678                                            apply_http2_settings_with_fc(
3679                                                &mut hpack,
3680                                                &mut peer_max_frame_size,
3681                                                Some(&mut flow_control),
3682                                                &f.payload,
3683                                            )?;
3684                                            framed
3685                                                .write_frame(
3686                                                    http2::FrameType::Settings,
3687                                                    FLAG_ACK,
3688                                                    0,
3689                                                    &[],
3690                                                )
3691                                                .await?;
3692                                            self.record_bytes_out(http2::FrameHeader::LEN as u64);
3693                                        }
3694                                    }
3695                                }
3696                                _ => {
3697                                    return Err(http2::Http2Error::Protocol(
3698                                        "unsupported frame while reading request body",
3699                                    )
3700                                    .into());
3701                                }
3702                            }
3703                        }
3704                        if stream_reset {
3705                            continue;
3706                        }
3707                        request.set_body(fastapi_core::Body::Bytes(body));
3708                    }
3709
3710                    let request_id = self.request_counter.fetch_add(1, Ordering::Relaxed);
3711                    let request_budget =
3712                        Budget::new().with_deadline(request_deadline(self.config.request_timeout));
3713                    let request_cx = request_cx_from_parent(cx, request_budget);
3714
3715                    let overrides = handler
3716                        .dependency_overrides()
3717                        .unwrap_or_else(|| Arc::new(fastapi_core::DependencyOverrides::new()));
3718
3719                    let ctx = RequestContext::with_overrides_and_body_limit(
3720                        request_cx,
3721                        request_id,
3722                        overrides,
3723                        default_body_limit,
3724                    );
3725
3726                    if let Err(err) = validate_host_header(&request, &self.config) {
3727                        let response = err.response();
3728                        self.write_h2_response(
3729                            &mut framed,
3730                            response,
3731                            stream_id,
3732                            peer_max_frame_size,
3733                            recv_max_frame_size,
3734                            Some(&mut flow_control),
3735                        )
3736                        .await?;
3737                        continue;
3738                    }
3739                    if let Err(response) = self.config.pre_body_validators.validate_all(&request) {
3740                        self.write_h2_response(
3741                            &mut framed,
3742                            response,
3743                            stream_id,
3744                            peer_max_frame_size,
3745                            recv_max_frame_size,
3746                            Some(&mut flow_control),
3747                        )
3748                        .await?;
3749                        continue;
3750                    }
3751
3752                    let response = handler.call(&ctx, &mut request).await;
3753                    self.write_h2_response(
3754                        &mut framed,
3755                        response,
3756                        stream_id,
3757                        peer_max_frame_size,
3758                        recv_max_frame_size,
3759                        Some(&mut flow_control),
3760                    )
3761                    .await?;
3762                }
3763                http2::FrameType::WindowUpdate => {
3764                    validate_window_update_payload(&frame.payload)?;
3765                    let increment = u32::from_be_bytes([
3766                        frame.payload[0],
3767                        frame.payload[1],
3768                        frame.payload[2],
3769                        frame.payload[3],
3770                    ]) & 0x7FFF_FFFF;
3771                    if frame.header.stream_id == 0 {
3772                        apply_send_conn_window_update(&mut flow_control, increment)?;
3773                    }
3774                }
3775                _ => {
3776                    handle_h2_idle_frame(&frame)?;
3777                }
3778            }
3779        }
3780    }
3781
3782    /// Handles a single connection using the Handler trait.
3783    ///
3784    /// This is a specialized version for trait objects where we cannot use a closure
3785    /// due to lifetime constraints of BoxFuture.
3786    async fn handle_connection_handler(
3787        &self,
3788        cx: &Cx,
3789        mut stream: TcpStream,
3790        _peer_addr: SocketAddr,
3791        handler: &dyn fastapi_core::Handler,
3792    ) -> Result<(), ServerError> {
3793        let (proto, buffered) = sniff_protocol(&mut stream, self.config.keep_alive_timeout).await?;
3794        if !buffered.is_empty() {
3795            self.record_bytes_in(buffered.len() as u64);
3796        }
3797        if proto == SniffedProtocol::Http2PriorKnowledge {
3798            return self
3799                .handle_connection_handler_http2(cx, stream, handler)
3800                .await;
3801        }
3802
3803        let mut parser = StatefulParser::new().with_limits(self.config.parse_limits.clone());
3804        if !buffered.is_empty() {
3805            parser.feed(&buffered)?;
3806        }
3807        let mut read_buffer = vec![0u8; self.config.read_buffer_size];
3808        let mut response_writer = ResponseWriter::new();
3809        let mut requests_on_connection: usize = 0;
3810        let max_requests = self.config.max_requests_per_connection;
3811
3812        loop {
3813            // Check for cancellation
3814            if cx.is_cancel_requested() {
3815                return Ok(());
3816            }
3817
3818            // Parse request from connection
3819            let parse_result = parser.feed(&[])?;
3820
3821            let mut request = match parse_result {
3822                ParseStatus::Complete { request, .. } => request,
3823                ParseStatus::Incomplete => {
3824                    let keep_alive_timeout = self.config.keep_alive_timeout;
3825                    let bytes_read = if keep_alive_timeout.is_zero() {
3826                        read_into_buffer(&mut stream, &mut read_buffer).await?
3827                    } else {
3828                        match read_with_timeout(&mut stream, &mut read_buffer, keep_alive_timeout)
3829                            .await
3830                        {
3831                            Ok(0) => return Ok(()),
3832                            Ok(n) => n,
3833                            Err(e) if e.kind() == io::ErrorKind::TimedOut => {
3834                                self.metrics_counters
3835                                    .total_timed_out
3836                                    .fetch_add(1, Ordering::Relaxed);
3837                                return Err(ServerError::KeepAliveTimeout);
3838                            }
3839                            Err(e) => return Err(ServerError::Io(e)),
3840                        }
3841                    };
3842
3843                    if bytes_read == 0 {
3844                        return Ok(());
3845                    }
3846
3847                    self.record_bytes_in(bytes_read as u64);
3848
3849                    match parser.feed(&read_buffer[..bytes_read])? {
3850                        ParseStatus::Complete { request, .. } => request,
3851                        ParseStatus::Incomplete => continue,
3852                    }
3853                }
3854            };
3855
3856            requests_on_connection += 1;
3857
3858            // Create request context
3859            let request_id = self.request_counter.fetch_add(1, Ordering::Relaxed);
3860            let request_budget =
3861                Budget::new().with_deadline(request_deadline(self.config.request_timeout));
3862            let request_cx = request_cx_from_parent(cx, request_budget);
3863            let ctx = RequestContext::new(request_cx, request_id);
3864
3865            // Validate Host header
3866            if let Err(err) = validate_host_header(&request, &self.config) {
3867                let response = err.response().header("connection", b"close".to_vec());
3868                let response_write = response_writer.write(response);
3869                write_response(&mut stream, response_write).await?;
3870                return Ok(());
3871            }
3872
3873            // Run header-only validators before reading any body bytes.
3874            if let Err(response) = self.config.pre_body_validators.validate_all(&request) {
3875                let response = response.header("connection", b"close".to_vec());
3876                let response_write = response_writer.write(response);
3877                write_response(&mut stream, response_write).await?;
3878                return Ok(());
3879            }
3880
3881            // Handle Expect: 100-continue
3882            match ExpectHandler::check_expect(&request) {
3883                ExpectResult::NoExpectation => {}
3884                ExpectResult::ExpectsContinue => {
3885                    write_raw_response(&mut stream, CONTINUE_RESPONSE).await?;
3886                }
3887                ExpectResult::UnknownExpectation(_) => {
3888                    let response =
3889                        ExpectHandler::expectation_failed("Unsupported Expect value".to_string());
3890                    let response_write = response_writer.write(response);
3891                    write_response(&mut stream, response_write).await?;
3892                    return Ok(());
3893                }
3894            }
3895
3896            // Call handler - ctx lives until after await
3897            let response = handler.call(&ctx, &mut request).await;
3898
3899            // Determine keep-alive behavior
3900            let client_wants_keep_alive = should_keep_alive(&request);
3901            let server_will_keep_alive = client_wants_keep_alive
3902                && (max_requests == 0 || requests_on_connection < max_requests);
3903
3904            let response = if server_will_keep_alive {
3905                response.header("connection", b"keep-alive".to_vec())
3906            } else {
3907                response.header("connection", b"close".to_vec())
3908            };
3909
3910            let response_write = response_writer.write(response);
3911            if let ResponseWrite::Full(ref bytes) = response_write {
3912                self.record_bytes_out(bytes.len() as u64);
3913            }
3914            write_response(&mut stream, response_write).await?;
3915
3916            if !server_will_keep_alive {
3917                return Ok(());
3918            }
3919        }
3920    }
3921
3922    /// The main accept loop.
3923    async fn accept_loop<H, Fut>(
3924        &self,
3925        cx: &Cx,
3926        listener: TcpListener,
3927        handler: H,
3928    ) -> Result<(), ServerError>
3929    where
3930        H: Fn(RequestContext, &mut Request) -> Fut + Send + Sync + 'static,
3931        Fut: Future<Output = Response> + Send + 'static,
3932    {
3933        let handler = Arc::new(handler);
3934
3935        loop {
3936            // Check for cancellation at each iteration.
3937            if cx.is_cancel_requested() {
3938                cx.trace("Server shutdown requested");
3939                return Ok(());
3940            }
3941
3942            // Check if draining (graceful shutdown)
3943            if self.is_draining() {
3944                cx.trace("Server draining, stopping accept loop");
3945                return Err(ServerError::Shutdown);
3946            }
3947
3948            // Accept a connection.
3949            let (mut stream, peer_addr) = match listener.accept().await {
3950                Ok(conn) => conn,
3951                Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
3952                    // Yield and retry.
3953                    continue;
3954                }
3955                Err(e) => {
3956                    cx.trace(&format!("Accept error: {e}"));
3957                    // For most errors, we continue accepting.
3958                    // Only fatal errors should propagate.
3959                    if is_fatal_accept_error(&e) {
3960                        return Err(ServerError::Io(e));
3961                    }
3962                    continue;
3963                }
3964            };
3965
3966            // Check connection limit before processing
3967            if !self.try_acquire_connection() {
3968                cx.trace(&format!(
3969                    "Connection limit reached ({}), rejecting {peer_addr}",
3970                    self.config.max_connections
3971                ));
3972
3973                // Send a 503 Service Unavailable response and close
3974                let response = Response::with_status(StatusCode::SERVICE_UNAVAILABLE)
3975                    .header("connection", b"close".to_vec())
3976                    .body(fastapi_core::ResponseBody::Bytes(
3977                        b"503 Service Unavailable: connection limit reached".to_vec(),
3978                    ));
3979                let mut writer = crate::response::ResponseWriter::new();
3980                let response_bytes = writer.write(response);
3981                let _ = write_response(&mut stream, response_bytes).await;
3982                continue;
3983            }
3984
3985            // Configure the connection.
3986            if self.config.tcp_nodelay {
3987                let _ = stream.set_nodelay(true);
3988            }
3989
3990            cx.trace(&format!(
3991                "Accepted connection from {peer_addr} ({}/{})",
3992                self.current_connections(),
3993                if self.config.max_connections == 0 {
3994                    "∞".to_string()
3995                } else {
3996                    self.config.max_connections.to_string()
3997                }
3998            ));
3999
4000            // Handle inline (single-threaded accept loop).
4001            //
4002            // For concurrent connection handling with structured concurrency, use
4003            // `TcpServer::serve_concurrent()` which spawns tasks via `RuntimeHandle`.
4004            let request_id = self.next_request_id();
4005            let request_budget =
4006                Budget::new().with_deadline(request_deadline(self.config.request_timeout));
4007
4008            // Create a RequestContext for this request from the runtime-bound context.
4009            let request_cx = request_cx_from_parent(cx, request_budget);
4010            let ctx = RequestContext::new(request_cx, request_id);
4011
4012            // Handle the connection and release the slot when done.
4013            let result = self
4014                .handle_connection(&ctx, stream, peer_addr, &*handler)
4015                .await;
4016
4017            // Release connection slot (always, regardless of success/failure)
4018            self.release_connection();
4019
4020            if let Err(e) = result {
4021                cx.trace(&format!("Connection error from {peer_addr}: {e}"));
4022            }
4023        }
4024    }
4025
4026    /// Handles a single connection.
4027    ///
4028    /// This reads requests from the connection, passes them to the handler,
4029    /// and sends responses. For HTTP/1.1, it handles keep-alive by processing
4030    /// multiple requests on the same connection.
4031    async fn handle_connection<H, Fut>(
4032        &self,
4033        ctx: &RequestContext,
4034        stream: TcpStream,
4035        peer_addr: SocketAddr,
4036        handler: &H,
4037    ) -> Result<(), ServerError>
4038    where
4039        H: Fn(RequestContext, &mut Request) -> Fut + Send + Sync,
4040        Fut: Future<Output = Response> + Send,
4041    {
4042        process_connection(
4043            ctx.cx(),
4044            &self.request_counter,
4045            stream,
4046            peer_addr,
4047            &self.config,
4048            |ctx, req| handler(ctx, req),
4049        )
4050        .await
4051    }
4052}
4053
4054/// Snapshot of server metrics at a point in time.
4055///
4056/// Returned by [`TcpServer::metrics()`]. All counters are monotonically
4057/// increasing except `active_connections` which reflects the current gauge.
4058#[derive(Debug, Clone, PartialEq, Eq)]
4059pub struct ServerMetrics {
4060    /// Current number of active (in-flight) connections.
4061    pub active_connections: u64,
4062    /// Total connections accepted since server start.
4063    pub total_accepted: u64,
4064    /// Total connections rejected due to connection limit.
4065    pub total_rejected: u64,
4066    /// Total requests that timed out.
4067    pub total_timed_out: u64,
4068    /// Total requests served since server start.
4069    pub total_requests: u64,
4070    /// Total bytes read from clients.
4071    pub bytes_in: u64,
4072    /// Total bytes written to clients.
4073    pub bytes_out: u64,
4074}
4075
4076/// Atomic counters backing [`ServerMetrics`].
4077///
4078/// These live inside `TcpServer` and are updated as connections are
4079/// accepted, rejected, or timed out.
4080#[derive(Debug)]
4081struct MetricsCounters {
4082    total_accepted: AtomicU64,
4083    total_rejected: AtomicU64,
4084    total_timed_out: AtomicU64,
4085    bytes_in: AtomicU64,
4086    bytes_out: AtomicU64,
4087}
4088
4089impl MetricsCounters {
4090    fn new() -> Self {
4091        Self {
4092            total_accepted: AtomicU64::new(0),
4093            total_rejected: AtomicU64::new(0),
4094            total_timed_out: AtomicU64::new(0),
4095            bytes_in: AtomicU64::new(0),
4096            bytes_out: AtomicU64::new(0),
4097        }
4098    }
4099}
4100
4101impl Default for TcpServer {
4102    fn default() -> Self {
4103        Self::new(ServerConfig::default())
4104    }
4105}
4106
4107/// Returns true if the accept error is fatal (should stop the server).
4108fn is_fatal_accept_error(e: &io::Error) -> bool {
4109    // These errors indicate the listener itself is broken.
4110    matches!(
4111        e.kind(),
4112        io::ErrorKind::NotConnected | io::ErrorKind::InvalidInput
4113    )
4114}
4115
4116/// Reads data from a TCP stream into a buffer.
4117///
4118/// Returns the number of bytes read, or 0 if the connection was closed.
4119///
4120/// This is a thin wrapper around [`AsyncRead::poll_read`] exposed for use in
4121/// custom connection handlers that need low-level stream I/O.
4122pub async fn read_into_buffer(stream: &mut TcpStream, buffer: &mut [u8]) -> io::Result<usize> {
4123    use std::future::poll_fn;
4124
4125    poll_fn(|cx| {
4126        let mut read_buf = ReadBuf::new(buffer);
4127        match Pin::new(&mut *stream).poll_read(cx, &mut read_buf) {
4128            Poll::Ready(Ok(())) => Poll::Ready(Ok(read_buf.filled().len())),
4129            Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
4130            Poll::Pending => Poll::Pending,
4131        }
4132    })
4133    .await
4134}
4135
4136/// Reads data from a TCP stream with a timeout.
4137///
4138/// Uses asupersync's timer system for proper async timeout handling.
4139/// The timeout is implemented using asupersync's `timeout` future wrapper,
4140/// which properly integrates with the async runtime's timer driver.
4141///
4142/// # Arguments
4143///
4144/// * `stream` - The TCP stream to read from
4145/// * `buffer` - The buffer to read into
4146/// * `timeout_duration` - Maximum time to wait for data
4147///
4148/// # Returns
4149///
4150/// * `Ok(n)` - Number of bytes read (0 means connection closed)
4151/// * `Err(TimedOut)` - Timeout expired with no data
4152/// * `Err(other)` - IO error from the underlying stream
4153async fn read_with_timeout(
4154    stream: &mut TcpStream,
4155    buffer: &mut [u8],
4156    timeout_duration: Duration,
4157) -> io::Result<usize> {
4158    // Get current time for the timeout calculation
4159    let now = current_time();
4160
4161    // Create the read future - we need to box it for Unpin
4162    let read_future = Box::pin(read_into_buffer(stream, buffer));
4163
4164    // Wrap with asupersync timeout
4165    match timeout(now, timeout_duration, read_future).await {
4166        Ok(result) => result,
4167        Err(_elapsed) => Err(io::Error::new(
4168            io::ErrorKind::TimedOut,
4169            "keep-alive timeout expired",
4170        )),
4171    }
4172}
4173
4174#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4175enum SniffedProtocol {
4176    Http1,
4177    Http2PriorKnowledge,
4178}
4179
4180/// Sniff whether the connection is HTTP/2 prior-knowledge (h2c preface).
4181///
4182/// Returns the inferred protocol and the bytes already consumed from the stream.
4183async fn sniff_protocol(
4184    stream: &mut TcpStream,
4185    keep_alive_timeout: Duration,
4186) -> io::Result<(SniffedProtocol, Vec<u8>)> {
4187    let mut buffered: Vec<u8> = Vec::new();
4188    let preface = http2::PREFACE;
4189
4190    while buffered.len() < preface.len() {
4191        let mut tmp = vec![0u8; preface.len() - buffered.len()];
4192        let n = if keep_alive_timeout.is_zero() {
4193            read_into_buffer(stream, &mut tmp).await?
4194        } else {
4195            read_with_timeout(stream, &mut tmp, keep_alive_timeout).await?
4196        };
4197        if n == 0 {
4198            // EOF before any meaningful determination; treat as HTTP/1 with whatever we saw.
4199            return Ok((SniffedProtocol::Http1, buffered));
4200        }
4201
4202        buffered.extend_from_slice(&tmp[..n]);
4203        if !preface.starts_with(&buffered) {
4204            return Ok((SniffedProtocol::Http1, buffered));
4205        }
4206    }
4207
4208    Ok((SniffedProtocol::Http2PriorKnowledge, buffered))
4209}
4210
4211const SETTINGS_HEADER_TABLE_SIZE: u16 = 0x1;
4212const SETTINGS_ENABLE_PUSH: u16 = 0x2;
4213const SETTINGS_MAX_CONCURRENT_STREAMS: u16 = 0x3;
4214const SETTINGS_INITIAL_WINDOW_SIZE: u16 = 0x4;
4215const SETTINGS_MAX_FRAME_SIZE: u16 = 0x5;
4216const SETTINGS_MAX_HEADER_LIST_SIZE: u16 = 0x6;
4217
4218fn apply_http2_settings(
4219    hpack: &mut http2::HpackDecoder,
4220    max_frame_size: &mut u32,
4221    payload: &[u8],
4222) -> Result<(), http2::Http2Error> {
4223    apply_http2_settings_with_fc(hpack, max_frame_size, None, payload)
4224}
4225
4226fn apply_http2_settings_with_fc(
4227    hpack: &mut http2::HpackDecoder,
4228    max_frame_size: &mut u32,
4229    mut flow_control: Option<&mut http2::H2FlowControl>,
4230    payload: &[u8],
4231) -> Result<(), http2::Http2Error> {
4232    // SETTINGS payload is a sequence of (u16 id, u32 value) pairs.
4233    if payload.len() % 6 != 0 {
4234        return Err(http2::Http2Error::Protocol(
4235            "SETTINGS length must be a multiple of 6",
4236        ));
4237    }
4238
4239    for chunk in payload.chunks_exact(6) {
4240        let id = u16::from_be_bytes([chunk[0], chunk[1]]);
4241        let value = u32::from_be_bytes([chunk[2], chunk[3], chunk[4], chunk[5]]);
4242        match id {
4243            SETTINGS_HEADER_TABLE_SIZE => {
4244                // SETTINGS_HEADER_TABLE_SIZE — cap to prevent memory exhaustion.
4245                let capped = (value as usize).min(MAX_HPACK_TABLE_SIZE);
4246                hpack.set_dynamic_table_max_size(capped);
4247            }
4248            SETTINGS_MAX_CONCURRENT_STREAMS => {
4249                // Informational until the server supports multiplexed streams.
4250            }
4251            SETTINGS_INITIAL_WINDOW_SIZE => {
4252                // SETTINGS_INITIAL_WINDOW_SIZE (RFC 7540 §6.5.2)
4253                // Must not exceed 2^31 - 1.
4254                if value > 0x7FFF_FFFF {
4255                    return Err(http2::Http2Error::Protocol(
4256                        "SETTINGS_INITIAL_WINDOW_SIZE exceeds maximum",
4257                    ));
4258                }
4259                if let Some(ref mut fc) = flow_control {
4260                    // The peer's INITIAL_WINDOW_SIZE controls the send window
4261                    // for streams the peer will receive data on (our response
4262                    // streams). It must not alter our receive-side threshold.
4263                    fc.set_peer_initial_window_size(value);
4264                }
4265            }
4266            SETTINGS_MAX_FRAME_SIZE => {
4267                // SETTINGS_MAX_FRAME_SIZE (RFC 7540: 16384..=16777215)
4268                if !(16_384..=16_777_215).contains(&value) {
4269                    return Err(http2::Http2Error::Protocol(
4270                        "invalid SETTINGS_MAX_FRAME_SIZE",
4271                    ));
4272                }
4273                *max_frame_size = value;
4274            }
4275            SETTINGS_ENABLE_PUSH if value > 1 => {
4276                return Err(http2::Http2Error::Protocol(
4277                    "SETTINGS_ENABLE_PUSH must be 0 or 1",
4278                ));
4279            }
4280            SETTINGS_ENABLE_PUSH => {
4281                // SETTINGS_ENABLE_PUSH (RFC 7540 §6.5.2): must be 0 or 1.
4282                // We don't implement server push, so just validate.
4283            }
4284            SETTINGS_MAX_HEADER_LIST_SIZE => {
4285                // SETTINGS_MAX_HEADER_LIST_SIZE
4286                hpack.set_max_header_list_size(value as usize);
4287            }
4288            _ => {
4289                // Ignore unknown/unsupported settings (RFC 7540 §6.5.2).
4290            }
4291        }
4292    }
4293    Ok(())
4294}
4295
4296fn validate_settings_frame(
4297    stream_id: u32,
4298    flags: u8,
4299    payload: &[u8],
4300) -> Result<bool, http2::Http2Error> {
4301    const FLAG_ACK: u8 = 0x1;
4302    if stream_id != 0 {
4303        return Err(http2::Http2Error::Protocol("SETTINGS must be on stream 0"));
4304    }
4305
4306    let is_ack = (flags & FLAG_ACK) != 0;
4307    if is_ack && !payload.is_empty() {
4308        return Err(http2::Http2Error::Protocol(
4309            "SETTINGS ACK frame must have empty payload",
4310        ));
4311    }
4312
4313    Ok(is_ack)
4314}
4315
4316fn validate_window_update_payload(payload: &[u8]) -> Result<(), http2::Http2Error> {
4317    if payload.len() != 4 {
4318        return Err(http2::Http2Error::Protocol(
4319            "WINDOW_UPDATE payload must be 4 bytes",
4320        ));
4321    }
4322
4323    let raw = u32::from_be_bytes([payload[0], payload[1], payload[2], payload[3]]);
4324    let increment = raw & 0x7FFF_FFFF;
4325    if increment == 0 {
4326        return Err(http2::Http2Error::Protocol(
4327            "WINDOW_UPDATE increment must be non-zero",
4328        ));
4329    }
4330
4331    Ok(())
4332}
4333
4334fn handle_h2_idle_frame(frame: &http2::Frame) -> Result<(), http2::Http2Error> {
4335    match frame.header.frame_type() {
4336        http2::FrameType::RstStream => {
4337            validate_rst_stream_payload(frame.header.stream_id, &frame.payload)
4338        }
4339        http2::FrameType::Priority => {
4340            validate_priority_payload(frame.header.stream_id, &frame.payload)
4341        }
4342        http2::FrameType::Data => Err(http2::Http2Error::Protocol(
4343            "unexpected DATA frame outside active request stream",
4344        )),
4345        http2::FrameType::Continuation => Err(http2::Http2Error::Protocol(
4346            "unexpected CONTINUATION frame outside header block",
4347        )),
4348        http2::FrameType::Unknown => Ok(()),
4349        _ => Ok(()),
4350    }
4351}
4352
4353/// Maximum flow-control window size (2^31 - 1) per RFC 7540 §6.9.1.
4354const MAX_FLOW_CONTROL_WINDOW: i64 = 0x7FFF_FFFF;
4355
4356/// Server SETTINGS payload advertising SETTINGS_MAX_CONCURRENT_STREAMS = 1.
4357/// The server processes streams serially, so advertising this informs clients
4358/// to avoid opening multiple concurrent streams on one connection.
4359const SERVER_SETTINGS_PAYLOAD: &[u8] = &[
4360    0x00, 0x03, // SETTINGS_MAX_CONCURRENT_STREAMS
4361    0x00, 0x00, 0x00, 0x01, // value = 1
4362];
4363
4364/// Maximum HPACK dynamic table size we allow from peer SETTINGS.
4365/// Capped at 64 KiB to prevent gradual memory exhaustion on long-lived
4366/// connections where a client sets SETTINGS_HEADER_TABLE_SIZE to 4 GB.
4367const MAX_HPACK_TABLE_SIZE: usize = 64 * 1024;
4368
4369/// Maximum accumulated header block size across HEADERS + CONTINUATION frames.
4370/// Prevents CONTINUATION bomb attacks where an attacker sends unlimited
4371/// CONTINUATION frames to exhaust server memory before HPACK decoding.
4372/// Set to 128 KiB — generous enough for legitimate requests while limiting
4373/// memory exposure (the HPACK decoder enforces its own `max_header_list_size`
4374/// on the decoded output, defaulting to 64 KiB).
4375const MAX_HEADER_BLOCK_SIZE: usize = 128 * 1024;
4376
4377/// Apply a connection-level WINDOW_UPDATE from the peer with overflow detection.
4378/// Returns `Err(FLOW_CONTROL_ERROR)` if the window would exceed 2^31-1.
4379fn apply_send_conn_window_update(
4380    fc: &mut http2::H2FlowControl,
4381    increment: u32,
4382) -> Result<(), http2::Http2Error> {
4383    let new_window = fc.send_conn_window() + i64::from(increment);
4384    if new_window > MAX_FLOW_CONTROL_WINDOW {
4385        return Err(http2::Http2Error::Protocol(
4386            "WINDOW_UPDATE causes flow-control window to exceed 2^31-1",
4387        ));
4388    }
4389    fc.peer_window_update_connection(increment);
4390    Ok(())
4391}
4392
4393fn apply_peer_window_update_for_send(
4394    flow_control: &mut http2::H2FlowControl,
4395    stream_send_window: &mut i64,
4396    current_stream_id: u32,
4397    frame_stream_id: u32,
4398    payload: &[u8],
4399) -> Result<(), http2::Http2Error> {
4400    validate_window_update_payload(payload)?;
4401
4402    let increment =
4403        u32::from_be_bytes([payload[0], payload[1], payload[2], payload[3]]) & 0x7FFF_FFFF;
4404    if frame_stream_id == 0 {
4405        apply_send_conn_window_update(flow_control, increment)?;
4406    } else if frame_stream_id == current_stream_id {
4407        let new_window = *stream_send_window + i64::from(increment);
4408        if new_window > MAX_FLOW_CONTROL_WINDOW {
4409            return Err(http2::Http2Error::Protocol(
4410                "WINDOW_UPDATE causes flow-control window to exceed 2^31-1",
4411            ));
4412        }
4413        *stream_send_window = new_window;
4414    }
4415
4416    Ok(())
4417}
4418
4419fn apply_peer_settings_for_send(
4420    flow_control: &mut http2::H2FlowControl,
4421    stream_send_window: &mut i64,
4422    peer_max_frame_size: &mut u32,
4423    payload: &[u8],
4424) -> Result<(), http2::Http2Error> {
4425    if payload.len() % 6 != 0 {
4426        return Err(http2::Http2Error::Protocol(
4427            "SETTINGS length must be a multiple of 6",
4428        ));
4429    }
4430
4431    for chunk in payload.chunks_exact(6) {
4432        let id = u16::from_be_bytes([chunk[0], chunk[1]]);
4433        let value = u32::from_be_bytes([chunk[2], chunk[3], chunk[4], chunk[5]]);
4434
4435        if id == SETTINGS_INITIAL_WINDOW_SIZE {
4436            // SETTINGS_INITIAL_WINDOW_SIZE applies to all existing stream send windows.
4437            if value > 0x7FFF_FFFF {
4438                return Err(http2::Http2Error::Protocol(
4439                    "SETTINGS_INITIAL_WINDOW_SIZE exceeds maximum",
4440                ));
4441            }
4442            let old = i64::from(flow_control.peer_initial_window_size());
4443            let new = i64::from(value);
4444            let delta = new - old;
4445            let updated = *stream_send_window + delta;
4446            if updated > MAX_FLOW_CONTROL_WINDOW {
4447                return Err(http2::Http2Error::Protocol(
4448                    "SETTINGS_INITIAL_WINDOW_SIZE change causes stream window to exceed 2^31-1",
4449                ));
4450            }
4451            flow_control.set_peer_initial_window_size(value);
4452            *stream_send_window = updated;
4453        } else if id == 0x5 {
4454            // SETTINGS_MAX_FRAME_SIZE (RFC 7540 §6.5.2): 16384..=16777215.
4455            if !(16_384..=16_777_215).contains(&value) {
4456                return Err(http2::Http2Error::Protocol(
4457                    "invalid SETTINGS_MAX_FRAME_SIZE",
4458                ));
4459            }
4460            *peer_max_frame_size = value;
4461        }
4462    }
4463
4464    Ok(())
4465}
4466
4467/// Build the 4-byte WINDOW_UPDATE payload for a given increment.
4468fn window_update_payload(increment: u32) -> [u8; 4] {
4469    (increment & 0x7FFF_FFFF).to_be_bytes()
4470}
4471
4472/// Send WINDOW_UPDATE frames for both connection and stream levels after
4473/// receiving DATA. Returns early on zero increments.
4474async fn send_window_updates(
4475    framed: &mut http2::FramedH2,
4476    conn_increment: u32,
4477    stream_id: u32,
4478    stream_increment: u32,
4479) -> Result<(), http2::Http2Error> {
4480    if conn_increment > 0 {
4481        let payload = window_update_payload(conn_increment);
4482        framed
4483            .write_frame(http2::FrameType::WindowUpdate, 0, 0, &payload)
4484            .await?;
4485    }
4486    if stream_increment > 0 {
4487        let payload = window_update_payload(stream_increment);
4488        framed
4489            .write_frame(http2::FrameType::WindowUpdate, 0, stream_id, &payload)
4490            .await?;
4491    }
4492    Ok(())
4493}
4494
4495/// HTTP/2 error codes (RFC 7540 §7).
4496#[allow(dead_code)]
4497mod h2_error_code {
4498    pub const NO_ERROR: u32 = 0x0;
4499    pub const PROTOCOL_ERROR: u32 = 0x1;
4500    pub const FLOW_CONTROL_ERROR: u32 = 0x3;
4501    pub const SETTINGS_TIMEOUT: u32 = 0x4;
4502    pub const STREAM_CLOSED: u32 = 0x5;
4503    pub const FRAME_SIZE_ERROR: u32 = 0x6;
4504    pub const REFUSED_STREAM: u32 = 0x7;
4505    pub const CANCEL: u32 = 0x8;
4506    pub const ENHANCE_YOUR_CALM: u32 = 0xb;
4507}
4508
4509/// Validate an incoming GOAWAY frame: payload must be at least 8 bytes (RFC 7540 §6.8).
4510fn validate_goaway_payload(payload: &[u8]) -> Result<(), http2::Http2Error> {
4511    if payload.len() < 8 {
4512        return Err(http2::Http2Error::Protocol(
4513            "GOAWAY payload must be at least 8 bytes",
4514        ));
4515    }
4516    Ok(())
4517}
4518
4519/// Build the GOAWAY frame payload: last-stream-id (4 bytes) + error-code (4 bytes).
4520fn goaway_payload(last_stream_id: u32, error_code: u32) -> [u8; 8] {
4521    let mut buf = [0u8; 8];
4522    buf[..4].copy_from_slice(&(last_stream_id & 0x7FFF_FFFF).to_be_bytes());
4523    buf[4..].copy_from_slice(&error_code.to_be_bytes());
4524    buf
4525}
4526
4527/// Send a GOAWAY frame on the connection. GOAWAY is always sent on stream 0.
4528async fn send_goaway(
4529    framed: &mut http2::FramedH2,
4530    last_stream_id: u32,
4531    error_code: u32,
4532) -> Result<(), http2::Http2Error> {
4533    let payload = goaway_payload(last_stream_id, error_code);
4534    framed
4535        .write_frame(http2::FrameType::Goaway, 0, 0, &payload)
4536        .await
4537}
4538
4539fn validate_rst_stream_payload(stream_id: u32, payload: &[u8]) -> Result<(), http2::Http2Error> {
4540    if stream_id == 0 {
4541        return Err(http2::Http2Error::Protocol(
4542            "RST_STREAM must not be on stream 0",
4543        ));
4544    }
4545    if payload.len() != 4 {
4546        return Err(http2::Http2Error::Protocol(
4547            "RST_STREAM payload must be 4 bytes",
4548        ));
4549    }
4550    Ok(())
4551}
4552
4553fn validate_priority_payload(stream_id: u32, payload: &[u8]) -> Result<(), http2::Http2Error> {
4554    if stream_id == 0 {
4555        return Err(http2::Http2Error::Protocol(
4556            "PRIORITY must not be on stream 0",
4557        ));
4558    }
4559
4560    if payload.len() != 5 {
4561        return Err(http2::Http2Error::Protocol(
4562            "PRIORITY payload must be 5 bytes",
4563        ));
4564    }
4565
4566    let dependency_raw = u32::from_be_bytes([payload[0], payload[1], payload[2], payload[3]]);
4567    let dependency_stream_id = dependency_raw & 0x7FFF_FFFF;
4568    if dependency_stream_id == stream_id {
4569        return Err(http2::Http2Error::Protocol(
4570            "PRIORITY stream dependency must not reference itself",
4571        ));
4572    }
4573
4574    Ok(())
4575}
4576
4577fn extract_header_block_fragment(
4578    flags: u8,
4579    payload: &[u8],
4580) -> Result<(bool, Vec<u8>), http2::Http2Error> {
4581    const FLAG_END_STREAM: u8 = 0x1;
4582    const FLAG_PADDED: u8 = 0x8;
4583    const FLAG_PRIORITY: u8 = 0x20;
4584
4585    let end_stream = (flags & FLAG_END_STREAM) != 0;
4586    let mut idx = 0usize;
4587
4588    let pad_len = if (flags & FLAG_PADDED) != 0 {
4589        if payload.is_empty() {
4590            return Err(http2::Http2Error::Protocol(
4591                "HEADERS PADDED set with empty payload",
4592            ));
4593        }
4594        let v = payload[0] as usize;
4595        idx += 1;
4596        v
4597    } else {
4598        0
4599    };
4600
4601    if (flags & FLAG_PRIORITY) != 0 {
4602        // 5 bytes priority fields: dep(4) + weight(1)
4603        if payload.len().saturating_sub(idx) < 5 {
4604            return Err(http2::Http2Error::Protocol(
4605                "HEADERS PRIORITY set but too short",
4606            ));
4607        }
4608        idx += 5;
4609    }
4610
4611    if payload.len() < idx {
4612        return Err(http2::Http2Error::Protocol("invalid HEADERS payload"));
4613    }
4614    let frag = &payload[idx..];
4615    if frag.len() < pad_len {
4616        return Err(http2::Http2Error::Protocol(
4617            "invalid HEADERS padding length",
4618        ));
4619    }
4620    let end = frag.len() - pad_len;
4621    Ok((end_stream, frag[..end].to_vec()))
4622}
4623
4624fn extract_data_payload(flags: u8, payload: &[u8]) -> Result<(&[u8], bool), http2::Http2Error> {
4625    const FLAG_END_STREAM: u8 = 0x1;
4626    const FLAG_PADDED: u8 = 0x8;
4627
4628    let end_stream = (flags & FLAG_END_STREAM) != 0;
4629    if (flags & FLAG_PADDED) == 0 {
4630        return Ok((payload, end_stream));
4631    }
4632    if payload.is_empty() {
4633        return Err(http2::Http2Error::Protocol(
4634            "DATA PADDED set with empty payload",
4635        ));
4636    }
4637    let pad_len = payload[0] as usize;
4638    let data = &payload[1..];
4639    if data.len() < pad_len {
4640        return Err(http2::Http2Error::Protocol("invalid DATA padding length"));
4641    }
4642    Ok((&data[..data.len() - pad_len], end_stream))
4643}
4644
4645fn request_from_h2_headers(headers: http2::HeaderList) -> Result<Request, http2::Http2Error> {
4646    let mut method: Option<fastapi_core::Method> = None;
4647    let mut path: Option<String> = None;
4648    let mut authority: Option<Vec<u8>> = None;
4649    let mut saw_regular_headers = false;
4650
4651    let mut req_headers: Vec<(String, Vec<u8>)> = Vec::new();
4652
4653    for (name, value) in headers {
4654        if name.starts_with(b":") {
4655            if saw_regular_headers {
4656                return Err(http2::Http2Error::Protocol(
4657                    "pseudo-headers must appear before regular headers",
4658                ));
4659            }
4660            match name.as_slice() {
4661                b":method" => {
4662                    if method.is_some() {
4663                        return Err(http2::Http2Error::Protocol(
4664                            "duplicate :method pseudo-header",
4665                        ));
4666                    }
4667                    method = Some(
4668                        fastapi_core::Method::from_bytes(&value)
4669                            .ok_or(http2::Http2Error::Protocol("invalid :method"))?,
4670                    );
4671                }
4672                b":path" => {
4673                    if path.is_some() {
4674                        return Err(http2::Http2Error::Protocol("duplicate :path pseudo-header"));
4675                    }
4676                    let s = std::str::from_utf8(&value)
4677                        .map_err(|_| http2::Http2Error::Protocol("non-utf8 :path"))?;
4678                    path = Some(s.to_string());
4679                }
4680                b":authority" => {
4681                    if authority.is_some() {
4682                        return Err(http2::Http2Error::Protocol(
4683                            "duplicate :authority pseudo-header",
4684                        ));
4685                    }
4686                    authority = Some(value);
4687                }
4688                b":scheme" => {}
4689                _ => return Err(http2::Http2Error::Protocol("unknown pseudo-header")),
4690            }
4691            continue;
4692        }
4693
4694        saw_regular_headers = true;
4695        let n = std::str::from_utf8(&name)
4696            .map_err(|_| http2::Http2Error::Protocol("non-utf8 header name"))?;
4697        req_headers.push((n.to_string(), value));
4698    }
4699
4700    let method = method.ok_or(http2::Http2Error::Protocol("missing :method"))?;
4701    let raw_path = path.ok_or(http2::Http2Error::Protocol("missing :path"))?;
4702    let (path_only, query) = match raw_path.split_once('?') {
4703        Some((p, q)) => (p.to_string(), Some(q.to_string())),
4704        None => (raw_path, None),
4705    };
4706
4707    let mut req = Request::with_version(method, path_only, fastapi_core::HttpVersion::Http2);
4708    req.set_query(query);
4709
4710    if let Some(auth) = authority {
4711        req.headers_mut().insert("host", auth);
4712    }
4713
4714    for (n, v) in req_headers {
4715        req.headers_mut().insert(n, v);
4716    }
4717
4718    Ok(req)
4719}
4720
4721fn is_h2_forbidden_header_name(name: &str) -> bool {
4722    // RFC 7540: connection-specific headers are not permitted in HTTP/2.
4723    // We conservatively drop common hop-by-hop headers here.
4724    name.eq_ignore_ascii_case("connection")
4725        || name.eq_ignore_ascii_case("keep-alive")
4726        || name.eq_ignore_ascii_case("proxy-connection")
4727        || name.eq_ignore_ascii_case("transfer-encoding")
4728        || name.eq_ignore_ascii_case("upgrade")
4729        || name.eq_ignore_ascii_case("te")
4730}
4731
4732/// Writes raw bytes to a TCP stream (e.g., for 100 Continue response).
4733///
4734/// This writes the bytes directly without any HTTP formatting.
4735async fn write_raw_response(stream: &mut TcpStream, bytes: &[u8]) -> io::Result<()> {
4736    use std::future::poll_fn;
4737    write_all(stream, bytes).await?;
4738    poll_fn(|cx| Pin::new(&mut *stream).poll_flush(cx)).await?;
4739    Ok(())
4740}
4741
4742/// Writes a response to a TCP stream.
4743///
4744/// Handles both full (buffered) and streaming (chunked) responses.
4745/// Flushes the stream after all data has been written.
4746pub async fn write_response(stream: &mut TcpStream, response: ResponseWrite) -> io::Result<()> {
4747    use std::future::poll_fn;
4748
4749    match response {
4750        ResponseWrite::Full(bytes) => {
4751            write_all(stream, &bytes).await?;
4752        }
4753        ResponseWrite::Stream(mut encoder) => {
4754            // Write chunks as they become available
4755            loop {
4756                let chunk = poll_fn(|cx| Pin::new(&mut encoder).poll_next(cx)).await;
4757                match chunk {
4758                    Some(bytes) => {
4759                        write_all(stream, &bytes).await?;
4760                    }
4761                    None => break,
4762                }
4763            }
4764        }
4765    }
4766
4767    // Flush the stream
4768    poll_fn(|cx| Pin::new(&mut *stream).poll_flush(cx)).await?;
4769
4770    Ok(())
4771}
4772
4773/// Writes all bytes to a stream, looping until the entire buffer is consumed.
4774pub async fn write_all(stream: &mut TcpStream, mut buf: &[u8]) -> io::Result<()> {
4775    use std::future::poll_fn;
4776
4777    while !buf.is_empty() {
4778        let n = poll_fn(|cx| Pin::new(&mut *stream).poll_write(cx, buf)).await?;
4779        if n == 0 {
4780            return Err(io::Error::new(
4781                io::ErrorKind::WriteZero,
4782                "failed to write whole buffer",
4783            ));
4784        }
4785        buf = &buf[n..];
4786    }
4787    Ok(())
4788}
4789
4790// Connection header handling moved to crate::connection module
4791
4792// ============================================================================
4793// Synchronous Server (for compatibility)
4794// ============================================================================
4795
4796/// Synchronous HTTP server for request/response conversion.
4797///
4798/// This is a simpler, non-async server that just provides parsing and
4799/// serialization utilities. It's useful for testing or when you don't
4800/// need full async TCP handling.
4801pub struct Server {
4802    parser: Parser,
4803}
4804
4805impl Server {
4806    /// Create a new server.
4807    #[must_use]
4808    pub fn new() -> Self {
4809        Self {
4810            parser: Parser::new(),
4811        }
4812    }
4813
4814    /// Parse a request from bytes.
4815    ///
4816    /// # Errors
4817    ///
4818    /// Returns an error if the request is malformed.
4819    pub fn parse_request(&self, bytes: &[u8]) -> Result<Request, ParseError> {
4820        self.parser.parse(bytes)
4821    }
4822
4823    /// Write a response to bytes.
4824    #[must_use]
4825    pub fn write_response(&self, response: Response) -> ResponseWrite {
4826        let mut writer = ResponseWriter::new();
4827        writer.write(response)
4828    }
4829}
4830
4831impl Default for Server {
4832    fn default() -> Self {
4833        Self::new()
4834    }
4835}
4836
4837#[cfg(test)]
4838mod tests {
4839    use super::*;
4840    use std::future::Future;
4841
4842    fn block_on<F: Future>(f: F) -> F::Output {
4843        let rt = asupersync::runtime::RuntimeBuilder::current_thread()
4844            .build()
4845            .expect("test runtime must build");
4846        rt.block_on(f)
4847    }
4848
4849    #[test]
4850    fn server_config_builder() {
4851        let config = ServerConfig::new("0.0.0.0:3000")
4852            .with_request_timeout_secs(60)
4853            .with_max_connections(1000)
4854            .with_tcp_nodelay(false)
4855            .with_allowed_hosts(["example.com", "api.example.com"])
4856            .with_trust_x_forwarded_host(true);
4857
4858        assert_eq!(config.bind_addr, "0.0.0.0:3000");
4859        assert_eq!(config.request_timeout, Time::from_secs(60));
4860        assert_eq!(config.max_connections, 1000);
4861        assert!(!config.tcp_nodelay);
4862        assert_eq!(config.allowed_hosts.len(), 2);
4863        assert!(config.trust_x_forwarded_host);
4864    }
4865
4866    #[test]
4867    fn server_config_defaults() {
4868        let config = ServerConfig::default();
4869        assert_eq!(config.bind_addr, "127.0.0.1:8080");
4870        assert_eq!(
4871            config.request_timeout,
4872            Time::from_secs(DEFAULT_REQUEST_TIMEOUT_SECS)
4873        );
4874        assert_eq!(config.max_connections, DEFAULT_MAX_CONNECTIONS);
4875        assert!(config.tcp_nodelay);
4876        assert!(config.allowed_hosts.is_empty());
4877        assert!(!config.trust_x_forwarded_host);
4878    }
4879
4880    #[test]
4881    fn tcp_server_creates_request_ids() {
4882        let server = TcpServer::default();
4883        let id1 = server.next_request_id();
4884        let id2 = server.next_request_id();
4885        let id3 = server.next_request_id();
4886
4887        assert_eq!(id1, 0);
4888        assert_eq!(id2, 1);
4889        assert_eq!(id3, 2);
4890    }
4891
4892    #[test]
4893    fn server_error_display() {
4894        let io_err = ServerError::Io(io::Error::new(io::ErrorKind::AddrInUse, "address in use"));
4895        assert!(io_err.to_string().contains("IO error"));
4896
4897        let shutdown_err = ServerError::Shutdown;
4898        assert_eq!(shutdown_err.to_string(), "Server shutdown");
4899
4900        let limit_err = ServerError::ConnectionLimitReached;
4901        assert_eq!(limit_err.to_string(), "Connection limit reached");
4902    }
4903
4904    #[test]
4905    fn sync_server_parses_request() {
4906        let server = Server::new();
4907        let request = b"GET /hello HTTP/1.1\r\nHost: localhost\r\n\r\n";
4908        let result = server.parse_request(request);
4909        assert!(result.is_ok());
4910    }
4911
4912    #[test]
4913    fn window_update_payload_validation_accepts_non_zero_increment() {
4914        let payload = 1u32.to_be_bytes();
4915        assert!(validate_window_update_payload(&payload).is_ok());
4916    }
4917
4918    #[test]
4919    fn window_update_payload_validation_rejects_bad_length() {
4920        let err = validate_window_update_payload(&[0, 0, 0]).unwrap_err();
4921        assert!(
4922            err.to_string()
4923                .contains("WINDOW_UPDATE payload must be 4 bytes")
4924        );
4925    }
4926
4927    #[test]
4928    fn window_update_payload_validation_rejects_zero_increment() {
4929        let payload = 0u32.to_be_bytes();
4930        let err = validate_window_update_payload(&payload).unwrap_err();
4931        assert!(
4932            err.to_string()
4933                .contains("WINDOW_UPDATE increment must be non-zero")
4934        );
4935    }
4936
4937    #[test]
4938    fn settings_frame_validation_accepts_non_ack_payload() {
4939        let payload = [0u8; 6];
4940        let is_ack = validate_settings_frame(0, 0, &payload).unwrap();
4941        assert!(!is_ack);
4942    }
4943
4944    #[test]
4945    fn settings_frame_validation_accepts_empty_ack_payload() {
4946        let is_ack = validate_settings_frame(0, 0x1, &[]).unwrap();
4947        assert!(is_ack);
4948    }
4949
4950    #[test]
4951    fn settings_frame_validation_rejects_non_zero_stream() {
4952        let err = validate_settings_frame(1, 0, &[]).unwrap_err();
4953        assert!(err.to_string().contains("SETTINGS must be on stream 0"));
4954    }
4955
4956    #[test]
4957    fn settings_frame_validation_rejects_non_empty_ack_payload() {
4958        let err = validate_settings_frame(0, 0x1, &[0, 0, 0, 0, 0, 0]).unwrap_err();
4959        assert!(
4960            err.to_string()
4961                .contains("SETTINGS ACK frame must have empty payload")
4962        );
4963    }
4964
4965    #[test]
4966    fn settings_enable_push_accepts_zero() {
4967        // SETTINGS_ENABLE_PUSH (id=0x2), value=0.
4968        let payload = [0x00, 0x02, 0x00, 0x00, 0x00, 0x00];
4969        let mut hpack = http2::HpackDecoder::new();
4970        let mut max_frame_size = 16384u32;
4971        assert!(apply_http2_settings(&mut hpack, &mut max_frame_size, &payload).is_ok());
4972    }
4973
4974    #[test]
4975    fn settings_enable_push_accepts_one() {
4976        let payload = [0x00, 0x02, 0x00, 0x00, 0x00, 0x01];
4977        let mut hpack = http2::HpackDecoder::new();
4978        let mut max_frame_size = 16384u32;
4979        assert!(apply_http2_settings(&mut hpack, &mut max_frame_size, &payload).is_ok());
4980    }
4981
4982    #[test]
4983    fn settings_enable_push_rejects_invalid_value() {
4984        let payload = [0x00, 0x02, 0x00, 0x00, 0x00, 0x02];
4985        let mut hpack = http2::HpackDecoder::new();
4986        let mut max_frame_size = 16384u32;
4987        let err = apply_http2_settings(&mut hpack, &mut max_frame_size, &payload).unwrap_err();
4988        assert!(
4989            err.to_string()
4990                .contains("SETTINGS_ENABLE_PUSH must be 0 or 1")
4991        );
4992    }
4993
4994    #[test]
4995    fn settings_max_concurrent_streams_is_informational() {
4996        let payload = [0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF];
4997        let mut hpack = http2::HpackDecoder::new();
4998        let mut max_frame_size = 16_384u32;
4999        let mut flow_control = http2::H2FlowControl::new();
5000        flow_control.set_initial_window_size(12_345);
5001        flow_control.set_peer_initial_window_size(23_456);
5002
5003        apply_http2_settings_with_fc(
5004            &mut hpack,
5005            &mut max_frame_size,
5006            Some(&mut flow_control),
5007            &payload,
5008        )
5009        .expect("SETTINGS_MAX_CONCURRENT_STREAMS is informational");
5010
5011        assert_eq!(flow_control.initial_window_size(), 12_345);
5012        assert_eq!(flow_control.peer_initial_window_size(), 23_456);
5013        assert_eq!(max_frame_size, 16_384);
5014    }
5015
5016    #[test]
5017    fn settings_initial_window_size_updates_peer_send_window_only() {
5018        let payload = [0x00, 0x04, 0x00, 0x01, 0x11, 0x70]; // id=4, value=70000
5019        let mut hpack = http2::HpackDecoder::new();
5020        let mut max_frame_size = 16_384u32;
5021        let mut flow_control = http2::H2FlowControl::new();
5022        flow_control.set_initial_window_size(12_345);
5023
5024        apply_http2_settings_with_fc(
5025            &mut hpack,
5026            &mut max_frame_size,
5027            Some(&mut flow_control),
5028            &payload,
5029        )
5030        .expect("valid SETTINGS_INITIAL_WINDOW_SIZE should apply");
5031
5032        assert_eq!(
5033            flow_control.initial_window_size(),
5034            12_345,
5035            "peer settings must not alter the server receive threshold"
5036        );
5037        assert_eq!(flow_control.peer_initial_window_size(), 70_000);
5038    }
5039
5040    #[test]
5041    fn settings_initial_window_size_rejects_value_above_maximum() {
5042        let payload = [0x00, 0x04, 0x80, 0x00, 0x00, 0x00];
5043        let mut hpack = http2::HpackDecoder::new();
5044        let mut max_frame_size = 16_384u32;
5045        let mut flow_control = http2::H2FlowControl::new();
5046
5047        let err = apply_http2_settings_with_fc(
5048            &mut hpack,
5049            &mut max_frame_size,
5050            Some(&mut flow_control),
5051            &payload,
5052        )
5053        .expect_err("SETTINGS_INITIAL_WINDOW_SIZE above 2^31 - 1 must fail");
5054
5055        assert!(
5056            err.to_string()
5057                .contains("SETTINGS_INITIAL_WINDOW_SIZE exceeds maximum")
5058        );
5059        assert_eq!(
5060            flow_control.peer_initial_window_size(),
5061            http2::DEFAULT_INITIAL_WINDOW_SIZE
5062        );
5063    }
5064
5065    #[test]
5066    fn rst_stream_payload_validation_accepts_valid_payload() {
5067        let payload = 8u32.to_be_bytes();
5068        assert!(validate_rst_stream_payload(1, &payload).is_ok());
5069    }
5070
5071    #[test]
5072    fn rst_stream_payload_validation_rejects_stream_zero() {
5073        let payload = 8u32.to_be_bytes();
5074        let err = validate_rst_stream_payload(0, &payload).unwrap_err();
5075        assert!(
5076            err.to_string()
5077                .contains("RST_STREAM must not be on stream 0")
5078        );
5079    }
5080
5081    #[test]
5082    fn rst_stream_payload_validation_rejects_bad_length() {
5083        let err = validate_rst_stream_payload(1, &[0, 0, 0]).unwrap_err();
5084        assert!(
5085            err.to_string()
5086                .contains("RST_STREAM payload must be 4 bytes")
5087        );
5088    }
5089
5090    #[test]
5091    fn priority_payload_validation_accepts_valid_priority() {
5092        let payload = [0, 0, 0, 0, 16];
5093        assert!(validate_priority_payload(1, &payload).is_ok());
5094    }
5095
5096    #[test]
5097    fn priority_payload_validation_rejects_stream_zero() {
5098        let payload = [0, 0, 0, 0, 16];
5099        let err = validate_priority_payload(0, &payload).unwrap_err();
5100        assert!(err.to_string().contains("PRIORITY must not be on stream 0"));
5101    }
5102
5103    #[test]
5104    fn priority_payload_validation_rejects_bad_length() {
5105        let err = validate_priority_payload(1, &[0, 0, 0, 0]).unwrap_err();
5106        assert!(err.to_string().contains("PRIORITY payload must be 5 bytes"));
5107    }
5108
5109    #[test]
5110    fn priority_payload_validation_rejects_self_dependency() {
5111        let payload = 1u32.to_be_bytes();
5112        let mut with_weight = [0u8; 5];
5113        with_weight[..4].copy_from_slice(&payload);
5114        with_weight[4] = 16;
5115        let err = validate_priority_payload(1, &with_weight).unwrap_err();
5116        assert!(
5117            err.to_string()
5118                .contains("PRIORITY stream dependency must not reference itself")
5119        );
5120    }
5121
5122    #[test]
5123    fn goaway_payload_validation_accepts_valid_payload() {
5124        let payload = goaway_payload(0, 0);
5125        assert!(validate_goaway_payload(&payload).is_ok());
5126    }
5127
5128    #[test]
5129    fn goaway_payload_validation_accepts_payload_with_debug_data() {
5130        let mut payload = Vec::from(goaway_payload(1, 0).as_slice());
5131        payload.extend_from_slice(b"debug info");
5132        assert!(validate_goaway_payload(&payload).is_ok());
5133    }
5134
5135    #[test]
5136    fn goaway_payload_validation_rejects_short_payload() {
5137        let err = validate_goaway_payload(&[0, 0, 0]).unwrap_err();
5138        assert!(
5139            err.to_string()
5140                .contains("GOAWAY payload must be at least 8 bytes")
5141        );
5142    }
5143
5144    #[test]
5145    fn goaway_payload_validation_rejects_empty() {
5146        let err = validate_goaway_payload(&[]).unwrap_err();
5147        assert!(
5148            err.to_string()
5149                .contains("GOAWAY payload must be at least 8 bytes")
5150        );
5151    }
5152
5153    fn h2_test_frame(
5154        frame_type: http2::FrameType,
5155        stream_id: u32,
5156        payload: Vec<u8>,
5157    ) -> http2::Frame {
5158        http2::Frame {
5159            header: http2::FrameHeader {
5160                length: payload.len() as u32,
5161                frame_type: frame_type as u8,
5162                flags: 0,
5163                stream_id,
5164            },
5165            payload,
5166        }
5167    }
5168
5169    #[test]
5170    fn h2_idle_frame_rejects_data_outside_request_stream() {
5171        let frame = h2_test_frame(http2::FrameType::Data, 1, Vec::new());
5172        let err = handle_h2_idle_frame(&frame).unwrap_err();
5173        assert!(
5174            err.to_string()
5175                .contains("unexpected DATA frame outside active request stream")
5176        );
5177    }
5178
5179    #[test]
5180    fn h2_idle_frame_rejects_continuation_outside_header_block() {
5181        let frame = h2_test_frame(http2::FrameType::Continuation, 1, Vec::new());
5182        let err = handle_h2_idle_frame(&frame).unwrap_err();
5183        assert!(
5184            err.to_string()
5185                .contains("unexpected CONTINUATION frame outside header block")
5186        );
5187    }
5188
5189    #[test]
5190    fn h2_idle_frame_validates_rst_stream_payload() {
5191        let invalid = h2_test_frame(http2::FrameType::RstStream, 0, 8u32.to_be_bytes().to_vec());
5192        let err = handle_h2_idle_frame(&invalid).unwrap_err();
5193        assert!(
5194            err.to_string()
5195                .contains("RST_STREAM must not be on stream 0")
5196        );
5197
5198        let valid = h2_test_frame(http2::FrameType::RstStream, 3, 8u32.to_be_bytes().to_vec());
5199        assert!(handle_h2_idle_frame(&valid).is_ok());
5200    }
5201
5202    #[test]
5203    fn h2_idle_frame_validates_priority_payload() {
5204        let invalid = h2_test_frame(http2::FrameType::Priority, 0, vec![0, 0, 0, 0, 16]);
5205        let err = handle_h2_idle_frame(&invalid).unwrap_err();
5206        assert!(err.to_string().contains("PRIORITY must not be on stream 0"));
5207
5208        let valid = h2_test_frame(http2::FrameType::Priority, 1, vec![0, 0, 0, 0, 16]);
5209        assert!(handle_h2_idle_frame(&valid).is_ok());
5210    }
5211
5212    #[test]
5213    fn max_header_block_size_is_128k() {
5214        assert_eq!(MAX_HEADER_BLOCK_SIZE, 128 * 1024);
5215    }
5216
5217    #[test]
5218    fn server_settings_payload_advertises_max_concurrent_streams() {
5219        // SETTINGS_MAX_CONCURRENT_STREAMS (0x3) = 1
5220        assert_eq!(SERVER_SETTINGS_PAYLOAD.len(), 6);
5221        assert_eq!(SERVER_SETTINGS_PAYLOAD[0..2], [0x00, 0x03]);
5222        assert_eq!(
5223            u32::from_be_bytes([
5224                SERVER_SETTINGS_PAYLOAD[2],
5225                SERVER_SETTINGS_PAYLOAD[3],
5226                SERVER_SETTINGS_PAYLOAD[4],
5227                SERVER_SETTINGS_PAYLOAD[5],
5228            ]),
5229            1
5230        );
5231    }
5232
5233    #[test]
5234    fn max_hpack_table_size_is_64k() {
5235        assert_eq!(MAX_HPACK_TABLE_SIZE, 64 * 1024);
5236    }
5237
5238    #[test]
5239    fn h2_send_window_update_ignores_other_streams() {
5240        let mut flow_control = http2::H2FlowControl::new();
5241        let mut stream_window = 123i64;
5242        let payload = 7u32.to_be_bytes();
5243
5244        apply_peer_window_update_for_send(&mut flow_control, &mut stream_window, 3, 5, &payload)
5245            .expect("window update on different stream should be ignored");
5246
5247        assert_eq!(stream_window, 123);
5248    }
5249
5250    #[test]
5251    fn h2_send_window_update_applies_connection_and_current_stream() {
5252        let mut flow_control = http2::H2FlowControl::new();
5253        let mut stream_window = 10i64;
5254
5255        let conn_before = flow_control.send_conn_window();
5256        let conn_payload = 11u32.to_be_bytes();
5257        apply_peer_window_update_for_send(
5258            &mut flow_control,
5259            &mut stream_window,
5260            9,
5261            0,
5262            &conn_payload,
5263        )
5264        .expect("connection window update should be applied");
5265        assert_eq!(flow_control.send_conn_window(), conn_before + 11);
5266        assert_eq!(stream_window, 10);
5267
5268        let stream_payload = 13u32.to_be_bytes();
5269        apply_peer_window_update_for_send(
5270            &mut flow_control,
5271            &mut stream_window,
5272            9,
5273            9,
5274            &stream_payload,
5275        )
5276        .expect("stream window update should be applied to current stream");
5277        assert_eq!(stream_window, 23);
5278    }
5279
5280    #[test]
5281    fn h2_send_settings_updates_current_stream_window_delta() {
5282        let mut flow_control = http2::H2FlowControl::new();
5283        let mut stream_window = 50i64;
5284        let mut peer_max_frame_size = 16_384u32;
5285
5286        let payload = [0x00, 0x04, 0x00, 0x01, 0x11, 0x70]; // id=4, value=70000
5287        apply_peer_settings_for_send(
5288            &mut flow_control,
5289            &mut stream_window,
5290            &mut peer_max_frame_size,
5291            &payload,
5292        )
5293        .expect("valid SETTINGS_INITIAL_WINDOW_SIZE should apply");
5294
5295        assert_eq!(flow_control.peer_initial_window_size(), 70_000);
5296        assert_eq!(stream_window, 4_515); // 50 + (70000 - 65535)
5297        assert_eq!(peer_max_frame_size, 16_384);
5298    }
5299
5300    #[test]
5301    fn h2_send_settings_rejects_invalid_payload_len() {
5302        let mut flow_control = http2::H2FlowControl::new();
5303        let mut stream_window = 0i64;
5304        let mut peer_max_frame_size = 16_384u32;
5305        let err = apply_peer_settings_for_send(
5306            &mut flow_control,
5307            &mut stream_window,
5308            &mut peer_max_frame_size,
5309            &[0, 1, 2],
5310        )
5311        .unwrap_err();
5312        assert!(
5313            err.to_string()
5314                .contains("SETTINGS length must be a multiple of 6")
5315        );
5316    }
5317
5318    #[test]
5319    fn h2_send_settings_rejects_initial_window_too_large() {
5320        let mut flow_control = http2::H2FlowControl::new();
5321        let mut stream_window = 0i64;
5322        let mut peer_max_frame_size = 16_384u32;
5323        let payload = [0x00, 0x04, 0x80, 0x00, 0x00, 0x00]; // id=4, value=2^31
5324        let err = apply_peer_settings_for_send(
5325            &mut flow_control,
5326            &mut stream_window,
5327            &mut peer_max_frame_size,
5328            &payload,
5329        )
5330        .unwrap_err();
5331        assert!(
5332            err.to_string()
5333                .contains("SETTINGS_INITIAL_WINDOW_SIZE exceeds maximum")
5334        );
5335    }
5336
5337    #[test]
5338    fn h2_send_settings_window_delta_overflow_is_flow_control_error() {
5339        let mut flow_control = http2::H2FlowControl::new();
5340        let mut peer_max_frame_size = 16_384u32;
5341        // Start with a stream window near the maximum.
5342        let mut stream_window: i64 = 0x7FFF_FFFF - 10;
5343        // Increase INITIAL_WINDOW_SIZE by more than 10 from default (65535).
5344        // Delta = new - old = 0x7FFF_FFFF - 65535 = 2147418112
5345        // New stream_window = (2^31-1 - 10) + 2147418112 > 2^31-1
5346        let new_initial: u32 = 0x7FFF_FFFF;
5347        let payload = [
5348            0x00,
5349            0x04,
5350            new_initial.to_be_bytes()[0],
5351            new_initial.to_be_bytes()[1],
5352            new_initial.to_be_bytes()[2],
5353            new_initial.to_be_bytes()[3],
5354        ];
5355        let err = apply_peer_settings_for_send(
5356            &mut flow_control,
5357            &mut stream_window,
5358            &mut peer_max_frame_size,
5359            &payload,
5360        )
5361        .unwrap_err();
5362        assert!(err.to_string().contains("stream window to exceed 2^31-1"));
5363    }
5364
5365    #[test]
5366    fn h2_send_settings_updates_peer_max_frame_size() {
5367        let mut flow_control = http2::H2FlowControl::new();
5368        let mut stream_window = 0i64;
5369        let mut peer_max_frame_size = 65_535u32;
5370        let payload = [0x00, 0x05, 0x00, 0x00, 0x40, 0x00]; // id=5, value=16384
5371
5372        apply_peer_settings_for_send(
5373            &mut flow_control,
5374            &mut stream_window,
5375            &mut peer_max_frame_size,
5376            &payload,
5377        )
5378        .expect("valid SETTINGS_MAX_FRAME_SIZE should apply");
5379
5380        assert_eq!(peer_max_frame_size, 16_384);
5381    }
5382
5383    #[test]
5384    fn h2_send_settings_rejects_invalid_max_frame_size() {
5385        let mut flow_control = http2::H2FlowControl::new();
5386        let mut stream_window = 0i64;
5387        let mut peer_max_frame_size = 16_384u32;
5388        let payload = [0x00, 0x05, 0x00, 0x00, 0x3F, 0xFF]; // id=5, value=16383
5389
5390        let err = apply_peer_settings_for_send(
5391            &mut flow_control,
5392            &mut stream_window,
5393            &mut peer_max_frame_size,
5394            &payload,
5395        )
5396        .unwrap_err();
5397        assert!(err.to_string().contains("invalid SETTINGS_MAX_FRAME_SIZE"));
5398    }
5399
5400    #[test]
5401    fn request_from_h2_headers_rejects_unknown_pseudo_header() {
5402        let headers: http2::HeaderList = vec![
5403            (b":method".to_vec(), b"GET".to_vec()),
5404            (b":path".to_vec(), b"/".to_vec()),
5405            (b":weird".to_vec(), b"value".to_vec()),
5406        ];
5407        let err = request_from_h2_headers(headers).unwrap_err();
5408        assert!(err.to_string().contains("unknown pseudo-header"));
5409    }
5410
5411    #[test]
5412    fn request_from_h2_headers_rejects_pseudo_after_regular_header() {
5413        let headers: http2::HeaderList = vec![
5414            (b":method".to_vec(), b"GET".to_vec()),
5415            (b":path".to_vec(), b"/".to_vec()),
5416            (b"x-test".to_vec(), b"ok".to_vec()),
5417            (b":authority".to_vec(), b"example.com".to_vec()),
5418        ];
5419        let err = request_from_h2_headers(headers).unwrap_err();
5420        assert!(
5421            err.to_string()
5422                .contains("pseudo-headers must appear before regular headers")
5423        );
5424    }
5425
5426    // ========================================================================
5427    // Host header validation tests
5428    // ========================================================================
5429
5430    #[test]
5431    fn host_validation_missing_host_rejected() {
5432        let config = ServerConfig::default();
5433        let request = Request::new(fastapi_core::Method::Get, "/");
5434        let err = validate_host_header(&request, &config).unwrap_err();
5435        assert_eq!(err.kind, HostValidationErrorKind::Missing);
5436        assert_eq!(err.response().status().as_u16(), 400);
5437    }
5438
5439    #[test]
5440    fn host_validation_allows_configured_host() {
5441        let config = ServerConfig::default().with_allowed_hosts(["example.com"]);
5442        let mut request = Request::new(fastapi_core::Method::Get, "/");
5443        request
5444            .headers_mut()
5445            .insert("Host".to_string(), b"example.com".to_vec());
5446        assert!(validate_host_header(&request, &config).is_ok());
5447    }
5448
5449    #[test]
5450    fn host_validation_rejects_disallowed_host() {
5451        let config = ServerConfig::default().with_allowed_hosts(["example.com"]);
5452        let mut request = Request::new(fastapi_core::Method::Get, "/");
5453        request
5454            .headers_mut()
5455            .insert("Host".to_string(), b"evil.com".to_vec());
5456        let err = validate_host_header(&request, &config).unwrap_err();
5457        assert_eq!(err.kind, HostValidationErrorKind::NotAllowed);
5458    }
5459
5460    #[test]
5461    fn host_validation_wildcard_allows_subdomains_only() {
5462        let config = ServerConfig::default().with_allowed_hosts(["*.example.com"]);
5463        let mut request = Request::new(fastapi_core::Method::Get, "/");
5464        request
5465            .headers_mut()
5466            .insert("Host".to_string(), b"api.example.com".to_vec());
5467        assert!(validate_host_header(&request, &config).is_ok());
5468
5469        let mut request = Request::new(fastapi_core::Method::Get, "/");
5470        request
5471            .headers_mut()
5472            .insert("Host".to_string(), b"example.com".to_vec());
5473        let err = validate_host_header(&request, &config).unwrap_err();
5474        assert_eq!(err.kind, HostValidationErrorKind::NotAllowed);
5475    }
5476
5477    #[test]
5478    fn host_validation_uses_x_forwarded_host_when_trusted() {
5479        let config = ServerConfig::default()
5480            .with_allowed_hosts(["example.com"])
5481            .with_trust_x_forwarded_host(true);
5482        let mut request = Request::new(fastapi_core::Method::Get, "/");
5483        request
5484            .headers_mut()
5485            .insert("Host".to_string(), b"internal.local".to_vec());
5486        request
5487            .headers_mut()
5488            .insert("X-Forwarded-Host".to_string(), b"example.com".to_vec());
5489        assert!(validate_host_header(&request, &config).is_ok());
5490    }
5491
5492    #[test]
5493    fn host_validation_rejects_invalid_host_value() {
5494        let config = ServerConfig::default();
5495        let mut request = Request::new(fastapi_core::Method::Get, "/");
5496        request
5497            .headers_mut()
5498            .insert("Host".to_string(), b"bad host".to_vec());
5499        let err = validate_host_header(&request, &config).unwrap_err();
5500        assert_eq!(err.kind, HostValidationErrorKind::Invalid);
5501    }
5502
5503    // ========================================================================
5504    // WebSocket upgrade request detection tests
5505    // ========================================================================
5506
5507    #[test]
5508    fn websocket_upgrade_detection_accepts_token_lists_case_insensitive() {
5509        let mut request = Request::new(fastapi_core::Method::Get, "/ws");
5510        request
5511            .headers_mut()
5512            .insert("Upgrade".to_string(), b"h2c, WebSocket".to_vec());
5513        request
5514            .headers_mut()
5515            .insert("Connection".to_string(), b"keep-alive, UPGRADE".to_vec());
5516
5517        assert!(is_websocket_upgrade_request(&request));
5518    }
5519
5520    #[test]
5521    fn websocket_upgrade_detection_rejects_missing_connection_upgrade_token() {
5522        let mut request = Request::new(fastapi_core::Method::Get, "/ws");
5523        request
5524            .headers_mut()
5525            .insert("Upgrade".to_string(), b"websocket".to_vec());
5526        request
5527            .headers_mut()
5528            .insert("Connection".to_string(), b"keep-alive".to_vec());
5529
5530        assert!(!is_websocket_upgrade_request(&request));
5531    }
5532
5533    #[test]
5534    fn websocket_upgrade_detection_rejects_non_get_method() {
5535        let mut request = Request::new(fastapi_core::Method::Post, "/ws");
5536        request
5537            .headers_mut()
5538            .insert("Upgrade".to_string(), b"websocket".to_vec());
5539        request
5540            .headers_mut()
5541            .insert("Connection".to_string(), b"upgrade".to_vec());
5542
5543        assert!(!is_websocket_upgrade_request(&request));
5544    }
5545
5546    // ========================================================================
5547    // Keep-alive detection tests
5548    // ========================================================================
5549
5550    #[test]
5551    fn keep_alive_default_http11() {
5552        // HTTP/1.1 defaults to keep-alive
5553        let mut request = Request::new(fastapi_core::Method::Get, "/path".to_string());
5554        request
5555            .headers_mut()
5556            .insert("Host".to_string(), b"example.com".to_vec());
5557        assert!(should_keep_alive(&request));
5558    }
5559
5560    #[test]
5561    fn keep_alive_explicit_keep_alive() {
5562        let mut request = Request::new(fastapi_core::Method::Get, "/path".to_string());
5563        request
5564            .headers_mut()
5565            .insert("Connection".to_string(), b"keep-alive".to_vec());
5566        assert!(should_keep_alive(&request));
5567    }
5568
5569    #[test]
5570    fn keep_alive_connection_close() {
5571        let mut request = Request::new(fastapi_core::Method::Get, "/path".to_string());
5572        request
5573            .headers_mut()
5574            .insert("Connection".to_string(), b"close".to_vec());
5575        assert!(!should_keep_alive(&request));
5576    }
5577
5578    #[test]
5579    fn keep_alive_connection_close_case_insensitive() {
5580        let mut request = Request::new(fastapi_core::Method::Get, "/path".to_string());
5581        request
5582            .headers_mut()
5583            .insert("Connection".to_string(), b"CLOSE".to_vec());
5584        assert!(!should_keep_alive(&request));
5585    }
5586
5587    #[test]
5588    fn keep_alive_multiple_values() {
5589        let mut request = Request::new(fastapi_core::Method::Get, "/path".to_string());
5590        request
5591            .headers_mut()
5592            .insert("Connection".to_string(), b"keep-alive, upgrade".to_vec());
5593        assert!(should_keep_alive(&request));
5594    }
5595
5596    // ========================================================================
5597    // Timeout behavior tests
5598    // ========================================================================
5599
5600    #[test]
5601    fn timeout_budget_created_relative_to_current_time() {
5602        let config = ServerConfig::new("127.0.0.1:8080").with_request_timeout_secs(45);
5603        let budget = Budget::new().with_deadline(request_deadline_at(
5604            Time::from_secs(90),
5605            config.request_timeout,
5606        ));
5607        assert_eq!(budget.deadline, Some(Time::from_secs(135)));
5608    }
5609
5610    #[test]
5611    fn timeout_duration_conversion_from_time() {
5612        let timeout = Time::from_secs(30);
5613        let duration = Duration::from_nanos(timeout.as_nanos());
5614        assert_eq!(duration, Duration::from_secs(30));
5615    }
5616
5617    #[test]
5618    fn timeout_duration_conversion_from_time_millis() {
5619        let timeout = Time::from_millis(1500);
5620        let duration = Duration::from_nanos(timeout.as_nanos());
5621        assert_eq!(duration, Duration::from_millis(1500));
5622    }
5623
5624    #[test]
5625    fn gateway_timeout_response_has_correct_status() {
5626        let response = Response::with_status(StatusCode::GATEWAY_TIMEOUT);
5627        assert_eq!(response.status().as_u16(), 504);
5628    }
5629
5630    #[test]
5631    fn gateway_timeout_response_with_body() {
5632        let response = Response::with_status(StatusCode::GATEWAY_TIMEOUT).body(
5633            fastapi_core::ResponseBody::Bytes(b"Request timed out".to_vec()),
5634        );
5635        assert_eq!(response.status().as_u16(), 504);
5636        // Verify body is set (not empty)
5637        assert!(response.body_ref().len() > 0);
5638    }
5639
5640    #[test]
5641    fn elapsed_time_check_logic() {
5642        // Test the timeout check logic in isolation
5643        let start = Instant::now();
5644        let timeout_duration = Duration::from_millis(10);
5645
5646        // Immediately after start, should not be timed out
5647        assert!(start.elapsed() <= timeout_duration);
5648
5649        // Wait a bit longer than the timeout
5650        std::thread::sleep(Duration::from_millis(20));
5651
5652        // Now should be timed out
5653        assert!(start.elapsed() > timeout_duration);
5654    }
5655
5656    // ========================================================================
5657    // Connection limit tests
5658    // ========================================================================
5659
5660    #[test]
5661    fn connection_counter_starts_at_zero() {
5662        let server = TcpServer::default();
5663        assert_eq!(server.current_connections(), 0);
5664    }
5665
5666    #[test]
5667    fn try_acquire_connection_unlimited() {
5668        // With max_connections = 0 (unlimited), should always succeed
5669        let server = TcpServer::default();
5670        assert_eq!(server.config().max_connections, 0);
5671
5672        // Acquire several connections
5673        for _ in 0..100 {
5674            assert!(server.try_acquire_connection());
5675        }
5676        assert_eq!(server.current_connections(), 100);
5677
5678        // Release them all
5679        for _ in 0..100 {
5680            server.release_connection();
5681        }
5682        assert_eq!(server.current_connections(), 0);
5683    }
5684
5685    #[test]
5686    fn try_acquire_connection_with_limit() {
5687        let config = ServerConfig::new("127.0.0.1:8080").with_max_connections(5);
5688        let server = TcpServer::new(config);
5689
5690        // Acquire up to the limit
5691        for i in 0..5 {
5692            assert!(
5693                server.try_acquire_connection(),
5694                "Should acquire connection {i}"
5695            );
5696        }
5697        assert_eq!(server.current_connections(), 5);
5698
5699        // Next one should fail
5700        assert!(!server.try_acquire_connection());
5701        assert_eq!(server.current_connections(), 5);
5702
5703        // Release one
5704        server.release_connection();
5705        assert_eq!(server.current_connections(), 4);
5706
5707        // Now we can acquire one more
5708        assert!(server.try_acquire_connection());
5709        assert_eq!(server.current_connections(), 5);
5710    }
5711
5712    #[test]
5713    fn try_acquire_connection_single_connection_limit() {
5714        let config = ServerConfig::new("127.0.0.1:8080").with_max_connections(1);
5715        let server = TcpServer::new(config);
5716
5717        // First acquire succeeds
5718        assert!(server.try_acquire_connection());
5719        assert_eq!(server.current_connections(), 1);
5720
5721        // Second fails
5722        assert!(!server.try_acquire_connection());
5723        assert_eq!(server.current_connections(), 1);
5724
5725        // After release, can acquire again
5726        server.release_connection();
5727        assert!(server.try_acquire_connection());
5728    }
5729
5730    #[test]
5731    fn app_connection_task_clone_shares_counters_but_not_handle_registry() {
5732        let server = TcpServer::new(ServerConfig::new("127.0.0.1:0").with_max_connections(4));
5733
5734        assert!(server.try_acquire_connection());
5735        let task_server = server.clone_for_connection_task();
5736
5737        assert_eq!(task_server.current_connections(), 1);
5738        assert_eq!(server.current_connections(), 1);
5739        assert_eq!(task_server.metrics().total_accepted, 1);
5740        assert_eq!(
5741            task_server
5742                .connection_handles
5743                .lock()
5744                .expect("task handle registry should not be poisoned")
5745                .len(),
5746            0
5747        );
5748
5749        task_server.release_connection();
5750        assert_eq!(server.current_connections(), 0);
5751    }
5752
5753    #[test]
5754    fn service_unavailable_response_has_correct_status() {
5755        let response = Response::with_status(StatusCode::SERVICE_UNAVAILABLE);
5756        assert_eq!(response.status().as_u16(), 503);
5757    }
5758
5759    #[test]
5760    fn service_unavailable_response_with_body() {
5761        let response = Response::with_status(StatusCode::SERVICE_UNAVAILABLE)
5762            .header("connection", b"close".to_vec())
5763            .body(fastapi_core::ResponseBody::Bytes(
5764                b"503 Service Unavailable: connection limit reached".to_vec(),
5765            ));
5766        assert_eq!(response.status().as_u16(), 503);
5767        assert!(response.body_ref().len() > 0);
5768    }
5769
5770    #[test]
5771    fn config_max_connections_default_is_zero() {
5772        let config = ServerConfig::default();
5773        assert_eq!(config.max_connections, 0);
5774    }
5775
5776    #[test]
5777    fn config_max_connections_can_be_set() {
5778        let config = ServerConfig::new("127.0.0.1:8080").with_max_connections(100);
5779        assert_eq!(config.max_connections, 100);
5780    }
5781
5782    // ========================================================================
5783    // Keep-alive configuration tests
5784    // ========================================================================
5785
5786    #[test]
5787    fn config_keep_alive_timeout_default() {
5788        let config = ServerConfig::default();
5789        assert_eq!(
5790            config.keep_alive_timeout,
5791            Duration::from_secs(DEFAULT_KEEP_ALIVE_TIMEOUT_SECS)
5792        );
5793    }
5794
5795    #[test]
5796    fn config_keep_alive_timeout_can_be_set() {
5797        let config =
5798            ServerConfig::new("127.0.0.1:8080").with_keep_alive_timeout(Duration::from_secs(120));
5799        assert_eq!(config.keep_alive_timeout, Duration::from_secs(120));
5800    }
5801
5802    #[test]
5803    fn config_keep_alive_timeout_can_be_set_secs() {
5804        let config = ServerConfig::new("127.0.0.1:8080").with_keep_alive_timeout_secs(90);
5805        assert_eq!(config.keep_alive_timeout, Duration::from_secs(90));
5806    }
5807
5808    #[test]
5809    fn config_max_requests_per_connection_default() {
5810        let config = ServerConfig::default();
5811        assert_eq!(
5812            config.max_requests_per_connection,
5813            DEFAULT_MAX_REQUESTS_PER_CONNECTION
5814        );
5815    }
5816
5817    #[test]
5818    fn config_max_requests_per_connection_can_be_set() {
5819        let config = ServerConfig::new("127.0.0.1:8080").with_max_requests_per_connection(50);
5820        assert_eq!(config.max_requests_per_connection, 50);
5821    }
5822
5823    #[test]
5824    fn config_max_requests_per_connection_unlimited() {
5825        let config = ServerConfig::new("127.0.0.1:8080").with_max_requests_per_connection(0);
5826        assert_eq!(config.max_requests_per_connection, 0);
5827    }
5828
5829    #[test]
5830    fn response_with_keep_alive_header() {
5831        let response = Response::ok().header("connection", b"keep-alive".to_vec());
5832        let headers = response.headers();
5833        let connection_header = headers
5834            .iter()
5835            .find(|(name, _)| name.eq_ignore_ascii_case("connection"));
5836        assert!(connection_header.is_some());
5837        assert_eq!(connection_header.unwrap().1, b"keep-alive");
5838    }
5839
5840    #[test]
5841    fn response_with_close_header() {
5842        let response = Response::ok().header("connection", b"close".to_vec());
5843        let headers = response.headers();
5844        let connection_header = headers
5845            .iter()
5846            .find(|(name, _)| name.eq_ignore_ascii_case("connection"));
5847        assert!(connection_header.is_some());
5848        assert_eq!(connection_header.unwrap().1, b"close");
5849    }
5850
5851    // ========================================================================
5852    // Connection draining tests
5853    // ========================================================================
5854
5855    #[test]
5856    fn config_drain_timeout_default() {
5857        let config = ServerConfig::default();
5858        assert_eq!(
5859            config.drain_timeout,
5860            Duration::from_secs(DEFAULT_DRAIN_TIMEOUT_SECS)
5861        );
5862    }
5863
5864    #[test]
5865    fn config_drain_timeout_can_be_set() {
5866        let config =
5867            ServerConfig::new("127.0.0.1:8080").with_drain_timeout(Duration::from_secs(60));
5868        assert_eq!(config.drain_timeout, Duration::from_secs(60));
5869    }
5870
5871    #[test]
5872    fn config_drain_timeout_can_be_set_secs() {
5873        let config = ServerConfig::new("127.0.0.1:8080").with_drain_timeout_secs(45);
5874        assert_eq!(config.drain_timeout, Duration::from_secs(45));
5875    }
5876
5877    #[test]
5878    fn server_not_draining_initially() {
5879        let server = TcpServer::default();
5880        assert!(!server.is_draining());
5881    }
5882
5883    #[test]
5884    fn server_start_drain_sets_flag() {
5885        let server = TcpServer::default();
5886        assert!(!server.is_draining());
5887        server.start_drain();
5888        assert!(server.is_draining());
5889    }
5890
5891    #[test]
5892    fn server_start_drain_idempotent() {
5893        let server = TcpServer::default();
5894        server.start_drain();
5895        assert!(server.is_draining());
5896        server.start_drain();
5897        assert!(server.is_draining());
5898    }
5899
5900    #[test]
5901    fn wait_for_drain_returns_true_when_no_connections() {
5902        block_on(async {
5903            let server = TcpServer::default();
5904            assert_eq!(server.current_connections(), 0);
5905            let result = server
5906                .wait_for_drain(Duration::from_millis(100), Some(Duration::from_millis(1)))
5907                .await;
5908            assert!(result);
5909        });
5910    }
5911
5912    #[test]
5913    fn wait_for_drain_timeout_with_connections() {
5914        block_on(async {
5915            let server = TcpServer::default();
5916            // Simulate active connections
5917            server.try_acquire_connection();
5918            server.try_acquire_connection();
5919            assert_eq!(server.current_connections(), 2);
5920
5921            // Wait should timeout since connections won't drain on their own
5922            let result = server
5923                .wait_for_drain(Duration::from_millis(50), Some(Duration::from_millis(5)))
5924                .await;
5925            assert!(!result);
5926            assert_eq!(server.current_connections(), 2);
5927        });
5928    }
5929
5930    #[test]
5931    fn drain_returns_zero_when_no_connections() {
5932        block_on(async {
5933            let server = TcpServer::new(
5934                ServerConfig::new("127.0.0.1:8080").with_drain_timeout(Duration::from_millis(100)),
5935            );
5936            assert_eq!(server.current_connections(), 0);
5937            let remaining = server.drain().await;
5938            assert_eq!(remaining, 0);
5939            assert!(server.is_draining());
5940        });
5941    }
5942
5943    #[test]
5944    fn drain_returns_count_when_connections_remain() {
5945        block_on(async {
5946            let server = TcpServer::new(
5947                ServerConfig::new("127.0.0.1:8080").with_drain_timeout(Duration::from_millis(50)),
5948            );
5949            // Simulate active connections that won't drain
5950            server.try_acquire_connection();
5951            server.try_acquire_connection();
5952            server.try_acquire_connection();
5953
5954            let remaining = server.drain().await;
5955            assert_eq!(remaining, 3);
5956            assert!(server.is_draining());
5957        });
5958    }
5959
5960    #[test]
5961    fn cleanup_completed_handles_prunes_finished_runtime_tasks() {
5962        use std::sync::mpsc;
5963        use std::time::{Duration, Instant as StdInstant};
5964
5965        let runtime = asupersync::runtime::RuntimeBuilder::new()
5966            .worker_threads(2)
5967            .build()
5968            .expect("runtime build");
5969        let handle = runtime.handle();
5970        let server = TcpServer::default();
5971        let (tx, rx) = mpsc::sync_channel(1);
5972
5973        let join = handle.spawn(async move {
5974            tx.send(()).expect("completion signal should send");
5975        });
5976
5977        server
5978            .connection_handles
5979            .lock()
5980            .expect("connection handle mutex should not be poisoned")
5981            .push(join);
5982
5983        rx.recv_timeout(Duration::from_secs(5))
5984            .expect("spawned runtime task should complete");
5985
5986        // Generous deadline so the test does not flake on heavily-loaded CI
5987        // runners (e.g. macos-latest with the full workspace test suite
5988        // running in parallel) where the runtime may take longer to mark the
5989        // JoinHandle finished after the task body returns.
5990        let deadline = StdInstant::now() + Duration::from_secs(5);
5991        loop {
5992            if server
5993                .connection_handles
5994                .lock()
5995                .expect("connection handle mutex should not be poisoned")[0]
5996                .is_finished()
5997            {
5998                break;
5999            }
6000            assert!(
6001                StdInstant::now() < deadline,
6002                "JoinHandle should report completion after task exit"
6003            );
6004            std::thread::sleep(Duration::from_millis(10));
6005        }
6006
6007        block_on(async {
6008            server.cleanup_completed_handles(&Cx::for_testing()).await;
6009        });
6010
6011        let remaining = server
6012            .connection_handles
6013            .lock()
6014            .expect("connection handle mutex should not be poisoned")
6015            .len();
6016        assert_eq!(remaining, 0);
6017    }
6018
6019    #[test]
6020    fn cleanup_completed_handles_reaps_panicked_runtime_tasks_without_panicking() {
6021        use std::time::{Duration, Instant as StdInstant};
6022
6023        let runtime = asupersync::runtime::RuntimeBuilder::new()
6024            .worker_threads(2)
6025            .build()
6026            .expect("runtime build");
6027        let handle = runtime.handle();
6028        let server = TcpServer::default();
6029
6030        let join = handle.spawn(async move {
6031            panic!("intentional panic to verify cleanup panics are observed");
6032        });
6033
6034        server
6035            .connection_handles
6036            .lock()
6037            .expect("connection handle mutex should not be poisoned")
6038            .push(join);
6039
6040        let deadline = StdInstant::now() + Duration::from_secs(1);
6041        loop {
6042            let finished = server
6043                .connection_handles
6044                .lock()
6045                .expect("connection handle mutex should not be poisoned")[0]
6046                .is_finished();
6047            if finished {
6048                break;
6049            }
6050            assert!(
6051                StdInstant::now() < deadline,
6052                "panicking task should finish promptly"
6053            );
6054            std::thread::sleep(Duration::from_millis(10));
6055        }
6056
6057        block_on(async {
6058            server.cleanup_completed_handles(&Cx::for_testing()).await;
6059        });
6060
6061        let remaining = server
6062            .connection_handles
6063            .lock()
6064            .expect("connection handle mutex should not be poisoned")
6065            .len();
6066        assert_eq!(remaining, 0);
6067    }
6068
6069    #[test]
6070    fn connection_slot_guard_releases_counter_when_task_panics() {
6071        use std::time::{Duration, Instant as StdInstant};
6072
6073        let runtime = asupersync::runtime::RuntimeBuilder::new()
6074            .worker_threads(2)
6075            .build()
6076            .expect("runtime build");
6077        let handle = runtime.handle();
6078        let counter = Arc::new(AtomicU64::new(1));
6079
6080        let panic_task = handle.spawn({
6081            let counter = Arc::clone(&counter);
6082            async move {
6083                let _connection_slot = ConnectionSlotGuard::new(counter);
6084                panic!("intentional panic to verify connection slot cleanup");
6085            }
6086        });
6087
6088        let deadline = StdInstant::now() + Duration::from_secs(1);
6089        while !panic_task.is_finished() {
6090            assert!(
6091                StdInstant::now() < deadline,
6092                "panicing task should finish promptly"
6093            );
6094            std::thread::sleep(Duration::from_millis(10));
6095        }
6096
6097        assert_eq!(
6098            counter.load(Ordering::Relaxed),
6099            0,
6100            "connection slot must be released even when the task unwinds"
6101        );
6102    }
6103
6104    #[test]
6105    fn connection_slot_guard_releases_counter_when_future_drops_before_poll() {
6106        let counter = Arc::new(AtomicU64::new(1));
6107        let connection_slot = ConnectionSlotGuard::new(Arc::clone(&counter));
6108
6109        let future = async move {
6110            let _connection_slot = connection_slot;
6111        };
6112
6113        drop(future);
6114
6115        assert_eq!(
6116            counter.load(Ordering::Relaxed),
6117            0,
6118            "connection slot must be released even if the spawned future is dropped before polling"
6119        );
6120    }
6121
6122    #[test]
6123    fn serve_concurrent_shutdown_wakes_idle_accept_loop() {
6124        use std::time::{Duration, Instant as StdInstant};
6125
6126        let server = Arc::new(TcpServer::new(ServerConfig::new("127.0.0.1:0")));
6127        let server_for_thread = Arc::clone(&server);
6128
6129        let serve_thread = std::thread::spawn(move || {
6130            block_on(async {
6131                let cx = Cx::for_testing();
6132                server_for_thread
6133                    .serve_concurrent(&cx, |_ctx, _req| async {
6134                        Response::ok().body(fastapi_core::ResponseBody::Bytes(b"ok".to_vec()))
6135                    })
6136                    .await
6137            })
6138        });
6139
6140        std::thread::sleep(Duration::from_millis(100));
6141        server.shutdown();
6142
6143        let deadline = StdInstant::now() + Duration::from_secs(2);
6144        while !serve_thread.is_finished() {
6145            assert!(
6146                StdInstant::now() < deadline,
6147                "serve_concurrent should exit promptly after shutdown without a new connection"
6148            );
6149            std::thread::sleep(Duration::from_millis(20));
6150        }
6151
6152        let result = serve_thread
6153            .join()
6154            .expect("serve_concurrent regression thread should not panic");
6155        assert!(
6156            result.is_ok(),
6157            "serve_concurrent should stop cleanly on shutdown"
6158        );
6159    }
6160
6161    #[test]
6162    fn server_shutdown_error_display() {
6163        let err = ServerError::Shutdown;
6164        assert_eq!(err.to_string(), "Server shutdown");
6165    }
6166
6167    // ========================================================================
6168    // Graceful shutdown controller tests
6169    // ========================================================================
6170
6171    #[test]
6172    fn server_has_shutdown_controller() {
6173        let server = TcpServer::default();
6174        let controller = server.shutdown_controller();
6175        assert!(!controller.is_shutting_down());
6176    }
6177
6178    #[test]
6179    fn server_subscribe_shutdown_returns_receiver() {
6180        let server = TcpServer::default();
6181        let receiver = server.subscribe_shutdown();
6182        assert!(!receiver.is_shutting_down());
6183    }
6184
6185    #[test]
6186    fn server_shutdown_sets_draining_and_controller() {
6187        let server = TcpServer::default();
6188        assert!(!server.is_shutting_down());
6189        assert!(!server.is_draining());
6190        assert!(!server.shutdown_controller().is_shutting_down());
6191
6192        server.shutdown();
6193
6194        assert!(server.is_shutting_down());
6195        assert!(server.is_draining());
6196        assert!(server.shutdown_controller().is_shutting_down());
6197    }
6198
6199    #[test]
6200    fn server_shutdown_notifies_receivers() {
6201        let server = TcpServer::default();
6202        let receiver1 = server.subscribe_shutdown();
6203        let receiver2 = server.subscribe_shutdown();
6204
6205        assert!(!receiver1.is_shutting_down());
6206        assert!(!receiver2.is_shutting_down());
6207
6208        server.shutdown();
6209
6210        assert!(receiver1.is_shutting_down());
6211        assert!(receiver2.is_shutting_down());
6212    }
6213
6214    #[test]
6215    fn server_shutdown_is_idempotent() {
6216        let server = TcpServer::default();
6217        let receiver = server.subscribe_shutdown();
6218
6219        server.shutdown();
6220        server.shutdown();
6221        server.shutdown();
6222
6223        assert!(server.is_shutting_down());
6224        assert!(receiver.is_shutting_down());
6225    }
6226
6227    // ========================================================================
6228    // Keep-alive timeout tests
6229    // ========================================================================
6230
6231    #[test]
6232    fn keep_alive_timeout_error_display() {
6233        let err = ServerError::KeepAliveTimeout;
6234        assert_eq!(err.to_string(), "Keep-alive timeout");
6235    }
6236
6237    #[test]
6238    fn keep_alive_timeout_zero_disables_timeout() {
6239        let config = ServerConfig::new("127.0.0.1:8080").with_keep_alive_timeout(Duration::ZERO);
6240        assert!(config.keep_alive_timeout.is_zero());
6241    }
6242
6243    #[test]
6244    fn keep_alive_timeout_default_is_non_zero() {
6245        let config = ServerConfig::default();
6246        assert!(!config.keep_alive_timeout.is_zero());
6247        assert_eq!(
6248            config.keep_alive_timeout,
6249            Duration::from_secs(DEFAULT_KEEP_ALIVE_TIMEOUT_SECS)
6250        );
6251    }
6252
6253    #[test]
6254    fn timed_out_io_error_kind() {
6255        let err = io::Error::new(io::ErrorKind::TimedOut, "test timeout");
6256        assert_eq!(err.kind(), io::ErrorKind::TimedOut);
6257    }
6258
6259    #[test]
6260    fn instant_deadline_calculation() {
6261        let timeout = Duration::from_millis(100);
6262        let deadline = Instant::now() + timeout;
6263
6264        // Deadline should be in the future
6265        assert!(deadline > Instant::now());
6266
6267        // After waiting, deadline should be in the past
6268        std::thread::sleep(Duration::from_millis(150));
6269        assert!(Instant::now() >= deadline);
6270    }
6271
6272    #[test]
6273    fn server_metrics_initial_state() {
6274        let server = TcpServer::default();
6275        let m = server.metrics();
6276        assert_eq!(m.active_connections, 0);
6277        assert_eq!(m.total_accepted, 0);
6278        assert_eq!(m.total_rejected, 0);
6279        assert_eq!(m.total_timed_out, 0);
6280        assert_eq!(m.total_requests, 0);
6281        assert_eq!(m.bytes_in, 0);
6282        assert_eq!(m.bytes_out, 0);
6283    }
6284
6285    #[test]
6286    fn server_metrics_after_acquire_release() {
6287        let server = TcpServer::new(ServerConfig::new("127.0.0.1:0").with_max_connections(10));
6288        assert!(server.try_acquire_connection());
6289        assert!(server.try_acquire_connection());
6290
6291        let m = server.metrics();
6292        assert_eq!(m.active_connections, 2);
6293        assert_eq!(m.total_accepted, 2);
6294        assert_eq!(m.total_rejected, 0);
6295
6296        server.release_connection();
6297        let m = server.metrics();
6298        assert_eq!(m.active_connections, 1);
6299        assert_eq!(m.total_accepted, 2); // monotonic
6300    }
6301
6302    #[test]
6303    fn server_metrics_rejection_counted() {
6304        let server = TcpServer::new(ServerConfig::new("127.0.0.1:0").with_max_connections(1));
6305        assert!(server.try_acquire_connection());
6306        assert!(!server.try_acquire_connection()); // rejected
6307
6308        let m = server.metrics();
6309        assert_eq!(m.total_accepted, 1);
6310        assert_eq!(m.total_rejected, 1);
6311        assert_eq!(m.active_connections, 1);
6312    }
6313
6314    #[test]
6315    fn server_metrics_bytes_tracking() {
6316        let server = TcpServer::default();
6317        server.record_bytes_in(1024);
6318        server.record_bytes_in(512);
6319        server.record_bytes_out(2048);
6320
6321        let m = server.metrics();
6322        assert_eq!(m.bytes_in, 1536);
6323        assert_eq!(m.bytes_out, 2048);
6324    }
6325
6326    #[test]
6327    fn server_metrics_unlimited_connections_accepted() {
6328        let server = TcpServer::new(ServerConfig::new("127.0.0.1:0").with_max_connections(0));
6329        for _ in 0..100 {
6330            assert!(server.try_acquire_connection());
6331        }
6332        let m = server.metrics();
6333        assert_eq!(m.total_accepted, 100);
6334        assert_eq!(m.total_rejected, 0);
6335        assert_eq!(m.active_connections, 100);
6336    }
6337
6338    #[test]
6339    fn server_metrics_clone_eq() {
6340        let server = TcpServer::default();
6341        server.record_bytes_in(42);
6342        let m1 = server.metrics();
6343        let m2 = m1.clone();
6344        assert_eq!(m1, m2);
6345    }
6346}
6347
6348// ============================================================================
6349// App Serve Extension
6350// ============================================================================
6351
6352/// Extension trait to add serve capability to [`App`].
6353///
6354/// This trait provides the `serve()` method that wires an App to the HTTP server,
6355/// enabling it to handle incoming HTTP requests.
6356///
6357/// # Example
6358///
6359/// ```ignore
6360/// use fastapi::prelude::*;
6361/// use fastapi_http::AppServeExt;
6362///
6363/// let app = App::builder()
6364///     .get("/", |_, _| async { Response::ok().body_text("Hello!") })
6365///     .build();
6366///
6367/// // Run the server
6368/// app.serve("0.0.0.0:8080").await?;
6369/// ```
6370pub trait AppServeExt {
6371    /// Starts the HTTP server and begins accepting connections.
6372    ///
6373    /// This method:
6374    /// 1. Runs all registered startup hooks
6375    /// 2. Binds to the specified address
6376    /// 3. Accepts connections and routes requests to handlers
6377    /// 4. Runs shutdown hooks when the server stops
6378    ///
6379    /// # Arguments
6380    ///
6381    /// * `addr` - The address to bind to (e.g., "0.0.0.0:8080" or "127.0.0.1:3000")
6382    ///
6383    /// # Errors
6384    ///
6385    /// Returns an error if:
6386    /// - A startup hook fails with `abort: true`
6387    /// - Binding to the address fails
6388    /// - An unrecoverable IO error occurs
6389    ///
6390    /// # Example
6391    ///
6392    /// ```ignore
6393    /// use fastapi::prelude::*;
6394    /// use fastapi_http::AppServeExt;
6395    ///
6396    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
6397    ///     let app = App::builder()
6398    ///         .get("/health", |_, _| async { Response::ok() })
6399    ///         .build();
6400    ///
6401    ///     let rt = asupersync::runtime::RuntimeBuilder::current_thread().build()?;
6402    ///     rt.block_on(async {
6403    ///         app.serve("0.0.0.0:8080").await?;
6404    ///         Ok::<(), fastapi_http::ServeError>(())
6405    ///     })?;
6406    ///
6407    ///     Ok(())
6408    /// }
6409    /// ```
6410    fn serve(self, addr: impl Into<String>) -> impl Future<Output = Result<(), ServeError>> + Send;
6411
6412    /// Starts the HTTP server with custom configuration.
6413    ///
6414    /// This method allows fine-grained control over server behavior including
6415    /// timeouts, connection limits, and keep-alive settings.
6416    ///
6417    /// # Arguments
6418    ///
6419    /// * `config` - Server configuration options
6420    ///
6421    /// # Example
6422    ///
6423    /// ```ignore
6424    /// use fastapi::prelude::*;
6425    /// use fastapi_http::{AppServeExt, ServerConfig};
6426    ///
6427    /// let config = ServerConfig::new("0.0.0.0:8080")
6428    ///     .with_request_timeout_secs(60)
6429    ///     .with_max_connections(1000)
6430    ///     .with_keep_alive_timeout_secs(120);
6431    ///
6432    /// app.serve_with_config(config).await?;
6433    /// ```
6434    fn serve_with_config(
6435        self,
6436        config: ServerConfig,
6437    ) -> impl Future<Output = Result<(), ServeError>> + Send;
6438}
6439
6440/// Error returned when starting or running the server fails.
6441#[derive(Debug)]
6442pub enum ServeError {
6443    /// A startup hook failed with abort.
6444    Startup(fastapi_core::StartupHookError),
6445    /// Server error during operation.
6446    Server(ServerError),
6447}
6448
6449impl std::fmt::Display for ServeError {
6450    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6451        match self {
6452            Self::Startup(e) => write!(f, "startup hook failed: {}", e.message),
6453            Self::Server(e) => write!(f, "server error: {e}"),
6454        }
6455    }
6456}
6457
6458impl std::error::Error for ServeError {
6459    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
6460        match self {
6461            Self::Startup(_) => None,
6462            Self::Server(e) => Some(e),
6463        }
6464    }
6465}
6466
6467impl From<ServerError> for ServeError {
6468    fn from(e: ServerError) -> Self {
6469        Self::Server(e)
6470    }
6471}
6472
6473impl AppServeExt for App {
6474    fn serve(self, addr: impl Into<String>) -> impl Future<Output = Result<(), ServeError>> + Send {
6475        let config = ServerConfig::new(addr);
6476        self.serve_with_config(config)
6477    }
6478
6479    #[allow(clippy::manual_async_fn)] // Using impl Future for trait compatibility
6480    fn serve_with_config(
6481        self,
6482        config: ServerConfig,
6483    ) -> impl Future<Output = Result<(), ServeError>> + Send {
6484        async move {
6485            // Run startup hooks
6486            match self.run_startup_hooks().await {
6487                fastapi_core::StartupOutcome::Success => {}
6488                fastapi_core::StartupOutcome::PartialSuccess { warnings } => {
6489                    // Log warnings but continue (non-fatal)
6490                    eprintln!("Warning: {warnings} startup hook(s) had non-fatal errors");
6491                }
6492                fastapi_core::StartupOutcome::Aborted(e) => {
6493                    return Err(ServeError::Startup(e));
6494                }
6495            }
6496
6497            // Create the TCP server
6498            let server = TcpServer::new(config);
6499
6500            // Wrap app in Arc for sharing.
6501            let app = Arc::new(self);
6502
6503            let cx = Cx::current().ok_or_else(|| {
6504                ServeError::Server(ServerError::Io(io::Error::other(
6505                    "fastapi App::serve must run inside an asupersync runtime",
6506                )))
6507            })?;
6508
6509            // Print startup banner
6510            let bind_addr = &server.config().bind_addr;
6511            println!("🚀 Server starting on http://{bind_addr}");
6512
6513            // Run the server with App-aware routing (enables protocol upgrades like WebSocket).
6514            let result = server.serve_app(&cx, Arc::clone(&app)).await;
6515
6516            // Run shutdown hooks (use the original Arc<App>)
6517            app.run_shutdown_hooks().await;
6518
6519            result.map_err(ServeError::from)
6520        }
6521    }
6522}
6523
6524/// Convenience function to serve an App on the given address.
6525///
6526/// This is equivalent to calling `app.serve(addr)` but can be more
6527/// ergonomic in some contexts.
6528///
6529/// # Example
6530///
6531/// ```ignore
6532/// use fastapi::prelude::*;
6533/// use fastapi_http::serve;
6534///
6535/// let app = App::builder()
6536///     .get("/", |_, _| async { Response::ok() })
6537///     .build();
6538///
6539/// serve(app, "0.0.0.0:8080").await?;
6540/// ```
6541pub async fn serve(app: App, addr: impl Into<String>) -> Result<(), ServeError> {
6542    app.serve(addr).await
6543}
6544
6545/// Convenience function to serve an App with custom configuration.
6546///
6547/// # Example
6548///
6549/// ```ignore
6550/// use fastapi::prelude::*;
6551/// use fastapi_http::{serve_with_config, ServerConfig};
6552///
6553/// let app = App::builder()
6554///     .get("/", |_, _| async { Response::ok() })
6555///     .build();
6556///
6557/// let config = ServerConfig::new("0.0.0.0:8080")
6558///     .with_max_connections(500);
6559///
6560/// serve_with_config(app, config).await?;
6561/// ```
6562pub async fn serve_with_config(app: App, config: ServerConfig) -> Result<(), ServeError> {
6563    app.serve_with_config(config).await
6564}