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