Skip to main content

fastapi_http/
parser.rs

1//! HTTP request parser.
2//!
3//! This module provides zero-copy HTTP/1.1 parsing for request lines and headers.
4//!
5//! # Zero-Copy Design
6//!
7//! The parser returns borrowed types that reference the original buffer,
8//! avoiding allocations on the hot path. Types:
9//!
10//! - [`RequestLine`] - borrowed request line (method, path, version)
11//! - [`HeadersIter`] - iterator over borrowed headers
12//!
13//! # Example
14//!
15//! ```ignore
16//! use fastapi_http::parser::{Parser, RequestLine};
17//!
18//! let buffer = b"GET /items/123?q=test HTTP/1.1\r\nHost: example.com\r\n\r\n";
19//! let request_line = RequestLine::parse(buffer)?;
20//!
21//! assert_eq!(request_line.method(), Method::Get);
22//! assert_eq!(request_line.path(), "/items/123");
23//! assert_eq!(request_line.query(), Some("q=test"));
24//! assert_eq!(request_line.version(), "HTTP/1.1");
25//! ```
26
27use crate::body::{BodyConfig, BodyError, parse_body_with_consumed};
28use fastapi_core::{Body, HttpVersion, Method, Request};
29use std::borrow::Cow;
30
31/// HTTP parsing error.
32#[derive(Debug)]
33pub enum ParseError {
34    /// Invalid request line.
35    InvalidRequestLine,
36    /// Invalid HTTP method.
37    InvalidMethod,
38    /// Invalid header.
39    InvalidHeader,
40    /// Invalid header name (non-token characters).
41    InvalidHeaderName,
42    /// Invalid bytes in header value.
43    InvalidHeaderBytes,
44    /// Request line too long.
45    RequestLineTooLong,
46    /// Header line too long.
47    HeaderLineTooLong,
48    /// Too many headers.
49    TooManyHeaders,
50    /// Header block too large.
51    HeadersTooLarge,
52    /// Unsupported or invalid Transfer-Encoding.
53    InvalidTransferEncoding,
54    /// Ambiguous body length (e.g., both Transfer-Encoding and Content-Length).
55    AmbiguousBodyLength,
56    /// Request too large.
57    TooLarge,
58    /// Incomplete request (need more data).
59    Incomplete,
60}
61
62impl std::fmt::Display for ParseError {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        match self {
65            Self::InvalidRequestLine => write!(f, "invalid request line"),
66            Self::InvalidMethod => write!(f, "invalid HTTP method"),
67            Self::InvalidHeader => write!(f, "invalid header"),
68            Self::InvalidHeaderName => write!(f, "invalid header name"),
69            Self::InvalidHeaderBytes => write!(f, "invalid header bytes"),
70            Self::RequestLineTooLong => write!(f, "request line too long"),
71            Self::HeaderLineTooLong => write!(f, "header line too long"),
72            Self::TooManyHeaders => write!(f, "too many headers"),
73            Self::HeadersTooLarge => write!(f, "headers too large"),
74            Self::InvalidTransferEncoding => write!(f, "invalid transfer-encoding"),
75            Self::AmbiguousBodyLength => write!(f, "ambiguous body length"),
76            Self::TooLarge => write!(f, "request too large"),
77            Self::Incomplete => write!(f, "incomplete request"),
78        }
79    }
80}
81
82impl std::error::Error for ParseError {}
83
84/// Parsing limits for request line and headers.
85#[derive(Debug, Clone)]
86pub struct ParseLimits {
87    /// Maximum total request size in bytes.
88    pub max_request_size: usize,
89    /// Maximum request line length in bytes.
90    pub max_request_line_len: usize,
91    /// Maximum number of headers.
92    pub max_header_count: usize,
93    /// Maximum length of a single header line.
94    pub max_header_line_len: usize,
95    /// Maximum total header block size (including CRLF terminator).
96    pub max_headers_size: usize,
97}
98
99impl Default for ParseLimits {
100    fn default() -> Self {
101        Self {
102            max_request_size: 1024 * 1024,  // 1MB
103            max_request_line_len: 8 * 1024, // 8KB
104            max_header_count: 100,
105            max_header_line_len: 8 * 1024, // 8KB
106            max_headers_size: 64 * 1024,   // 64KB
107        }
108    }
109}
110
111fn has_invalid_request_line_bytes(line: &[u8]) -> bool {
112    line.iter().any(|&b| b == 0 || b == b'\r' || b == b'\n')
113}
114
115// ============================================================================
116// Zero-Copy Request Line Parser
117// ============================================================================
118
119/// A zero-copy view of an HTTP request line.
120///
121/// This type borrows from the original buffer and performs no allocations.
122/// It provides access to the parsed method, path, query string, and HTTP version.
123///
124/// # Zero-Allocation Guarantee
125///
126/// All methods return borrowed data (`&str` or `Method`). No heap allocations
127/// are performed during parsing or access.
128///
129/// # Example
130///
131/// ```ignore
132/// let buffer = b"GET /items/123?q=test HTTP/1.1\r\n";
133/// let line = RequestLine::parse(buffer)?;
134///
135/// assert_eq!(line.method(), Method::Get);
136/// assert_eq!(line.path(), "/items/123");
137/// assert_eq!(line.query(), Some("q=test"));
138/// assert_eq!(line.version(), "HTTP/1.1");
139/// ```
140#[derive(Debug, Clone, Copy)]
141pub struct RequestLine<'a> {
142    method: Method,
143    /// Full URI as it appeared on the request line (path + optional `?query`).
144    ///
145    /// This is a zero-copy slice into the request buffer.
146    uri: &'a str,
147    path: &'a str,
148    query: Option<&'a str>,
149    version: &'a str,
150}
151
152impl<'a> RequestLine<'a> {
153    /// Parse a request line from bytes.
154    ///
155    /// The buffer should contain just the request line, ending with `\r\n` or EOF.
156    /// Example: `GET /path?query HTTP/1.1\r\n`
157    ///
158    /// # Errors
159    ///
160    /// Returns `ParseError::InvalidRequestLine` if the format is invalid.
161    /// Returns `ParseError::InvalidMethod` if the HTTP method is not recognized.
162    pub fn parse(buffer: &'a [u8]) -> Result<Self, ParseError> {
163        // Find the end of the request line (before any \r\n)
164        let line_end = buffer
165            .windows(2)
166            .position(|w| w == b"\r\n")
167            .unwrap_or(buffer.len());
168
169        let line = &buffer[..line_end];
170        if has_invalid_request_line_bytes(line) {
171            return Err(ParseError::InvalidRequestLine);
172        }
173
174        // Split by spaces: METHOD SP URI SP VERSION
175        let mut parts = line.splitn(3, |&b| b == b' ');
176
177        // Parse method
178        let method_bytes = parts.next().ok_or(ParseError::InvalidRequestLine)?;
179        let method = Method::from_bytes(method_bytes).ok_or(ParseError::InvalidMethod)?;
180
181        // Parse URI (path + optional query)
182        let uri_bytes = parts.next().ok_or(ParseError::InvalidRequestLine)?;
183        if uri_bytes.is_empty() {
184            return Err(ParseError::InvalidRequestLine);
185        }
186        let uri = std::str::from_utf8(uri_bytes).map_err(|_| ParseError::InvalidRequestLine)?;
187
188        // Parse version
189        let version_bytes = parts.next().ok_or(ParseError::InvalidRequestLine)?;
190        let version =
191            std::str::from_utf8(version_bytes).map_err(|_| ParseError::InvalidRequestLine)?;
192
193        // Split path and query from URI
194        let (path, query) = if let Some(q_pos) = uri.find('?') {
195            (&uri[..q_pos], Some(&uri[q_pos + 1..]))
196        } else {
197            (uri, None)
198        };
199
200        Ok(Self {
201            method,
202            uri,
203            path,
204            query,
205            version,
206        })
207    }
208
209    /// Parse a request line from a buffer, returning bytes consumed.
210    ///
211    /// This is useful for incremental parsing where you need to know
212    /// how much of the buffer was consumed.
213    ///
214    /// # Returns
215    ///
216    /// Returns `(RequestLine, bytes_consumed)` on success.
217    /// `bytes_consumed` includes the trailing `\r\n` if present.
218    pub fn parse_with_len(buffer: &'a [u8]) -> Result<(Self, usize), ParseError> {
219        let line_end = buffer
220            .windows(2)
221            .position(|w| w == b"\r\n")
222            .ok_or(ParseError::Incomplete)?;
223
224        let line = Self::parse(&buffer[..line_end])?;
225        // +2 for the \r\n
226        Ok((line, line_end + 2))
227    }
228
229    /// Returns the HTTP method.
230    #[inline]
231    #[must_use]
232    pub fn method(&self) -> Method {
233        self.method
234    }
235
236    /// Returns the request path (without query string).
237    ///
238    /// Example: For `GET /items/123?q=test HTTP/1.1`, returns `/items/123`.
239    #[inline]
240    #[must_use]
241    pub fn path(&self) -> &'a str {
242        self.path
243    }
244
245    /// Returns the query string (without the leading `?`), if present.
246    ///
247    /// Example: For `GET /items?q=test HTTP/1.1`, returns `Some("q=test")`.
248    #[inline]
249    #[must_use]
250    pub fn query(&self) -> Option<&'a str> {
251        self.query
252    }
253
254    /// Returns the full URI (path + query string).
255    ///
256    /// Example: For `GET /items?q=test HTTP/1.1`, returns `/items?q=test`.
257    #[must_use]
258    pub fn uri(&self) -> &'a str {
259        self.uri
260    }
261
262    /// Returns the HTTP version string.
263    ///
264    /// Example: For `GET /path HTTP/1.1`, returns `HTTP/1.1`.
265    #[inline]
266    #[must_use]
267    pub fn version(&self) -> &'a str {
268        self.version
269    }
270
271    /// Returns true if this is HTTP/1.1.
272    #[inline]
273    #[must_use]
274    pub fn is_http11(&self) -> bool {
275        self.version == "HTTP/1.1"
276    }
277
278    /// Returns true if this is HTTP/1.0.
279    #[inline]
280    #[must_use]
281    pub fn is_http10(&self) -> bool {
282        self.version == "HTTP/1.0"
283    }
284}
285
286// ============================================================================
287// Zero-Copy Header Parser
288// ============================================================================
289
290fn is_token_char(b: u8) -> bool {
291    matches!(
292        b,
293        b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'-' | b'.' | b'^' | b'_' | b'`'
294            | b'|' | b'~' | b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z'
295    )
296}
297
298fn is_valid_header_name(bytes: &[u8]) -> bool {
299    !bytes.is_empty() && bytes.iter().all(|&b| is_token_char(b))
300}
301
302fn has_invalid_header_value_bytes(value: &[u8]) -> bool {
303    value
304        .iter()
305        .any(|&b| b == 0 || b == 0x7f || (b < 0x20 && b != b'\t' && b != b' '))
306}
307
308fn has_invalid_header_line_bytes(line: &[u8]) -> bool {
309    line.iter().any(|&b| b == 0)
310}
311
312/// A zero-copy view of a single HTTP header.
313#[derive(Debug, Clone, Copy)]
314pub struct Header<'a> {
315    name: &'a str,
316    name_bytes: &'a [u8],
317    value: &'a [u8],
318}
319
320impl<'a> Header<'a> {
321    /// Returns the header name (case-preserved from original).
322    #[inline]
323    #[must_use]
324    pub fn name(&self) -> &'a str {
325        self.name
326    }
327
328    /// Returns the header name as raw bytes.
329    #[inline]
330    #[must_use]
331    pub fn name_bytes(&self) -> &'a [u8] {
332        self.name_bytes
333    }
334
335    /// Returns the header as a raw `(&[u8], &[u8])` pair.
336    ///
337    /// This is useful for zero-allocation header processing.
338    #[inline]
339    #[must_use]
340    pub fn as_bytes_pair(&self) -> (&'a [u8], &'a [u8]) {
341        (self.name_bytes, self.value)
342    }
343
344    /// Returns the header value as bytes.
345    #[inline]
346    #[must_use]
347    pub fn value(&self) -> &'a [u8] {
348        self.value
349    }
350
351    /// Returns the header value as a string, if valid UTF-8.
352    #[must_use]
353    pub fn value_str(&self) -> Option<&'a str> {
354        std::str::from_utf8(self.value).ok()
355    }
356
357    /// Returns true if this header name matches (case-insensitive).
358    #[must_use]
359    pub fn name_eq_ignore_case(&self, other: &str) -> bool {
360        self.name.eq_ignore_ascii_case(other)
361    }
362
363    /// Returns true if this is the Content-Length header.
364    #[inline]
365    #[must_use]
366    pub fn is_content_length(&self) -> bool {
367        self.name_eq_ignore_case("content-length")
368    }
369
370    /// Returns true if this is the Transfer-Encoding header.
371    #[inline]
372    #[must_use]
373    pub fn is_transfer_encoding(&self) -> bool {
374        self.name_eq_ignore_case("transfer-encoding")
375    }
376
377    /// Parses the value as Content-Length (usize).
378    ///
379    /// Returns `None` if this isn't Content-Length or value isn't a valid integer.
380    #[must_use]
381    pub fn as_content_length(&self) -> Option<usize> {
382        if !self.is_content_length() {
383            return None;
384        }
385        self.value_str()?.trim().parse().ok()
386    }
387
388    /// Returns true if Transfer-Encoding is exactly "chunked" (case-insensitive,
389    /// ignoring optional whitespace). We do NOT accept compound codings like
390    /// "gzip, chunked" since the server does not support non-chunked transfer
391    /// codings; rejecting them prevents request smuggling via unsupported
392    /// intermediate codings.
393    #[must_use]
394    pub fn is_chunked_encoding(&self) -> bool {
395        if !self.is_transfer_encoding() {
396            return false;
397        }
398        self.value_str()
399            .is_some_and(|v| v.trim().eq_ignore_ascii_case("chunked"))
400    }
401}
402
403/// Iterator over HTTP headers in a buffer.
404///
405/// Zero-copy: returns borrowed [`Header`] references into the original buffer.
406pub struct HeadersIter<'a> {
407    remaining: &'a [u8],
408}
409
410impl<'a> HeadersIter<'a> {
411    /// Create a new headers iterator from a buffer.
412    ///
413    /// The buffer should start at the first header line (after the request line).
414    #[must_use]
415    pub fn new(buffer: &'a [u8]) -> Self {
416        Self { remaining: buffer }
417    }
418
419    /// Parse a single header from the buffer.
420    fn parse_header(line: &'a [u8]) -> Result<Header<'a>, ParseError> {
421        if has_invalid_header_line_bytes(line) {
422            return Err(ParseError::InvalidHeaderBytes);
423        }
424
425        let colon_pos = line
426            .iter()
427            .position(|&b| b == b':')
428            .ok_or(ParseError::InvalidHeader)?;
429
430        let name_bytes = &line[..colon_pos];
431        if !is_valid_header_name(name_bytes) {
432            return Err(ParseError::InvalidHeaderName);
433        }
434        let name = std::str::from_utf8(name_bytes).map_err(|_| ParseError::InvalidHeader)?;
435
436        // Trim leading whitespace from value
437        let value_start = line[colon_pos + 1..]
438            .iter()
439            .position(|&b| b != b' ' && b != b'\t')
440            .map_or(colon_pos + 1, |p| colon_pos + 1 + p);
441
442        let value = &line[value_start..];
443        if has_invalid_header_value_bytes(value) {
444            return Err(ParseError::InvalidHeaderBytes);
445        }
446
447        // Trim the name (but keep original bytes for raw access)
448        let trimmed_name = name.trim();
449
450        Ok(Header {
451            name: trimmed_name,
452            name_bytes,
453            value,
454        })
455    }
456}
457
458impl<'a> Iterator for HeadersIter<'a> {
459    type Item = Result<Header<'a>, ParseError>;
460
461    fn next(&mut self) -> Option<Self::Item> {
462        if self.remaining.is_empty() {
463            return None;
464        }
465
466        // Find the end of this header line
467        let line_end = self
468            .remaining
469            .windows(2)
470            .position(|w| w == b"\r\n")
471            .unwrap_or(self.remaining.len());
472
473        // Empty line signals end of headers
474        if line_end == 0 {
475            self.remaining = &[];
476            return None;
477        }
478
479        let line = &self.remaining[..line_end];
480
481        // Advance past this line and its \r\n
482        self.remaining = if line_end + 2 <= self.remaining.len() {
483            &self.remaining[line_end + 2..]
484        } else {
485            &[]
486        };
487
488        Some(Self::parse_header(line))
489    }
490}
491
492// ============================================================================
493// Headers Collection with Common Helpers
494// ============================================================================
495
496/// Body length indicator from headers.
497#[derive(Debug, Clone, Copy, PartialEq, Eq)]
498pub enum BodyLength {
499    /// Content-Length header specifies exact byte count.
500    ContentLength(usize),
501    /// Transfer-Encoding: chunked.
502    Chunked,
503    /// No body expected (no Content-Length or Transfer-Encoding).
504    None,
505    /// Multiple conflicting length indicators.
506    Conflicting,
507}
508
509/// Parses headers and extracts Content-Length / Transfer-Encoding.
510///
511/// This is a zero-copy parser that yields headers while tracking
512/// the body length indicator.
513///
514/// # Example
515///
516/// ```ignore
517/// let buffer = b"Content-Length: 42\r\nHost: example.com\r\n\r\n";
518/// let headers = HeadersParser::parse(buffer)?;
519///
520/// assert_eq!(headers.body_length(), BodyLength::ContentLength(42));
521/// assert_eq!(headers.content_length(), Some(42));
522/// ```
523pub struct HeadersParser<'a> {
524    buffer: &'a [u8],
525    bytes_consumed: usize,
526    content_length: Option<usize>,
527    is_chunked: bool,
528}
529
530impl<'a> HeadersParser<'a> {
531    /// Parse all headers from a buffer.
532    ///
533    /// Returns the parser with pre-computed Content-Length and Transfer-Encoding.
534    /// The buffer should start at the first header line (after request line).
535    pub fn parse(buffer: &'a [u8]) -> Result<Self, ParseError> {
536        Self::parse_with_limits(buffer, &ParseLimits::default())
537    }
538
539    /// Parse all headers from a buffer with limits.
540    ///
541    /// Enforces header count, line length, and total size limits. Also
542    /// rejects ambiguous body length indicators (Transfer-Encoding + Content-Length).
543    pub fn parse_with_limits(buffer: &'a [u8], limits: &ParseLimits) -> Result<Self, ParseError> {
544        let header_end = buffer.windows(4).position(|w| w == b"\r\n\r\n");
545        let header_block_len = header_end.map_or(buffer.len(), |pos| pos + 4);
546        if header_block_len > limits.max_headers_size {
547            return Err(ParseError::HeadersTooLarge);
548        }
549
550        let mut content_length = None;
551        let mut saw_transfer_encoding = false;
552        let mut is_chunked = false;
553        let mut header_count = 0usize;
554
555        let mut remaining = &buffer[..header_block_len];
556        while !remaining.is_empty() {
557            let line_end = remaining
558                .windows(2)
559                .position(|w| w == b"\r\n")
560                .unwrap_or(remaining.len());
561
562            if line_end == 0 {
563                break;
564            }
565
566            if line_end > limits.max_header_line_len {
567                return Err(ParseError::HeaderLineTooLong);
568            }
569
570            let line = &remaining[..line_end];
571            if matches!(line.first(), Some(b' ' | b'\t')) {
572                return Err(ParseError::InvalidHeader);
573            }
574
575            let header = HeadersIter::parse_header(line)?;
576            header_count += 1;
577            if header_count > limits.max_header_count {
578                return Err(ParseError::TooManyHeaders);
579            }
580
581            if header.is_content_length() {
582                let len = header
583                    .as_content_length()
584                    .ok_or(ParseError::InvalidHeader)?;
585                if content_length.is_some() && content_length != Some(len) {
586                    return Err(ParseError::InvalidHeader);
587                }
588                content_length = Some(len);
589            }
590
591            if header.is_transfer_encoding() {
592                saw_transfer_encoding = true;
593                if header.is_chunked_encoding() {
594                    is_chunked = true;
595                } else {
596                    return Err(ParseError::InvalidTransferEncoding);
597                }
598            }
599
600            remaining = if line_end + 2 <= remaining.len() {
601                &remaining[line_end + 2..]
602            } else {
603                &[]
604            };
605        }
606
607        if saw_transfer_encoding && content_length.is_some() {
608            return Err(ParseError::AmbiguousBodyLength);
609        }
610
611        let bytes_consumed = header_block_len;
612
613        Ok(Self {
614            buffer,
615            bytes_consumed,
616            content_length,
617            is_chunked,
618        })
619    }
620
621    /// Returns the body length indicator.
622    ///
623    /// Per RFC 7230:
624    /// - If Transfer-Encoding: chunked is present, use chunked decoding
625    /// - Else if Content-Length is present, use that
626    /// - Else no body
627    #[must_use]
628    pub fn body_length(&self) -> BodyLength {
629        if self.is_chunked {
630            if self.content_length.is_some() {
631                return BodyLength::Conflicting;
632            }
633            return BodyLength::Chunked;
634        }
635
636        if let Some(len) = self.content_length {
637            return BodyLength::ContentLength(len);
638        }
639
640        BodyLength::None
641    }
642
643    /// Returns the Content-Length value if present.
644    #[must_use]
645    pub fn content_length(&self) -> Option<usize> {
646        self.content_length
647    }
648
649    /// Returns true if Transfer-Encoding: chunked.
650    #[must_use]
651    pub fn is_chunked(&self) -> bool {
652        self.is_chunked
653    }
654
655    /// Returns the number of bytes consumed (including final \r\n\r\n).
656    #[must_use]
657    pub fn bytes_consumed(&self) -> usize {
658        self.bytes_consumed
659    }
660
661    /// Returns an iterator over all headers.
662    #[must_use]
663    pub fn iter(&self) -> HeadersIter<'a> {
664        HeadersIter::new(self.buffer)
665    }
666
667    /// Finds a header by name (case-insensitive).
668    #[must_use]
669    pub fn get(&self, name: &str) -> Option<Header<'a>> {
670        self.iter()
671            .filter_map(Result::ok)
672            .find(|h| h.name_eq_ignore_case(name))
673    }
674
675    /// Returns all headers matching a name (case-insensitive).
676    pub fn get_all<'b>(&'b self, name: &'b str) -> impl Iterator<Item = Header<'a>> + 'b {
677        self.iter()
678            .filter_map(Result::ok)
679            .filter(move |h| h.name_eq_ignore_case(name))
680    }
681}
682
683// ============================================================================
684// High-Level Parser (with owned Request for convenience)
685// ============================================================================
686
687/// Zero-copy HTTP request parser.
688pub struct Parser {
689    limits: ParseLimits,
690}
691
692impl Parser {
693    /// Create a new parser with default settings.
694    #[must_use]
695    pub fn new() -> Self {
696        Self {
697            limits: ParseLimits::default(),
698        }
699    }
700
701    /// Set maximum request size.
702    #[must_use]
703    pub fn with_max_size(mut self, size: usize) -> Self {
704        self.limits.max_request_size = size;
705        self
706    }
707
708    /// Set all parsing limits.
709    #[must_use]
710    pub fn with_limits(mut self, limits: ParseLimits) -> Self {
711        self.limits = limits;
712        self
713    }
714
715    /// Parse an HTTP request from bytes.
716    ///
717    /// # Errors
718    ///
719    /// Returns an error if the request is malformed.
720    pub fn parse(&self, buffer: &[u8]) -> Result<Request, ParseError> {
721        if buffer.len() > self.limits.max_request_size {
722            return Err(ParseError::TooLarge);
723        }
724
725        // Find end of headers
726        let header_end = find_header_end(buffer).ok_or(ParseError::Incomplete)?;
727
728        let header_bytes = &buffer[..header_end];
729        let body_bytes = &buffer[header_end + 4..]; // Skip \r\n\r\n
730
731        // Parse request line
732        let first_line_end = header_bytes
733            .windows(2)
734            .position(|w| w == b"\r\n")
735            .ok_or(ParseError::InvalidRequestLine)?;
736        if first_line_end > self.limits.max_request_line_len {
737            return Err(ParseError::RequestLineTooLong);
738        }
739
740        let request_line = &header_bytes[..first_line_end];
741        let (method, path, query, http_version) = parse_request_line(request_line)?;
742
743        let header_start = first_line_end + 2;
744        let header_block_len = header_end + 4 - header_start;
745        if header_block_len > self.limits.max_headers_size {
746            return Err(ParseError::HeadersTooLarge);
747        }
748
749        // Parse headers
750        let headers =
751            HeadersParser::parse_with_limits(&buffer[header_start..header_end + 4], &self.limits)?;
752
753        // Build request with HTTP version
754        let mut request = Request::with_version(method, path, http_version);
755        request.set_query(query);
756
757        // Set headers (using optimized insert to avoid double allocation)
758        for header in headers.iter() {
759            let header = header?;
760            request
761                .headers_mut()
762                .insert_from_slice(header.name(), header.value());
763        }
764
765        // Set body
766        if !body_bytes.is_empty() {
767            request.set_body(Body::Bytes(body_bytes.to_vec()));
768        }
769
770        Ok(request)
771    }
772}
773
774impl Default for Parser {
775    fn default() -> Self {
776        Self::new()
777    }
778}
779
780// ============================================================================
781// Incremental Stateful Parser
782// ============================================================================
783
784/// Result of an incremental parse attempt.
785#[derive(Debug)]
786#[allow(clippy::large_enum_variant)]
787pub enum ParseStatus {
788    /// Parsing completed with a request and bytes consumed.
789    Complete { request: Request, consumed: usize },
790    /// More data is required to complete the request.
791    Incomplete,
792}
793
794#[derive(Debug)]
795enum ParseState {
796    RequestLine,
797    Headers {
798        method: Method,
799        path: String,
800        query: Option<String>,
801        http_version: HttpVersion,
802        header_start: usize,
803    },
804    Body {
805        request: Request,
806        body_length: BodyLength,
807        body_start: usize,
808    },
809}
810
811/// Incremental HTTP/1.1 parser that handles partial reads.
812///
813/// Feed bytes via [`feed`][Self::feed]. When a full request is available,
814/// returns [`ParseStatus::Complete`]. On partial data, returns
815/// [`ParseStatus::Incomplete`].
816pub struct StatefulParser {
817    limits: ParseLimits,
818    body_config: BodyConfig,
819    buffer: Vec<u8>,
820    state: ParseState,
821}
822
823impl StatefulParser {
824    /// Create a new stateful parser with default settings.
825    #[must_use]
826    pub fn new() -> Self {
827        Self {
828            limits: ParseLimits::default(),
829            body_config: BodyConfig::default(),
830            buffer: Vec::new(),
831            state: ParseState::RequestLine,
832        }
833    }
834
835    /// Set maximum request size.
836    #[must_use]
837    pub fn with_max_size(mut self, size: usize) -> Self {
838        self.limits.max_request_size = size;
839        self
840    }
841
842    /// Set all parsing limits.
843    #[must_use]
844    pub fn with_limits(mut self, limits: ParseLimits) -> Self {
845        self.limits = limits;
846        self
847    }
848
849    /// Set the body parsing configuration.
850    #[must_use]
851    pub fn with_body_config(mut self, config: BodyConfig) -> Self {
852        self.body_config = config;
853        self
854    }
855
856    /// Returns the current buffered byte count.
857    #[must_use]
858    pub fn buffered_len(&self) -> usize {
859        self.buffer.len()
860    }
861
862    /// Take the currently buffered (unconsumed) bytes.
863    ///
864    /// This is primarily used for protocol upgrades (e.g., WebSocket) where the HTTP parser
865    /// must hand off any already-read bytes to the next protocol layer.
866    #[must_use]
867    pub fn take_buffered(&mut self) -> Vec<u8> {
868        std::mem::take(&mut self.buffer)
869    }
870
871    /// Clear buffered data and reset state.
872    pub fn clear(&mut self) {
873        self.buffer.clear();
874        self.state = ParseState::RequestLine;
875    }
876
877    /// Feed new bytes into the parser and attempt to parse a request.
878    ///
879    /// To parse subsequent requests in the buffer, call `feed` again with
880    /// an empty slice after a successful parse.
881    pub fn feed(&mut self, bytes: &[u8]) -> Result<ParseStatus, ParseError> {
882        if !bytes.is_empty() {
883            self.buffer.extend_from_slice(bytes);
884        }
885
886        if self.buffer.len() > self.limits.max_request_size {
887            return Err(ParseError::TooLarge);
888        }
889
890        loop {
891            let state = std::mem::replace(&mut self.state, ParseState::RequestLine);
892            match state {
893                ParseState::RequestLine => match parse_request_line_with_len_limit(
894                    &self.buffer,
895                    self.limits.max_request_line_len,
896                ) {
897                    Ok((method, path, query, http_version, header_start)) => {
898                        self.state = ParseState::Headers {
899                            method,
900                            path,
901                            query,
902                            http_version,
903                            header_start,
904                        };
905                    }
906                    Err(ParseError::Incomplete) => {
907                        if self.buffer.len() > self.limits.max_request_line_len {
908                            self.state = ParseState::RequestLine;
909                            return Err(ParseError::RequestLineTooLong);
910                        }
911                        self.state = ParseState::RequestLine;
912                        return Ok(ParseStatus::Incomplete);
913                    }
914                    Err(err) => return Err(err),
915                },
916                ParseState::Headers {
917                    method,
918                    path,
919                    query,
920                    http_version,
921                    header_start,
922                } => {
923                    let header_end = match find_header_end_from(&self.buffer, header_start) {
924                        Some(pos) => pos,
925                        None => {
926                            if self.buffer.len().saturating_sub(header_start)
927                                > self.limits.max_headers_size
928                            {
929                                self.state = ParseState::Headers {
930                                    method,
931                                    path,
932                                    query,
933                                    http_version,
934                                    header_start,
935                                };
936                                return Err(ParseError::HeadersTooLarge);
937                            }
938                            self.state = ParseState::Headers {
939                                method,
940                                path,
941                                query,
942                                http_version,
943                                header_start,
944                            };
945                            return Ok(ParseStatus::Incomplete);
946                        }
947                    };
948
949                    let body_start = header_end + 4;
950                    let header_block_len = body_start - header_start;
951                    if header_block_len > self.limits.max_headers_size {
952                        return Err(ParseError::HeadersTooLarge);
953                    }
954                    let header_slice = &self.buffer[header_start..body_start];
955                    let headers = HeadersParser::parse_with_limits(header_slice, &self.limits)?;
956
957                    let mut request = Request::with_version(method, path, http_version);
958                    request.set_query(query);
959
960                    // Use optimized insert to avoid double allocation
961                    for header in headers.iter() {
962                        let header = header?;
963                        request
964                            .headers_mut()
965                            .insert_from_slice(header.name(), header.value());
966                    }
967
968                    let body_length = headers.body_length();
969                    if matches!(body_length, BodyLength::None) {
970                        let consumed = body_start;
971                        self.consume(consumed);
972                        return Ok(ParseStatus::Complete { request, consumed });
973                    }
974
975                    self.state = ParseState::Body {
976                        request,
977                        body_length,
978                        body_start,
979                    };
980                }
981                ParseState::Body {
982                    mut request,
983                    body_length,
984                    body_start,
985                } => {
986                    let body_slice = &self.buffer[body_start..];
987                    match parse_body_with_consumed(body_slice, body_length, &self.body_config) {
988                        Ok((body, body_consumed)) => {
989                            if let Some(body) = body {
990                                request.set_body(Body::Bytes(body));
991                            }
992                            let consumed = body_start + body_consumed;
993                            self.consume(consumed);
994                            return Ok(ParseStatus::Complete { request, consumed });
995                        }
996                        Err(err) => {
997                            let mapped = map_body_error(err);
998                            if matches!(mapped, ParseError::Incomplete) {
999                                self.state = ParseState::Body {
1000                                    request,
1001                                    body_length,
1002                                    body_start,
1003                                };
1004                                return Ok(ParseStatus::Incomplete);
1005                            }
1006                            return Err(mapped);
1007                        }
1008                    }
1009                }
1010            }
1011        }
1012    }
1013
1014    fn consume(&mut self, consumed: usize) {
1015        if consumed >= self.buffer.len() {
1016            self.buffer.clear();
1017        } else {
1018            self.buffer.drain(..consumed);
1019        }
1020        self.state = ParseState::RequestLine;
1021    }
1022}
1023
1024impl Default for StatefulParser {
1025    fn default() -> Self {
1026        Self::new()
1027    }
1028}
1029
1030fn find_header_end(buffer: &[u8]) -> Option<usize> {
1031    buffer.windows(4).position(|w| w == b"\r\n\r\n")
1032}
1033
1034fn find_header_end_from(buffer: &[u8], start: usize) -> Option<usize> {
1035    find_header_end(&buffer[start..]).map(|pos| start + pos)
1036}
1037
1038fn parse_request_line_with_len_limit(
1039    buffer: &[u8],
1040    max_len: usize,
1041) -> Result<(Method, String, Option<String>, HttpVersion, usize), ParseError> {
1042    let line_end = buffer
1043        .windows(2)
1044        .position(|w| w == b"\r\n")
1045        .ok_or(ParseError::Incomplete)?;
1046    if line_end > max_len {
1047        return Err(ParseError::RequestLineTooLong);
1048    }
1049    let (method, path, query, http_version) = parse_request_line(&buffer[..line_end])?;
1050    Ok((method, path, query, http_version, line_end + 2))
1051}
1052
1053fn map_body_error(error: BodyError) -> ParseError {
1054    match error {
1055        BodyError::TooLarge { .. } => ParseError::TooLarge,
1056        BodyError::Incomplete { .. } | BodyError::UnexpectedEof => ParseError::Incomplete,
1057        BodyError::Parse(err) => err,
1058        BodyError::InvalidChunkedEncoding { .. } => ParseError::InvalidHeader,
1059    }
1060}
1061
1062fn parse_request_line(
1063    line: &[u8],
1064) -> Result<(Method, String, Option<String>, HttpVersion), ParseError> {
1065    if has_invalid_request_line_bytes(line) {
1066        return Err(ParseError::InvalidRequestLine);
1067    }
1068    let mut parts = line.split(|&b| b == b' ');
1069
1070    let method_bytes = parts.next().ok_or(ParseError::InvalidRequestLine)?;
1071    let method = Method::from_bytes(method_bytes).ok_or(ParseError::InvalidMethod)?;
1072
1073    let uri_bytes = parts.next().ok_or(ParseError::InvalidRequestLine)?;
1074    let uri = std::str::from_utf8(uri_bytes).map_err(|_| ParseError::InvalidRequestLine)?;
1075
1076    // Parse HTTP version
1077    let version_bytes = parts.next().ok_or(ParseError::InvalidRequestLine)?;
1078    let version_str =
1079        std::str::from_utf8(version_bytes).map_err(|_| ParseError::InvalidRequestLine)?;
1080    let http_version = HttpVersion::parse(version_str).ok_or(ParseError::InvalidRequestLine)?;
1081
1082    // Reject request lines with extra parts (RFC 7230 Section 3.1.1)
1083    if parts.next().is_some() {
1084        return Err(ParseError::InvalidRequestLine);
1085    }
1086
1087    // Split path and query
1088    let (path, query) = if let Some(q_pos) = uri.find('?') {
1089        (
1090            percent_decode_path(&uri[..q_pos])?,
1091            Some(uri[q_pos + 1..].to_string()),
1092        )
1093    } else {
1094        (percent_decode_path(uri)?, None)
1095    };
1096
1097    let path = match path {
1098        Cow::Borrowed(borrowed) => borrowed.to_string(),
1099        Cow::Owned(owned) => owned,
1100    };
1101
1102    Ok((method, path, query, http_version))
1103}
1104
1105/// Percent-decode a path segment.
1106///
1107/// Returns `Cow::Borrowed` if no decoding was needed, or `Cow::Owned` if
1108/// percent sequences were decoded. Plus signs are preserved (no space decoding).
1109///
1110/// Invalid percent sequences are left as-is.
1111fn percent_decode_path(s: &str) -> Result<Cow<'_, str>, ParseError> {
1112    if !s.contains('%') {
1113        return Ok(Cow::Borrowed(s));
1114    }
1115
1116    let mut result = Vec::with_capacity(s.len());
1117    let bytes = s.as_bytes();
1118    let mut i = 0;
1119
1120    while i < bytes.len() {
1121        match bytes[i] {
1122            b'%' if i + 2 < bytes.len() => {
1123                if let (Some(hi), Some(lo)) = (hex_digit(bytes[i + 1]), hex_digit(bytes[i + 2])) {
1124                    result.push(hi << 4 | lo);
1125                    i += 3;
1126                } else {
1127                    result.push(b'%');
1128                    i += 1;
1129                }
1130            }
1131            b => {
1132                result.push(b);
1133                i += 1;
1134            }
1135        }
1136    }
1137
1138    String::from_utf8(result)
1139        .map(Cow::Owned)
1140        .map_err(|_| ParseError::InvalidRequestLine)
1141}
1142
1143fn hex_digit(b: u8) -> Option<u8> {
1144    match b {
1145        b'0'..=b'9' => Some(b - b'0'),
1146        b'a'..=b'f' => Some(b - b'a' + 10),
1147        b'A'..=b'F' => Some(b - b'A' + 10),
1148        _ => None,
1149    }
1150}
1151
1152#[cfg(test)]
1153mod tests {
1154    use super::*;
1155
1156    // ========================================================================
1157    // RequestLine Tests
1158    // ========================================================================
1159
1160    #[test]
1161    fn percent_decode_path_no_encoding() {
1162        let decoded = percent_decode_path("/simple/path").unwrap();
1163        assert!(matches!(decoded, Cow::Borrowed(_)));
1164        assert_eq!(&*decoded, "/simple/path");
1165    }
1166
1167    #[test]
1168    fn percent_decode_path_simple() {
1169        assert_eq!(
1170            &*percent_decode_path("/hello%20world").unwrap(),
1171            "/hello world"
1172        );
1173        assert_eq!(&*percent_decode_path("%2F").unwrap(), "/");
1174    }
1175
1176    #[test]
1177    fn percent_decode_path_utf8() {
1178        assert_eq!(&*percent_decode_path("/caf%C3%A9").unwrap(), "/café");
1179    }
1180
1181    #[test]
1182    fn percent_decode_path_plus_preserved() {
1183        assert_eq!(&*percent_decode_path("/a+b").unwrap(), "/a+b");
1184    }
1185
1186    #[test]
1187    fn percent_decode_path_rejects_invalid_utf8() {
1188        // %C0%AF is an overlong encoding of '/' — produces invalid UTF-8
1189        assert!(percent_decode_path("/%C0%AF").is_err());
1190    }
1191
1192    #[test]
1193    fn parse_request_line_decodes_path() {
1194        let line = b"GET /hello%20world HTTP/1.1";
1195        let (_method, path, _query, _version) =
1196            parse_request_line(line).expect("parse request line");
1197        assert_eq!(path, "/hello world");
1198    }
1199
1200    #[test]
1201    fn request_line_simple_get() {
1202        let buffer = b"GET /path HTTP/1.1\r\n";
1203        let line = RequestLine::parse(buffer).unwrap();
1204
1205        assert_eq!(line.method(), Method::Get);
1206        assert_eq!(line.path(), "/path");
1207        assert_eq!(line.query(), None);
1208        assert_eq!(line.version(), "HTTP/1.1");
1209        assert!(line.is_http11());
1210        assert!(!line.is_http10());
1211    }
1212
1213    #[test]
1214    fn request_line_with_query() {
1215        let buffer = b"GET /items?q=test&page=1 HTTP/1.1\r\n";
1216        let line = RequestLine::parse(buffer).unwrap();
1217
1218        assert_eq!(line.method(), Method::Get);
1219        assert_eq!(line.uri(), "/items?q=test&page=1");
1220        assert_eq!(line.path(), "/items");
1221        assert_eq!(line.query(), Some("q=test&page=1"));
1222        assert_eq!(line.version(), "HTTP/1.1");
1223    }
1224
1225    #[test]
1226    fn request_line_uri_without_query_matches_path() {
1227        let buffer = b"GET /path HTTP/1.1\r\n";
1228        let line = RequestLine::parse(buffer).unwrap();
1229        assert_eq!(line.uri(), "/path");
1230        assert_eq!(line.path(), "/path");
1231        assert_eq!(line.query(), None);
1232    }
1233
1234    #[test]
1235    fn request_line_post() {
1236        let buffer = b"POST /api/users HTTP/1.1\r\n";
1237        let line = RequestLine::parse(buffer).unwrap();
1238
1239        assert_eq!(line.method(), Method::Post);
1240        assert_eq!(line.path(), "/api/users");
1241    }
1242
1243    #[test]
1244    fn request_line_all_methods() {
1245        let methods = [
1246            ("GET", Method::Get),
1247            ("POST", Method::Post),
1248            ("PUT", Method::Put),
1249            ("DELETE", Method::Delete),
1250            ("PATCH", Method::Patch),
1251            ("OPTIONS", Method::Options),
1252            ("HEAD", Method::Head),
1253            ("TRACE", Method::Trace),
1254        ];
1255
1256        for (method_str, expected_method) in methods {
1257            let buffer = format!("{method_str} /path HTTP/1.1\r\n");
1258            let line = RequestLine::parse(buffer.as_bytes()).unwrap();
1259            assert_eq!(line.method(), expected_method, "Failed for {method_str}");
1260        }
1261    }
1262
1263    #[test]
1264    fn request_line_http10() {
1265        let buffer = b"GET /legacy HTTP/1.0\r\n";
1266        let line = RequestLine::parse(buffer).unwrap();
1267
1268        assert_eq!(line.version(), "HTTP/1.0");
1269        assert!(line.is_http10());
1270        assert!(!line.is_http11());
1271    }
1272
1273    #[test]
1274    fn request_line_without_crlf() {
1275        // Should still parse if no \r\n (end of buffer)
1276        let buffer = b"GET /path HTTP/1.1";
1277        let line = RequestLine::parse(buffer).unwrap();
1278
1279        assert_eq!(line.method(), Method::Get);
1280        assert_eq!(line.path(), "/path");
1281    }
1282
1283    #[test]
1284    fn request_line_with_len() {
1285        let buffer = b"GET /path HTTP/1.1\r\nHost: example.com\r\n";
1286        let (line, consumed) = RequestLine::parse_with_len(buffer).unwrap();
1287
1288        assert_eq!(line.method(), Method::Get);
1289        assert_eq!(line.path(), "/path");
1290        assert_eq!(consumed, 20); // "GET /path HTTP/1.1\r\n" = 20 bytes
1291    }
1292
1293    #[test]
1294    fn request_line_invalid_method() {
1295        let buffer = b"INVALID /path HTTP/1.1\r\n";
1296        let result = RequestLine::parse(buffer);
1297
1298        assert!(matches!(result, Err(ParseError::InvalidMethod)));
1299    }
1300
1301    #[test]
1302    fn request_line_missing_path() {
1303        let buffer = b"GET\r\n";
1304        let result = RequestLine::parse(buffer);
1305
1306        assert!(matches!(result, Err(ParseError::InvalidRequestLine)));
1307    }
1308
1309    #[test]
1310    fn request_line_missing_version() {
1311        let buffer = b"GET /path\r\n";
1312        let result = RequestLine::parse(buffer);
1313
1314        assert!(matches!(result, Err(ParseError::InvalidRequestLine)));
1315    }
1316
1317    #[test]
1318    fn request_line_complex_path() {
1319        let buffer = b"GET /api/v1/users/123/posts HTTP/1.1\r\n";
1320        let line = RequestLine::parse(buffer).unwrap();
1321
1322        assert_eq!(line.path(), "/api/v1/users/123/posts");
1323    }
1324
1325    #[test]
1326    fn request_line_query_with_special_chars() {
1327        let buffer = b"GET /search?q=hello%20world&filter=a%3Db HTTP/1.1\r\n";
1328        let line = RequestLine::parse(buffer).unwrap();
1329
1330        assert_eq!(line.path(), "/search");
1331        assert_eq!(line.query(), Some("q=hello%20world&filter=a%3Db"));
1332    }
1333
1334    // ========================================================================
1335    // Header Tests
1336    // ========================================================================
1337
1338    #[test]
1339    fn header_simple() {
1340        let buffer = b"Host: example.com\r\n";
1341        let mut iter = HeadersIter::new(buffer);
1342
1343        let header = iter.next().unwrap().unwrap();
1344        assert_eq!(header.name(), "Host");
1345        assert_eq!(header.value(), b"example.com");
1346        assert_eq!(header.value_str(), Some("example.com"));
1347
1348        assert!(iter.next().is_none());
1349    }
1350
1351    #[test]
1352    fn headers_multiple() {
1353        let buffer =
1354            b"Host: example.com\r\nContent-Type: application/json\r\nContent-Length: 42\r\n";
1355        let headers: Vec<_> = HeadersIter::new(buffer).collect();
1356
1357        assert_eq!(headers.len(), 3);
1358
1359        let h0 = headers[0].as_ref().unwrap();
1360        assert_eq!(h0.name(), "Host");
1361        assert_eq!(h0.value_str(), Some("example.com"));
1362
1363        let h1 = headers[1].as_ref().unwrap();
1364        assert_eq!(h1.name(), "Content-Type");
1365        assert_eq!(h1.value_str(), Some("application/json"));
1366
1367        let h2 = headers[2].as_ref().unwrap();
1368        assert_eq!(h2.name(), "Content-Length");
1369        assert_eq!(h2.value_str(), Some("42"));
1370    }
1371
1372    #[test]
1373    fn headers_with_empty_line() {
1374        let buffer = b"Host: example.com\r\n\r\n";
1375        let headers: Vec<_> = HeadersIter::new(buffer).collect();
1376
1377        assert_eq!(headers.len(), 1);
1378    }
1379
1380    #[test]
1381    fn header_with_leading_space() {
1382        let buffer = b"Host:   example.com\r\n";
1383        let mut iter = HeadersIter::new(buffer);
1384
1385        let header = iter.next().unwrap().unwrap();
1386        assert_eq!(header.name(), "Host");
1387        assert_eq!(header.value_str(), Some("example.com"));
1388    }
1389
1390    #[test]
1391    fn header_binary_value() {
1392        let buffer = b"X-Binary: \xff\xfe\r\n";
1393        let mut iter = HeadersIter::new(buffer);
1394
1395        let header = iter.next().unwrap().unwrap();
1396        assert_eq!(header.name(), "X-Binary");
1397        assert_eq!(header.value(), b"\xff\xfe");
1398        assert!(header.value_str().is_none()); // Invalid UTF-8
1399    }
1400
1401    #[test]
1402    fn header_missing_colon() {
1403        let buffer = b"InvalidHeader\r\n";
1404        let mut iter = HeadersIter::new(buffer);
1405
1406        let result = iter.next().unwrap();
1407        assert!(matches!(result, Err(ParseError::InvalidHeader)));
1408    }
1409
1410    // ========================================================================
1411    // Zero-Copy Verification Tests
1412    // ========================================================================
1413
1414    #[test]
1415    fn request_line_borrows_from_buffer() {
1416        let buffer = b"GET /borrowed/path HTTP/1.1\r\n";
1417        let line = RequestLine::parse(buffer).unwrap();
1418
1419        // Verify that path is a slice into the original buffer
1420        // Using safe pointer range comparison
1421        let buffer_range = buffer.as_ptr_range();
1422        let path_ptr = line.path().as_ptr();
1423
1424        // Path pointer should be within buffer bounds
1425        assert!(buffer_range.contains(&path_ptr));
1426    }
1427
1428    #[test]
1429    fn header_borrows_from_buffer() {
1430        let buffer = b"Host: borrowed.example.com\r\n";
1431        let mut iter = HeadersIter::new(buffer);
1432        let header = iter.next().unwrap().unwrap();
1433
1434        // Verify that value is a slice into the original buffer
1435        // Using safe pointer range comparison
1436        let buffer_range = buffer.as_ptr_range();
1437        let value_ptr = header.value().as_ptr();
1438
1439        assert!(buffer_range.contains(&value_ptr));
1440    }
1441
1442    // ========================================================================
1443    // Header Enhancement Tests
1444    // ========================================================================
1445
1446    #[test]
1447    fn header_as_bytes_pair() {
1448        let buffer = b"Content-Type: application/json\r\n";
1449        let mut iter = HeadersIter::new(buffer);
1450        let header = iter.next().unwrap().unwrap();
1451
1452        let (name, value) = header.as_bytes_pair();
1453        assert_eq!(name, b"Content-Type");
1454        assert_eq!(value, b"application/json");
1455    }
1456
1457    #[test]
1458    fn header_name_bytes() {
1459        let buffer = b"X-Custom-Header: value\r\n";
1460        let mut iter = HeadersIter::new(buffer);
1461        let header = iter.next().unwrap().unwrap();
1462
1463        assert_eq!(header.name_bytes(), b"X-Custom-Header");
1464        assert_eq!(header.name(), "X-Custom-Header");
1465    }
1466
1467    #[test]
1468    fn header_case_insensitive_match() {
1469        let buffer = b"content-type: text/html\r\n";
1470        let mut iter = HeadersIter::new(buffer);
1471        let header = iter.next().unwrap().unwrap();
1472
1473        assert!(header.name_eq_ignore_case("Content-Type"));
1474        assert!(header.name_eq_ignore_case("CONTENT-TYPE"));
1475        assert!(header.name_eq_ignore_case("content-type"));
1476        assert!(!header.name_eq_ignore_case("Content-Length"));
1477    }
1478
1479    #[test]
1480    fn header_content_length_detection() {
1481        let buffer = b"Content-Length: 1234\r\n";
1482        let mut iter = HeadersIter::new(buffer);
1483        let header = iter.next().unwrap().unwrap();
1484
1485        assert!(header.is_content_length());
1486        assert!(!header.is_transfer_encoding());
1487        assert_eq!(header.as_content_length(), Some(1234));
1488    }
1489
1490    #[test]
1491    fn header_content_length_case_insensitive() {
1492        let buffer = b"content-length: 5678\r\n";
1493        let mut iter = HeadersIter::new(buffer);
1494        let header = iter.next().unwrap().unwrap();
1495
1496        assert!(header.is_content_length());
1497        assert_eq!(header.as_content_length(), Some(5678));
1498    }
1499
1500    #[test]
1501    fn header_transfer_encoding_chunked() {
1502        let buffer = b"Transfer-Encoding: chunked\r\n";
1503        let mut iter = HeadersIter::new(buffer);
1504        let header = iter.next().unwrap().unwrap();
1505
1506        assert!(header.is_transfer_encoding());
1507        assert!(header.is_chunked_encoding());
1508        assert!(!header.is_content_length());
1509    }
1510
1511    #[test]
1512    fn header_transfer_encoding_gzip_chunked_is_not_chunked() {
1513        // Compound TE like "gzip, chunked" must NOT be accepted as chunked
1514        // since the server does not support gzip transfer coding.
1515        let buffer = b"Transfer-Encoding: gzip, chunked\r\n";
1516        let mut iter = HeadersIter::new(buffer);
1517        let header = iter.next().unwrap().unwrap();
1518
1519        assert!(header.is_transfer_encoding());
1520        assert!(!header.is_chunked_encoding());
1521    }
1522
1523    #[test]
1524    fn header_transfer_encoding_not_chunked() {
1525        let buffer = b"Transfer-Encoding: gzip\r\n";
1526        let mut iter = HeadersIter::new(buffer);
1527        let header = iter.next().unwrap().unwrap();
1528
1529        assert!(header.is_transfer_encoding());
1530        assert!(!header.is_chunked_encoding());
1531    }
1532
1533    // ========================================================================
1534    // HeadersParser Tests
1535    // ========================================================================
1536
1537    #[test]
1538    fn headers_parser_content_length() {
1539        let buffer = b"Host: example.com\r\nContent-Length: 42\r\n\r\n";
1540        let parser = HeadersParser::parse(buffer).unwrap();
1541
1542        assert_eq!(parser.content_length(), Some(42));
1543        assert!(!parser.is_chunked());
1544        assert_eq!(parser.body_length(), BodyLength::ContentLength(42));
1545    }
1546
1547    #[test]
1548    fn headers_parser_chunked() {
1549        let buffer = b"Host: example.com\r\nTransfer-Encoding: chunked\r\n\r\n";
1550        let parser = HeadersParser::parse(buffer).unwrap();
1551
1552        assert_eq!(parser.content_length(), None);
1553        assert!(parser.is_chunked());
1554        assert_eq!(parser.body_length(), BodyLength::Chunked);
1555    }
1556
1557    #[test]
1558    fn headers_parser_no_body() {
1559        let buffer = b"Host: example.com\r\nAccept: */*\r\n\r\n";
1560        let parser = HeadersParser::parse(buffer).unwrap();
1561
1562        assert_eq!(parser.content_length(), None);
1563        assert!(!parser.is_chunked());
1564        assert_eq!(parser.body_length(), BodyLength::None);
1565    }
1566
1567    #[test]
1568    fn headers_parser_chunked_takes_precedence() {
1569        // Strict parsing rejects ambiguous body length.
1570        let buffer =
1571            b"Host: example.com\r\nContent-Length: 100\r\nTransfer-Encoding: chunked\r\n\r\n";
1572        let result = HeadersParser::parse(buffer);
1573        assert!(matches!(result, Err(ParseError::AmbiguousBodyLength)));
1574    }
1575
1576    #[test]
1577    fn headers_parser_invalid_transfer_encoding() {
1578        let buffer = b"Host: example.com\r\nTransfer-Encoding: gzip\r\n\r\n";
1579        let result = HeadersParser::parse(buffer);
1580        assert!(matches!(result, Err(ParseError::InvalidTransferEncoding)));
1581    }
1582
1583    #[test]
1584    fn headers_parser_bytes_consumed() {
1585        let buffer = b"Host: example.com\r\nContent-Length: 5\r\n\r\nHello";
1586        let parser = HeadersParser::parse(buffer).unwrap();
1587
1588        // Headers section is "Host: example.com\r\nContent-Length: 5\r\n\r\n" = 40 bytes
1589        assert_eq!(parser.bytes_consumed(), 40);
1590    }
1591
1592    #[test]
1593    fn headers_parser_get_header() {
1594        let buffer = b"Host: example.com\r\nContent-Type: application/json\r\n\r\n";
1595        let parser = HeadersParser::parse(buffer).unwrap();
1596
1597        let host = parser.get("Host").unwrap();
1598        assert_eq!(host.value_str(), Some("example.com"));
1599
1600        let ct = parser.get("content-type").unwrap();
1601        assert_eq!(ct.value_str(), Some("application/json"));
1602
1603        assert!(parser.get("X-Missing").is_none());
1604    }
1605
1606    #[test]
1607    fn headers_parser_get_all() {
1608        let buffer = b"Set-Cookie: a=1\r\nSet-Cookie: b=2\r\nHost: example.com\r\n\r\n";
1609        let parser = HeadersParser::parse(buffer).unwrap();
1610
1611        let cookies: Vec<_> = parser.get_all("Set-Cookie").collect();
1612        assert_eq!(cookies.len(), 2);
1613        assert_eq!(cookies[0].value_str(), Some("a=1"));
1614        assert_eq!(cookies[1].value_str(), Some("b=2"));
1615    }
1616
1617    #[test]
1618    fn headers_parser_iter() {
1619        let buffer = b"A: 1\r\nB: 2\r\nC: 3\r\n\r\n";
1620        let parser = HeadersParser::parse(buffer).unwrap();
1621
1622        let headers: Vec<_> = parser.iter().collect();
1623        assert_eq!(headers.len(), 3);
1624    }
1625
1626    #[test]
1627    fn headers_parser_conflicting_content_length() {
1628        // Two different Content-Length values should error
1629        let buffer = b"Content-Length: 10\r\nContent-Length: 20\r\n\r\n";
1630        let result = HeadersParser::parse(buffer);
1631
1632        assert!(matches!(result, Err(ParseError::InvalidHeader)));
1633    }
1634
1635    #[test]
1636    fn headers_parser_duplicate_same_content_length() {
1637        // Two identical Content-Length values are OK (per some implementations)
1638        let buffer = b"Content-Length: 42\r\nContent-Length: 42\r\n\r\n";
1639        let parser = HeadersParser::parse(buffer).unwrap();
1640
1641        assert_eq!(parser.content_length(), Some(42));
1642    }
1643
1644    // ========================================================================
1645    // Header Limits / Security Tests
1646    // ========================================================================
1647
1648    #[test]
1649    fn headers_parser_too_many_headers() {
1650        let mut limits = ParseLimits::default();
1651        limits.max_header_count = 1;
1652        let buffer = b"A: 1\r\nB: 2\r\n\r\n";
1653        let result = HeadersParser::parse_with_limits(buffer, &limits);
1654        assert!(matches!(result, Err(ParseError::TooManyHeaders)));
1655    }
1656
1657    #[test]
1658    fn headers_parser_header_line_too_long() {
1659        let mut limits = ParseLimits::default();
1660        limits.max_header_line_len = 8;
1661        let buffer = b"Long-Header: 123\r\n\r\n";
1662        let result = HeadersParser::parse_with_limits(buffer, &limits);
1663        assert!(matches!(result, Err(ParseError::HeaderLineTooLong)));
1664    }
1665
1666    #[test]
1667    fn headers_parser_headers_too_large() {
1668        let mut limits = ParseLimits::default();
1669        limits.max_headers_size = 7;
1670        let buffer = b"A: 1\r\n\r\n";
1671        let result = HeadersParser::parse_with_limits(buffer, &limits);
1672        assert!(matches!(result, Err(ParseError::HeadersTooLarge)));
1673    }
1674
1675    #[test]
1676    fn headers_parser_invalid_header_name() {
1677        let buffer = b"Bad Header: value\r\n\r\n";
1678        let result = HeadersParser::parse(buffer);
1679        assert!(matches!(result, Err(ParseError::InvalidHeaderName)));
1680    }
1681
1682    #[test]
1683    fn headers_parser_invalid_header_bytes() {
1684        let buffer = b"X-Test: hi\0there\r\n\r\n";
1685        let result = HeadersParser::parse(buffer);
1686        assert!(matches!(result, Err(ParseError::InvalidHeaderBytes)));
1687    }
1688
1689    #[test]
1690    fn request_line_too_long() {
1691        let mut limits = ParseLimits::default();
1692        limits.max_request_line_len = 8;
1693        let mut parser = StatefulParser::new().with_limits(limits);
1694        let result = parser.feed(b"GET /toolong HTTP/1.1\r\n\r\n");
1695        assert!(matches!(result, Err(ParseError::RequestLineTooLong)));
1696    }
1697
1698    // ========================================================================
1699    // Stateful Parser Tests
1700    // ========================================================================
1701
1702    #[test]
1703    fn stateful_parser_content_length_partial() {
1704        let mut parser = StatefulParser::new();
1705        let full = b"GET /hello HTTP/1.1\r\nHost: example.com\r\nContent-Length: 5\r\n\r\nHello";
1706
1707        let status = parser.feed(&full[..full.len() - 3]).unwrap();
1708        assert!(matches!(status, ParseStatus::Incomplete));
1709
1710        let status = parser.feed(&full[full.len() - 3..]).unwrap();
1711        let (request, consumed) = match status {
1712            ParseStatus::Complete { request, consumed } => (request, consumed),
1713            ParseStatus::Incomplete => panic!("expected complete request"),
1714        };
1715
1716        assert_eq!(consumed, full.len());
1717        assert_eq!(request.method(), Method::Get);
1718        assert_eq!(request.path(), "/hello");
1719        assert!(request.query().is_none());
1720
1721        match request.body() {
1722            Body::Bytes(bytes) => assert_eq!(bytes, b"Hello"),
1723            _ => panic!("expected bytes body"),
1724        }
1725
1726        assert_eq!(parser.buffered_len(), 0);
1727    }
1728
1729    #[test]
1730    fn stateful_parser_headers_partial() {
1731        let mut parser = StatefulParser::new();
1732        let part1 = b"GET /x HTTP/1.1\r\nHost: example.com\r\nContent-Length: 5\r\n";
1733        let part2 = b"\r\nHello";
1734
1735        let status = parser.feed(part1).unwrap();
1736        assert!(matches!(status, ParseStatus::Incomplete));
1737
1738        let status = parser.feed(part2).unwrap();
1739        let (request, consumed) = match status {
1740            ParseStatus::Complete { request, consumed } => (request, consumed),
1741            ParseStatus::Incomplete => panic!("expected complete request"),
1742        };
1743
1744        assert_eq!(consumed, part1.len() + part2.len());
1745        assert_eq!(request.path(), "/x");
1746        match request.body() {
1747            Body::Bytes(bytes) => assert_eq!(bytes, b"Hello"),
1748            _ => panic!("expected bytes body"),
1749        }
1750    }
1751
1752    #[test]
1753    fn stateful_parser_chunked_body() {
1754        let mut parser = StatefulParser::new();
1755        let full = b"GET /chunk HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n\
17564\r\nWiki\r\n5\r\npedia\r\n0\r\n\r\n";
1757
1758        let status = parser.feed(full).unwrap();
1759        let (request, consumed) = match status {
1760            ParseStatus::Complete { request, consumed } => (request, consumed),
1761            ParseStatus::Incomplete => panic!("expected complete request"),
1762        };
1763
1764        assert_eq!(consumed, full.len());
1765        assert_eq!(request.path(), "/chunk");
1766        match request.body() {
1767            Body::Bytes(bytes) => assert_eq!(bytes, b"Wikipedia"),
1768            _ => panic!("expected bytes body"),
1769        }
1770    }
1771
1772    #[test]
1773    fn stateful_parser_pipelined_requests() {
1774        let mut parser = StatefulParser::new();
1775        let req1 = b"GET /a HTTP/1.1\r\nContent-Length: 1\r\n\r\na";
1776        let req2 = b"GET /b HTTP/1.1\r\nContent-Length: 1\r\n\r\nb";
1777        let mut combined = Vec::new();
1778        combined.extend_from_slice(req1);
1779        combined.extend_from_slice(req2);
1780
1781        let status = parser.feed(&combined).unwrap();
1782        let (request, consumed) = match status {
1783            ParseStatus::Complete { request, consumed } => (request, consumed),
1784            ParseStatus::Incomplete => panic!("expected complete request"),
1785        };
1786
1787        assert_eq!(consumed, req1.len());
1788        assert_eq!(request.path(), "/a");
1789        assert_eq!(parser.buffered_len(), req2.len());
1790
1791        let status = parser.feed(&[]).unwrap();
1792        let (request, consumed) = match status {
1793            ParseStatus::Complete { request, consumed } => (request, consumed),
1794            ParseStatus::Incomplete => panic!("expected second request"),
1795        };
1796
1797        assert_eq!(consumed, req2.len());
1798        assert_eq!(request.path(), "/b");
1799        assert_eq!(parser.buffered_len(), 0);
1800    }
1801
1802    // ========================================================================
1803    // HTTP Request Smuggling Tests (Security)
1804    // ========================================================================
1805
1806    #[test]
1807    fn security_rejects_cl_te_smuggling_attempt() {
1808        // CL.TE smuggling: Both Content-Length and Transfer-Encoding present
1809        // This should be rejected as ambiguous per RFC 7230
1810        let buffer =
1811            b"POST /admin HTTP/1.1\r\nContent-Length: 13\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nSMUGGLED";
1812        let parser = Parser::new();
1813        let result = parser.parse(buffer);
1814        // Should fail due to ambiguous body length
1815        assert!(
1816            result.is_err() || {
1817                // If it doesn't fail, at least verify it doesn't parse the smuggled content
1818                let req = result.unwrap();
1819                !matches!(req.body(), Body::Bytes(b) if b == b"SMUGGLED")
1820            }
1821        );
1822    }
1823
1824    #[test]
1825    fn security_ambiguous_body_length_rejected() {
1826        // Headers-only test: both CL and TE present
1827        let buffer = b"Content-Length: 100\r\nTransfer-Encoding: chunked\r\n\r\n";
1828        let result = HeadersParser::parse(buffer);
1829        assert!(matches!(result, Err(ParseError::AmbiguousBodyLength)));
1830    }
1831
1832    #[test]
1833    fn security_crlf_injection_in_path_rejected() {
1834        // Attempt to inject a header via CRLF in path
1835        let buffer = b"GET /path\r\nX-Injected: evil HTTP/1.1\r\nHost: example.com\r\n\r\n";
1836        let result = RequestLine::parse(buffer);
1837        // This should either fail or not include the injected header in the path
1838        match result {
1839            Err(_) => {} // Good - rejected
1840            Ok(line) => {
1841                // If parsed, the path should not contain CRLF
1842                assert!(!line.path().contains('\r'));
1843                assert!(!line.path().contains('\n'));
1844            }
1845        }
1846    }
1847
1848    #[test]
1849    fn security_null_byte_in_request_line_rejected() {
1850        let buffer = b"GET /path\x00evil HTTP/1.1\r\n";
1851        let result = RequestLine::parse(buffer);
1852        assert!(matches!(result, Err(ParseError::InvalidRequestLine)));
1853    }
1854
1855    #[test]
1856    fn security_null_byte_in_header_name_rejected() {
1857        let buffer = b"X-Test\x00Header: value\r\n\r\n";
1858        let result = HeadersParser::parse(buffer);
1859        assert!(result.is_err());
1860    }
1861
1862    #[test]
1863    fn security_header_injection_via_value() {
1864        // Header value with CRLF should be rejected
1865        let buffer = b"X-Test: value\r\nX-Injected: evil\r\n\r\n";
1866        let parser = HeadersParser::parse(buffer).unwrap();
1867        // The parser should see "X-Test" and "X-Injected" as separate headers
1868        // (which is normal behavior) - this tests that CRLF in values doesn't
1869        // create extra headers unexpectedly
1870        assert!(parser.get("X-Test").is_some());
1871        assert!(parser.get("X-Injected").is_some());
1872    }
1873
1874    #[test]
1875    fn security_oversized_chunk_size_rejected() {
1876        // Attempt to use a chunk size that overflows usize (must be rejected).
1877        let mut parser = StatefulParser::new();
1878        let buffer = b"GET /chunk HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\nFFFFFFFFFFFFFFFFF\r\n";
1879        let result = parser.feed(buffer);
1880        assert!(matches!(result, Err(ParseError::InvalidHeader)));
1881    }
1882
1883    #[test]
1884    fn security_negative_content_length_rejected() {
1885        // Content-Length with non-numeric value
1886        let buffer = b"Content-Length: -1\r\n\r\n";
1887        let result = HeadersParser::parse(buffer);
1888        // Should fail because -1 is not a valid usize
1889        assert!(matches!(result, Err(ParseError::InvalidHeader)));
1890    }
1891
1892    #[test]
1893    fn security_content_length_overflow() {
1894        // Extremely large Content-Length
1895        let buffer = b"Content-Length: 99999999999999999999999999\r\n\r\n";
1896        let result = HeadersParser::parse(buffer);
1897        // Should fail due to parse error
1898        assert!(matches!(result, Err(ParseError::InvalidHeader)));
1899    }
1900
1901    #[test]
1902    fn security_request_line_space_injection() {
1903        // Extra spaces shouldn't create unexpected behavior
1904        let buffer = b"GET  /path  HTTP/1.1\r\n";
1905        let result = RequestLine::parse(buffer);
1906        // Should handle gracefully - either reject or parse with extra spaces in path
1907        match result {
1908            Ok(line) => {
1909                // If parsed, path includes leading space (zero-copy parser is lenient)
1910                // The important thing is it doesn't crash and we can inspect the result
1911                let _ = line.path();
1912                let _ = line.version();
1913            }
1914            Err(_) => {} // Also acceptable to reject
1915        }
1916    }
1917
1918    #[test]
1919    fn security_obs_fold_header_rejected() {
1920        // Obsolete line folding (RFC 7230 deprecated)
1921        // Line starting with space/tab is continuation of previous header
1922        let buffer = b"X-Test: value\r\n continuation\r\n\r\n";
1923        let result = HeadersParser::parse(buffer);
1924        // Should reject obs-fold per RFC 7230
1925        assert!(matches!(result, Err(ParseError::InvalidHeader)));
1926    }
1927
1928    #[test]
1929    fn security_duplicate_transfer_encoding() {
1930        // Multiple Transfer-Encoding headers
1931        let buffer = b"Transfer-Encoding: chunked\r\nTransfer-Encoding: chunked\r\n\r\n";
1932        let parser = HeadersParser::parse(buffer).unwrap();
1933        // Should still recognize as chunked (parser handles duplicates)
1934        assert!(parser.is_chunked());
1935    }
1936
1937    // ========================================================================
1938    // Edge Case Tests - Request Line
1939    // ========================================================================
1940
1941    #[test]
1942    fn edge_root_path() {
1943        let buffer = b"GET / HTTP/1.1\r\n";
1944        let line = RequestLine::parse(buffer).unwrap();
1945        assert_eq!(line.path(), "/");
1946        assert_eq!(line.query(), None);
1947    }
1948
1949    #[test]
1950    fn edge_root_path_with_query() {
1951        let buffer = b"GET /?key=value HTTP/1.1\r\n";
1952        let line = RequestLine::parse(buffer).unwrap();
1953        assert_eq!(line.path(), "/");
1954        assert_eq!(line.query(), Some("key=value"));
1955    }
1956
1957    #[test]
1958    fn edge_empty_query_string() {
1959        let buffer = b"GET /path? HTTP/1.1\r\n";
1960        let line = RequestLine::parse(buffer).unwrap();
1961        assert_eq!(line.path(), "/path");
1962        assert_eq!(line.query(), Some(""));
1963    }
1964
1965    #[test]
1966    fn edge_double_slashes_in_path() {
1967        let buffer = b"GET //api//v1//users HTTP/1.1\r\n";
1968        let line = RequestLine::parse(buffer).unwrap();
1969        assert_eq!(line.path(), "//api//v1//users");
1970    }
1971
1972    #[test]
1973    fn edge_dot_segments_in_path() {
1974        let buffer = b"GET /api/../admin/./config HTTP/1.1\r\n";
1975        let line = RequestLine::parse(buffer).unwrap();
1976        // Parser doesn't normalize - that's router's job
1977        assert_eq!(line.path(), "/api/../admin/./config");
1978    }
1979
1980    #[test]
1981    fn edge_percent_encoded_slash() {
1982        let buffer = b"GET /path%2Fwith%2Fslashes HTTP/1.1\r\n";
1983        let line = RequestLine::parse(buffer).unwrap();
1984        // Zero-copy parser keeps encoding
1985        assert!(line.path().contains("%2F") || line.path().contains("/with/"));
1986    }
1987
1988    #[test]
1989    fn edge_very_long_path() {
1990        let long_path = "/".to_string() + &"a".repeat(4000);
1991        let buffer = format!("GET {} HTTP/1.1\r\n", long_path);
1992        let line = RequestLine::parse(buffer.as_bytes()).unwrap();
1993        assert_eq!(line.path().len(), 4001);
1994    }
1995
1996    #[test]
1997    fn edge_unicode_in_path() {
1998        // UTF-8 bytes for "café"
1999        let buffer = b"GET /caf\xc3\xa9 HTTP/1.1\r\n";
2000        let result = RequestLine::parse(buffer);
2001        // Should handle UTF-8 gracefully
2002        match result {
2003            Ok(line) => assert!(line.path().len() > 0),
2004            Err(_) => {} // Also acceptable to reject
2005        }
2006    }
2007
2008    #[test]
2009    fn edge_http_version_http10() {
2010        let buffer = b"GET /path HTTP/1.0\r\n";
2011        let line = RequestLine::parse(buffer).unwrap();
2012        assert!(line.is_http10());
2013        assert!(!line.is_http11());
2014    }
2015
2016    #[test]
2017    fn edge_http_version_unknown() {
2018        let buffer = b"GET /path HTTP/2.0\r\n";
2019        let line = RequestLine::parse(buffer).unwrap();
2020        assert_eq!(line.version(), "HTTP/2.0");
2021        assert!(!line.is_http10());
2022        assert!(!line.is_http11());
2023    }
2024
2025    #[test]
2026    fn edge_lowercase_method_rejected() {
2027        let buffer = b"get /path HTTP/1.1\r\n";
2028        let result = RequestLine::parse(buffer);
2029        assert!(matches!(result, Err(ParseError::InvalidMethod)));
2030    }
2031
2032    #[test]
2033    fn edge_mixed_case_method_rejected() {
2034        let buffer = b"Get /path HTTP/1.1\r\n";
2035        let result = RequestLine::parse(buffer);
2036        assert!(matches!(result, Err(ParseError::InvalidMethod)));
2037    }
2038
2039    #[test]
2040    fn edge_connect_method() {
2041        let buffer = b"CONNECT example.com:443 HTTP/1.1\r\n";
2042        let result = RequestLine::parse(buffer);
2043        // CONNECT might not be supported
2044        match result {
2045            Ok(line) => assert_eq!(line.path(), "example.com:443"),
2046            Err(ParseError::InvalidMethod) => {} // Also acceptable
2047            Err(_) => panic!("unexpected error"),
2048        }
2049    }
2050
2051    // ========================================================================
2052    // Edge Case Tests - Headers
2053    // ========================================================================
2054
2055    #[test]
2056    fn edge_empty_header_value() {
2057        let buffer = b"X-Empty:\r\n\r\n";
2058        let parser = HeadersParser::parse(buffer).unwrap();
2059        let header = parser.get("X-Empty").unwrap();
2060        assert_eq!(header.value(), b"");
2061    }
2062
2063    #[test]
2064    fn edge_header_with_only_spaces() {
2065        let buffer = b"X-Spaces:   \r\n\r\n";
2066        let parser = HeadersParser::parse(buffer).unwrap();
2067        let header = parser.get("X-Spaces").unwrap();
2068        // Leading whitespace trimmed, trailing may remain
2069        assert!(header.value().is_empty() || header.value() == b"   ");
2070    }
2071
2072    #[test]
2073    fn edge_very_long_header_value() {
2074        let long_value = "x".repeat(7000);
2075        let buffer = format!("X-Long: {}\r\n\r\n", long_value);
2076        let parser = HeadersParser::parse(buffer.as_bytes()).unwrap();
2077        let header = parser.get("X-Long").unwrap();
2078        assert_eq!(header.value().len(), 7000);
2079    }
2080
2081    #[test]
2082    fn edge_header_with_colon_in_value() {
2083        let buffer = b"X-Time: 12:30:45\r\n\r\n";
2084        let parser = HeadersParser::parse(buffer).unwrap();
2085        let header = parser.get("X-Time").unwrap();
2086        assert_eq!(header.value_str(), Some("12:30:45"));
2087    }
2088
2089    #[test]
2090    fn edge_header_name_with_numbers() {
2091        let buffer = b"X-Header-123: value\r\n\r\n";
2092        let parser = HeadersParser::parse(buffer).unwrap();
2093        assert!(parser.get("X-Header-123").is_some());
2094    }
2095
2096    #[test]
2097    fn edge_header_value_with_utf8() {
2098        let buffer = "X-Message: Hello, 世界!\r\n\r\n".as_bytes();
2099        let parser = HeadersParser::parse(buffer).unwrap();
2100        let header = parser.get("X-Message").unwrap();
2101        assert!(header.value_str().is_some());
2102    }
2103
2104    #[test]
2105    fn edge_many_small_headers() {
2106        let mut buffer = String::new();
2107        for i in 0..50 {
2108            buffer.push_str(&format!("X-H{}: v{}\r\n", i, i));
2109        }
2110        buffer.push_str("\r\n");
2111        let parser = HeadersParser::parse(buffer.as_bytes()).unwrap();
2112        assert!(parser.get("X-H0").is_some());
2113        assert!(parser.get("X-H49").is_some());
2114    }
2115
2116    #[test]
2117    fn edge_content_length_with_leading_zeros() {
2118        let buffer = b"Content-Length: 00042\r\n\r\n";
2119        let parser = HeadersParser::parse(buffer).unwrap();
2120        assert_eq!(parser.content_length(), Some(42));
2121    }
2122
2123    #[test]
2124    fn edge_content_length_with_whitespace() {
2125        let buffer = b"Content-Length:   42  \r\n\r\n";
2126        let parser = HeadersParser::parse(buffer).unwrap();
2127        assert_eq!(parser.content_length(), Some(42));
2128    }
2129
2130    // ========================================================================
2131    // High-Level Parser Tests
2132    // ========================================================================
2133
2134    #[test]
2135    fn parser_simple_get() {
2136        let parser = Parser::new();
2137        let buffer = b"GET /api/users HTTP/1.1\r\nHost: example.com\r\n\r\n";
2138        let request = parser.parse(buffer).unwrap();
2139
2140        assert_eq!(request.method(), Method::Get);
2141        assert_eq!(request.path(), "/api/users");
2142        assert!(request.query().is_none());
2143    }
2144
2145    #[test]
2146    fn parser_post_with_json_body() {
2147        let parser = Parser::new();
2148        let buffer = b"POST /api/items HTTP/1.1\r\nHost: example.com\r\nContent-Type: application/json\r\nContent-Length: 13\r\n\r\n{\"id\": \"123\"}";
2149        let request = parser.parse(buffer).unwrap();
2150
2151        assert_eq!(request.method(), Method::Post);
2152        assert_eq!(request.path(), "/api/items");
2153        match request.body() {
2154            Body::Bytes(bytes) => assert_eq!(bytes, b"{\"id\": \"123\"}"),
2155            _ => panic!("expected bytes body"),
2156        }
2157    }
2158
2159    #[test]
2160    fn parser_request_with_query() {
2161        let parser = Parser::new();
2162        let buffer = b"GET /search?q=rust&limit=10 HTTP/1.1\r\nHost: example.com\r\n\r\n";
2163        let request = parser.parse(buffer).unwrap();
2164
2165        assert_eq!(request.path(), "/search");
2166        assert_eq!(
2167            request.query(),
2168            Some("q=rust&limit=10".to_string()).as_deref()
2169        );
2170    }
2171
2172    #[test]
2173    fn parser_max_size_respected() {
2174        let parser = Parser::new().with_max_size(50);
2175        let buffer = b"GET /path HTTP/1.1\r\nHost: example.com\r\nX-Long: this is a very long header that exceeds the limit\r\n\r\n";
2176        let result = parser.parse(buffer);
2177        assert!(matches!(result, Err(ParseError::TooLarge)));
2178    }
2179
2180    #[test]
2181    fn parser_custom_limits() {
2182        let limits = ParseLimits {
2183            max_request_size: 1024,
2184            max_request_line_len: 100,
2185            max_header_count: 10,
2186            max_header_line_len: 200,
2187            max_headers_size: 500,
2188        };
2189        let parser = Parser::new().with_limits(limits);
2190        let buffer = b"GET /path HTTP/1.1\r\nHost: example.com\r\n\r\n";
2191        let request = parser.parse(buffer).unwrap();
2192        assert_eq!(request.method(), Method::Get);
2193    }
2194
2195    #[test]
2196    fn parser_incomplete_request() {
2197        let parser = Parser::new();
2198        let buffer = b"GET /path HTTP/1.1\r\nHost: example.com\r\n";
2199        // Missing final \r\n
2200        let result = parser.parse(buffer);
2201        assert!(matches!(result, Err(ParseError::Incomplete)));
2202    }
2203
2204    #[test]
2205    fn parser_preserves_headers() {
2206        let parser = Parser::new();
2207        let buffer =
2208            b"GET /path HTTP/1.1\r\nHost: example.com\r\nX-Custom: my-value\r\nAccept: */*\r\n\r\n";
2209        let request = parser.parse(buffer).unwrap();
2210
2211        assert!(request.headers().get("Host").is_some());
2212        assert!(request.headers().get("X-Custom").is_some());
2213        assert!(request.headers().get("Accept").is_some());
2214    }
2215
2216    // ========================================================================
2217    // Malformed Request Tests
2218    // ========================================================================
2219
2220    #[test]
2221    fn malformed_empty_input() {
2222        let parser = Parser::new();
2223        let result = parser.parse(b"");
2224        assert!(matches!(result, Err(ParseError::Incomplete)));
2225    }
2226
2227    #[test]
2228    fn malformed_only_crlf() {
2229        let parser = Parser::new();
2230        let result = parser.parse(b"\r\n\r\n");
2231        assert!(result.is_err());
2232    }
2233
2234    #[test]
2235    fn malformed_no_method() {
2236        let buffer = b"/path HTTP/1.1\r\n\r\n";
2237        let result = RequestLine::parse(buffer);
2238        assert!(result.is_err());
2239    }
2240
2241    #[test]
2242    fn malformed_garbage_input() {
2243        let parser = Parser::new();
2244        let result = parser.parse(b"not a valid http request at all");
2245        assert!(result.is_err());
2246    }
2247
2248    #[test]
2249    fn malformed_binary_garbage() {
2250        let parser = Parser::new();
2251        let result = parser.parse(b"\x00\x01\x02\x03\x04\x05\r\n\r\n");
2252        assert!(result.is_err());
2253    }
2254
2255    #[test]
2256    fn malformed_tab_instead_of_space() {
2257        let buffer = b"GET\t/path\tHTTP/1.1\r\n\r\n";
2258        let result = RequestLine::parse(buffer);
2259        // Should fail since tab is not space
2260        assert!(result.is_err());
2261    }
2262
2263    #[test]
2264    fn malformed_lf_only_line_ending() {
2265        let buffer = b"GET /path HTTP/1.1\nHost: example.com\n\n";
2266        let parser = Parser::new();
2267        let result = parser.parse(buffer);
2268        // LF-only should fail (CRLF required)
2269        assert!(result.is_err());
2270    }
2271
2272    #[test]
2273    fn malformed_cr_only_line_ending() {
2274        let buffer = b"GET /path HTTP/1.1\rHost: example.com\r\r";
2275        let parser = Parser::new();
2276        let result = parser.parse(buffer);
2277        assert!(result.is_err());
2278    }
2279
2280    // ========================================================================
2281    // StatefulParser Additional Tests
2282    // ========================================================================
2283
2284    #[test]
2285    fn stateful_parser_clear_resets_state() {
2286        let mut parser = StatefulParser::new();
2287        parser.feed(b"GET /partial").unwrap();
2288        assert!(parser.buffered_len() > 0);
2289
2290        parser.clear();
2291        assert_eq!(parser.buffered_len(), 0);
2292
2293        // Should be able to parse fresh - needs a proper request with headers
2294        let result = parser
2295            .feed(b"GET /new HTTP/1.1\r\nHost: example.com\r\n\r\n")
2296            .unwrap();
2297        assert!(matches!(result, ParseStatus::Complete { .. }));
2298    }
2299
2300    #[test]
2301    fn stateful_parser_byte_at_a_time() {
2302        let mut parser = StatefulParser::new();
2303        let request = b"GET /path HTTP/1.1\r\nHost: x\r\n\r\n";
2304
2305        for (i, byte) in request.iter().enumerate() {
2306            let result = parser.feed(&[*byte]).unwrap();
2307            if i == request.len() - 1 {
2308                assert!(matches!(result, ParseStatus::Complete { .. }));
2309            } else {
2310                assert!(matches!(result, ParseStatus::Incomplete));
2311            }
2312        }
2313    }
2314
2315    #[test]
2316    fn stateful_parser_with_body_config() {
2317        use crate::body::BodyConfig;
2318
2319        let config = BodyConfig::new().with_max_size(10);
2320        let mut parser = StatefulParser::new().with_body_config(config);
2321
2322        // Body within limit
2323        let result = parser.feed(b"GET /path HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello");
2324        assert!(result.is_ok());
2325    }
2326
2327    #[test]
2328    fn stateful_parser_pipelining_two_requests_in_one_buffer() {
2329        let mut parser = StatefulParser::new();
2330
2331        // Send two complete requests in a single buffer (HTTP pipelining)
2332        let pipelined = b"GET /first HTTP/1.1\r\nHost: localhost\r\n\r\nGET /second HTTP/1.1\r\nHost: localhost\r\n\r\n";
2333        let result = parser.feed(pipelined).unwrap();
2334
2335        // First request should be returned
2336        match result {
2337            ParseStatus::Complete { request, .. } => {
2338                assert_eq!(request.path(), "/first");
2339            }
2340            ParseStatus::Incomplete => panic!("expected first request"),
2341        }
2342
2343        // Second request should already be buffered — feed empty to retrieve it
2344        let result = parser.feed(&[]).unwrap();
2345        match result {
2346            ParseStatus::Complete { request, .. } => {
2347                assert_eq!(request.path(), "/second");
2348            }
2349            ParseStatus::Incomplete => panic!("expected second pipelined request"),
2350        }
2351    }
2352
2353    #[test]
2354    fn stateful_parser_pipelining_three_requests() {
2355        let mut parser = StatefulParser::new();
2356
2357        let pipelined = b"GET /a HTTP/1.1\r\nHost: h\r\n\r\n\
2358                           GET /b HTTP/1.1\r\nHost: h\r\n\r\n\
2359                           GET /c HTTP/1.1\r\nHost: h\r\n\r\n";
2360        let r1 = parser.feed(pipelined).unwrap();
2361        assert!(matches!(r1, ParseStatus::Complete { .. }));
2362
2363        let r2 = parser.feed(&[]).unwrap();
2364        assert!(matches!(r2, ParseStatus::Complete { .. }));
2365
2366        let r3 = parser.feed(&[]).unwrap();
2367        assert!(matches!(r3, ParseStatus::Complete { .. }));
2368
2369        // No more requests buffered
2370        let r4 = parser.feed(&[]).unwrap();
2371        assert!(matches!(r4, ParseStatus::Incomplete));
2372    }
2373}