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 version: u8,
266}
267
268impl Request {
269 #[inline]
270 pub fn parse(&mut self, src: &[u8]) -> Result<usize> {
275 let mut st = State::default();
276 self.parse_with_state(src, &mut st)
277 }
278
279 #[inline]
280 pub fn parse_with_state(&mut self, src: &[u8], st: &mut State) -> Result<usize> {
290 const EMPTY_LINES: u8 = 0;
291 const METHOD: u8 = 1;
292 const SPACES_BEFORE_URI: u8 = 2;
293 const URI: u8 = 3;
294 const SPACES_BEFORE_VERSION: u8 = 4;
295 const VERSION: u8 = 5;
296 const NEWLINE: u8 = 6;
297
298 if st.state > NEWLINE || st.start > st.cursor || st.cursor > src.len() {
299 return Err(Error::Token);
300 }
301 let mut bytes = Bytes::new(src, st);
302
303 if bytes.st.state == EMPTY_LINES {
304 complete!(utils::skip_empty_lines(&mut bytes));
305 bytes.st.state = METHOD;
306 }
307 if bytes.st.state == METHOD {
308 self.method = complete!(parse_method_inner(&mut bytes));
309 bytes.st.state = SPACES_BEFORE_URI;
310 }
311 if bytes.st.state == SPACES_BEFORE_URI {
312 complete!(utils::skip_spaces(&mut bytes));
313 bytes.st.state = URI;
314 }
315 if bytes.st.state == URI {
316 self.path = complete!(parse_uri_inner(&mut bytes));
317 bytes.st.state = SPACES_BEFORE_VERSION;
318 }
319 if bytes.st.state == SPACES_BEFORE_VERSION {
320 complete!(utils::skip_spaces(&mut bytes));
321 bytes.st.state = VERSION;
322 }
323 if bytes.st.state == VERSION {
324 let mut tmp = *bytes.st;
326 self.version = complete!(version::parse_version_inner(&mut Bytes::new(src, &mut tmp)));
327 *bytes.st = tmp;
328 bytes.st.state = NEWLINE;
329 }
330
331 newline!(bytes);
332 Ok(Status::Complete(bytes.cursor()))
333 }
334}
335
336#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
353pub struct Response {
354 pub version: u8,
356 pub code: u16,
358 pub reason: SlicePos,
361}
362
363impl Response {
364 #[inline]
365 pub fn parse(&mut self, src: &[u8]) -> Result<usize> {
371 let mut st = State::default();
372 self.parse_with_state(src, &mut st)
373 }
374
375 #[inline]
376 pub fn parse_with_state(&mut self, src: &[u8], st: &mut State) -> Result<usize> {
386 const EMPTY_LINES: u8 = 0;
387 const VERSION: u8 = 1;
388 const SPACES_BEFORE_CODE: u8 = 2;
389 const CODE: u8 = 3;
390 const AFTER_CODE: u8 = 4;
391 const SPACES_BEFORE_REASON: u8 = 5;
392 const REASON: u8 = 6;
393 const REASON_OBS_TEXT: u8 = 7;
394
395 if st.state > REASON_OBS_TEXT || st.start > st.cursor || st.cursor > src.len() {
396 return Err(Error::Status);
397 }
398 let mut bytes = Bytes::new(src, st);
399
400 if bytes.st.state == EMPTY_LINES {
401 complete!(utils::skip_empty_lines(&mut bytes));
402 bytes.st.state = VERSION;
403 }
404 if bytes.st.state == VERSION {
405 let mut tmp = *bytes.st;
407 let mut b = Bytes::new(src, &mut tmp);
408 self.version = complete!(version::parse_version_inner(&mut b));
409 expect!(b.next() == b' ' => Err(Error::Version));
410 b.commit();
411 *bytes.st = tmp;
412 bytes.st.state = SPACES_BEFORE_CODE;
413 }
414 if bytes.st.state == SPACES_BEFORE_CODE {
415 complete!(utils::skip_spaces(&mut bytes));
416 bytes.st.state = CODE;
417 }
418 if bytes.st.state == CODE {
419 let mut tmp = *bytes.st;
421 self.code = complete!(parse_code(&mut Bytes::new(src, &mut tmp)));
422 *bytes.st = tmp;
423 bytes.st.state = AFTER_CODE;
424 }
425 if bytes.st.state == AFTER_CODE {
426 match next!(bytes) {
436 b' ' => bytes.st.state = SPACES_BEFORE_REASON,
437 b'\r' => {
438 expect_lf!(bytes => Err(Error::Status));
439 bytes.commit();
440 self.reason = SlicePos::default();
441 return Ok(Status::Complete(bytes.cursor()));
442 }
443 b'\n' => {
444 bytes.commit();
445 self.reason = SlicePos::default();
446 return Ok(Status::Complete(bytes.cursor()));
447 }
448 _ => return Err(Error::Status),
449 }
450 }
451 if bytes.st.state == SPACES_BEFORE_REASON {
452 complete!(utils::skip_spaces(&mut bytes));
453 bytes.st.state = REASON;
454 }
455
456 let mut seen_obs_text = bytes.st.state == REASON_OBS_TEXT;
457 let res = parse_reason(&mut bytes, &mut seen_obs_text);
458 if seen_obs_text {
459 bytes.st.state = REASON_OBS_TEXT;
460 }
461 self.reason = complete!(res);
462 Ok(Status::Complete(bytes.cursor()))
463 }
464}
465
466#[inline]
467#[doc(hidden)]
468pub fn parse_method(src: &[u8]) -> Result<&str> {
471 let mut st = State::default();
472 let mut bytes = Bytes::new(src, &mut st);
473 complete!(utils::skip_empty_lines(&mut bytes));
474 let s = complete!(parse_method_inner(&mut bytes));
475 let m = unsafe { str::from_utf8_unchecked(&src[s.start..s.end]) };
477 Ok(Status::Complete(m))
478}
479
480#[inline]
483fn parse_method_inner(bytes: &mut Bytes<'_, '_>) -> Result<SlicePos> {
484 const GET: [u8; 4] = *b"GET ";
485 const POST: [u8; 4] = *b"POST";
486
487 if bytes.cursor() == bytes.start() {
488 match bytes.peek_n::<4>() {
489 Some(GET) => {
490 bytes.advance(4);
492 return Ok(Status::Complete(bytes.slice_position(1)));
493 }
494 Some(POST) if bytes.peek_ahead(4) == Some(b' ') => {
495 bytes.advance(5);
497 return Ok(Status::Complete(bytes.slice_position(1)));
498 }
499 _ => {}
500 }
501 if !utils::is_method_token(next!(bytes)) {
504 return Err(Error::Token);
505 }
506 }
507
508 loop {
509 let b = next!(bytes);
510 if b == b' ' {
511 return Ok(Status::Complete(bytes.slice_position(1)));
513 } else if !utils::is_method_token(b) {
514 return Err(Error::Token);
515 }
516 }
517}
518
519#[inline]
536fn parse_reason(bytes: &mut Bytes<'_, '_>, seen_obs_text: &mut bool) -> Result<SlicePos> {
537 const SCALAR_PREFIX: usize = 8;
540 let mut n = 0;
541 while let Some(b) = bytes.peek() {
542 if n == SCALAR_PREFIX {
543 match_reason_vectored(bytes, seen_obs_text);
544 break;
545 }
546 if b < 0x80 && utils::is_header_value_token(b) {
547 bytes.advance(1);
548 n += 1;
549 } else {
550 break;
551 }
552 }
553
554 loop {
555 let b = next!(bytes);
556 let skip = if b == b'\r' {
557 expect_lf!(bytes => Err(Error::Status));
558 2
559 } else if b == b'\n' {
560 1
561 } else if !(b == 0x09 || b == b' ' || (0x21..=0x7E).contains(&b) || b >= 0x80) {
562 return Err(Error::Status);
563 } else {
564 if b >= 0x80 {
565 *seen_obs_text = true;
566 }
567 continue;
568 };
569
570 return Ok(Status::Complete(if *seen_obs_text {
573 bytes.commit();
574 SlicePos::default()
575 } else {
576 bytes.slice_position(skip)
577 }));
578 }
579}
580
581#[inline(never)]
582fn match_reason_vectored(bytes: &mut Bytes<'_, '_>, seen_obs_text: &mut bool) {
583 let from = bytes.cursor();
584 simd::match_header_value_vectored(bytes);
585 if !*seen_obs_text && !bytes.since(from).is_ascii() {
586 *seen_obs_text = true;
587 }
588}
589
590#[inline]
591#[doc(hidden)]
592pub fn parse_uri(src: &[u8]) -> Result<&str> {
598 let mut st = State::default();
599 let mut bytes = Bytes::new(src, &mut st);
600 if let Status::Complete(pos) = parse_uri_inner(&mut bytes)? {
601 complete!(utils::skip_spaces(&mut bytes));
602 if let Ok(path) = simdutf8::basic::from_utf8(&src[pos.start..pos.end]) {
603 Ok(Status::Complete(path))
604 } else {
605 Err(Error::Token)
606 }
607 } else {
608 Ok(Status::Partial)
609 }
610}
611
612#[inline]
613fn parse_uri_inner(bytes: &mut Bytes<'_, '_>) -> Result<SlicePos> {
614 let start = bytes.start();
615 simd::match_uri_vectored(bytes);
616 let b_end = bytes.cursor();
617
618 if next!(bytes) == b' ' {
619 if start == b_end {
621 return Err(Error::Token);
622 }
623
624 let end = bytes.cursor() - 1;
626 bytes.commit();
627 Ok(Status::Complete(SlicePos { start, end }))
628 } else {
629 Err(Error::Token)
630 }
631}
632
633#[inline]
634fn parse_code(bytes: &mut Bytes<'_, '_>) -> Result<u16> {
635 let hundreds = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
636 let tens = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
637 let ones = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
638
639 Ok(Status::Complete(
640 (hundreds - b'0') as u16 * 100 + (tens - b'0') as u16 * 10 + (ones - b'0') as u16,
641 ))
642}
643
644pub fn parse_chunk_size(buf: &[u8]) -> result::Result<Status<(usize, u64)>, InvalidChunkSize> {
662 const RADIX: u64 = 16;
663 let mut st = State::default();
664 let mut bytes = Bytes::new(buf, &mut st);
665 let mut size = 0;
666 let mut in_chunk_size = true;
667 let mut in_ext = false;
668 let mut count = 0;
669 loop {
670 let b = next!(bytes);
671 match b {
672 b'0'..=b'9' if in_chunk_size => {
673 if count > 15 {
674 return Err(InvalidChunkSize);
675 }
676 count += 1;
677 if cfg!(debug_assertions) && size > (u64::MAX / RADIX) {
678 return Err(InvalidChunkSize);
683 }
684 size *= RADIX;
685 size += (b - b'0') as u64;
686 }
687 b'a'..=b'f' | b'A'..=b'F' if in_chunk_size => {
688 if count > 15 {
689 return Err(InvalidChunkSize);
690 }
691 count += 1;
692 if cfg!(debug_assertions) && size > (u64::MAX / RADIX) {
693 return Err(InvalidChunkSize);
694 }
695 size *= RADIX;
696 size += ((b | 0x20) + 10 - b'a') as u64;
697 }
698 b'\r' if count == 0 => return Err(InvalidChunkSize),
700 b'\r' => match next!(bytes) {
701 b'\n' => break,
702 _ => return Err(InvalidChunkSize),
703 },
704 b';' if !in_ext => {
706 in_ext = true;
707 in_chunk_size = false;
708 }
709 b'\t' | b' ' if !in_ext && !in_chunk_size => {}
712 b'\t' | b' ' if in_chunk_size => in_chunk_size = false,
714 0x00..=0x08 | 0x0a..=0x1f | 0x7f if in_ext => return Err(InvalidChunkSize),
717 _ if in_ext => {}
723 _ => return Err(InvalidChunkSize),
726 }
727 }
728 Ok(Status::Complete((bytes.cursor(), size)))
729}
730
731#[cfg(test)]
732mod tests {
733 #![allow(clippy::items_after_statements)]
734 use super::*;
735
736 macro_rules! req {
737 ($name:ident, $buf:expr, |$len:ident, $method:ident, $path:ident, $version:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
738 #[test]
739 fn $name() {
740 let mut req = Request::default();
741 let mut b = $buf.as_ref();
742 if let Ok(Status::Complete(l)) = req.parse(b) {
743 let mut consumed = l;
744 let mut headers = Vec::new();
745 let mut header = Header::default();
746 let mut headers_eof = false;
747 b = &b[consumed..];
748
749 while let Status::Complete(hdr) = header.parse(b).unwrap() {
750 match hdr {
751 HeaderParsed::Header(l) => {
752 consumed += l;
753 let name = String::from_utf8(Vec::from(
754 &b[header.name.start..header.name.end],
755 ))
756 .unwrap();
757 let value = Vec::from(&b[header.value.start..header.value.end]);
758 headers.push((name, value));
759 b = &b[l..];
760 }
761 HeaderParsed::Eof(l) => {
762 consumed += l;
763 headers_eof = true;
764 break;
765 }
766 }
767 }
768
769 let (path, method) = unsafe {
771 (
772 str::from_utf8_unchecked(&$buf.as_ref()[req.path.start..req.path.end]),
773 str::from_utf8_unchecked(
774 &$buf.as_ref()[req.method.start..req.method.end],
775 ),
776 )
777 };
778
779 closure(consumed, method, path, req.version, headers, headers_eof);
780 } else {
781 panic!()
782 }
783
784 fn closure(
785 $len: usize,
786 $method: &str,
787 $path: &str,
788 $version: u8,
789 $headers: Vec<(String, Vec<u8>)>,
790 $headers_eof: bool,
791 ) {
792 $body
793 }
794 }
795 };
796 }
797
798 macro_rules! headers {
799 ($name:ident, $buf:expr, |$len:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
800 #[test]
801 fn $name() {
802 let mut b = $buf.as_ref();
803 let mut consumed = 0;
804 let mut headers = Vec::new();
805 let mut header = Header::default();
806 let mut headers_eof = false;
807
808 while let Status::Complete(hdr) = header.parse(b).unwrap() {
809 match hdr {
810 HeaderParsed::Header(l) => {
811 consumed += l;
812 let name = String::from_utf8(Vec::from(
813 &b[header.name.start..header.name.end],
814 ))
815 .unwrap();
816 let value = Vec::from(&b[header.value.start..header.value.end]);
817 headers.push((name, value));
818 b = &b[l..];
819 }
820 HeaderParsed::Eof(l) => {
821 consumed += l;
822 headers_eof = true;
823 break;
824 }
825 }
826 }
827 closure(consumed, headers, headers_eof);
828
829 fn closure($len: usize, $headers: Vec<(String, Vec<u8>)>, $headers_eof: bool) {
830 $body
831 }
832 }
833 };
834 }
835
836 macro_rules! req_err {
837 ($name:ident, $buf:expr, $err:expr) => {
838 #[test]
839 fn $name() {
840 assert_eq!(Request::default().parse($buf.as_ref()), $err);
841 }
842 };
843 }
844
845 macro_rules! req_par {
846 ($name:ident, $buf:expr) => {
847 #[test]
848 fn $name() {
849 assert_eq!(Request::default().parse($buf.as_ref()), Ok(Status::Partial));
850 }
851 };
852 }
853
854 macro_rules! headers_err {
855 ($name:ident, $buf:expr, $err:expr) => {
856 #[test]
857 fn $name() {
858 let mut consumed = 0;
859 let mut header = Header::default();
860
861 let result = loop {
862 match header.parse(&$buf.as_ref()[consumed..]) {
863 Ok(Status::Complete(HeaderParsed::Header(l))) => {
864 consumed += l;
865 }
866 Ok(_) => break Ok(()),
867 Err(e) => break Err(e),
868 }
869 };
870 assert_eq!(result, $err);
871 }
872 };
873 }
874
875 req! {
876 test_request_simple,
877 b"GET / HTTP/1.1\r\n\r\n",
878 |len, method, path, version, headers, eof| {
879 assert_eq!(len, 18);
880 assert_eq!(method, "GET");
881 assert_eq!(path, "/");
882 assert_eq!(version, 1);
883 assert_eq!(headers.len(), 0);
884 assert!(eof);
885 }
886 }
887
888 req! {
889 test_request_simple_with_query_params,
890 b"GET /thing?data=a HTTP/1.1\r\n\r\n",
891 |len, method, path, version, headers, eof| {
892 assert_eq!(len, 30);
893 assert_eq!(method, "GET");
894 assert_eq!(path, "/thing?data=a");
895 assert_eq!(version, 1);
896 assert_eq!(headers.len(), 0);
897 assert!(eof);
898 }
899 }
900
901 req! {
902 test_request_simple_with_whatwg_query_params,
903 b"GET /thing?data=a^ HTTP/1.1\r\n\r\n",
904 |len, method, path, version, headers, eof| {
905 assert_eq!(len, 31);
906 assert_eq!(method, "GET");
907 assert_eq!(path, "/thing?data=a^");
908 assert_eq!(version, 1);
909 assert_eq!(headers.len(), 0);
910 assert!(eof);
911 }
912 }
913
914 req! {
915 test_request_headers,
916 b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \r\n\r\n ",
917 |len, method, path, version, headers, eof| {
918 assert_eq!(len, 43);
919 assert_eq!(method, "GET");
920 assert_eq!(path, "/");
921 assert_eq!(version, 1);
922 assert_eq!(headers.len(), 2);
923 assert_eq!(headers[0].0, "Host");
924 assert_eq!(headers[0].1, b"foo.com");
925 assert_eq!(headers[1].0, "Cookie");
926 assert_eq!(headers[1].1, b"");
927 assert!(eof);
928 }
929 }
930
931 req! {
932 test_request_headers_optional_whitespace,
933 b"GET / HTTP/1.1\r\nHost: \tfoo.com\t \r\nCookie: \t \r\n\r\n",
934 |len, method, path, version, headers, eof| {
935 assert_eq!(len, 48);
936 assert_eq!(method, "GET");
937 assert_eq!(path, "/");
938 assert_eq!(version, 1);
939 assert_eq!(headers.len(), 2);
940 assert_eq!(headers[0].0, "Host");
941 assert_eq!(headers[0].1, b"foo.com");
942 assert_eq!(headers[1].0, "Cookie");
943 assert_eq!(headers[1].1, b"");
944 assert!(eof);
945 }
946 }
947
948 req! {
949 test_request_header_value_htab_short,
951 b"GET / HTTP/1.1\r\nUser-Agent: some\tagent\r\n\r\n",
952 |len, method, path, version, headers, eof| {
953 assert_eq!(len, 42);
954 assert_eq!(method, "GET");
955 assert_eq!(path, "/");
956 assert_eq!(version, 1);
957 assert_eq!(headers.len(), 1);
958 assert_eq!(headers[0].0, "User-Agent");
959 assert_eq!(headers[0].1, b"some\tagent");
960 assert!(eof);
961 }
962 }
963
964 req! {
965 test_request_header_value_htab_med,
967 b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\tagent\r\n\r\n",
968 |len, method, path, version, headers, eof| {
969 assert_eq!(len, 52);
970 assert_eq!(method, "GET");
971 assert_eq!(path, "/");
972 assert_eq!(version, 1);
973 assert_eq!(headers.len(), 1);
974 assert_eq!(headers[0].0, "User-Agent");
975 assert_eq!(headers[0].1, b"1234567890some\tagent");
976 assert!(eof);
977 }
978 }
979
980 req! {
981 test_request_header_value_htab_long,
983 b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\t1234567890agent1234567890\r\n\r\n",
984 |len, method, path, version, headers, eof| {
985 assert_eq!(len, 72);
986 assert_eq!(method, "GET");
987 assert_eq!(path, "/");
988 assert_eq!(version, 1);
989 assert_eq!(headers.len(), 1);
990 assert_eq!(headers[0].0, "User-Agent");
991 assert_eq!(headers[0].1, &b"1234567890some\t1234567890agent1234567890"[..]);
992 assert!(eof);
993 }
994 }
995
996 req! {
997 test_request_header_no_space_after_colon,
999 b"GET / HTTP/1.1\r\nUser-Agent:omg-no-space1234567890some1234567890agent1234567890\r\n\r\n",
1000 |len, method, path, version, headers, eof| {
1001 assert_eq!(len, 82);
1002 assert_eq!(method, "GET");
1003 assert_eq!(path, "/");
1004 assert_eq!(version, 1);
1005 assert_eq!(headers.len(), 1);
1006 assert_eq!(headers[0].0, "User-Agent");
1007 assert_eq!(headers[0].1, &b"omg-no-space1234567890some1234567890agent1234567890"[..]);
1008 assert!(eof);
1009 }
1010 }
1011
1012 req! {
1013 test_request_headers_max,
1014 b"GET / HTTP/1.1\r\nA: A\r\nB: B\r\nC: C\r\nD: D\r\n\r\n",
1015 |_len, _method, _path, _verion, headers, eof| {
1016 assert_eq!(headers.len(), 4);
1017 assert!(eof);
1018 }
1019 }
1020
1021 req! {
1022 test_request_multibyte,
1023 b"GET / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: \xe3\x81\xb2\xe3/1.0\r\n\r\n",
1024 |len, method, path, version, headers, eof| {
1025 assert_eq!(len, 55);
1026 assert_eq!(method, "GET");
1027 assert_eq!(path, "/");
1028 assert_eq!(version, 1);
1029 assert_eq!(headers.len(), 2);
1030 assert_eq!(headers[0].0, "Host");
1031 assert_eq!(headers[0].1, b"foo.com");
1032 assert_eq!(headers[1].0, "User-Agent");
1033 assert_eq!(headers[1].1, b"\xe3\x81\xb2\xe3/1.0");
1034 assert!(eof);
1035 }
1036 }
1037
1038 req_par! {
1040 test_request_one_byte_method,
1041 b"G"
1042 }
1043
1044 req_par! {
1046 test_request_partial_method,
1047 b"GE"
1048 }
1049
1050 req_par! {
1052 test_request_method_no_delimiter,
1053 b"GET"
1054 }
1055
1056 req_par! {
1059 test_request_method_only,
1060 b"GET "
1061 }
1062
1063 req! {
1064 test_request_partial,
1065 b"GET / HTTP/1.1\r\n\r",
1066 |len, method, path, version, headers, eof| {
1067 assert_eq!(len, b"GET / HTTP/1.1\r\n\r".len() - 1);
1068 assert_eq!(method, "GET");
1069 assert_eq!(path, "/");
1070 assert_eq!(version, 1);
1071 assert_eq!(headers.len(), 0);
1072 assert!(!eof);
1073 }
1074 }
1075
1076 req_par! {
1077 test_request_partial_version,
1078 b"GET / HTTP/1."
1079 }
1080
1081 req_par! {
1082 test_request_method_path_no_delimiter,
1083 b"GET /"
1084 }
1085
1086 req_par! {
1087 test_request_method_path_only,
1088 b"GET / "
1089 }
1090
1091 req! {
1092 test_request_partial_parses_headers_as_much_as_it_can,
1093 b"GET / HTTP/1.1\r\nHost: yolo\r\n",
1094 |len, method, path, version, headers, eof| {
1095 assert_eq!(len, 28);
1096 assert_eq!(method, "GET");
1097 assert_eq!(path, "/");
1098 assert_eq!(version, 1);
1099 assert_eq!(headers.len(), 1);
1100 assert_eq!(headers[0].0, "Host");
1101 assert_eq!(headers[0].1, b"yolo");
1102 assert!(!eof);
1103 }
1104 }
1105
1106 req! {
1107 test_request_newlines,
1108 b"GET / HTTP/1.1\nHost: foo.bar\n\n",
1109 |_len, _method, _path, _verion, _headers, eof| {
1110 assert!(eof);
1111 }
1112 }
1113
1114 req! {
1115 test_request_empty_lines_prefix,
1116 b"\r\n\r\nGET / HTTP/1.1\r\n\r\n",
1117 |len, method, path, version, headers, eof| {
1118 assert_eq!(len, 22);
1119 assert_eq!(method, "GET");
1120 assert_eq!(path, "/");
1121 assert_eq!(version, 1);
1122 assert_eq!(headers.len(), 0);
1123 assert!(eof);
1124 }
1125 }
1126
1127 req! {
1128 test_request_empty_lines_prefix_lf_only,
1129 b"\n\nGET / HTTP/1.1\n\n",
1130 |len, method, path, version, headers, eof| {
1131 assert_eq!(len, 18);
1132 assert_eq!(method, "GET");
1133 assert_eq!(path, "/");
1134 assert_eq!(version, 1);
1135 assert_eq!(headers.len(), 0);
1136 assert!(eof);
1137 }
1138 }
1139
1140 req! {
1141 test_request_path_backslash,
1142 b"\n\nGET /\\?wayne\\=5 HTTP/1.1\n\n",
1143 |len, method, path, version, headers, eof| {
1144 assert_eq!(len, 28);
1145 assert_eq!(method, "GET");
1146 assert_eq!(path, "/\\?wayne\\=5");
1147 assert_eq!(version, 1);
1148 assert_eq!(headers.len(), 0);
1149 assert!(eof);
1150 }
1151 }
1152
1153 req_err! {
1154 test_request_with_invalid_token_delimiter,
1155 b"GET\n/ HTTP/1.1\r\nHost: foo.bar\r\n\r\n",
1156 Err(Error::Token)
1157 }
1158
1159 req_err! {
1160 test_request_with_invalid_but_short_version,
1161 b"GET / HTTP/1!",
1162 Err(Error::Version)
1163 }
1164
1165 req_err! {
1166 test_request_with_empty_method,
1167 b" / HTTP/1.1\r\n\r\n",
1168 Err(Error::Token)
1169 }
1170
1171 req_err! {
1172 test_request_with_empty_path,
1173 b"GET HTTP/1.1\r\n\r\n",
1174 Err(Error::Token)
1175 }
1176
1177 req_err! {
1178 test_request_with_empty_method_and_path,
1179 b" HTTP/1.1\r\n\r\n",
1180 Err(Error::Token)
1181 }
1182
1183 headers! {
1184 test_headers_optional_whitespace,
1185 b"Host: \tfoo.com\t \r\nCookie: \t \r\n",
1186 |len, headers, eof| {
1187 assert_eq!(len, 30);
1188 assert_eq!(headers.len(), 2);
1189 assert_eq!(headers[0].0, "Host");
1190 assert_eq!(headers[0].1, b"foo.com");
1191 assert_eq!(headers[1].0, "Cookie");
1192 assert_eq!(headers[1].1, b"");
1193 assert!(!eof);
1194 }
1195 }
1196
1197 #[test]
1198 fn test_headers_with_state() {
1199 const B1: &[u8] = b"Host";
1200 const B2: &[u8] = b"Host: \t";
1201 const B3: &[u8] = b"Host: \tfoo.com\t ";
1202 const B4: &[u8] = b"Host: \tfoo.com\t \r\nCoo";
1203 const B5: &[u8] = b"Host: \tfoo.com\t \r\nCookie: \t \r\n\r\n";
1204
1205 let mut st = State::default();
1206 let mut header = Header::default();
1207 assert!(header.parse_with_state(B1, &mut st).unwrap().is_partial());
1208 assert_eq!(st.state, 1);
1209 assert_eq!(st.start, 0);
1210 assert_eq!(st.cursor, 4);
1211 assert_eq!(header.name, SlicePos { start: 0, end: 0 });
1212 assert!(header.parse_with_state(B2, &mut st).unwrap().is_partial());
1213 assert_eq!(st.state, 2);
1214 assert_eq!(st.start, 0);
1215 assert_eq!(st.cursor, 7);
1216 assert_eq!(header.name, SlicePos { start: 0, end: 4 });
1217 assert_eq!(&B5[header.name.start..header.name.end], b"Host");
1218
1219 assert!(header.parse_with_state(B3, &mut st).unwrap().is_partial());
1220 assert_eq!(st.state, 3);
1221 assert_eq!(st.start, 0);
1222 assert_eq!(st.cursor, 16);
1223 assert_eq!(header.name, SlicePos { start: 0, end: 4 });
1224 assert_eq!(header.value, SlicePos { start: 7, end: 0 });
1225
1226 assert!(header.parse_with_state(B4, &mut st).unwrap().is_complete());
1227 assert_eq!(st.state, 0);
1228 assert_eq!(st.start, 18);
1229 assert_eq!(st.cursor, 18);
1230 assert_eq!(header.value, SlicePos { start: 7, end: 14 });
1231 assert_eq!(&B5[header.value.start..header.value.end], b"foo.com");
1232
1233 assert!(header.parse_with_state(B5, &mut st).unwrap().is_complete());
1234 assert_eq!(st.state, 0);
1235 assert_eq!(st.start, 30);
1236 assert_eq!(st.cursor, 30);
1237 assert_eq!(header.name, SlicePos { start: 18, end: 24 });
1238 assert_eq!(header.value, SlicePos { start: 0, end: 0 });
1239 assert_eq!(&B5[header.name.start..header.name.end], b"Cookie");
1240 assert_eq!(&B5[header.value.start..header.value.end], b"");
1241
1242 assert_eq!(
1243 header.parse_with_state(B5, &mut st),
1244 Ok(Status::Complete(HeaderParsed::Eof(32)))
1245 );
1246 assert_eq!(st.state, 0);
1247 assert_eq!(st.start, 32);
1248 assert_eq!(st.cursor, 32);
1249 }
1250
1251 headers_err! {
1252 test_headers_with_obsolete_line_folding_at_start,
1253 b"Line-Folded-Header: \r\n \r\n hello there\r\n\r\n",
1254 Err(Error::HeaderName)
1255 }
1256
1257 headers_err! {
1258 test_header_with_invalid_name,
1259 b"Host : foo.bar\r\n\r\n",
1260 Err(Error::HeaderName)
1261 }
1262
1263 macro_rules! res {
1264 ($name:ident, $buf:expr, |$len:ident, $version:ident, $code:ident, $reason:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
1265 #[test]
1266 fn $name() {
1267 let mut b = $buf.as_ref();
1268 let mut res = Response::default();
1269 let mut consumed = res.parse($buf.as_ref()).unwrap().unwrap();
1270 let mut headers = Vec::new();
1271 let mut header = Header::default();
1272 let mut headers_eof = false;
1273 b = &b[consumed..];
1274
1275 while let Status::Complete(hdr) = header.parse(b).unwrap() {
1276 match hdr {
1277 HeaderParsed::Header(l) => {
1278 consumed += l;
1279 let name = String::from_utf8(Vec::from(
1280 &b[header.name.start..header.name.end],
1281 ))
1282 .unwrap();
1283 let value = Vec::from(&b[header.value.start..header.value.end]);
1284 headers.push((name, value));
1285 b = &b[l..];
1286 }
1287 HeaderParsed::Eof(l) => {
1288 consumed += l;
1289 headers_eof = true;
1290 break;
1291 }
1292 }
1293 }
1294
1295 let reason = unsafe {
1297 str::from_utf8_unchecked(&$buf.as_ref()[res.reason.start..res.reason.end])
1298 };
1299
1300 closure(
1301 consumed,
1302 res.version,
1303 res.code,
1304 reason,
1305 headers,
1306 headers_eof,
1307 );
1308
1309 fn closure(
1310 $len: usize,
1311 $version: u8,
1312 $code: u16,
1313 $reason: &str,
1314 $headers: Vec<(String, Vec<u8>)>,
1315 $headers_eof: bool,
1316 ) {
1317 $body
1318 }
1319 }
1320 };
1321 }
1322
1323 macro_rules! res_err {
1324 ($name:ident, $buf:expr, $err:expr) => {
1325 #[test]
1326 fn $name() {
1327 assert_eq!(Response::default().parse($buf.as_ref()), $err);
1328 }
1329 };
1330 }
1331
1332 macro_rules! res_par {
1333 ($name:ident, $buf:expr) => {
1334 #[test]
1335 fn $name() {
1336 assert_eq!(
1337 Response::default().parse($buf.as_ref()),
1338 Ok(Status::Partial)
1339 );
1340 }
1341 };
1342 }
1343
1344 res_err! {
1345 test_response_newline_after_version,
1346 b"HTTP/1.1\r\n\r\n 200 OK\r\n\r\n",
1347 Err(Error::Version)
1348 }
1349
1350 res_err! {
1351 test_response_bare_newline_after_version,
1352 b"HTTP/1.1\n 200 OK\r\n\r\n",
1353 Err(Error::Version)
1354 }
1355
1356 res! {
1357 test_response_simple,
1358 b"HTTP/1.1 200 OK\r\n\r\n",
1359 |len, version, code, reason, headers, eof| {
1360 assert_eq!(len, 19);
1361 assert_eq!(version, 1);
1362 assert_eq!(code, 200);
1363 assert_eq!(reason, "OK");
1364 assert_eq!(headers.len(), 0);
1365 assert!(eof);
1366 }
1367 }
1368
1369 res! {
1370 test_response_newlines,
1371 b"HTTP/1.0 403 Forbidden\nServer: foo.bar\n\n",
1372 |len, version, code, reason, headers, eof| {
1373 assert_eq!(len, 40);
1374 assert_eq!(version, 0);
1375 assert_eq!(code, 403);
1376 assert_eq!(reason, "Forbidden");
1377 assert_eq!(headers.len(), 1);
1378 assert_eq!(headers[0].0, "Server");
1379 assert_eq!(headers[0].1, b"foo.bar");
1380 assert!(eof);
1381 }
1382 }
1383
1384 res! {
1385 test_response_reason_missing,
1386 b"HTTP/1.1 200 \r\n\r\n",
1387 |len, version, code, reason, headers, eof| {
1388 assert_eq!(len, 17);
1389 assert_eq!(version, 1);
1390 assert_eq!(code, 200);
1391 assert_eq!(reason, "");
1392 assert_eq!(headers.len(), 0);
1393 assert!(eof);
1394 }
1395 }
1396
1397 res! {
1398 test_response_reason_missing_no_space,
1399 b"HTTP/1.1 200\r\n\r\n",
1400 |len, version, code, reason, headers, eof| {
1401 assert_eq!(len, 16);
1402 assert_eq!(version, 1);
1403 assert_eq!(code, 200);
1404 assert_eq!(reason, "");
1405 assert_eq!(headers.len(), 0);
1406 assert!(eof);
1407 }
1408 }
1409
1410 res! {
1411 test_response_reason_missing_no_space_with_headers,
1412 b"HTTP/1.1 200\r\nFoo: bar\r\n\r\n",
1413 |len, version, code, reason, headers, eof| {
1414 assert_eq!(len, 26);
1415 assert_eq!(version, 1);
1416 assert_eq!(code, 200);
1417 assert_eq!(reason, "");
1418 assert_eq!(headers.len(), 1);
1419 assert_eq!(headers[0].0, "Foo");
1420 assert_eq!(headers[0].1, b"bar");
1421 assert!(eof);
1422 }
1423 }
1424
1425 res! {
1426 test_response_reason_with_space_and_tab,
1427 b"HTTP/1.1 101 Switching Protocols\t\r\n\r\n",
1428 |len, version, code, reason, headers, eof| {
1429 assert_eq!(len, 37);
1430 assert_eq!(version, 1);
1431 assert_eq!(code, 101);
1432 assert_eq!(reason, "Switching Protocols\t");
1433 assert_eq!(headers.len(), 0);
1434 assert!(eof);
1435 }
1436 }
1437
1438 res! {
1439 test_response_reason_with_obsolete_text_byte,
1440 b"HTTP/1.1 200 X\xFFZ\r\n\r\n",
1441 |len, version, code, reason, headers, eof| {
1442 assert_eq!(len, 20);
1443 assert_eq!(version, 1);
1444 assert_eq!(code, 200);
1445 assert_eq!(reason, "");
1447 assert_eq!(headers.len(), 0);
1448 assert!(eof);
1449 }
1450 }
1451
1452 res_err! {
1453 test_response_reason_with_nul_byte,
1454 b"HTTP/1.1 200 \x00\r\n\r\n",
1455 Err(crate::Error::Status)
1456 }
1457
1458 #[test]
1459 fn test_response_reason_bytes_at_every_position() {
1460 const PREFIX: &[u8] = b"HTTP/1.1 200 ";
1461 for len in 1..80 {
1462 for pos in 0..len {
1463 for b in [b'\t', b' ', b'~', 0x00, 0x0b, 0x7f, 0x80, 0xff] {
1464 let mut reason = vec![b'a'; len];
1465 reason[pos] = b;
1466 let buf = [PREFIX, &reason, b"\r\n"].concat();
1467 let valid = b == b'\t' || b == b' ' || b == b'~' || b >= 0x80;
1468
1469 for split in PREFIX.len()..=buf.len() {
1471 let mut resp = Response::default();
1472 let mut st = State::default();
1473 let first = resp.parse_with_state(&buf[..split], &mut st);
1474 let res = if split == buf.len() {
1475 first
1476 } else if first.is_err() {
1477 assert!(!valid, "len {len} pos {pos} byte {b:#x} split {split}");
1478 continue;
1479 } else {
1480 assert_eq!(first, Ok(Status::Partial));
1481 resp.parse_with_state(&buf, &mut st)
1482 };
1483 let ctx = format!("len {len} pos {pos} byte {b:#x} split {split}");
1484 if !valid {
1485 assert_eq!(res, Err(crate::Error::Status), "{ctx}");
1486 } else if b >= 0x80 {
1487 assert_eq!(res, Ok(Status::Complete(buf.len())), "{ctx}");
1488 assert_eq!(resp.reason, SlicePos::default(), "{ctx}");
1489 } else {
1490 assert_eq!(res, Ok(Status::Complete(buf.len())), "{ctx}");
1491 let start = usize::from(pos == 0 && b == b' ');
1493 assert_eq!(
1494 &buf[resp.reason.start..resp.reason.end],
1495 &reason[start..],
1496 "{ctx}"
1497 );
1498 }
1499 }
1500 }
1501 }
1502 }
1503 }
1504
1505 res_par! {
1506 test_response_version_missing_space,
1507 b"HTTP/1.1"
1508 }
1509
1510 res_par! {
1511 test_response_code_missing_space,
1512 b"HTTP/1.1 200"
1513 }
1514
1515 res! {
1516 test_response_partial_parses_headers_as_much_as_it_can,
1517 b"HTTP/1.1 200 OK\r\nServer: yolo\r\n",
1518 |len, version, code, reason, headers, eof| {
1519 assert_eq!(len, 31);
1520 assert_eq!(version, 1);
1521 assert_eq!(code, 200);
1522 assert_eq!(reason, "OK");
1523 assert_eq!(headers.len(), 1);
1524 assert_eq!(headers[0].0, "Server");
1525 assert_eq!(headers[0].1, b"yolo");
1526 assert!(!eof);
1527 }
1528 }
1529
1530 res! {
1531 test_response_empty_lines_prefix_lf_only,
1532 b"\n\nHTTP/1.1 200 OK\n\n",
1533 |len, version, code, reason, headers, eof| {
1534 assert_eq!(len, 19);
1535 assert_eq!(version, 1);
1536 assert_eq!(code, 200);
1537 assert_eq!(reason, "OK");
1538 assert_eq!(headers.len(), 0);
1539 assert!(eof);
1540 }
1541 }
1542
1543 res! {
1544 test_response_no_cr,
1545 b"HTTP/1.0 200\nContent-type: text/html\n\n",
1546 |len, version, code, reason, headers, eof| {
1547 assert_eq!(len, 38);
1548 assert_eq!(version, 0);
1549 assert_eq!(code, 200);
1550 assert_eq!(reason, "");
1551 assert_eq!(headers.len(), 1);
1552 assert_eq!(headers[0].0, "Content-type");
1553 assert_eq!(headers[0].1, b"text/html");
1554 assert!(eof);
1555 }
1556 }
1557
1558 #[test]
1560 fn partial_permutations() {
1561 let req_str = "GET / HTTP/1.1\r\n";
1562 let mut req = Request::default();
1563 for i in 0..req_str.len() {
1564 let status = req.parse(&req_str.as_bytes()[..i]);
1565 assert_eq!(
1566 status,
1567 Ok(Status::Partial),
1568 "partial request line should return partial. \
1569 Portion which failed: '{seg}' (below {i})",
1570 seg = &req_str[..i]
1571 );
1572 }
1573 }
1574
1575 headers_err! {
1576 test_forbid_headers_with_whitespace_between_header_name_and_colon,
1577 b"Access-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n",
1578 Err(Error::HeaderName)
1579 }
1580
1581 headers_err! {
1582 test_forbid_headers_with_obsolete_line_folding_at_end,
1583 b"Line-Folded-Header: hello there\r\n \r\n \r\n\r\n",
1584 Err(Error::HeaderName)
1585 }
1586
1587 headers_err! {
1588 test_forbid_headers_with_obsolete_line_folding_in_middle,
1589 b"Line-Folded-Header: hello \r\n \r\n there\r\n\r\n",
1590 Err(Error::HeaderName)
1591 }
1592
1593 headers_err! {
1594 test_forbid_headers_with_obsolete_line_folding_in_empty_header,
1595 b"Line-Folded-Header: \r\n \r\n \r\n\r\n",
1596 Err(Error::HeaderName)
1597 }
1598
1599 headers_err! {
1600 test_forbid_headers_with_empty_header_name,
1601 b": hello\r\nBread: baguette\r\n\r\n",
1602 Err(Error::HeaderName)
1603 }
1604
1605 headers_err! {
1606 test_forbid_headers_with_empty_header_name_second,
1607 b"Bread: baguette\r\n: hello\r\n\r\n",
1608 Err(Error::HeaderName)
1609 }
1610
1611 #[test]
1612 fn test_chunk_size() {
1613 assert_eq!(parse_chunk_size(b"0\r\n"), Ok(Status::Complete((3, 0))));
1614 assert_eq!(
1615 parse_chunk_size(b"12\r\nchunk"),
1616 Ok(Status::Complete((4, 18)))
1617 );
1618 assert_eq!(
1619 parse_chunk_size(b"3086d\r\n"),
1620 Ok(Status::Complete((7, 198_765)))
1621 );
1622 assert_eq!(
1623 parse_chunk_size(b"3735AB1;foo bar*\r\n"),
1624 Ok(Status::Complete((18, 57_891_505)))
1625 );
1626 assert_eq!(
1627 parse_chunk_size(b"3735ab1 ; baz \r\n"),
1628 Ok(Status::Complete((16, 57_891_505)))
1629 );
1630 assert_eq!(parse_chunk_size(b"77a65\r"), Ok(Status::Partial));
1631 assert_eq!(parse_chunk_size(b"ab"), Ok(Status::Partial));
1632 assert_eq!(
1633 parse_chunk_size(b"567f8a\rfoo"),
1634 Err(crate::InvalidChunkSize)
1635 );
1636 assert_eq!(
1637 parse_chunk_size(b"567f8a\rfoo"),
1638 Err(crate::InvalidChunkSize)
1639 );
1640 assert_eq!(
1641 parse_chunk_size(b"567xf8a\r\n"),
1642 Err(crate::InvalidChunkSize)
1643 );
1644 assert_eq!(
1645 parse_chunk_size(b"ffffffffffffffff\r\n"),
1646 Ok(Status::Complete((18, u64::MAX)))
1647 );
1648 assert_eq!(
1649 parse_chunk_size(b"1ffffffffffffffff\r\n"),
1650 Err(crate::InvalidChunkSize)
1651 );
1652 assert_eq!(
1653 parse_chunk_size(b"Affffffffffffffff\r\n"),
1654 Err(crate::InvalidChunkSize)
1655 );
1656 assert_eq!(
1657 parse_chunk_size(b"fffffffffffffffff\r\n"),
1658 Err(crate::InvalidChunkSize)
1659 );
1660 }
1661
1662 #[test]
1663 fn test_chunk_size_empty() {
1664 for buf in [&b"\r\n"[..], b";a\r\n", b" \r\n", b"\t;a\r\n", b"\r"] {
1665 assert_eq!(
1666 parse_chunk_size(buf),
1667 Err(crate::InvalidChunkSize),
1668 "{buf:?}"
1669 );
1670 }
1671 assert_eq!(parse_chunk_size(b"0\r\n"), Ok(Status::Complete((3, 0))));
1672 }
1673
1674 fn header_split(buf: &[u8]) -> (Result<HeaderParsed>, Header) {
1677 let mut one = Header::default();
1678 let expected = one.parse(buf);
1679 for split in 0..buf.len() {
1680 let mut h = Header::default();
1681 let mut st = State::default();
1682 let first = h.parse_with_state(&buf[..split], &mut st);
1683 if first.is_err() {
1684 assert_eq!(first, expected, "split {split} of {buf:?}");
1685 continue;
1686 }
1687 assert_eq!(first, Ok(Status::Partial), "split {split} of {buf:?}");
1688 let res = h.parse_with_state(buf, &mut st);
1689 assert_eq!(res, expected, "split {split} of {buf:?}");
1690 if expected.is_ok() {
1691 assert_eq!(h, one, "split {split} of {buf:?}");
1692 }
1693 }
1694 (expected, one)
1695 }
1696
1697 #[test]
1698 fn test_header_split_bare_cr() {
1699 assert_eq!(header_split(b"X: a\rb\r\n").0, Err(Error::HeaderValue));
1700 assert_eq!(header_split(b"X:\rb\r\n").0, Err(Error::HeaderValue));
1701 assert_eq!(header_split(b"\rX: b\r\n").0, Err(Error::NewLine));
1702
1703 let (res, h) = header_split(b"X: a b\r\n");
1704 assert_eq!(res, Ok(Status::Complete(HeaderParsed::Header(8))));
1705 assert_eq!(h.value, SlicePos { start: 3, end: 6 });
1706 assert_eq!(
1707 header_split(b"X:\r\n").0,
1708 Ok(Status::Complete(HeaderParsed::Header(4)))
1709 );
1710 assert_eq!(
1711 header_split(b"\r\n").0,
1712 Ok(Status::Complete(HeaderParsed::Eof(2)))
1713 );
1714 }
1715
1716 #[test]
1717 fn test_request_split() {
1718 for buf in [
1719 &b"GET /path HTTP/1.1\r\n"[..],
1720 b"GET /path HTTP/1.1\r\n",
1721 b"PUT /path HTTP/1.0\n",
1722 b"CUSTOM /path HTTP/1.1\r\n",
1723 b"\r\n\nPOST /path HTTP/1.1\r\n",
1724 b"GET /path HTTP/1.1\r\r\n",
1725 b"GET /path HTTP/1.1\rX",
1726 b"\rGET /path HTTP/1.1\r\n",
1727 b"GE\x00T /path HTTP/1.1\r\n",
1728 b"GET /pa\x7fth HTTP/1.1\r\n",
1729 b"GET /path HTTP/2.0\r\n",
1730 ] {
1731 check_split(buf, Request::parse_with_state, Request::parse);
1732 }
1733 }
1734
1735 #[test]
1736 fn test_response_split() {
1737 for buf in [
1738 &b"HTTP/1.1 200 OK\r\n"[..],
1739 b"HTTP/1.0 404 Not Found \n",
1740 b"\r\nHTTP/1.1 200\r\n",
1741 b"HTTP/1.1 200\n",
1742 b"HTTP/1.1 200 \r\n",
1743 b"HTTP/1.1 200 caf\xc3\xa9\r\n",
1744 b"HTTP/1.1 200 OK\r\r\n",
1745 b"HTTP/1.1 200\rX",
1746 b"HTTP/1.1 2x0 OK\r\n",
1747 b"HTTP/1.1\r\n200 OK\r\n",
1748 b"HTTP/1.1 200 O\x00K\r\n",
1749 ] {
1750 check_split(buf, Response::parse_with_state, Response::parse);
1751 }
1752 }
1753
1754 fn check_split<T: Default + PartialEq + fmt::Debug>(
1757 buf: &[u8],
1758 parse: fn(&mut T, &[u8], &mut State) -> Result<usize>,
1759 one_shot: fn(&mut T, &[u8]) -> Result<usize>,
1760 ) {
1761 let mut one = T::default();
1762 let expected = one_shot(&mut one, buf);
1763 let check = |res: Result<usize>, v: &T, what: &str| {
1764 assert_eq!(res, expected, "{what} of {buf:?}");
1765 if expected.is_ok() {
1766 assert_eq!(v, &one, "{what} of {buf:?}");
1767 }
1768 };
1769
1770 for split in 0..buf.len() {
1771 let mut v = T::default();
1772 let mut st = State::default();
1773 let first = parse(&mut v, &buf[..split], &mut st);
1774 if first.is_err() {
1775 check(first, &v, &format!("split {split}"));
1776 continue;
1777 }
1778 assert_eq!(first, Ok(Status::Partial), "split {split} of {buf:?}");
1779 let res = parse(&mut v, buf, &mut st);
1780 check(res, &v, &format!("split {split}"));
1781 }
1782
1783 let mut v = T::default();
1784 let mut st = State::default();
1785 for len in 0..=buf.len() {
1786 let res = parse(&mut v, &buf[..len], &mut st);
1787 if res != Ok(Status::Partial) || len == buf.len() {
1788 check(res, &v, &format!("byte by byte, len {len}"));
1789 break;
1790 }
1791 assert!(st.cursor + 9 >= len, "len {len} of {buf:?}");
1794 }
1795 }
1796
1797 #[test]
1798 fn test_request_resumes_long_method() {
1799 let buf = [b'A'; 4096];
1800 let mut req = Request::default();
1801 let mut st = State::default();
1802 assert_eq!(req.parse_with_state(&buf, &mut st), Ok(Status::Partial));
1803 assert_eq!((st.state, st.start, st.cursor), (1, 0, 4096));
1804 }
1805
1806 #[test]
1807 fn test_header_name_chars_at_every_position() {
1808 for len in [1, 15, 16, 17, 31, 32, 33, 47, 48, 70] {
1810 for pos in 1..len {
1811 for b in 0..=255_u8 {
1812 let mut buf = vec![b'x'; len];
1813 buf[pos] = b;
1814 buf.extend_from_slice(b": v\r\n");
1815 let res = Header::default().parse(&buf);
1816 if utils::TOKEN_MAP[b as usize] {
1817 assert_eq!(
1818 res,
1819 Ok(Status::Complete(HeaderParsed::Header(len + 5))),
1820 "len {len} pos {pos} byte {b}"
1821 );
1822 } else if b == b':' {
1823 assert!(res.is_ok(), "len {len} pos {pos}");
1824 } else {
1825 assert_eq!(res, Err(Error::HeaderName), "len {len} pos {pos} byte {b}");
1826 }
1827 }
1828 }
1829 }
1830 }
1831
1832 #[test]
1833 fn test_header_state_reuse() {
1834 let buf = b"A: 1\r\nB: 2\r\n\r\nbody";
1835 let mut h = Header::default();
1836 let mut st = State::default();
1837 assert_eq!(
1838 h.parse_with_state(buf, &mut st),
1839 Ok(Status::Complete(HeaderParsed::Header(6)))
1840 );
1841 assert_eq!(
1842 h.parse_with_state(buf, &mut st),
1843 Ok(Status::Complete(HeaderParsed::Header(12)))
1844 );
1845 assert_eq!((h.name.start, h.value.end), (6, 10));
1846 assert_eq!(
1847 h.parse_with_state(buf, &mut st),
1848 Ok(Status::Complete(HeaderParsed::Eof(14)))
1849 );
1850 }
1851
1852 #[test]
1853 fn test_invalid_state() {
1854 for st in [
1855 State {
1856 state: 3,
1857 start: 0,
1858 cursor: 100,
1859 },
1860 State {
1861 state: 1,
1862 start: 2,
1863 cursor: 1,
1864 },
1865 State {
1866 state: 8,
1867 start: 0,
1868 cursor: 0,
1869 },
1870 ] {
1871 let mut s = st;
1872 assert_eq!(
1873 Header::default().parse_with_state(b"abc", &mut s),
1874 Err(Error::HeaderName)
1875 );
1876 let mut s = st;
1877 assert_eq!(
1878 Request::default().parse_with_state(b"abc", &mut s),
1879 Err(Error::Token)
1880 );
1881 let mut s = st;
1882 assert_eq!(
1883 Response::default().parse_with_state(b"abc", &mut s),
1884 Err(Error::Status)
1885 );
1886 }
1887 }
1888
1889 #[test]
1890 fn test_chunk_size_extension_control_chars() {
1891 for buf in [
1892 &b"4;a\nX\r\n"[..],
1893 b"4;a=\"\nX\"\r\n",
1894 b"4;a\x00\r\n",
1895 b"4;a\x7f\r\n",
1896 b"4;\n",
1897 ] {
1898 assert_eq!(
1899 parse_chunk_size(buf),
1900 Err(crate::InvalidChunkSize),
1901 "{buf:?}"
1902 );
1903 }
1904 assert_eq!(
1905 parse_chunk_size(b"4 ;a=\"b\tc \x80\";d\t\r\n"),
1906 Ok(Status::Complete((17, 4)))
1907 );
1908 }
1909
1910 res! {
1911 test_allow_response_with_multiple_space_delimiters,
1912 b"HTTP/1.1 200 OK\r\n\r\n",
1913 |len, version, code, reason, headers, eof| {
1914 assert_eq!(len, 22);
1915 assert_eq!(version, 1);
1916 assert_eq!(code, 200);
1917 assert_eq!(reason, "OK");
1918 assert_eq!(headers.len(), 0);
1919 assert!(eof);
1920 }
1921 }
1922
1923 res_err! {
1926 test_forbid_response_with_weird_whitespace_delimiters,
1927 b"HTTP/1.1 200\rOK\r\n\r\n",
1928 Err(Error::Status)
1929 }
1930
1931 req! {
1932 test_allow_request_with_multiple_space_delimiters,
1933 b"GET / HTTP/1.1\r\n\r\n",
1934 |len, method, path, version, headers, eof| {
1935 assert_eq!(len, 22);
1936 assert_eq!(method, "GET");
1937 assert_eq!(path, "/");
1938 assert_eq!(version, 1);
1939 assert_eq!(headers.len(), 0);
1940 assert!(eof);
1941 }
1942 }
1943
1944 req_err! {
1947 test_forbid_request_with_weird_whitespace_delimiters,
1948 b"GET\r/\rHTTP/1.1\r\n\r\n",
1949 Err(Error::Token)
1950 }
1951
1952 req_err! {
1953 test_request_with_multiple_spaces_and_bad_path,
1954 b"GET /foo ohno HTTP/1.1\r\n\r\n",
1955 Err(Error::Version)
1956 }
1957
1958 req_err! {
1962 test_request_with_del_in_path,
1963 b"GET /foo\x7Fohno HTTP/1.1\r\n\r\n",
1964 Err(Error::Token)
1965 }
1966
1967 res_err! {
2111 test_response_with_spaces_in_code,
2112 b"HTTP/1.1 99 200 OK\r\n\r\n",
2113 Err(Error::Status)
2114 }
2115
2116 headers_err! {
2117 test_headers_with_whitespace_between_header_name_and_colon,
2118 b"Access-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n",
2119 Err(Error::HeaderName)
2120 }
2121
2122 headers_err! {
2123 test_headers_with_invalid_char_between_header_name_and_colon,
2124 b"Access-Control-Allow-Credentials\xFF: true\r\nBread: baguette\r\n\r\n",
2125 Err(Error::HeaderName)
2126 }
2127
2128 headers_err! {
2129 test_ignore_header_line_with_missing_colon_in_response,
2130 b"Access-Control-Allow-Credentials\r\nBread: baguette\r\n\r\n",
2131 Err(Error::HeaderName)
2132 }
2133
2134 headers_err! {
2135 test_headers_header_with_missing_colon_with_folding,
2136 b"Access-Control-Allow-Credentials \r\n hello\r\nBread: baguette\r\n\r\n",
2137 Err(Error::HeaderName)
2138 }
2139
2140 headers_err! {
2141 test_headers_header_with_nul_in_header_name,
2142 b"Access-Control-Allow-Cred\0entials: hello\r\nBread: baguette\r\n\r\n",
2143 Err(Error::HeaderName)
2144 }
2145
2146 headers_err! {
2147 test_header_with_cr_in_header_name,
2148 b"Access-Control-Allow-Cred\rentials: hello\r\nBread: baguette\r\n\r\n",
2149 Err(Error::HeaderName)
2150 }
2151
2152 headers_err! {
2153 test_header_with_nul_in_whitespace_before_colon,
2154 b"Access-Control-Allow-Credentials \0: hello\r\nBread: baguette\r\n\r\n",
2155 Err(Error::HeaderName)
2156 }
2157
2158 headers_err! {
2159 test_header_with_nul_in_value,
2160 b"Access-Control-Allow-Credentials: hell\0o\r\nBread: baguette\r\n\r\n",
2161 Err(Error::HeaderValue)
2162 }
2163
2164 headers_err! {
2165 test_header_with_invalid_char_in_value,
2166 b"Access-Control-Allow-Credentials: hell\x01o\r\nBread: baguette\r\n\r\n",
2167 Err(Error::HeaderValue)
2168 }
2169
2170 headers_err! {
2171 test_header_with_invalid_char_in_value_with_folding,
2172 b"Access-Control-Allow-Credentials: hell\x01o \n world!\r\nBread: baguette\r\n\r\n",
2173 Err(Error::HeaderValue)
2174 }
2175
2176 headers_err! {
2177 test_header_with_space_before_first_header,
2178 b" Space-Before-Header: hello there\r\n\r\n",
2179 Err(Error::HeaderName)
2180 }
2181
2182 res! {
2183 test_response_no_space_after_colon,
2184 b"HTTP/1.1 200 OK\r\nfoo:bar\r\n\r\n",
2185 |len, version, code, reason, headers, eof| {
2186 assert_eq!(len, 28);
2187 assert_eq!(version, 1);
2188 assert_eq!(code, 200);
2189 assert_eq!(reason, "OK");
2190 assert_eq!(headers.len(), 1);
2191 assert_eq!(headers[0].0, "foo");
2192 assert_eq!(headers[0].1, b"bar");
2193 assert!(eof);
2194 }
2195 }
2196
2197 req_err! {
2198 test_request_with_leading_space,
2199 b" GET / HTTP/1.1\r\nfoo:bar\r\n\r\n",
2200 Err(Error::Token)
2201 }
2202
2203 req_err! {
2204 test_request_with_invalid_method,
2205 b"P()ST / HTTP/1.1\r\nfoo:bar\r\n\r\n",
2206 Err(Error::Token)
2207 }
2208
2209 req! {
2210 test_utf8_in_path_ok,
2211 b"GET /test?post=I\xE2\x80\x99msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n",
2212 |len, method, path, version, headers, eof| {
2213 assert_eq!(len, 67);
2214 assert_eq!(method, "GET");
2215 assert_eq!(path, "/test?post=I’msorryIforkedyou");
2216 assert_eq!(version, 1);
2217 assert_eq!(headers.len(), 1);
2218 assert_eq!(headers[0].0, "Host");
2219 assert_eq!(headers[0].1, b"example.org");
2220 assert!(eof);
2221 }
2222 }
2223
2224 #[test]
2225 fn test_bad_utf8_in_path() {
2226 const BUF: &[u8] =
2227 b"GET /test?post=I\xE2msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n";
2228
2229 let mut req = Request::default();
2230 assert!(req.parse(BUF).unwrap().is_complete());
2231 assert!(str::from_utf8(&BUF[req.path.start..req.path.end]).is_err());
2232 }
2233
2234 #[rustfmt::skip]
2235 res! {
2236 test_response_bench,
2237 b"\
2238HTTP/1.0 200 OK\r\n\
2239Date: Wed, 21 Oct 2015 07:28:00 GMT\r\n\
2240Set-Cookie: session=60; user_id=1\r\n\r\n",
2241 |len, version, code, reason, headers, eof| {
2242 assert_eq!(len, 91);
2243 assert_eq!(version, 0);
2244 assert_eq!(code, 200);
2245 assert_eq!(reason, "OK");
2246 assert_eq!(headers.len(), 2);
2247 assert_eq!(headers[0].0, "Date");
2248 assert_eq!(headers[0].1, b"Wed, 21 Oct 2015 07:28:00 GMT");
2249 assert_eq!(headers[1].0, "Set-Cookie");
2250 assert_eq!(headers[1].1, b"session=60; user_id=1");
2251 assert!(eof);
2252 }
2253 }
2254}