1#![deny(clippy::pedantic, clippy::missing_safety_doc)]
2#![allow(
3 clippy::cast_lossless,
4 clippy::cast_possible_truncation,
5 clippy::missing_panics_doc,
6 clippy::missing_errors_doc
7)]
8#![cfg_attr(not(any(test, feature = "std")), no_std)]
9#![cfg_attr(test, deny(warnings))]
10
11use core::{fmt, result, str};
80
81mod iter;
82#[macro_use]
83mod macros;
84mod headers;
85mod simd;
86mod utils;
87mod version;
88
89pub use crate::headers::{Header, HeaderParsed};
90pub use crate::version::parse_version;
91
92use crate::iter::Bytes;
93
94#[derive(Copy, Clone, PartialEq, Eq, Debug)]
96pub enum Error {
97 HeaderName,
99 HeaderValue,
101 NewLine,
103 Status,
105 Token,
107 TooManyHeaders,
109 Version,
111}
112
113impl Error {
114 #[inline]
115 fn description_str(self) -> &'static str {
116 match self {
117 Error::HeaderName => "invalid header name",
118 Error::HeaderValue => "invalid header value",
119 Error::NewLine => "invalid new line",
120 Error::Status => "invalid response status",
121 Error::Token => "invalid token",
122 Error::TooManyHeaders => "too many headers",
123 Error::Version => "invalid HTTP version",
124 }
125 }
126}
127
128impl fmt::Display for Error {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 f.write_str(self.description_str())
131 }
132}
133
134#[cfg(feature = "std")]
135impl std::error::Error for Error {
136 fn description(&self) -> &str {
137 self.description_str()
138 }
139}
140
141#[derive(Debug, PartialEq, Eq)]
143pub struct InvalidChunkSize;
144
145impl fmt::Display for InvalidChunkSize {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.write_str("invalid chunk size")
148 }
149}
150
151pub type Result<T> = result::Result<Status<T>, Error>;
157
158#[derive(Copy, Clone, Eq, PartialEq, Debug)]
164pub enum Status<T> {
165 Complete(T),
167 Partial,
169}
170
171impl<T> Status<T> {
172 #[inline]
174 pub fn is_complete(&self) -> bool {
175 match *self {
176 Status::Complete(..) => true,
177 Status::Partial => false,
178 }
179 }
180
181 #[inline]
183 pub fn is_partial(&self) -> bool {
184 match *self {
185 Status::Complete(..) => false,
186 Status::Partial => true,
187 }
188 }
189
190 #[inline]
193 pub fn unwrap(self) -> T {
194 match self {
195 Status::Complete(t) => t,
196 Status::Partial => panic!("Tried to unwrap Status::Partial"),
197 }
198 }
199}
200
201#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
208pub struct State {
209 pub state: u8,
211 pub start: usize,
213 pub cursor: usize,
215}
216
217#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
218pub struct SlicePos {
222 pub start: usize,
224 pub end: usize,
226}
227
228impl SlicePos {
229 pub(crate) fn reset(&mut self) {
230 self.start = 0;
231 self.end = 0;
232 }
233}
234
235#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
258pub struct Request {
259 pub method: SlicePos,
261 pub path: SlicePos,
264 pub path_ascii: bool,
267 pub query: Option<usize>,
270 pub version: u8,
272}
273
274impl Request {
275 #[inline]
276 pub fn parse(&mut self, src: &[u8]) -> Result<usize> {
281 let mut st = State::default();
282 self.parse_with_state(src, &mut st)
283 }
284
285 #[inline]
286 pub fn parse_with_state(&mut self, src: &[u8], st: &mut State) -> Result<usize> {
296 const EMPTY_LINES: u8 = 0;
297 const METHOD: u8 = 1;
298 const SPACES_BEFORE_URI: u8 = 2;
299 const URI: u8 = 3;
300 const SPACES_BEFORE_VERSION: u8 = 4;
301 const VERSION: u8 = 5;
302 const NEWLINE: u8 = 6;
303
304 if st.state > NEWLINE || st.start > st.cursor || st.cursor > src.len() {
305 return Err(Error::Token);
306 }
307 let mut bytes = Bytes::new(src, st);
308
309 if bytes.st.state == EMPTY_LINES {
310 complete!(utils::skip_empty_lines(&mut bytes));
311 bytes.st.state = METHOD;
312 }
313 if bytes.st.state == METHOD {
314 self.method = complete!(parse_method_inner(&mut bytes));
315 bytes.st.state = SPACES_BEFORE_URI;
316 }
317 if bytes.st.state == SPACES_BEFORE_URI {
318 complete!(utils::skip_spaces(&mut bytes));
319 self.path_ascii = true;
320 self.query = None;
321 bytes.st.state = URI;
322 }
323 if bytes.st.state == URI {
324 self.path = complete!(parse_target_inner(
325 &mut bytes,
326 &mut self.query,
327 &mut self.path_ascii
328 ));
329 bytes.st.state = SPACES_BEFORE_VERSION;
330 }
331 if bytes.st.state == SPACES_BEFORE_VERSION {
332 complete!(utils::skip_spaces(&mut bytes));
333 bytes.st.state = VERSION;
334 }
335 if bytes.st.state == VERSION {
336 let mut tmp = *bytes.st;
338 self.version = complete!(version::parse_version_inner(&mut Bytes::new(src, &mut tmp)));
339 *bytes.st = tmp;
340 bytes.st.state = NEWLINE;
341 }
342
343 newline!(bytes);
344 Ok(Status::Complete(bytes.cursor()))
345 }
346}
347
348#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
365pub struct Response {
366 pub version: u8,
368 pub code: u16,
370 pub reason: SlicePos,
373}
374
375impl Response {
376 #[inline]
377 pub fn parse(&mut self, src: &[u8]) -> Result<usize> {
383 let mut st = State::default();
384 self.parse_with_state(src, &mut st)
385 }
386
387 #[inline]
388 pub fn parse_with_state(&mut self, src: &[u8], st: &mut State) -> Result<usize> {
398 const EMPTY_LINES: u8 = 0;
399 const VERSION: u8 = 1;
400 const SPACES_BEFORE_CODE: u8 = 2;
401 const CODE: u8 = 3;
402 const AFTER_CODE: u8 = 4;
403 const SPACES_BEFORE_REASON: u8 = 5;
404 const REASON: u8 = 6;
405 const REASON_OBS_TEXT: u8 = 7;
406
407 if st.state > REASON_OBS_TEXT || st.start > st.cursor || st.cursor > src.len() {
408 return Err(Error::Status);
409 }
410 let mut bytes = Bytes::new(src, st);
411
412 if bytes.st.state == EMPTY_LINES {
413 complete!(utils::skip_empty_lines(&mut bytes));
414 bytes.st.state = VERSION;
415 }
416 if bytes.st.state == VERSION {
417 let mut tmp = *bytes.st;
419 let mut b = Bytes::new(src, &mut tmp);
420 self.version = complete!(version::parse_version_inner(&mut b));
421 expect!(b.next() == b' ' => Err(Error::Version));
422 b.commit();
423 *bytes.st = tmp;
424 bytes.st.state = SPACES_BEFORE_CODE;
425 }
426 if bytes.st.state == SPACES_BEFORE_CODE {
427 complete!(utils::skip_spaces(&mut bytes));
428 bytes.st.state = CODE;
429 }
430 if bytes.st.state == CODE {
431 let mut tmp = *bytes.st;
433 self.code = complete!(parse_code(&mut Bytes::new(src, &mut tmp)));
434 *bytes.st = tmp;
435 bytes.st.state = AFTER_CODE;
436 }
437 if bytes.st.state == AFTER_CODE {
438 match next!(bytes) {
448 b' ' => bytes.st.state = SPACES_BEFORE_REASON,
449 b'\r' => {
450 expect_lf!(bytes => Err(Error::Status));
451 bytes.commit();
452 self.reason = SlicePos::default();
453 return Ok(Status::Complete(bytes.cursor()));
454 }
455 b'\n' => {
456 bytes.commit();
457 self.reason = SlicePos::default();
458 return Ok(Status::Complete(bytes.cursor()));
459 }
460 _ => return Err(Error::Status),
461 }
462 }
463 if bytes.st.state == SPACES_BEFORE_REASON {
464 complete!(utils::skip_spaces(&mut bytes));
465 bytes.st.state = REASON;
466 }
467
468 let mut seen_obs_text = bytes.st.state == REASON_OBS_TEXT;
469 let res = parse_reason(&mut bytes, &mut seen_obs_text);
470 if seen_obs_text {
471 bytes.st.state = REASON_OBS_TEXT;
472 }
473 self.reason = complete!(res);
474 Ok(Status::Complete(bytes.cursor()))
475 }
476}
477
478#[inline]
479#[doc(hidden)]
480pub fn parse_method(src: &[u8]) -> Result<&str> {
483 let mut st = State::default();
484 let mut bytes = Bytes::new(src, &mut st);
485 complete!(utils::skip_empty_lines(&mut bytes));
486 let s = complete!(parse_method_inner(&mut bytes));
487 let m = unsafe { str::from_utf8_unchecked(&src[s.start..s.end]) };
489 Ok(Status::Complete(m))
490}
491
492#[inline]
495fn parse_method_inner(bytes: &mut Bytes<'_, '_>) -> Result<SlicePos> {
496 const GET: [u8; 4] = *b"GET ";
497 const POST: [u8; 4] = *b"POST";
498
499 if bytes.cursor() == bytes.start() {
500 match bytes.peek_n::<4>() {
501 Some(GET) => {
502 bytes.advance(4);
504 return Ok(Status::Complete(bytes.slice_position(1)));
505 }
506 Some(POST) if bytes.peek_ahead(4) == Some(b' ') => {
507 bytes.advance(5);
509 return Ok(Status::Complete(bytes.slice_position(1)));
510 }
511 _ => {}
512 }
513 if !utils::is_method_token(next!(bytes)) {
516 return Err(Error::Token);
517 }
518 }
519
520 loop {
521 let b = next!(bytes);
522 if b == b' ' {
523 return Ok(Status::Complete(bytes.slice_position(1)));
525 } else if !utils::is_method_token(b) {
526 return Err(Error::Token);
527 }
528 }
529}
530
531#[inline]
548fn parse_reason(bytes: &mut Bytes<'_, '_>, seen_obs_text: &mut bool) -> Result<SlicePos> {
549 const SCALAR_PREFIX: usize = 8;
552 let mut n = 0;
553 while let Some(b) = bytes.peek() {
554 if n == SCALAR_PREFIX {
555 if !matches!(b, b'\r' | b'\n') {
556 match_reason_vectored(bytes, seen_obs_text);
557 }
558 break;
559 }
560 if utils::is_header_value_token(b) {
561 if b >= 0x80 {
562 *seen_obs_text = true;
563 }
564 bytes.advance(1);
565 n += 1;
566 } else {
567 break;
568 }
569 }
570
571 loop {
572 let b = next!(bytes);
573 let skip = if b == b'\r' {
574 expect_lf!(bytes => Err(Error::Status));
575 2
576 } else if b == b'\n' {
577 1
578 } else if !(b == 0x09 || b == b' ' || (0x21..=0x7E).contains(&b) || b >= 0x80) {
579 return Err(Error::Status);
580 } else {
581 if b >= 0x80 {
582 *seen_obs_text = true;
583 }
584 continue;
585 };
586
587 let reason = bytes.slice_position(skip);
590 return Ok(Status::Complete(
591 if *seen_obs_text || reason.start == reason.end {
592 SlicePos::default()
593 } else {
594 reason
595 },
596 ));
597 }
598}
599
600#[inline(never)]
601fn match_reason_vectored(bytes: &mut Bytes<'_, '_>, seen_obs_text: &mut bool) {
602 let from = bytes.cursor();
603 simd::match_header_value_vectored(bytes);
604 if !*seen_obs_text {
605 let scanned = bytes.since(from);
606 let (blocks, tail) = scanned.split_at(scanned.len() & !63);
609 if !blocks.is_ascii() || !tail.is_ascii() {
610 *seen_obs_text = true;
611 }
612 }
613}
614
615#[inline]
616#[doc(hidden)]
617pub fn parse_uri(src: &[u8]) -> Result<&str> {
623 let mut st = State::default();
624 let mut bytes = Bytes::new(src, &mut st);
625 if let Status::Complete(pos) = parse_uri_inner(&mut bytes)? {
626 if let Ok(path) = simdutf8::basic::from_utf8(&src[pos.start..pos.end]) {
627 Ok(Status::Complete(path))
628 } else {
629 Err(Error::Token)
630 }
631 } else {
632 Ok(Status::Partial)
633 }
634}
635
636#[inline]
637fn parse_uri_inner(bytes: &mut Bytes<'_, '_>) -> Result<SlicePos> {
638 let start = bytes.start();
639 simd::match_uri_vectored(bytes);
640 let b_end = bytes.cursor();
641
642 if next!(bytes) == b' ' {
643 if start == b_end {
645 return Err(Error::Token);
646 }
647
648 let end = bytes.cursor() - 1;
650 bytes.commit();
651 Ok(Status::Complete(SlicePos { start, end }))
652 } else {
653 Err(Error::Token)
654 }
655}
656
657#[inline]
663fn parse_target_inner(
664 bytes: &mut Bytes<'_, '_>,
665 query: &mut Option<usize>,
666 ascii: &mut bool,
667) -> Result<SlicePos> {
668 let start = bytes.start();
669 loop {
672 if !*ascii {
673 simd::match_uri_vectored(bytes);
674 break;
675 }
676 if query.is_none() {
677 simd::match_uri_ascii_vectored::<true>(bytes);
678 } else {
679 simd::match_uri_ascii_vectored::<false>(bytes);
680 }
681 match bytes.peek() {
682 Some(b'?') if query.is_none() => {
683 *query = Some(bytes.cursor());
684 bytes.advance(1);
685 }
686 Some(b) if b >= 0x80 => *ascii = false,
687 _ => break,
688 }
689 }
690 let end = bytes.cursor();
691
692 if next!(bytes) == b' ' {
693 if start == end {
695 return Err(Error::Token);
696 }
697 if !*ascii && query.is_none() {
699 *query = bytes
700 .since(start)
701 .iter()
702 .position(|&b| b == b'?')
703 .map(|i| start + i);
704 }
705 bytes.commit();
706 Ok(Status::Complete(SlicePos { start, end }))
707 } else {
708 Err(Error::Token)
709 }
710}
711
712#[inline]
713fn parse_code(bytes: &mut Bytes<'_, '_>) -> Result<u16> {
714 let hundreds = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
715 let tens = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
716 let ones = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
717
718 Ok(Status::Complete(
719 (hundreds - b'0') as u16 * 100 + (tens - b'0') as u16 * 10 + (ones - b'0') as u16,
720 ))
721}
722
723pub fn parse_chunk_size(buf: &[u8]) -> result::Result<Status<(usize, u64)>, InvalidChunkSize> {
741 const RADIX: u64 = 16;
742 let mut st = State::default();
743 let mut bytes = Bytes::new(buf, &mut st);
744 let mut size = 0;
745 let mut in_chunk_size = true;
746 let mut in_ext = false;
747 let mut count = 0;
748 loop {
749 let b = next!(bytes);
750 match b {
751 b'0'..=b'9' if in_chunk_size => {
752 if count > 15 {
753 return Err(InvalidChunkSize);
754 }
755 count += 1;
756 if cfg!(debug_assertions) && size > (u64::MAX / RADIX) {
757 return Err(InvalidChunkSize);
762 }
763 size *= RADIX;
764 size += (b - b'0') as u64;
765 }
766 b'a'..=b'f' | b'A'..=b'F' if in_chunk_size => {
767 if count > 15 {
768 return Err(InvalidChunkSize);
769 }
770 count += 1;
771 if cfg!(debug_assertions) && size > (u64::MAX / RADIX) {
772 return Err(InvalidChunkSize);
773 }
774 size *= RADIX;
775 size += ((b | 0x20) + 10 - b'a') as u64;
776 }
777 b'\r' if count == 0 => return Err(InvalidChunkSize),
779 b'\r' => match next!(bytes) {
780 b'\n' => break,
781 _ => return Err(InvalidChunkSize),
782 },
783 b';' if !in_ext => {
785 in_ext = true;
786 in_chunk_size = false;
787 }
788 b'\t' | b' ' if !in_ext && !in_chunk_size => {}
791 b'\t' | b' ' if in_chunk_size => in_chunk_size = false,
793 0x00..=0x08 | 0x0a..=0x1f | 0x7f if in_ext => return Err(InvalidChunkSize),
796 _ if in_ext => {}
802 _ => return Err(InvalidChunkSize),
805 }
806 }
807 Ok(Status::Complete((bytes.cursor(), size)))
808}
809
810#[cfg(test)]
811mod tests {
812 #![allow(clippy::items_after_statements)]
813 use super::*;
814
815 macro_rules! req {
816 ($name:ident, $buf:expr, |$len:ident, $method:ident, $path:ident, $version:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
817 #[test]
818 fn $name() {
819 let mut req = Request::default();
820 let mut b = $buf.as_ref();
821 if let Ok(Status::Complete(l)) = req.parse(b) {
822 let mut consumed = l;
823 let mut headers = Vec::new();
824 let mut header = Header::default();
825 let mut headers_eof = false;
826 b = &b[consumed..];
827
828 while let Status::Complete(hdr) = header.parse(b).unwrap() {
829 match hdr {
830 HeaderParsed::Header(l) => {
831 consumed += l;
832 let name = String::from_utf8(Vec::from(
833 &b[header.name.start..header.name.end],
834 ))
835 .unwrap();
836 let value = Vec::from(&b[header.value.start..header.value.end]);
837 headers.push((name, value));
838 b = &b[l..];
839 }
840 HeaderParsed::Eof(l) => {
841 consumed += l;
842 headers_eof = true;
843 break;
844 }
845 }
846 }
847
848 let (path, method) = unsafe {
850 (
851 str::from_utf8_unchecked(&$buf.as_ref()[req.path.start..req.path.end]),
852 str::from_utf8_unchecked(
853 &$buf.as_ref()[req.method.start..req.method.end],
854 ),
855 )
856 };
857
858 closure(consumed, method, path, req.version, headers, headers_eof);
859 } else {
860 panic!()
861 }
862
863 fn closure(
864 $len: usize,
865 $method: &str,
866 $path: &str,
867 $version: u8,
868 $headers: Vec<(String, Vec<u8>)>,
869 $headers_eof: bool,
870 ) {
871 $body
872 }
873 }
874 };
875 }
876
877 macro_rules! headers {
878 ($name:ident, $buf:expr, |$len:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
879 #[test]
880 fn $name() {
881 let mut b = $buf.as_ref();
882 let mut consumed = 0;
883 let mut headers = Vec::new();
884 let mut header = Header::default();
885 let mut headers_eof = false;
886
887 while let Status::Complete(hdr) = header.parse(b).unwrap() {
888 match hdr {
889 HeaderParsed::Header(l) => {
890 consumed += l;
891 let name = String::from_utf8(Vec::from(
892 &b[header.name.start..header.name.end],
893 ))
894 .unwrap();
895 let value = Vec::from(&b[header.value.start..header.value.end]);
896 headers.push((name, value));
897 b = &b[l..];
898 }
899 HeaderParsed::Eof(l) => {
900 consumed += l;
901 headers_eof = true;
902 break;
903 }
904 }
905 }
906 closure(consumed, headers, headers_eof);
907
908 fn closure($len: usize, $headers: Vec<(String, Vec<u8>)>, $headers_eof: bool) {
909 $body
910 }
911 }
912 };
913 }
914
915 macro_rules! req_err {
916 ($name:ident, $buf:expr, $err:expr) => {
917 #[test]
918 fn $name() {
919 assert_eq!(Request::default().parse($buf.as_ref()), $err);
920 }
921 };
922 }
923
924 macro_rules! req_par {
925 ($name:ident, $buf:expr) => {
926 #[test]
927 fn $name() {
928 assert_eq!(Request::default().parse($buf.as_ref()), Ok(Status::Partial));
929 }
930 };
931 }
932
933 macro_rules! headers_err {
934 ($name:ident, $buf:expr, $err:expr) => {
935 #[test]
936 fn $name() {
937 let mut consumed = 0;
938 let mut header = Header::default();
939
940 let result = loop {
941 match header.parse(&$buf.as_ref()[consumed..]) {
942 Ok(Status::Complete(HeaderParsed::Header(l))) => {
943 consumed += l;
944 }
945 Ok(_) => break Ok(()),
946 Err(e) => break Err(e),
947 }
948 };
949 assert_eq!(result, $err);
950 }
951 };
952 }
953
954 req! {
955 test_request_simple,
956 b"GET / HTTP/1.1\r\n\r\n",
957 |len, method, path, version, headers, eof| {
958 assert_eq!(len, 18);
959 assert_eq!(method, "GET");
960 assert_eq!(path, "/");
961 assert_eq!(version, 1);
962 assert_eq!(headers.len(), 0);
963 assert!(eof);
964 }
965 }
966
967 req! {
968 test_request_simple_with_query_params,
969 b"GET /thing?data=a HTTP/1.1\r\n\r\n",
970 |len, method, path, version, headers, eof| {
971 assert_eq!(len, 30);
972 assert_eq!(method, "GET");
973 assert_eq!(path, "/thing?data=a");
974 assert_eq!(version, 1);
975 assert_eq!(headers.len(), 0);
976 assert!(eof);
977 }
978 }
979
980 req! {
981 test_request_simple_with_whatwg_query_params,
982 b"GET /thing?data=a^ HTTP/1.1\r\n\r\n",
983 |len, method, path, version, headers, eof| {
984 assert_eq!(len, 31);
985 assert_eq!(method, "GET");
986 assert_eq!(path, "/thing?data=a^");
987 assert_eq!(version, 1);
988 assert_eq!(headers.len(), 0);
989 assert!(eof);
990 }
991 }
992
993 req! {
994 test_request_headers,
995 b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \r\n\r\n ",
996 |len, method, path, version, headers, eof| {
997 assert_eq!(len, 43);
998 assert_eq!(method, "GET");
999 assert_eq!(path, "/");
1000 assert_eq!(version, 1);
1001 assert_eq!(headers.len(), 2);
1002 assert_eq!(headers[0].0, "Host");
1003 assert_eq!(headers[0].1, b"foo.com");
1004 assert_eq!(headers[1].0, "Cookie");
1005 assert_eq!(headers[1].1, b"");
1006 assert!(eof);
1007 }
1008 }
1009
1010 req! {
1011 test_request_headers_optional_whitespace,
1012 b"GET / HTTP/1.1\r\nHost: \tfoo.com\t \r\nCookie: \t \r\n\r\n",
1013 |len, method, path, version, headers, eof| {
1014 assert_eq!(len, 48);
1015 assert_eq!(method, "GET");
1016 assert_eq!(path, "/");
1017 assert_eq!(version, 1);
1018 assert_eq!(headers.len(), 2);
1019 assert_eq!(headers[0].0, "Host");
1020 assert_eq!(headers[0].1, b"foo.com");
1021 assert_eq!(headers[1].0, "Cookie");
1022 assert_eq!(headers[1].1, b"");
1023 assert!(eof);
1024 }
1025 }
1026
1027 req! {
1028 test_request_header_value_htab_short,
1030 b"GET / HTTP/1.1\r\nUser-Agent: some\tagent\r\n\r\n",
1031 |len, method, path, version, headers, eof| {
1032 assert_eq!(len, 42);
1033 assert_eq!(method, "GET");
1034 assert_eq!(path, "/");
1035 assert_eq!(version, 1);
1036 assert_eq!(headers.len(), 1);
1037 assert_eq!(headers[0].0, "User-Agent");
1038 assert_eq!(headers[0].1, b"some\tagent");
1039 assert!(eof);
1040 }
1041 }
1042
1043 req! {
1044 test_request_header_value_htab_med,
1046 b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\tagent\r\n\r\n",
1047 |len, method, path, version, headers, eof| {
1048 assert_eq!(len, 52);
1049 assert_eq!(method, "GET");
1050 assert_eq!(path, "/");
1051 assert_eq!(version, 1);
1052 assert_eq!(headers.len(), 1);
1053 assert_eq!(headers[0].0, "User-Agent");
1054 assert_eq!(headers[0].1, b"1234567890some\tagent");
1055 assert!(eof);
1056 }
1057 }
1058
1059 req! {
1060 test_request_header_value_htab_long,
1062 b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\t1234567890agent1234567890\r\n\r\n",
1063 |len, method, path, version, headers, eof| {
1064 assert_eq!(len, 72);
1065 assert_eq!(method, "GET");
1066 assert_eq!(path, "/");
1067 assert_eq!(version, 1);
1068 assert_eq!(headers.len(), 1);
1069 assert_eq!(headers[0].0, "User-Agent");
1070 assert_eq!(headers[0].1, &b"1234567890some\t1234567890agent1234567890"[..]);
1071 assert!(eof);
1072 }
1073 }
1074
1075 req! {
1076 test_request_header_no_space_after_colon,
1078 b"GET / HTTP/1.1\r\nUser-Agent:omg-no-space1234567890some1234567890agent1234567890\r\n\r\n",
1079 |len, method, path, version, headers, eof| {
1080 assert_eq!(len, 82);
1081 assert_eq!(method, "GET");
1082 assert_eq!(path, "/");
1083 assert_eq!(version, 1);
1084 assert_eq!(headers.len(), 1);
1085 assert_eq!(headers[0].0, "User-Agent");
1086 assert_eq!(headers[0].1, &b"omg-no-space1234567890some1234567890agent1234567890"[..]);
1087 assert!(eof);
1088 }
1089 }
1090
1091 req! {
1092 test_request_headers_max,
1093 b"GET / HTTP/1.1\r\nA: A\r\nB: B\r\nC: C\r\nD: D\r\n\r\n",
1094 |_len, _method, _path, _verion, headers, eof| {
1095 assert_eq!(headers.len(), 4);
1096 assert!(eof);
1097 }
1098 }
1099
1100 req! {
1101 test_request_multibyte,
1102 b"GET / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: \xe3\x81\xb2\xe3/1.0\r\n\r\n",
1103 |len, method, path, version, headers, eof| {
1104 assert_eq!(len, 55);
1105 assert_eq!(method, "GET");
1106 assert_eq!(path, "/");
1107 assert_eq!(version, 1);
1108 assert_eq!(headers.len(), 2);
1109 assert_eq!(headers[0].0, "Host");
1110 assert_eq!(headers[0].1, b"foo.com");
1111 assert_eq!(headers[1].0, "User-Agent");
1112 assert_eq!(headers[1].1, b"\xe3\x81\xb2\xe3/1.0");
1113 assert!(eof);
1114 }
1115 }
1116
1117 req_par! {
1119 test_request_one_byte_method,
1120 b"G"
1121 }
1122
1123 req_par! {
1125 test_request_partial_method,
1126 b"GE"
1127 }
1128
1129 req_par! {
1131 test_request_method_no_delimiter,
1132 b"GET"
1133 }
1134
1135 req_par! {
1138 test_request_method_only,
1139 b"GET "
1140 }
1141
1142 req! {
1143 test_request_partial,
1144 b"GET / HTTP/1.1\r\n\r",
1145 |len, method, path, version, headers, eof| {
1146 assert_eq!(len, b"GET / HTTP/1.1\r\n\r".len() - 1);
1147 assert_eq!(method, "GET");
1148 assert_eq!(path, "/");
1149 assert_eq!(version, 1);
1150 assert_eq!(headers.len(), 0);
1151 assert!(!eof);
1152 }
1153 }
1154
1155 req_par! {
1156 test_request_partial_version,
1157 b"GET / HTTP/1."
1158 }
1159
1160 req_par! {
1161 test_request_method_path_no_delimiter,
1162 b"GET /"
1163 }
1164
1165 req_par! {
1166 test_request_method_path_only,
1167 b"GET / "
1168 }
1169
1170 req! {
1171 test_request_partial_parses_headers_as_much_as_it_can,
1172 b"GET / HTTP/1.1\r\nHost: yolo\r\n",
1173 |len, method, path, version, headers, eof| {
1174 assert_eq!(len, 28);
1175 assert_eq!(method, "GET");
1176 assert_eq!(path, "/");
1177 assert_eq!(version, 1);
1178 assert_eq!(headers.len(), 1);
1179 assert_eq!(headers[0].0, "Host");
1180 assert_eq!(headers[0].1, b"yolo");
1181 assert!(!eof);
1182 }
1183 }
1184
1185 req! {
1186 test_request_newlines,
1187 b"GET / HTTP/1.1\nHost: foo.bar\n\n",
1188 |_len, _method, _path, _verion, _headers, eof| {
1189 assert!(eof);
1190 }
1191 }
1192
1193 req! {
1194 test_request_empty_lines_prefix,
1195 b"\r\n\r\nGET / HTTP/1.1\r\n\r\n",
1196 |len, method, path, version, headers, eof| {
1197 assert_eq!(len, 22);
1198 assert_eq!(method, "GET");
1199 assert_eq!(path, "/");
1200 assert_eq!(version, 1);
1201 assert_eq!(headers.len(), 0);
1202 assert!(eof);
1203 }
1204 }
1205
1206 req! {
1207 test_request_empty_lines_prefix_lf_only,
1208 b"\n\nGET / HTTP/1.1\n\n",
1209 |len, method, path, version, headers, eof| {
1210 assert_eq!(len, 18);
1211 assert_eq!(method, "GET");
1212 assert_eq!(path, "/");
1213 assert_eq!(version, 1);
1214 assert_eq!(headers.len(), 0);
1215 assert!(eof);
1216 }
1217 }
1218
1219 req! {
1220 test_request_path_backslash,
1221 b"\n\nGET /\\?wayne\\=5 HTTP/1.1\n\n",
1222 |len, method, path, version, headers, eof| {
1223 assert_eq!(len, 28);
1224 assert_eq!(method, "GET");
1225 assert_eq!(path, "/\\?wayne\\=5");
1226 assert_eq!(version, 1);
1227 assert_eq!(headers.len(), 0);
1228 assert!(eof);
1229 }
1230 }
1231
1232 req_err! {
1233 test_request_with_invalid_token_delimiter,
1234 b"GET\n/ HTTP/1.1\r\nHost: foo.bar\r\n\r\n",
1235 Err(Error::Token)
1236 }
1237
1238 req_err! {
1239 test_request_with_invalid_but_short_version,
1240 b"GET / HTTP/1!",
1241 Err(Error::Version)
1242 }
1243
1244 req_err! {
1245 test_request_with_empty_method,
1246 b" / HTTP/1.1\r\n\r\n",
1247 Err(Error::Token)
1248 }
1249
1250 req_err! {
1251 test_request_with_empty_path,
1252 b"GET HTTP/1.1\r\n\r\n",
1253 Err(Error::Token)
1254 }
1255
1256 req_err! {
1257 test_request_with_empty_method_and_path,
1258 b" HTTP/1.1\r\n\r\n",
1259 Err(Error::Token)
1260 }
1261
1262 headers! {
1263 test_headers_optional_whitespace,
1264 b"Host: \tfoo.com\t \r\nCookie: \t \r\n",
1265 |len, headers, eof| {
1266 assert_eq!(len, 30);
1267 assert_eq!(headers.len(), 2);
1268 assert_eq!(headers[0].0, "Host");
1269 assert_eq!(headers[0].1, b"foo.com");
1270 assert_eq!(headers[1].0, "Cookie");
1271 assert_eq!(headers[1].1, b"");
1272 assert!(!eof);
1273 }
1274 }
1275
1276 #[test]
1277 fn test_headers_with_state() {
1278 const B1: &[u8] = b"Host";
1279 const B2: &[u8] = b"Host: \t";
1280 const B3: &[u8] = b"Host: \tfoo.com\t ";
1281 const B4: &[u8] = b"Host: \tfoo.com\t \r\nCoo";
1282 const B5: &[u8] = b"Host: \tfoo.com\t \r\nCookie: \t \r\n\r\n";
1283
1284 let mut st = State::default();
1285 let mut header = Header::default();
1286 assert!(header.parse_with_state(B1, &mut st).unwrap().is_partial());
1287 assert_eq!(st.state, 1);
1288 assert_eq!(st.start, 0);
1289 assert_eq!(st.cursor, 4);
1290 assert_eq!(header.name, SlicePos { start: 0, end: 0 });
1291 assert!(header.parse_with_state(B2, &mut st).unwrap().is_partial());
1292 assert_eq!(st.state, 2);
1293 assert_eq!(st.start, 0);
1294 assert_eq!(st.cursor, 7);
1295 assert_eq!(header.name, SlicePos { start: 0, end: 4 });
1296 assert_eq!(&B5[header.name.start..header.name.end], b"Host");
1297
1298 assert!(header.parse_with_state(B3, &mut st).unwrap().is_partial());
1299 assert_eq!(st.state, 3);
1300 assert_eq!(st.start, 0);
1301 assert_eq!(st.cursor, 16);
1302 assert_eq!(header.name, SlicePos { start: 0, end: 4 });
1303 assert_eq!(header.value, SlicePos { start: 7, end: 0 });
1304
1305 assert!(header.parse_with_state(B4, &mut st).unwrap().is_complete());
1306 assert_eq!(st.state, 0);
1307 assert_eq!(st.start, 18);
1308 assert_eq!(st.cursor, 18);
1309 assert_eq!(header.value, SlicePos { start: 7, end: 14 });
1310 assert_eq!(&B5[header.value.start..header.value.end], b"foo.com");
1311
1312 assert!(header.parse_with_state(B5, &mut st).unwrap().is_complete());
1313 assert_eq!(st.state, 0);
1314 assert_eq!(st.start, 30);
1315 assert_eq!(st.cursor, 30);
1316 assert_eq!(header.name, SlicePos { start: 18, end: 24 });
1317 assert_eq!(header.value, SlicePos { start: 0, end: 0 });
1318 assert_eq!(&B5[header.name.start..header.name.end], b"Cookie");
1319 assert_eq!(&B5[header.value.start..header.value.end], b"");
1320
1321 assert_eq!(
1322 header.parse_with_state(B5, &mut st),
1323 Ok(Status::Complete(HeaderParsed::Eof(32)))
1324 );
1325 assert_eq!(st.state, 0);
1326 assert_eq!(st.start, 32);
1327 assert_eq!(st.cursor, 32);
1328 }
1329
1330 headers_err! {
1331 test_headers_with_obsolete_line_folding_at_start,
1332 b"Line-Folded-Header: \r\n \r\n hello there\r\n\r\n",
1333 Err(Error::HeaderName)
1334 }
1335
1336 headers_err! {
1337 test_header_with_invalid_name,
1338 b"Host : foo.bar\r\n\r\n",
1339 Err(Error::HeaderName)
1340 }
1341
1342 macro_rules! res {
1343 ($name:ident, $buf:expr, |$len:ident, $version:ident, $code:ident, $reason:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
1344 #[test]
1345 fn $name() {
1346 let mut b = $buf.as_ref();
1347 let mut res = Response::default();
1348 let mut consumed = res.parse($buf.as_ref()).unwrap().unwrap();
1349 let mut headers = Vec::new();
1350 let mut header = Header::default();
1351 let mut headers_eof = false;
1352 b = &b[consumed..];
1353
1354 while let Status::Complete(hdr) = header.parse(b).unwrap() {
1355 match hdr {
1356 HeaderParsed::Header(l) => {
1357 consumed += l;
1358 let name = String::from_utf8(Vec::from(
1359 &b[header.name.start..header.name.end],
1360 ))
1361 .unwrap();
1362 let value = Vec::from(&b[header.value.start..header.value.end]);
1363 headers.push((name, value));
1364 b = &b[l..];
1365 }
1366 HeaderParsed::Eof(l) => {
1367 consumed += l;
1368 headers_eof = true;
1369 break;
1370 }
1371 }
1372 }
1373
1374 let reason = unsafe {
1376 str::from_utf8_unchecked(&$buf.as_ref()[res.reason.start..res.reason.end])
1377 };
1378
1379 closure(
1380 consumed,
1381 res.version,
1382 res.code,
1383 reason,
1384 headers,
1385 headers_eof,
1386 );
1387
1388 fn closure(
1389 $len: usize,
1390 $version: u8,
1391 $code: u16,
1392 $reason: &str,
1393 $headers: Vec<(String, Vec<u8>)>,
1394 $headers_eof: bool,
1395 ) {
1396 $body
1397 }
1398 }
1399 };
1400 }
1401
1402 macro_rules! res_err {
1403 ($name:ident, $buf:expr, $err:expr) => {
1404 #[test]
1405 fn $name() {
1406 assert_eq!(Response::default().parse($buf.as_ref()), $err);
1407 }
1408 };
1409 }
1410
1411 macro_rules! res_par {
1412 ($name:ident, $buf:expr) => {
1413 #[test]
1414 fn $name() {
1415 assert_eq!(
1416 Response::default().parse($buf.as_ref()),
1417 Ok(Status::Partial)
1418 );
1419 }
1420 };
1421 }
1422
1423 res_err! {
1424 test_response_newline_after_version,
1425 b"HTTP/1.1\r\n\r\n 200 OK\r\n\r\n",
1426 Err(Error::Version)
1427 }
1428
1429 res_err! {
1430 test_response_bare_newline_after_version,
1431 b"HTTP/1.1\n 200 OK\r\n\r\n",
1432 Err(Error::Version)
1433 }
1434
1435 res! {
1436 test_response_simple,
1437 b"HTTP/1.1 200 OK\r\n\r\n",
1438 |len, version, code, reason, headers, eof| {
1439 assert_eq!(len, 19);
1440 assert_eq!(version, 1);
1441 assert_eq!(code, 200);
1442 assert_eq!(reason, "OK");
1443 assert_eq!(headers.len(), 0);
1444 assert!(eof);
1445 }
1446 }
1447
1448 res! {
1449 test_response_newlines,
1450 b"HTTP/1.0 403 Forbidden\nServer: foo.bar\n\n",
1451 |len, version, code, reason, headers, eof| {
1452 assert_eq!(len, 40);
1453 assert_eq!(version, 0);
1454 assert_eq!(code, 403);
1455 assert_eq!(reason, "Forbidden");
1456 assert_eq!(headers.len(), 1);
1457 assert_eq!(headers[0].0, "Server");
1458 assert_eq!(headers[0].1, b"foo.bar");
1459 assert!(eof);
1460 }
1461 }
1462
1463 res! {
1464 test_response_reason_missing,
1465 b"HTTP/1.1 200 \r\n\r\n",
1466 |len, version, code, reason, headers, eof| {
1467 assert_eq!(len, 17);
1468 assert_eq!(version, 1);
1469 assert_eq!(code, 200);
1470 assert_eq!(reason, "");
1471 assert_eq!(headers.len(), 0);
1472 assert!(eof);
1473 }
1474 }
1475
1476 res! {
1477 test_response_reason_missing_no_space,
1478 b"HTTP/1.1 200\r\n\r\n",
1479 |len, version, code, reason, headers, eof| {
1480 assert_eq!(len, 16);
1481 assert_eq!(version, 1);
1482 assert_eq!(code, 200);
1483 assert_eq!(reason, "");
1484 assert_eq!(headers.len(), 0);
1485 assert!(eof);
1486 }
1487 }
1488
1489 res! {
1490 test_response_reason_missing_no_space_with_headers,
1491 b"HTTP/1.1 200\r\nFoo: bar\r\n\r\n",
1492 |len, version, code, reason, headers, eof| {
1493 assert_eq!(len, 26);
1494 assert_eq!(version, 1);
1495 assert_eq!(code, 200);
1496 assert_eq!(reason, "");
1497 assert_eq!(headers.len(), 1);
1498 assert_eq!(headers[0].0, "Foo");
1499 assert_eq!(headers[0].1, b"bar");
1500 assert!(eof);
1501 }
1502 }
1503
1504 res! {
1505 test_response_reason_with_space_and_tab,
1506 b"HTTP/1.1 101 Switching Protocols\t\r\n\r\n",
1507 |len, version, code, reason, headers, eof| {
1508 assert_eq!(len, 37);
1509 assert_eq!(version, 1);
1510 assert_eq!(code, 101);
1511 assert_eq!(reason, "Switching Protocols\t");
1512 assert_eq!(headers.len(), 0);
1513 assert!(eof);
1514 }
1515 }
1516
1517 res! {
1518 test_response_reason_with_obsolete_text_byte,
1519 b"HTTP/1.1 200 X\xFFZ\r\n\r\n",
1520 |len, version, code, reason, headers, eof| {
1521 assert_eq!(len, 20);
1522 assert_eq!(version, 1);
1523 assert_eq!(code, 200);
1524 assert_eq!(reason, "");
1526 assert_eq!(headers.len(), 0);
1527 assert!(eof);
1528 }
1529 }
1530
1531 res_err! {
1532 test_response_reason_with_nul_byte,
1533 b"HTTP/1.1 200 \x00\r\n\r\n",
1534 Err(crate::Error::Status)
1535 }
1536
1537 #[test]
1538 fn test_response_reason_bytes_at_every_position() {
1539 const PREFIX: &[u8] = b"HTTP/1.1 200 ";
1540 let lens: Vec<usize> = if cfg!(miri) {
1542 vec![1, 8, 9, 17, 33]
1543 } else {
1544 (1..80).collect()
1545 };
1546 for len in lens {
1547 for pos in 0..len {
1548 for b in [b'\t', b' ', b'~', 0x00, 0x0b, 0x7f, 0x80, 0xff] {
1549 let mut reason = vec![b'a'; len];
1550 reason[pos] = b;
1551 let buf = [PREFIX, &reason, b"\r\n"].concat();
1552 let valid = b == b'\t' || b == b' ' || b == b'~' || b >= 0x80;
1553
1554 let splits: Vec<usize> = if cfg!(miri) {
1556 let at = PREFIX.len() + pos;
1557 vec![PREFIX.len(), at, at + 1, buf.len()]
1558 } else {
1559 (PREFIX.len()..=buf.len()).collect()
1560 };
1561 for split in splits {
1562 let mut resp = Response::default();
1563 let mut st = State::default();
1564 let first = resp.parse_with_state(&buf[..split], &mut st);
1565 let res = if split == buf.len() {
1566 first
1567 } else if first.is_err() {
1568 assert!(!valid, "len {len} pos {pos} byte {b:#x} split {split}");
1569 continue;
1570 } else {
1571 assert_eq!(first, Ok(Status::Partial));
1572 resp.parse_with_state(&buf, &mut st)
1573 };
1574 let ctx = format!("len {len} pos {pos} byte {b:#x} split {split}");
1575 if !valid {
1576 assert_eq!(res, Err(crate::Error::Status), "{ctx}");
1577 } else if b >= 0x80 {
1578 assert_eq!(res, Ok(Status::Complete(buf.len())), "{ctx}");
1579 assert_eq!(resp.reason, SlicePos::default(), "{ctx}");
1580 } else {
1581 assert_eq!(res, Ok(Status::Complete(buf.len())), "{ctx}");
1582 let start = usize::from(pos == 0 && b == b' ');
1584 assert_eq!(
1585 &buf[resp.reason.start..resp.reason.end],
1586 &reason[start..],
1587 "{ctx}"
1588 );
1589 }
1590 }
1591 }
1592 }
1593 }
1594 }
1595
1596 #[test]
1597 fn test_response_reason_ascii_block_boundaries() {
1598 const PREFIX: &[u8] = b"HTTP/1.1 200 ";
1599 for len in [
1600 63, 64, 65, 71, 72, 73, 127, 128, 129, 135, 136, 137, 199, 200, 255, 256,
1601 ] {
1602 for pos in [
1603 None,
1604 Some(0),
1605 Some(7),
1606 Some(8),
1607 Some(63),
1608 Some(64),
1609 Some(len - 1),
1610 ] {
1611 if pos.is_some_and(|pos| pos >= len) {
1612 continue;
1613 }
1614 let mut reason = vec![b'a'; len];
1615 if let Some(pos) = pos {
1616 reason[pos] = 0xff;
1617 }
1618 let mut buf = [PREFIX, &reason, b"\r\n"].concat();
1619 let consumed = buf.len();
1620 let expected = if pos.is_some() {
1621 SlicePos::default()
1622 } else {
1623 SlicePos {
1624 start: PREFIX.len(),
1625 end: PREFIX.len() + len,
1626 }
1627 };
1628 let mut resp = Response::default();
1629 assert_eq!(resp.parse(&buf), Ok(Status::Complete(consumed)));
1630 assert_eq!(resp.reason, expected, "len {len} pos {pos:?}");
1631 check_split(&buf, Response::parse_with_state, Response::parse);
1632
1633 buf.extend_from_slice(b"X-Next: \xff\r\n\r\n");
1634 let mut resp = Response::default();
1635 assert_eq!(resp.parse(&buf), Ok(Status::Complete(consumed)));
1636 assert_eq!(resp.reason, expected, "len {len} pos {pos:?}");
1637 }
1638 }
1639 }
1640
1641 res_par! {
1642 test_response_version_missing_space,
1643 b"HTTP/1.1"
1644 }
1645
1646 res_par! {
1647 test_response_code_missing_space,
1648 b"HTTP/1.1 200"
1649 }
1650
1651 res! {
1652 test_response_partial_parses_headers_as_much_as_it_can,
1653 b"HTTP/1.1 200 OK\r\nServer: yolo\r\n",
1654 |len, version, code, reason, headers, eof| {
1655 assert_eq!(len, 31);
1656 assert_eq!(version, 1);
1657 assert_eq!(code, 200);
1658 assert_eq!(reason, "OK");
1659 assert_eq!(headers.len(), 1);
1660 assert_eq!(headers[0].0, "Server");
1661 assert_eq!(headers[0].1, b"yolo");
1662 assert!(!eof);
1663 }
1664 }
1665
1666 res! {
1667 test_response_empty_lines_prefix_lf_only,
1668 b"\n\nHTTP/1.1 200 OK\n\n",
1669 |len, version, code, reason, headers, eof| {
1670 assert_eq!(len, 19);
1671 assert_eq!(version, 1);
1672 assert_eq!(code, 200);
1673 assert_eq!(reason, "OK");
1674 assert_eq!(headers.len(), 0);
1675 assert!(eof);
1676 }
1677 }
1678
1679 res! {
1680 test_response_no_cr,
1681 b"HTTP/1.0 200\nContent-type: text/html\n\n",
1682 |len, version, code, reason, headers, eof| {
1683 assert_eq!(len, 38);
1684 assert_eq!(version, 0);
1685 assert_eq!(code, 200);
1686 assert_eq!(reason, "");
1687 assert_eq!(headers.len(), 1);
1688 assert_eq!(headers[0].0, "Content-type");
1689 assert_eq!(headers[0].1, b"text/html");
1690 assert!(eof);
1691 }
1692 }
1693
1694 #[test]
1696 fn partial_permutations() {
1697 let req_str = "GET / HTTP/1.1\r\n";
1698 let mut req = Request::default();
1699 for i in 0..req_str.len() {
1700 let status = req.parse(&req_str.as_bytes()[..i]);
1701 assert_eq!(
1702 status,
1703 Ok(Status::Partial),
1704 "partial request line should return partial. \
1705 Portion which failed: '{seg}' (below {i})",
1706 seg = &req_str[..i]
1707 );
1708 }
1709 }
1710
1711 headers_err! {
1712 test_forbid_headers_with_whitespace_between_header_name_and_colon,
1713 b"Access-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n",
1714 Err(Error::HeaderName)
1715 }
1716
1717 headers_err! {
1718 test_forbid_headers_with_obsolete_line_folding_at_end,
1719 b"Line-Folded-Header: hello there\r\n \r\n \r\n\r\n",
1720 Err(Error::HeaderName)
1721 }
1722
1723 headers_err! {
1724 test_forbid_headers_with_obsolete_line_folding_in_middle,
1725 b"Line-Folded-Header: hello \r\n \r\n there\r\n\r\n",
1726 Err(Error::HeaderName)
1727 }
1728
1729 headers_err! {
1730 test_forbid_headers_with_obsolete_line_folding_in_empty_header,
1731 b"Line-Folded-Header: \r\n \r\n \r\n\r\n",
1732 Err(Error::HeaderName)
1733 }
1734
1735 headers_err! {
1736 test_forbid_headers_with_empty_header_name,
1737 b": hello\r\nBread: baguette\r\n\r\n",
1738 Err(Error::HeaderName)
1739 }
1740
1741 headers_err! {
1742 test_forbid_headers_with_empty_header_name_second,
1743 b"Bread: baguette\r\n: hello\r\n\r\n",
1744 Err(Error::HeaderName)
1745 }
1746
1747 #[test]
1748 fn test_chunk_size() {
1749 assert_eq!(parse_chunk_size(b"0\r\n"), Ok(Status::Complete((3, 0))));
1750 assert_eq!(
1751 parse_chunk_size(b"12\r\nchunk"),
1752 Ok(Status::Complete((4, 18)))
1753 );
1754 assert_eq!(
1755 parse_chunk_size(b"3086d\r\n"),
1756 Ok(Status::Complete((7, 198_765)))
1757 );
1758 assert_eq!(
1759 parse_chunk_size(b"3735AB1;foo bar*\r\n"),
1760 Ok(Status::Complete((18, 57_891_505)))
1761 );
1762 assert_eq!(
1763 parse_chunk_size(b"3735ab1 ; baz \r\n"),
1764 Ok(Status::Complete((16, 57_891_505)))
1765 );
1766 assert_eq!(parse_chunk_size(b"77a65\r"), Ok(Status::Partial));
1767 assert_eq!(parse_chunk_size(b"ab"), Ok(Status::Partial));
1768 assert_eq!(
1769 parse_chunk_size(b"567f8a\rfoo"),
1770 Err(crate::InvalidChunkSize)
1771 );
1772 assert_eq!(
1773 parse_chunk_size(b"567f8a\rfoo"),
1774 Err(crate::InvalidChunkSize)
1775 );
1776 assert_eq!(
1777 parse_chunk_size(b"567xf8a\r\n"),
1778 Err(crate::InvalidChunkSize)
1779 );
1780 assert_eq!(
1781 parse_chunk_size(b"ffffffffffffffff\r\n"),
1782 Ok(Status::Complete((18, u64::MAX)))
1783 );
1784 assert_eq!(
1785 parse_chunk_size(b"1ffffffffffffffff\r\n"),
1786 Err(crate::InvalidChunkSize)
1787 );
1788 assert_eq!(
1789 parse_chunk_size(b"Affffffffffffffff\r\n"),
1790 Err(crate::InvalidChunkSize)
1791 );
1792 assert_eq!(
1793 parse_chunk_size(b"fffffffffffffffff\r\n"),
1794 Err(crate::InvalidChunkSize)
1795 );
1796 }
1797
1798 #[test]
1799 fn test_chunk_size_empty() {
1800 for buf in [&b"\r\n"[..], b";a\r\n", b" \r\n", b"\t;a\r\n", b"\r"] {
1801 assert_eq!(
1802 parse_chunk_size(buf),
1803 Err(crate::InvalidChunkSize),
1804 "{buf:?}"
1805 );
1806 }
1807 assert_eq!(parse_chunk_size(b"0\r\n"), Ok(Status::Complete((3, 0))));
1808 }
1809
1810 fn header_split(buf: &[u8]) -> (Result<HeaderParsed>, Header) {
1813 let mut one = Header::default();
1814 let expected = one.parse(buf);
1815 for split in 0..buf.len() {
1816 let mut h = Header::default();
1817 let mut st = State::default();
1818 let first = h.parse_with_state(&buf[..split], &mut st);
1819 if first.is_err() {
1820 assert_eq!(first, expected, "split {split} of {buf:?}");
1821 continue;
1822 }
1823 assert_eq!(first, Ok(Status::Partial), "split {split} of {buf:?}");
1824 let res = h.parse_with_state(buf, &mut st);
1825 assert_eq!(res, expected, "split {split} of {buf:?}");
1826 if expected.is_ok() {
1827 assert_eq!(h, one, "split {split} of {buf:?}");
1828 }
1829 }
1830 (expected, one)
1831 }
1832
1833 #[test]
1834 fn test_header_split_bare_cr() {
1835 assert_eq!(header_split(b"X: a\rb\r\n").0, Err(Error::HeaderValue));
1836 assert_eq!(header_split(b"X:\rb\r\n").0, Err(Error::HeaderValue));
1837 assert_eq!(header_split(b"\rX: b\r\n").0, Err(Error::NewLine));
1838
1839 let (res, h) = header_split(b"X: a b\r\n");
1840 assert_eq!(res, Ok(Status::Complete(HeaderParsed::Header(8))));
1841 assert_eq!(h.value, SlicePos { start: 3, end: 6 });
1842 assert_eq!(
1843 header_split(b"X:\r\n").0,
1844 Ok(Status::Complete(HeaderParsed::Header(4)))
1845 );
1846 assert_eq!(
1847 header_split(b"\r\n").0,
1848 Ok(Status::Complete(HeaderParsed::Eof(2)))
1849 );
1850 }
1851
1852 #[test]
1853 fn test_request_split() {
1854 for buf in [
1855 &b"GET /path HTTP/1.1\r\n"[..],
1856 b"GET /path HTTP/1.1\r\n",
1857 b"PUT /path HTTP/1.0\n",
1858 b"CUSTOM /path HTTP/1.1\r\n",
1859 b"\r\n\nPOST /path HTTP/1.1\r\n",
1860 b"GET /path HTTP/1.1\r\r\n",
1861 b"GET /path HTTP/1.1\rX",
1862 b"\rGET /path HTTP/1.1\r\n",
1863 b"GE\x00T /path HTTP/1.1\r\n",
1864 b"GET /pa\x7fth HTTP/1.1\r\n",
1865 b"GET /path HTTP/2.0\r\n",
1866 ] {
1867 check_split(buf, Request::parse_with_state, Request::parse);
1868 }
1869 }
1870
1871 #[test]
1872 fn test_response_split() {
1873 for buf in [
1874 &b"HTTP/1.1 200 OK\r\n"[..],
1875 b"HTTP/1.0 404 Not Found \n",
1876 b"\r\nHTTP/1.1 200\r\n",
1877 b"HTTP/1.1 200\n",
1878 b"HTTP/1.1 200 \r\n",
1879 b"HTTP/1.1 200 caf\xc3\xa9\r\n",
1880 b"HTTP/1.1 200 OK\r\r\n",
1881 b"HTTP/1.1 200\rX",
1882 b"HTTP/1.1 2x0 OK\r\n",
1883 b"HTTP/1.1\r\n200 OK\r\n",
1884 b"HTTP/1.1 200 O\x00K\r\n",
1885 ] {
1886 check_split(buf, Response::parse_with_state, Response::parse);
1887 }
1888 }
1889
1890 #[test]
1891 fn test_response_empty_reason() {
1892 for buf in [
1893 &b"HTTP/1.1 200\r\n"[..],
1894 b"HTTP/1.1 200 \r\n",
1895 b"HTTP/1.1 200 \r\n",
1896 b"HTTP/1.1 200\n",
1897 b"HTTP/1.1 200 \n",
1898 b"\r\nHTTP/1.1 200 \n",
1899 ] {
1900 let mut resp = Response::default();
1901 assert_eq!(resp.parse(buf), Ok(Status::Complete(buf.len())));
1902 assert_eq!(resp.reason, SlicePos::default());
1903 check_split(buf, Response::parse_with_state, Response::parse);
1904 }
1905 }
1906
1907 fn check_split<T: Default + PartialEq + fmt::Debug>(
1910 buf: &[u8],
1911 parse: fn(&mut T, &[u8], &mut State) -> Result<usize>,
1912 one_shot: fn(&mut T, &[u8]) -> Result<usize>,
1913 ) {
1914 let mut one = T::default();
1915 let expected = one_shot(&mut one, buf);
1916 let check = |res: Result<usize>, v: &T, what: &str| {
1917 assert_eq!(res, expected, "{what} of {buf:?}");
1918 if expected.is_ok() {
1919 assert_eq!(v, &one, "{what} of {buf:?}");
1920 }
1921 };
1922
1923 for split in 0..buf.len() {
1924 let mut v = T::default();
1925 let mut st = State::default();
1926 let first = parse(&mut v, &buf[..split], &mut st);
1927 if first.is_err() {
1928 check(first, &v, &format!("split {split}"));
1929 continue;
1930 }
1931 assert_eq!(first, Ok(Status::Partial), "split {split} of {buf:?}");
1932 let res = parse(&mut v, buf, &mut st);
1933 check(res, &v, &format!("split {split}"));
1934 }
1935
1936 let mut v = T::default();
1937 let mut st = State::default();
1938 for len in 0..=buf.len() {
1939 let res = parse(&mut v, &buf[..len], &mut st);
1940 if res != Ok(Status::Partial) || len == buf.len() {
1941 check(res, &v, &format!("byte by byte, len {len}"));
1942 break;
1943 }
1944 assert!(st.cursor + 9 >= len, "len {len} of {buf:?}");
1947 }
1948 }
1949
1950 #[test]
1951 fn test_request_resumes_long_method() {
1952 let buf = [b'A'; 4096];
1953 let mut req = Request::default();
1954 let mut st = State::default();
1955 assert_eq!(req.parse_with_state(&buf, &mut st), Ok(Status::Partial));
1956 assert_eq!((st.state, st.start, st.cursor), (1, 0, 4096));
1957 }
1958
1959 #[test]
1960 fn test_header_name_chars_at_every_position() {
1961 let (lens, bytes): (&[usize], Vec<u8>) = if cfg!(miri) {
1963 let bytes = [
1965 0x00, b'\t', b' ', b'!', b'"', b'x', b':', b'~', 0x7f, 0x80, 0xff,
1966 ];
1967 (&[1, 16, 17, 33], bytes.to_vec())
1968 } else {
1969 (
1970 &[1, 15, 16, 17, 31, 32, 33, 47, 48, 70],
1971 (0..=255).collect(),
1972 )
1973 };
1974 for &len in lens {
1975 for pos in 1..len {
1976 for &b in &bytes {
1977 let mut buf = vec![b'x'; len];
1978 buf[pos] = b;
1979 buf.extend_from_slice(b": v\r\n");
1980 let res = Header::default().parse(&buf);
1981 if utils::TOKEN_MAP[b as usize] {
1982 assert_eq!(
1983 res,
1984 Ok(Status::Complete(HeaderParsed::Header(len + 5))),
1985 "len {len} pos {pos} byte {b}"
1986 );
1987 } else if b == b':' {
1988 assert!(res.is_ok(), "len {len} pos {pos}");
1989 } else {
1990 assert_eq!(res, Err(Error::HeaderName), "len {len} pos {pos} byte {b}");
1991 }
1992 }
1993 }
1994 }
1995 }
1996
1997 #[test]
1998 fn test_header_state_reuse() {
1999 let buf = b"A: 1\r\nB: 2\r\n\r\nbody";
2000 let mut h = Header::default();
2001 let mut st = State::default();
2002 assert_eq!(
2003 h.parse_with_state(buf, &mut st),
2004 Ok(Status::Complete(HeaderParsed::Header(6)))
2005 );
2006 assert_eq!(
2007 h.parse_with_state(buf, &mut st),
2008 Ok(Status::Complete(HeaderParsed::Header(12)))
2009 );
2010 assert_eq!((h.name.start, h.value.end), (6, 10));
2011 assert_eq!(
2012 h.parse_with_state(buf, &mut st),
2013 Ok(Status::Complete(HeaderParsed::Eof(14)))
2014 );
2015 }
2016
2017 #[test]
2018 fn test_invalid_state() {
2019 for st in [
2020 State {
2021 state: 3,
2022 start: 0,
2023 cursor: 100,
2024 },
2025 State {
2026 state: 1,
2027 start: 2,
2028 cursor: 1,
2029 },
2030 State {
2031 state: 8,
2032 start: 0,
2033 cursor: 0,
2034 },
2035 ] {
2036 let mut s = st;
2037 assert_eq!(
2038 Header::default().parse_with_state(b"abc", &mut s),
2039 Err(Error::HeaderName)
2040 );
2041 let mut s = st;
2042 assert_eq!(
2043 Request::default().parse_with_state(b"abc", &mut s),
2044 Err(Error::Token)
2045 );
2046 let mut s = st;
2047 assert_eq!(
2048 Response::default().parse_with_state(b"abc", &mut s),
2049 Err(Error::Status)
2050 );
2051 }
2052 }
2053
2054 #[test]
2055 fn test_chunk_size_extension_control_chars() {
2056 for buf in [
2057 &b"4;a\nX\r\n"[..],
2058 b"4;a=\"\nX\"\r\n",
2059 b"4;a\x00\r\n",
2060 b"4;a\x7f\r\n",
2061 b"4;\n",
2062 ] {
2063 assert_eq!(
2064 parse_chunk_size(buf),
2065 Err(crate::InvalidChunkSize),
2066 "{buf:?}"
2067 );
2068 }
2069 assert_eq!(
2070 parse_chunk_size(b"4 ;a=\"b\tc \x80\";d\t\r\n"),
2071 Ok(Status::Complete((17, 4)))
2072 );
2073 }
2074
2075 res! {
2076 test_allow_response_with_multiple_space_delimiters,
2077 b"HTTP/1.1 200 OK\r\n\r\n",
2078 |len, version, code, reason, headers, eof| {
2079 assert_eq!(len, 22);
2080 assert_eq!(version, 1);
2081 assert_eq!(code, 200);
2082 assert_eq!(reason, "OK");
2083 assert_eq!(headers.len(), 0);
2084 assert!(eof);
2085 }
2086 }
2087
2088 res_err! {
2091 test_forbid_response_with_weird_whitespace_delimiters,
2092 b"HTTP/1.1 200\rOK\r\n\r\n",
2093 Err(Error::Status)
2094 }
2095
2096 req! {
2097 test_allow_request_with_multiple_space_delimiters,
2098 b"GET / HTTP/1.1\r\n\r\n",
2099 |len, method, path, version, headers, eof| {
2100 assert_eq!(len, 22);
2101 assert_eq!(method, "GET");
2102 assert_eq!(path, "/");
2103 assert_eq!(version, 1);
2104 assert_eq!(headers.len(), 0);
2105 assert!(eof);
2106 }
2107 }
2108
2109 req_err! {
2112 test_forbid_request_with_weird_whitespace_delimiters,
2113 b"GET\r/\rHTTP/1.1\r\n\r\n",
2114 Err(Error::Token)
2115 }
2116
2117 req_err! {
2118 test_request_with_multiple_spaces_and_bad_path,
2119 b"GET /foo ohno HTTP/1.1\r\n\r\n",
2120 Err(Error::Version)
2121 }
2122
2123 req_err! {
2127 test_request_with_del_in_path,
2128 b"GET /foo\x7Fohno HTTP/1.1\r\n\r\n",
2129 Err(Error::Token)
2130 }
2131
2132 res_err! {
2276 test_response_with_spaces_in_code,
2277 b"HTTP/1.1 99 200 OK\r\n\r\n",
2278 Err(Error::Status)
2279 }
2280
2281 headers_err! {
2282 test_headers_with_whitespace_between_header_name_and_colon,
2283 b"Access-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n",
2284 Err(Error::HeaderName)
2285 }
2286
2287 headers_err! {
2288 test_headers_with_invalid_char_between_header_name_and_colon,
2289 b"Access-Control-Allow-Credentials\xFF: true\r\nBread: baguette\r\n\r\n",
2290 Err(Error::HeaderName)
2291 }
2292
2293 headers_err! {
2294 test_ignore_header_line_with_missing_colon_in_response,
2295 b"Access-Control-Allow-Credentials\r\nBread: baguette\r\n\r\n",
2296 Err(Error::HeaderName)
2297 }
2298
2299 headers_err! {
2300 test_headers_header_with_missing_colon_with_folding,
2301 b"Access-Control-Allow-Credentials \r\n hello\r\nBread: baguette\r\n\r\n",
2302 Err(Error::HeaderName)
2303 }
2304
2305 headers_err! {
2306 test_headers_header_with_nul_in_header_name,
2307 b"Access-Control-Allow-Cred\0entials: hello\r\nBread: baguette\r\n\r\n",
2308 Err(Error::HeaderName)
2309 }
2310
2311 headers_err! {
2312 test_header_with_cr_in_header_name,
2313 b"Access-Control-Allow-Cred\rentials: hello\r\nBread: baguette\r\n\r\n",
2314 Err(Error::HeaderName)
2315 }
2316
2317 headers_err! {
2318 test_header_with_nul_in_whitespace_before_colon,
2319 b"Access-Control-Allow-Credentials \0: hello\r\nBread: baguette\r\n\r\n",
2320 Err(Error::HeaderName)
2321 }
2322
2323 headers_err! {
2324 test_header_with_nul_in_value,
2325 b"Access-Control-Allow-Credentials: hell\0o\r\nBread: baguette\r\n\r\n",
2326 Err(Error::HeaderValue)
2327 }
2328
2329 headers_err! {
2330 test_header_with_invalid_char_in_value,
2331 b"Access-Control-Allow-Credentials: hell\x01o\r\nBread: baguette\r\n\r\n",
2332 Err(Error::HeaderValue)
2333 }
2334
2335 headers_err! {
2336 test_header_with_invalid_char_in_value_with_folding,
2337 b"Access-Control-Allow-Credentials: hell\x01o \n world!\r\nBread: baguette\r\n\r\n",
2338 Err(Error::HeaderValue)
2339 }
2340
2341 headers_err! {
2342 test_header_with_space_before_first_header,
2343 b" Space-Before-Header: hello there\r\n\r\n",
2344 Err(Error::HeaderName)
2345 }
2346
2347 res! {
2348 test_response_no_space_after_colon,
2349 b"HTTP/1.1 200 OK\r\nfoo:bar\r\n\r\n",
2350 |len, version, code, reason, headers, eof| {
2351 assert_eq!(len, 28);
2352 assert_eq!(version, 1);
2353 assert_eq!(code, 200);
2354 assert_eq!(reason, "OK");
2355 assert_eq!(headers.len(), 1);
2356 assert_eq!(headers[0].0, "foo");
2357 assert_eq!(headers[0].1, b"bar");
2358 assert!(eof);
2359 }
2360 }
2361
2362 req_err! {
2363 test_request_with_leading_space,
2364 b" GET / HTTP/1.1\r\nfoo:bar\r\n\r\n",
2365 Err(Error::Token)
2366 }
2367
2368 req_err! {
2369 test_request_with_invalid_method,
2370 b"P()ST / HTTP/1.1\r\nfoo:bar\r\n\r\n",
2371 Err(Error::Token)
2372 }
2373
2374 req! {
2375 test_utf8_in_path_ok,
2376 b"GET /test?post=I\xE2\x80\x99msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n",
2377 |len, method, path, version, headers, eof| {
2378 assert_eq!(len, 67);
2379 assert_eq!(method, "GET");
2380 assert_eq!(path, "/test?post=I’msorryIforkedyou");
2381 assert_eq!(version, 1);
2382 assert_eq!(headers.len(), 1);
2383 assert_eq!(headers[0].0, "Host");
2384 assert_eq!(headers[0].1, b"example.org");
2385 assert!(eof);
2386 }
2387 }
2388
2389 #[test]
2390 fn test_bad_utf8_in_path() {
2391 const BUF: &[u8] =
2392 b"GET /test?post=I\xE2msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n";
2393
2394 let mut req = Request::default();
2395 assert!(req.parse(BUF).unwrap().is_complete());
2396 assert!(str::from_utf8(&BUF[req.path.start..req.path.end]).is_err());
2397 }
2398
2399 #[test]
2410 fn test_path_ascii() {
2411 let long = "/a".repeat(40);
2412 let mut req = Request::default();
2413 for path in ["/", "/baseline11?a=13&b=42", long.as_str()] {
2414 let buf = format!("GET {path} HTTP/1.1\r\n");
2415 assert!(req.parse(buf.as_bytes()).unwrap().is_complete());
2416 assert!(req.path_ascii, "{path}");
2417 }
2418
2419 let long = [b"/".repeat(40), b"\xC3\xA9".to_vec(), b"/".repeat(40)].concat();
2420 for path in [&b"/\xC3\xA9"[..], b"\xFF/a?b", b"/a?b=\x80", &long] {
2421 req.path_ascii = true;
2422 let buf = [b"GET ", path, b" HTTP/1.1\r\n"].concat();
2423 assert!(req.parse(&buf).unwrap().is_complete());
2424 assert!(!req.path_ascii, "{path:?}");
2425 }
2426
2427 const BUF: &[u8] = b"GET /caf\xC3\xA9?q=1 HTTP/1.1\r\n";
2428 let mut req = Request {
2429 path_ascii: true,
2430 ..Request::default()
2431 };
2432 let mut st = State::default();
2433 assert_eq!(
2434 req.parse_with_state(&BUF[..9], &mut st),
2435 Ok(Status::Partial)
2436 );
2437 assert!(req.parse_with_state(BUF, &mut st).unwrap().is_complete());
2438 assert!(!req.path_ascii);
2439 assert_eq!(&BUF[req.path.start..req.path.end], b"/caf\xC3\xA9?q=1");
2440 }
2441
2442 #[test]
2452 fn test_request_query() {
2453 let mut targets: Vec<Vec<u8>> = vec![
2454 b"/".to_vec(),
2455 b"?".to_vec(),
2456 b"/a?".to_vec(),
2457 b"/a?b?c".to_vec(),
2458 b"/\xC3\xA9?q=1".to_vec(),
2459 b"/q?\xC3\xA9".to_vec(),
2460 b"/\xC3\xA9/no-query".to_vec(),
2461 b"/baseline11?a=13&b=42".to_vec(),
2462 ];
2463 for pos in [1, 7, 8, 15, 16, 17, 31, 32, 33, 63, 64, 65] {
2464 let mut t = vec![b'/'; 70];
2465 t[pos] = b'?';
2466 targets.push(t.clone());
2467 t[pos + 3] = b'?';
2468 targets.push(t.clone());
2469 t[pos / 2] = 0x80;
2470 targets.push(t);
2471 }
2472
2473 let mut req = Request::default();
2474 for target in &targets {
2475 let buf = [b"GET ", &target[..], b" HTTP/1.1\r\n"].concat();
2476 let query = target.iter().position(|&b| b == b'?').map(|i| i + 4);
2477 let ascii = target.is_ascii();
2478
2479 assert!(req.parse(&buf).unwrap().is_complete());
2480 assert_eq!(&buf[req.path.start..req.path.end], &target[..]);
2481 assert_eq!(req.query, query, "{target:?}");
2482 assert_eq!(req.path_ascii, ascii, "{target:?}");
2483
2484 let mut resumed = Request::default();
2485 let mut st = State::default();
2486 for n in 1..buf.len() {
2487 assert_eq!(
2488 resumed.parse_with_state(&buf[..n], &mut st),
2489 Ok(Status::Partial)
2490 );
2491 }
2492 assert!(
2493 resumed
2494 .parse_with_state(&buf, &mut st)
2495 .unwrap()
2496 .is_complete()
2497 );
2498 assert_eq!(resumed.path, req.path);
2499 assert_eq!(resumed.query, query, "resumed {target:?}");
2500 assert_eq!(resumed.path_ascii, ascii, "resumed {target:?}");
2501 }
2502 }
2503
2504 #[test]
2505 fn test_parse_uri_delimiter() {
2506 for input in [&b"/path "[..], b"/path ", b"/path HTTP/1.1\r\n"] {
2507 assert_eq!(parse_uri(input), Ok(Status::Complete("/path")));
2508 }
2509 assert_eq!(parse_uri(b"/path"), Ok(Status::Partial));
2510 }
2511
2512 #[test]
2513 fn test_parse_uri_utf8() {
2514 assert_eq!(
2515 parse_uri(b"/caf\xc3\xa9 "),
2516 Ok(Status::Complete("/caf\u{e9}"))
2517 );
2518 assert_eq!(parse_uri(b"/\xff "), Err(Error::Token));
2519 assert_eq!(parse_uri(b"/\xc3 "), Err(Error::Token));
2520 }
2521
2522 #[rustfmt::skip]
2523 res! {
2524 test_response_bench,
2525 b"\
2526HTTP/1.0 200 OK\r\n\
2527Date: Wed, 21 Oct 2015 07:28:00 GMT\r\n\
2528Set-Cookie: session=60; user_id=1\r\n\r\n",
2529 |len, version, code, reason, headers, eof| {
2530 assert_eq!(len, 91);
2531 assert_eq!(version, 0);
2532 assert_eq!(code, 200);
2533 assert_eq!(reason, "OK");
2534 assert_eq!(headers.len(), 2);
2535 assert_eq!(headers[0].0, "Date");
2536 assert_eq!(headers[0].1, b"Wed, 21 Oct 2015 07:28:00 GMT");
2537 assert_eq!(headers[1].0, "Set-Cookie");
2538 assert_eq!(headers[1].1, b"session=60; user_id=1");
2539 assert!(eof);
2540 }
2541 }
2542}