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 path: &'a str,
144 query: Option<&'a str>,
145 version: &'a str,
146}
147
148impl<'a> RequestLine<'a> {
149 pub fn parse(buffer: &'a [u8]) -> Result<Self, ParseError> {
159 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 let mut parts = line.splitn(3, |&b| b == b' ');
172
173 let method_bytes = parts.next().ok_or(ParseError::InvalidRequestLine)?;
175 let method = Method::from_bytes(method_bytes).ok_or(ParseError::InvalidMethod)?;
176
177 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 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 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 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 Ok((line, line_end + 2))
222 }
223
224 #[inline]
226 #[must_use]
227 pub fn method(&self) -> Method {
228 self.method
229 }
230
231 #[inline]
235 #[must_use]
236 pub fn path(&self) -> &'a str {
237 self.path
238 }
239
240 #[inline]
244 #[must_use]
245 pub fn query(&self) -> Option<&'a str> {
246 self.query
247 }
248
249 #[must_use]
253 pub fn uri(&self) -> &'a str {
254 self.path
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]
390 pub fn is_chunked_encoding(&self) -> bool {
391 if !self.is_transfer_encoding() {
392 return false;
393 }
394 self.value_str()
396 .is_some_and(|v| contains_ignore_ascii_case(v, "chunked"))
397 }
398}
399
400#[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
416pub struct HeadersIter<'a> {
420 remaining: &'a [u8],
421}
422
423impl<'a> HeadersIter<'a> {
424 #[must_use]
428 pub fn new(buffer: &'a [u8]) -> Self {
429 Self { remaining: buffer }
430 }
431
432 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 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 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 let line_end = self
481 .remaining
482 .windows(2)
483 .position(|w| w == b"\r\n")
484 .unwrap_or(self.remaining.len());
485
486 if line_end == 0 {
488 self.remaining = &[];
489 return None;
490 }
491
492 let line = &self.remaining[..line_end];
493
494 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
511pub enum BodyLength {
512 ContentLength(usize),
514 Chunked,
516 None,
518 Conflicting,
520}
521
522pub 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 pub fn parse(buffer: &'a [u8]) -> Result<Self, ParseError> {
549 Self::parse_with_limits(buffer, &ParseLimits::default())
550 }
551
552 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 #[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 #[must_use]
658 pub fn content_length(&self) -> Option<usize> {
659 self.content_length
660 }
661
662 #[must_use]
664 pub fn is_chunked(&self) -> bool {
665 self.is_chunked
666 }
667
668 #[must_use]
670 pub fn bytes_consumed(&self) -> usize {
671 self.bytes_consumed
672 }
673
674 #[must_use]
676 pub fn iter(&self) -> HeadersIter<'a> {
677 HeadersIter::new(self.buffer)
678 }
679
680 #[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 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
696pub struct Parser {
702 limits: ParseLimits,
703}
704
705impl Parser {
706 #[must_use]
708 pub fn new() -> Self {
709 Self {
710 limits: ParseLimits::default(),
711 }
712 }
713
714 #[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 #[must_use]
723 pub fn with_limits(mut self, limits: ParseLimits) -> Self {
724 self.limits = limits;
725 self
726 }
727
728 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 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..]; 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 let headers =
764 HeadersParser::parse_with_limits(&buffer[header_start..header_end + 4], &self.limits)?;
765
766 let mut request = Request::with_version(method, path, http_version);
768 request.set_query(query);
769
770 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 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#[derive(Debug)]
799#[allow(clippy::large_enum_variant)]
800pub enum ParseStatus {
801 Complete { request: Request, consumed: usize },
803 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
824pub struct StatefulParser {
830 limits: ParseLimits,
831 body_config: BodyConfig,
832 buffer: Vec<u8>,
833 state: ParseState,
834}
835
836impl StatefulParser {
837 #[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 #[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 #[must_use]
857 pub fn with_limits(mut self, limits: ParseLimits) -> Self {
858 self.limits = limits;
859 self
860 }
861
862 #[must_use]
864 pub fn with_body_config(mut self, config: BodyConfig) -> Self {
865 self.body_config = config;
866 self
867 }
868
869 #[must_use]
871 pub fn buffered_len(&self) -> usize {
872 self.buffer.len()
873 }
874
875 pub fn clear(&mut self) {
877 self.buffer.clear();
878 self.state = ParseState::RequestLine;
879 }
880
881 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 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 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 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
1104fn 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 #[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 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); }
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 #[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()); }
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 #[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 let buffer_range = buffer.as_ptr_range();
1400 let path_ptr = line.path().as_ptr();
1401
1402 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 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 #[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 #[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 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 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 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 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 #[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 #[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 #[test]
1783 fn security_rejects_cl_te_smuggling_attempt() {
1784 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 assert!(
1792 result.is_err() || {
1793 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 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 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 match result {
1815 Err(_) => {} Ok(line) => {
1817 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 let buffer = b"X-Test: value\r\nX-Injected: evil\r\n\r\n";
1842 let parser = HeadersParser::parse(buffer).unwrap();
1843 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 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 }
1859
1860 #[test]
1861 fn security_negative_content_length_rejected() {
1862 let buffer = b"Content-Length: -1\r\n\r\n";
1864 let result = HeadersParser::parse(buffer);
1865 assert!(matches!(result, Err(ParseError::InvalidHeader)));
1867 }
1868
1869 #[test]
1870 fn security_content_length_overflow() {
1871 let buffer = b"Content-Length: 99999999999999999999999999\r\n\r\n";
1873 let result = HeadersParser::parse(buffer);
1874 assert!(matches!(result, Err(ParseError::InvalidHeader)));
1876 }
1877
1878 #[test]
1879 fn security_request_line_space_injection() {
1880 let buffer = b"GET /path HTTP/1.1\r\n";
1882 let result = RequestLine::parse(buffer);
1883 match result {
1885 Ok(line) => {
1886 let _ = line.path();
1889 let _ = line.version();
1890 }
1891 Err(_) => {} }
1893 }
1894
1895 #[test]
1896 fn security_obs_fold_header_rejected() {
1897 let buffer = b"X-Test: value\r\n continuation\r\n\r\n";
1900 let result = HeadersParser::parse(buffer);
1901 assert!(matches!(result, Err(ParseError::InvalidHeader)));
1903 }
1904
1905 #[test]
1906 fn security_duplicate_transfer_encoding() {
1907 let buffer = b"Transfer-Encoding: chunked\r\nTransfer-Encoding: chunked\r\n\r\n";
1909 let parser = HeadersParser::parse(buffer).unwrap();
1910 assert!(parser.is_chunked());
1912 }
1913
1914 #[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 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 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 let buffer = b"GET /caf\xc3\xa9 HTTP/1.1\r\n";
1977 let result = RequestLine::parse(buffer);
1978 match result {
1980 Ok(line) => assert!(line.path().len() > 0),
1981 Err(_) => {} }
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 match result {
2022 Ok(line) => assert_eq!(line.path(), "example.com:443"),
2023 Err(ParseError::InvalidMethod) => {} Err(_) => panic!("unexpected error"),
2025 }
2026 }
2027
2028 #[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 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 #[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 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 #[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 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 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 #[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 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 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 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 match result {
2314 ParseStatus::Complete { request, .. } => {
2315 assert_eq!(request.path(), "/first");
2316 }
2317 ParseStatus::Incomplete => panic!("expected first request"),
2318 }
2319
2320 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 let r4 = parser.feed(&[]).unwrap();
2348 assert!(matches!(r4, ParseStatus::Incomplete));
2349 }
2350}