1use crate::body::{BodyConfig, BodyError, parse_body_with_consumed};
28use fastapi_core::{Body, HttpVersion, Method, Request};
29use std::borrow::Cow;
30
31#[derive(Debug)]
33pub enum ParseError {
34 InvalidRequestLine,
36 InvalidMethod,
38 InvalidHeader,
40 InvalidHeaderName,
42 InvalidHeaderBytes,
44 RequestLineTooLong,
46 HeaderLineTooLong,
48 TooManyHeaders,
50 HeadersTooLarge,
52 InvalidTransferEncoding,
54 AmbiguousBodyLength,
56 TooLarge,
58 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#[derive(Debug, Clone)]
86pub struct ParseLimits {
87 pub max_request_size: usize,
89 pub max_request_line_len: usize,
91 pub max_header_count: usize,
93 pub max_header_line_len: usize,
95 pub max_headers_size: usize,
97}
98
99impl Default for ParseLimits {
100 fn default() -> Self {
101 Self {
102 max_request_size: 1024 * 1024, max_request_line_len: 8 * 1024, max_header_count: 100,
105 max_header_line_len: 8 * 1024, max_headers_size: 64 * 1024, }
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#[derive(Debug, Clone, Copy)]
141pub struct RequestLine<'a> {
142 method: Method,
143 uri: &'a str,
147 path: &'a str,
148 query: Option<&'a str>,
149 version: &'a str,
150}
151
152impl<'a> RequestLine<'a> {
153 pub fn parse(buffer: &'a [u8]) -> Result<Self, ParseError> {
163 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 let mut parts = line.splitn(3, |&b| b == b' ');
176
177 let method_bytes = parts.next().ok_or(ParseError::InvalidRequestLine)?;
179 let method = Method::from_bytes(method_bytes).ok_or(ParseError::InvalidMethod)?;
180
181 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 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 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 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 Ok((line, line_end + 2))
227 }
228
229 #[inline]
231 #[must_use]
232 pub fn method(&self) -> Method {
233 self.method
234 }
235
236 #[inline]
240 #[must_use]
241 pub fn path(&self) -> &'a str {
242 self.path
243 }
244
245 #[inline]
249 #[must_use]
250 pub fn query(&self) -> Option<&'a str> {
251 self.query
252 }
253
254 #[must_use]
258 pub fn uri(&self) -> &'a str {
259 self.uri
260 }
261
262 #[inline]
266 #[must_use]
267 pub fn version(&self) -> &'a str {
268 self.version
269 }
270
271 #[inline]
273 #[must_use]
274 pub fn is_http11(&self) -> bool {
275 self.version == "HTTP/1.1"
276 }
277
278 #[inline]
280 #[must_use]
281 pub fn is_http10(&self) -> bool {
282 self.version == "HTTP/1.0"
283 }
284}
285
286fn 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#[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 #[inline]
323 #[must_use]
324 pub fn name(&self) -> &'a str {
325 self.name
326 }
327
328 #[inline]
330 #[must_use]
331 pub fn name_bytes(&self) -> &'a [u8] {
332 self.name_bytes
333 }
334
335 #[inline]
339 #[must_use]
340 pub fn as_bytes_pair(&self) -> (&'a [u8], &'a [u8]) {
341 (self.name_bytes, self.value)
342 }
343
344 #[inline]
346 #[must_use]
347 pub fn value(&self) -> &'a [u8] {
348 self.value
349 }
350
351 #[must_use]
353 pub fn value_str(&self) -> Option<&'a str> {
354 std::str::from_utf8(self.value).ok()
355 }
356
357 #[must_use]
359 pub fn name_eq_ignore_case(&self, other: &str) -> bool {
360 self.name.eq_ignore_ascii_case(other)
361 }
362
363 #[inline]
365 #[must_use]
366 pub fn is_content_length(&self) -> bool {
367 self.name_eq_ignore_case("content-length")
368 }
369
370 #[inline]
372 #[must_use]
373 pub fn is_transfer_encoding(&self) -> bool {
374 self.name_eq_ignore_case("transfer-encoding")
375 }
376
377 #[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 #[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
403pub struct HeadersIter<'a> {
407 remaining: &'a [u8],
408}
409
410impl<'a> HeadersIter<'a> {
411 #[must_use]
415 pub fn new(buffer: &'a [u8]) -> Self {
416 Self { remaining: buffer }
417 }
418
419 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 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 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 let line_end = self
468 .remaining
469 .windows(2)
470 .position(|w| w == b"\r\n")
471 .unwrap_or(self.remaining.len());
472
473 if line_end == 0 {
475 self.remaining = &[];
476 return None;
477 }
478
479 let line = &self.remaining[..line_end];
480
481 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
498pub enum BodyLength {
499 ContentLength(usize),
501 Chunked,
503 None,
505 Conflicting,
507}
508
509pub 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 pub fn parse(buffer: &'a [u8]) -> Result<Self, ParseError> {
536 Self::parse_with_limits(buffer, &ParseLimits::default())
537 }
538
539 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 #[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 #[must_use]
645 pub fn content_length(&self) -> Option<usize> {
646 self.content_length
647 }
648
649 #[must_use]
651 pub fn is_chunked(&self) -> bool {
652 self.is_chunked
653 }
654
655 #[must_use]
657 pub fn bytes_consumed(&self) -> usize {
658 self.bytes_consumed
659 }
660
661 #[must_use]
663 pub fn iter(&self) -> HeadersIter<'a> {
664 HeadersIter::new(self.buffer)
665 }
666
667 #[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 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
683pub struct Parser {
689 limits: ParseLimits,
690}
691
692impl Parser {
693 #[must_use]
695 pub fn new() -> Self {
696 Self {
697 limits: ParseLimits::default(),
698 }
699 }
700
701 #[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 #[must_use]
710 pub fn with_limits(mut self, limits: ParseLimits) -> Self {
711 self.limits = limits;
712 self
713 }
714
715 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 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..]; 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 let headers =
751 HeadersParser::parse_with_limits(&buffer[header_start..header_end + 4], &self.limits)?;
752
753 let mut request = Request::with_version(method, path, http_version);
755 request.set_query(query);
756
757 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 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#[derive(Debug)]
786#[allow(clippy::large_enum_variant)]
787pub enum ParseStatus {
788 Complete { request: Request, consumed: usize },
790 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
811pub struct StatefulParser {
817 limits: ParseLimits,
818 body_config: BodyConfig,
819 buffer: Vec<u8>,
820 state: ParseState,
821}
822
823impl StatefulParser {
824 #[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 #[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 #[must_use]
844 pub fn with_limits(mut self, limits: ParseLimits) -> Self {
845 self.limits = limits;
846 self
847 }
848
849 #[must_use]
851 pub fn with_body_config(mut self, config: BodyConfig) -> Self {
852 self.body_config = config;
853 self
854 }
855
856 #[must_use]
858 pub fn buffered_len(&self) -> usize {
859 self.buffer.len()
860 }
861
862 #[must_use]
867 pub fn take_buffered(&mut self) -> Vec<u8> {
868 std::mem::take(&mut self.buffer)
869 }
870
871 pub fn clear(&mut self) {
873 self.buffer.clear();
874 self.state = ParseState::RequestLine;
875 }
876
877 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 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 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 if parts.next().is_some() {
1084 return Err(ParseError::InvalidRequestLine);
1085 }
1086
1087 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
1105fn 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 #[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 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 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); }
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 #[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()); }
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 #[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 let buffer_range = buffer.as_ptr_range();
1422 let path_ptr = line.path().as_ptr();
1423
1424 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 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 #[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 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 #[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 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 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 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 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 #[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 #[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 #[test]
1807 fn security_rejects_cl_te_smuggling_attempt() {
1808 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 assert!(
1816 result.is_err() || {
1817 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 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 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 match result {
1839 Err(_) => {} Ok(line) => {
1841 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 let buffer = b"X-Test: value\r\nX-Injected: evil\r\n\r\n";
1866 let parser = HeadersParser::parse(buffer).unwrap();
1867 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 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 let buffer = b"Content-Length: -1\r\n\r\n";
1887 let result = HeadersParser::parse(buffer);
1888 assert!(matches!(result, Err(ParseError::InvalidHeader)));
1890 }
1891
1892 #[test]
1893 fn security_content_length_overflow() {
1894 let buffer = b"Content-Length: 99999999999999999999999999\r\n\r\n";
1896 let result = HeadersParser::parse(buffer);
1897 assert!(matches!(result, Err(ParseError::InvalidHeader)));
1899 }
1900
1901 #[test]
1902 fn security_request_line_space_injection() {
1903 let buffer = b"GET /path HTTP/1.1\r\n";
1905 let result = RequestLine::parse(buffer);
1906 match result {
1908 Ok(line) => {
1909 let _ = line.path();
1912 let _ = line.version();
1913 }
1914 Err(_) => {} }
1916 }
1917
1918 #[test]
1919 fn security_obs_fold_header_rejected() {
1920 let buffer = b"X-Test: value\r\n continuation\r\n\r\n";
1923 let result = HeadersParser::parse(buffer);
1924 assert!(matches!(result, Err(ParseError::InvalidHeader)));
1926 }
1927
1928 #[test]
1929 fn security_duplicate_transfer_encoding() {
1930 let buffer = b"Transfer-Encoding: chunked\r\nTransfer-Encoding: chunked\r\n\r\n";
1932 let parser = HeadersParser::parse(buffer).unwrap();
1933 assert!(parser.is_chunked());
1935 }
1936
1937 #[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 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 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 let buffer = b"GET /caf\xc3\xa9 HTTP/1.1\r\n";
2000 let result = RequestLine::parse(buffer);
2001 match result {
2003 Ok(line) => assert!(line.path().len() > 0),
2004 Err(_) => {} }
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 match result {
2045 Ok(line) => assert_eq!(line.path(), "example.com:443"),
2046 Err(ParseError::InvalidMethod) => {} Err(_) => panic!("unexpected error"),
2048 }
2049 }
2050
2051 #[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 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 #[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 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 #[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 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 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 #[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 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 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 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 match result {
2337 ParseStatus::Complete { request, .. } => {
2338 assert_eq!(request.path(), "/first");
2339 }
2340 ParseStatus::Incomplete => panic!("expected first request"),
2341 }
2342
2343 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 let r4 = parser.feed(&[]).unwrap();
2371 assert!(matches!(r4, ParseStatus::Incomplete));
2372 }
2373}