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 if !matches!(b, b'\r' | b'\n') {
544 match_reason_vectored(bytes, seen_obs_text);
545 }
546 break;
547 }
548 if utils::is_header_value_token(b) {
549 if b >= 0x80 {
550 *seen_obs_text = true;
551 }
552 bytes.advance(1);
553 n += 1;
554 } else {
555 break;
556 }
557 }
558
559 loop {
560 let b = next!(bytes);
561 let skip = if b == b'\r' {
562 expect_lf!(bytes => Err(Error::Status));
563 2
564 } else if b == b'\n' {
565 1
566 } else if !(b == 0x09 || b == b' ' || (0x21..=0x7E).contains(&b) || b >= 0x80) {
567 return Err(Error::Status);
568 } else {
569 if b >= 0x80 {
570 *seen_obs_text = true;
571 }
572 continue;
573 };
574
575 let reason = bytes.slice_position(skip);
578 return Ok(Status::Complete(
579 if *seen_obs_text || reason.start == reason.end {
580 SlicePos::default()
581 } else {
582 reason
583 },
584 ));
585 }
586}
587
588#[inline(never)]
589fn match_reason_vectored(bytes: &mut Bytes<'_, '_>, seen_obs_text: &mut bool) {
590 let from = bytes.cursor();
591 simd::match_header_value_vectored(bytes);
592 if !*seen_obs_text {
593 let scanned = bytes.since(from);
594 let (blocks, tail) = scanned.split_at(scanned.len() & !63);
597 if !blocks.is_ascii() || !tail.is_ascii() {
598 *seen_obs_text = true;
599 }
600 }
601}
602
603#[inline]
604#[doc(hidden)]
605pub fn parse_uri(src: &[u8]) -> Result<&str> {
611 let mut st = State::default();
612 let mut bytes = Bytes::new(src, &mut st);
613 if let Status::Complete(pos) = parse_uri_inner(&mut bytes)? {
614 if let Ok(path) = simdutf8::basic::from_utf8(&src[pos.start..pos.end]) {
615 Ok(Status::Complete(path))
616 } else {
617 Err(Error::Token)
618 }
619 } else {
620 Ok(Status::Partial)
621 }
622}
623
624#[inline]
625fn parse_uri_inner(bytes: &mut Bytes<'_, '_>) -> Result<SlicePos> {
626 let start = bytes.start();
627 simd::match_uri_vectored(bytes);
628 let b_end = bytes.cursor();
629
630 if next!(bytes) == b' ' {
631 if start == b_end {
633 return Err(Error::Token);
634 }
635
636 let end = bytes.cursor() - 1;
638 bytes.commit();
639 Ok(Status::Complete(SlicePos { start, end }))
640 } else {
641 Err(Error::Token)
642 }
643}
644
645#[inline]
646fn parse_code(bytes: &mut Bytes<'_, '_>) -> Result<u16> {
647 let hundreds = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
648 let tens = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
649 let ones = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
650
651 Ok(Status::Complete(
652 (hundreds - b'0') as u16 * 100 + (tens - b'0') as u16 * 10 + (ones - b'0') as u16,
653 ))
654}
655
656pub fn parse_chunk_size(buf: &[u8]) -> result::Result<Status<(usize, u64)>, InvalidChunkSize> {
674 const RADIX: u64 = 16;
675 let mut st = State::default();
676 let mut bytes = Bytes::new(buf, &mut st);
677 let mut size = 0;
678 let mut in_chunk_size = true;
679 let mut in_ext = false;
680 let mut count = 0;
681 loop {
682 let b = next!(bytes);
683 match b {
684 b'0'..=b'9' if in_chunk_size => {
685 if count > 15 {
686 return Err(InvalidChunkSize);
687 }
688 count += 1;
689 if cfg!(debug_assertions) && size > (u64::MAX / RADIX) {
690 return Err(InvalidChunkSize);
695 }
696 size *= RADIX;
697 size += (b - b'0') as u64;
698 }
699 b'a'..=b'f' | b'A'..=b'F' if in_chunk_size => {
700 if count > 15 {
701 return Err(InvalidChunkSize);
702 }
703 count += 1;
704 if cfg!(debug_assertions) && size > (u64::MAX / RADIX) {
705 return Err(InvalidChunkSize);
706 }
707 size *= RADIX;
708 size += ((b | 0x20) + 10 - b'a') as u64;
709 }
710 b'\r' if count == 0 => return Err(InvalidChunkSize),
712 b'\r' => match next!(bytes) {
713 b'\n' => break,
714 _ => return Err(InvalidChunkSize),
715 },
716 b';' if !in_ext => {
718 in_ext = true;
719 in_chunk_size = false;
720 }
721 b'\t' | b' ' if !in_ext && !in_chunk_size => {}
724 b'\t' | b' ' if in_chunk_size => in_chunk_size = false,
726 0x00..=0x08 | 0x0a..=0x1f | 0x7f if in_ext => return Err(InvalidChunkSize),
729 _ if in_ext => {}
735 _ => return Err(InvalidChunkSize),
738 }
739 }
740 Ok(Status::Complete((bytes.cursor(), size)))
741}
742
743#[cfg(test)]
744mod tests {
745 #![allow(clippy::items_after_statements)]
746 use super::*;
747
748 macro_rules! req {
749 ($name:ident, $buf:expr, |$len:ident, $method:ident, $path:ident, $version:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
750 #[test]
751 fn $name() {
752 let mut req = Request::default();
753 let mut b = $buf.as_ref();
754 if let Ok(Status::Complete(l)) = req.parse(b) {
755 let mut consumed = l;
756 let mut headers = Vec::new();
757 let mut header = Header::default();
758 let mut headers_eof = false;
759 b = &b[consumed..];
760
761 while let Status::Complete(hdr) = header.parse(b).unwrap() {
762 match hdr {
763 HeaderParsed::Header(l) => {
764 consumed += l;
765 let name = String::from_utf8(Vec::from(
766 &b[header.name.start..header.name.end],
767 ))
768 .unwrap();
769 let value = Vec::from(&b[header.value.start..header.value.end]);
770 headers.push((name, value));
771 b = &b[l..];
772 }
773 HeaderParsed::Eof(l) => {
774 consumed += l;
775 headers_eof = true;
776 break;
777 }
778 }
779 }
780
781 let (path, method) = unsafe {
783 (
784 str::from_utf8_unchecked(&$buf.as_ref()[req.path.start..req.path.end]),
785 str::from_utf8_unchecked(
786 &$buf.as_ref()[req.method.start..req.method.end],
787 ),
788 )
789 };
790
791 closure(consumed, method, path, req.version, headers, headers_eof);
792 } else {
793 panic!()
794 }
795
796 fn closure(
797 $len: usize,
798 $method: &str,
799 $path: &str,
800 $version: u8,
801 $headers: Vec<(String, Vec<u8>)>,
802 $headers_eof: bool,
803 ) {
804 $body
805 }
806 }
807 };
808 }
809
810 macro_rules! headers {
811 ($name:ident, $buf:expr, |$len:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
812 #[test]
813 fn $name() {
814 let mut b = $buf.as_ref();
815 let mut consumed = 0;
816 let mut headers = Vec::new();
817 let mut header = Header::default();
818 let mut headers_eof = false;
819
820 while let Status::Complete(hdr) = header.parse(b).unwrap() {
821 match hdr {
822 HeaderParsed::Header(l) => {
823 consumed += l;
824 let name = String::from_utf8(Vec::from(
825 &b[header.name.start..header.name.end],
826 ))
827 .unwrap();
828 let value = Vec::from(&b[header.value.start..header.value.end]);
829 headers.push((name, value));
830 b = &b[l..];
831 }
832 HeaderParsed::Eof(l) => {
833 consumed += l;
834 headers_eof = true;
835 break;
836 }
837 }
838 }
839 closure(consumed, headers, headers_eof);
840
841 fn closure($len: usize, $headers: Vec<(String, Vec<u8>)>, $headers_eof: bool) {
842 $body
843 }
844 }
845 };
846 }
847
848 macro_rules! req_err {
849 ($name:ident, $buf:expr, $err:expr) => {
850 #[test]
851 fn $name() {
852 assert_eq!(Request::default().parse($buf.as_ref()), $err);
853 }
854 };
855 }
856
857 macro_rules! req_par {
858 ($name:ident, $buf:expr) => {
859 #[test]
860 fn $name() {
861 assert_eq!(Request::default().parse($buf.as_ref()), Ok(Status::Partial));
862 }
863 };
864 }
865
866 macro_rules! headers_err {
867 ($name:ident, $buf:expr, $err:expr) => {
868 #[test]
869 fn $name() {
870 let mut consumed = 0;
871 let mut header = Header::default();
872
873 let result = loop {
874 match header.parse(&$buf.as_ref()[consumed..]) {
875 Ok(Status::Complete(HeaderParsed::Header(l))) => {
876 consumed += l;
877 }
878 Ok(_) => break Ok(()),
879 Err(e) => break Err(e),
880 }
881 };
882 assert_eq!(result, $err);
883 }
884 };
885 }
886
887 req! {
888 test_request_simple,
889 b"GET / HTTP/1.1\r\n\r\n",
890 |len, method, path, version, headers, eof| {
891 assert_eq!(len, 18);
892 assert_eq!(method, "GET");
893 assert_eq!(path, "/");
894 assert_eq!(version, 1);
895 assert_eq!(headers.len(), 0);
896 assert!(eof);
897 }
898 }
899
900 req! {
901 test_request_simple_with_query_params,
902 b"GET /thing?data=a HTTP/1.1\r\n\r\n",
903 |len, method, path, version, headers, eof| {
904 assert_eq!(len, 30);
905 assert_eq!(method, "GET");
906 assert_eq!(path, "/thing?data=a");
907 assert_eq!(version, 1);
908 assert_eq!(headers.len(), 0);
909 assert!(eof);
910 }
911 }
912
913 req! {
914 test_request_simple_with_whatwg_query_params,
915 b"GET /thing?data=a^ HTTP/1.1\r\n\r\n",
916 |len, method, path, version, headers, eof| {
917 assert_eq!(len, 31);
918 assert_eq!(method, "GET");
919 assert_eq!(path, "/thing?data=a^");
920 assert_eq!(version, 1);
921 assert_eq!(headers.len(), 0);
922 assert!(eof);
923 }
924 }
925
926 req! {
927 test_request_headers,
928 b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \r\n\r\n ",
929 |len, method, path, version, headers, eof| {
930 assert_eq!(len, 43);
931 assert_eq!(method, "GET");
932 assert_eq!(path, "/");
933 assert_eq!(version, 1);
934 assert_eq!(headers.len(), 2);
935 assert_eq!(headers[0].0, "Host");
936 assert_eq!(headers[0].1, b"foo.com");
937 assert_eq!(headers[1].0, "Cookie");
938 assert_eq!(headers[1].1, b"");
939 assert!(eof);
940 }
941 }
942
943 req! {
944 test_request_headers_optional_whitespace,
945 b"GET / HTTP/1.1\r\nHost: \tfoo.com\t \r\nCookie: \t \r\n\r\n",
946 |len, method, path, version, headers, eof| {
947 assert_eq!(len, 48);
948 assert_eq!(method, "GET");
949 assert_eq!(path, "/");
950 assert_eq!(version, 1);
951 assert_eq!(headers.len(), 2);
952 assert_eq!(headers[0].0, "Host");
953 assert_eq!(headers[0].1, b"foo.com");
954 assert_eq!(headers[1].0, "Cookie");
955 assert_eq!(headers[1].1, b"");
956 assert!(eof);
957 }
958 }
959
960 req! {
961 test_request_header_value_htab_short,
963 b"GET / HTTP/1.1\r\nUser-Agent: some\tagent\r\n\r\n",
964 |len, method, path, version, headers, eof| {
965 assert_eq!(len, 42);
966 assert_eq!(method, "GET");
967 assert_eq!(path, "/");
968 assert_eq!(version, 1);
969 assert_eq!(headers.len(), 1);
970 assert_eq!(headers[0].0, "User-Agent");
971 assert_eq!(headers[0].1, b"some\tagent");
972 assert!(eof);
973 }
974 }
975
976 req! {
977 test_request_header_value_htab_med,
979 b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\tagent\r\n\r\n",
980 |len, method, path, version, headers, eof| {
981 assert_eq!(len, 52);
982 assert_eq!(method, "GET");
983 assert_eq!(path, "/");
984 assert_eq!(version, 1);
985 assert_eq!(headers.len(), 1);
986 assert_eq!(headers[0].0, "User-Agent");
987 assert_eq!(headers[0].1, b"1234567890some\tagent");
988 assert!(eof);
989 }
990 }
991
992 req! {
993 test_request_header_value_htab_long,
995 b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\t1234567890agent1234567890\r\n\r\n",
996 |len, method, path, version, headers, eof| {
997 assert_eq!(len, 72);
998 assert_eq!(method, "GET");
999 assert_eq!(path, "/");
1000 assert_eq!(version, 1);
1001 assert_eq!(headers.len(), 1);
1002 assert_eq!(headers[0].0, "User-Agent");
1003 assert_eq!(headers[0].1, &b"1234567890some\t1234567890agent1234567890"[..]);
1004 assert!(eof);
1005 }
1006 }
1007
1008 req! {
1009 test_request_header_no_space_after_colon,
1011 b"GET / HTTP/1.1\r\nUser-Agent:omg-no-space1234567890some1234567890agent1234567890\r\n\r\n",
1012 |len, method, path, version, headers, eof| {
1013 assert_eq!(len, 82);
1014 assert_eq!(method, "GET");
1015 assert_eq!(path, "/");
1016 assert_eq!(version, 1);
1017 assert_eq!(headers.len(), 1);
1018 assert_eq!(headers[0].0, "User-Agent");
1019 assert_eq!(headers[0].1, &b"omg-no-space1234567890some1234567890agent1234567890"[..]);
1020 assert!(eof);
1021 }
1022 }
1023
1024 req! {
1025 test_request_headers_max,
1026 b"GET / HTTP/1.1\r\nA: A\r\nB: B\r\nC: C\r\nD: D\r\n\r\n",
1027 |_len, _method, _path, _verion, headers, eof| {
1028 assert_eq!(headers.len(), 4);
1029 assert!(eof);
1030 }
1031 }
1032
1033 req! {
1034 test_request_multibyte,
1035 b"GET / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: \xe3\x81\xb2\xe3/1.0\r\n\r\n",
1036 |len, method, path, version, headers, eof| {
1037 assert_eq!(len, 55);
1038 assert_eq!(method, "GET");
1039 assert_eq!(path, "/");
1040 assert_eq!(version, 1);
1041 assert_eq!(headers.len(), 2);
1042 assert_eq!(headers[0].0, "Host");
1043 assert_eq!(headers[0].1, b"foo.com");
1044 assert_eq!(headers[1].0, "User-Agent");
1045 assert_eq!(headers[1].1, b"\xe3\x81\xb2\xe3/1.0");
1046 assert!(eof);
1047 }
1048 }
1049
1050 req_par! {
1052 test_request_one_byte_method,
1053 b"G"
1054 }
1055
1056 req_par! {
1058 test_request_partial_method,
1059 b"GE"
1060 }
1061
1062 req_par! {
1064 test_request_method_no_delimiter,
1065 b"GET"
1066 }
1067
1068 req_par! {
1071 test_request_method_only,
1072 b"GET "
1073 }
1074
1075 req! {
1076 test_request_partial,
1077 b"GET / HTTP/1.1\r\n\r",
1078 |len, method, path, version, headers, eof| {
1079 assert_eq!(len, b"GET / HTTP/1.1\r\n\r".len() - 1);
1080 assert_eq!(method, "GET");
1081 assert_eq!(path, "/");
1082 assert_eq!(version, 1);
1083 assert_eq!(headers.len(), 0);
1084 assert!(!eof);
1085 }
1086 }
1087
1088 req_par! {
1089 test_request_partial_version,
1090 b"GET / HTTP/1."
1091 }
1092
1093 req_par! {
1094 test_request_method_path_no_delimiter,
1095 b"GET /"
1096 }
1097
1098 req_par! {
1099 test_request_method_path_only,
1100 b"GET / "
1101 }
1102
1103 req! {
1104 test_request_partial_parses_headers_as_much_as_it_can,
1105 b"GET / HTTP/1.1\r\nHost: yolo\r\n",
1106 |len, method, path, version, headers, eof| {
1107 assert_eq!(len, 28);
1108 assert_eq!(method, "GET");
1109 assert_eq!(path, "/");
1110 assert_eq!(version, 1);
1111 assert_eq!(headers.len(), 1);
1112 assert_eq!(headers[0].0, "Host");
1113 assert_eq!(headers[0].1, b"yolo");
1114 assert!(!eof);
1115 }
1116 }
1117
1118 req! {
1119 test_request_newlines,
1120 b"GET / HTTP/1.1\nHost: foo.bar\n\n",
1121 |_len, _method, _path, _verion, _headers, eof| {
1122 assert!(eof);
1123 }
1124 }
1125
1126 req! {
1127 test_request_empty_lines_prefix,
1128 b"\r\n\r\nGET / HTTP/1.1\r\n\r\n",
1129 |len, method, path, version, headers, eof| {
1130 assert_eq!(len, 22);
1131 assert_eq!(method, "GET");
1132 assert_eq!(path, "/");
1133 assert_eq!(version, 1);
1134 assert_eq!(headers.len(), 0);
1135 assert!(eof);
1136 }
1137 }
1138
1139 req! {
1140 test_request_empty_lines_prefix_lf_only,
1141 b"\n\nGET / HTTP/1.1\n\n",
1142 |len, method, path, version, headers, eof| {
1143 assert_eq!(len, 18);
1144 assert_eq!(method, "GET");
1145 assert_eq!(path, "/");
1146 assert_eq!(version, 1);
1147 assert_eq!(headers.len(), 0);
1148 assert!(eof);
1149 }
1150 }
1151
1152 req! {
1153 test_request_path_backslash,
1154 b"\n\nGET /\\?wayne\\=5 HTTP/1.1\n\n",
1155 |len, method, path, version, headers, eof| {
1156 assert_eq!(len, 28);
1157 assert_eq!(method, "GET");
1158 assert_eq!(path, "/\\?wayne\\=5");
1159 assert_eq!(version, 1);
1160 assert_eq!(headers.len(), 0);
1161 assert!(eof);
1162 }
1163 }
1164
1165 req_err! {
1166 test_request_with_invalid_token_delimiter,
1167 b"GET\n/ HTTP/1.1\r\nHost: foo.bar\r\n\r\n",
1168 Err(Error::Token)
1169 }
1170
1171 req_err! {
1172 test_request_with_invalid_but_short_version,
1173 b"GET / HTTP/1!",
1174 Err(Error::Version)
1175 }
1176
1177 req_err! {
1178 test_request_with_empty_method,
1179 b" / HTTP/1.1\r\n\r\n",
1180 Err(Error::Token)
1181 }
1182
1183 req_err! {
1184 test_request_with_empty_path,
1185 b"GET HTTP/1.1\r\n\r\n",
1186 Err(Error::Token)
1187 }
1188
1189 req_err! {
1190 test_request_with_empty_method_and_path,
1191 b" HTTP/1.1\r\n\r\n",
1192 Err(Error::Token)
1193 }
1194
1195 headers! {
1196 test_headers_optional_whitespace,
1197 b"Host: \tfoo.com\t \r\nCookie: \t \r\n",
1198 |len, headers, eof| {
1199 assert_eq!(len, 30);
1200 assert_eq!(headers.len(), 2);
1201 assert_eq!(headers[0].0, "Host");
1202 assert_eq!(headers[0].1, b"foo.com");
1203 assert_eq!(headers[1].0, "Cookie");
1204 assert_eq!(headers[1].1, b"");
1205 assert!(!eof);
1206 }
1207 }
1208
1209 #[test]
1210 fn test_headers_with_state() {
1211 const B1: &[u8] = b"Host";
1212 const B2: &[u8] = b"Host: \t";
1213 const B3: &[u8] = b"Host: \tfoo.com\t ";
1214 const B4: &[u8] = b"Host: \tfoo.com\t \r\nCoo";
1215 const B5: &[u8] = b"Host: \tfoo.com\t \r\nCookie: \t \r\n\r\n";
1216
1217 let mut st = State::default();
1218 let mut header = Header::default();
1219 assert!(header.parse_with_state(B1, &mut st).unwrap().is_partial());
1220 assert_eq!(st.state, 1);
1221 assert_eq!(st.start, 0);
1222 assert_eq!(st.cursor, 4);
1223 assert_eq!(header.name, SlicePos { start: 0, end: 0 });
1224 assert!(header.parse_with_state(B2, &mut st).unwrap().is_partial());
1225 assert_eq!(st.state, 2);
1226 assert_eq!(st.start, 0);
1227 assert_eq!(st.cursor, 7);
1228 assert_eq!(header.name, SlicePos { start: 0, end: 4 });
1229 assert_eq!(&B5[header.name.start..header.name.end], b"Host");
1230
1231 assert!(header.parse_with_state(B3, &mut st).unwrap().is_partial());
1232 assert_eq!(st.state, 3);
1233 assert_eq!(st.start, 0);
1234 assert_eq!(st.cursor, 16);
1235 assert_eq!(header.name, SlicePos { start: 0, end: 4 });
1236 assert_eq!(header.value, SlicePos { start: 7, end: 0 });
1237
1238 assert!(header.parse_with_state(B4, &mut st).unwrap().is_complete());
1239 assert_eq!(st.state, 0);
1240 assert_eq!(st.start, 18);
1241 assert_eq!(st.cursor, 18);
1242 assert_eq!(header.value, SlicePos { start: 7, end: 14 });
1243 assert_eq!(&B5[header.value.start..header.value.end], b"foo.com");
1244
1245 assert!(header.parse_with_state(B5, &mut st).unwrap().is_complete());
1246 assert_eq!(st.state, 0);
1247 assert_eq!(st.start, 30);
1248 assert_eq!(st.cursor, 30);
1249 assert_eq!(header.name, SlicePos { start: 18, end: 24 });
1250 assert_eq!(header.value, SlicePos { start: 0, end: 0 });
1251 assert_eq!(&B5[header.name.start..header.name.end], b"Cookie");
1252 assert_eq!(&B5[header.value.start..header.value.end], b"");
1253
1254 assert_eq!(
1255 header.parse_with_state(B5, &mut st),
1256 Ok(Status::Complete(HeaderParsed::Eof(32)))
1257 );
1258 assert_eq!(st.state, 0);
1259 assert_eq!(st.start, 32);
1260 assert_eq!(st.cursor, 32);
1261 }
1262
1263 headers_err! {
1264 test_headers_with_obsolete_line_folding_at_start,
1265 b"Line-Folded-Header: \r\n \r\n hello there\r\n\r\n",
1266 Err(Error::HeaderName)
1267 }
1268
1269 headers_err! {
1270 test_header_with_invalid_name,
1271 b"Host : foo.bar\r\n\r\n",
1272 Err(Error::HeaderName)
1273 }
1274
1275 macro_rules! res {
1276 ($name:ident, $buf:expr, |$len:ident, $version:ident, $code:ident, $reason:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
1277 #[test]
1278 fn $name() {
1279 let mut b = $buf.as_ref();
1280 let mut res = Response::default();
1281 let mut consumed = res.parse($buf.as_ref()).unwrap().unwrap();
1282 let mut headers = Vec::new();
1283 let mut header = Header::default();
1284 let mut headers_eof = false;
1285 b = &b[consumed..];
1286
1287 while let Status::Complete(hdr) = header.parse(b).unwrap() {
1288 match hdr {
1289 HeaderParsed::Header(l) => {
1290 consumed += l;
1291 let name = String::from_utf8(Vec::from(
1292 &b[header.name.start..header.name.end],
1293 ))
1294 .unwrap();
1295 let value = Vec::from(&b[header.value.start..header.value.end]);
1296 headers.push((name, value));
1297 b = &b[l..];
1298 }
1299 HeaderParsed::Eof(l) => {
1300 consumed += l;
1301 headers_eof = true;
1302 break;
1303 }
1304 }
1305 }
1306
1307 let reason = unsafe {
1309 str::from_utf8_unchecked(&$buf.as_ref()[res.reason.start..res.reason.end])
1310 };
1311
1312 closure(
1313 consumed,
1314 res.version,
1315 res.code,
1316 reason,
1317 headers,
1318 headers_eof,
1319 );
1320
1321 fn closure(
1322 $len: usize,
1323 $version: u8,
1324 $code: u16,
1325 $reason: &str,
1326 $headers: Vec<(String, Vec<u8>)>,
1327 $headers_eof: bool,
1328 ) {
1329 $body
1330 }
1331 }
1332 };
1333 }
1334
1335 macro_rules! res_err {
1336 ($name:ident, $buf:expr, $err:expr) => {
1337 #[test]
1338 fn $name() {
1339 assert_eq!(Response::default().parse($buf.as_ref()), $err);
1340 }
1341 };
1342 }
1343
1344 macro_rules! res_par {
1345 ($name:ident, $buf:expr) => {
1346 #[test]
1347 fn $name() {
1348 assert_eq!(
1349 Response::default().parse($buf.as_ref()),
1350 Ok(Status::Partial)
1351 );
1352 }
1353 };
1354 }
1355
1356 res_err! {
1357 test_response_newline_after_version,
1358 b"HTTP/1.1\r\n\r\n 200 OK\r\n\r\n",
1359 Err(Error::Version)
1360 }
1361
1362 res_err! {
1363 test_response_bare_newline_after_version,
1364 b"HTTP/1.1\n 200 OK\r\n\r\n",
1365 Err(Error::Version)
1366 }
1367
1368 res! {
1369 test_response_simple,
1370 b"HTTP/1.1 200 OK\r\n\r\n",
1371 |len, version, code, reason, headers, eof| {
1372 assert_eq!(len, 19);
1373 assert_eq!(version, 1);
1374 assert_eq!(code, 200);
1375 assert_eq!(reason, "OK");
1376 assert_eq!(headers.len(), 0);
1377 assert!(eof);
1378 }
1379 }
1380
1381 res! {
1382 test_response_newlines,
1383 b"HTTP/1.0 403 Forbidden\nServer: foo.bar\n\n",
1384 |len, version, code, reason, headers, eof| {
1385 assert_eq!(len, 40);
1386 assert_eq!(version, 0);
1387 assert_eq!(code, 403);
1388 assert_eq!(reason, "Forbidden");
1389 assert_eq!(headers.len(), 1);
1390 assert_eq!(headers[0].0, "Server");
1391 assert_eq!(headers[0].1, b"foo.bar");
1392 assert!(eof);
1393 }
1394 }
1395
1396 res! {
1397 test_response_reason_missing,
1398 b"HTTP/1.1 200 \r\n\r\n",
1399 |len, version, code, reason, headers, eof| {
1400 assert_eq!(len, 17);
1401 assert_eq!(version, 1);
1402 assert_eq!(code, 200);
1403 assert_eq!(reason, "");
1404 assert_eq!(headers.len(), 0);
1405 assert!(eof);
1406 }
1407 }
1408
1409 res! {
1410 test_response_reason_missing_no_space,
1411 b"HTTP/1.1 200\r\n\r\n",
1412 |len, version, code, reason, headers, eof| {
1413 assert_eq!(len, 16);
1414 assert_eq!(version, 1);
1415 assert_eq!(code, 200);
1416 assert_eq!(reason, "");
1417 assert_eq!(headers.len(), 0);
1418 assert!(eof);
1419 }
1420 }
1421
1422 res! {
1423 test_response_reason_missing_no_space_with_headers,
1424 b"HTTP/1.1 200\r\nFoo: bar\r\n\r\n",
1425 |len, version, code, reason, headers, eof| {
1426 assert_eq!(len, 26);
1427 assert_eq!(version, 1);
1428 assert_eq!(code, 200);
1429 assert_eq!(reason, "");
1430 assert_eq!(headers.len(), 1);
1431 assert_eq!(headers[0].0, "Foo");
1432 assert_eq!(headers[0].1, b"bar");
1433 assert!(eof);
1434 }
1435 }
1436
1437 res! {
1438 test_response_reason_with_space_and_tab,
1439 b"HTTP/1.1 101 Switching Protocols\t\r\n\r\n",
1440 |len, version, code, reason, headers, eof| {
1441 assert_eq!(len, 37);
1442 assert_eq!(version, 1);
1443 assert_eq!(code, 101);
1444 assert_eq!(reason, "Switching Protocols\t");
1445 assert_eq!(headers.len(), 0);
1446 assert!(eof);
1447 }
1448 }
1449
1450 res! {
1451 test_response_reason_with_obsolete_text_byte,
1452 b"HTTP/1.1 200 X\xFFZ\r\n\r\n",
1453 |len, version, code, reason, headers, eof| {
1454 assert_eq!(len, 20);
1455 assert_eq!(version, 1);
1456 assert_eq!(code, 200);
1457 assert_eq!(reason, "");
1459 assert_eq!(headers.len(), 0);
1460 assert!(eof);
1461 }
1462 }
1463
1464 res_err! {
1465 test_response_reason_with_nul_byte,
1466 b"HTTP/1.1 200 \x00\r\n\r\n",
1467 Err(crate::Error::Status)
1468 }
1469
1470 #[test]
1471 fn test_response_reason_bytes_at_every_position() {
1472 const PREFIX: &[u8] = b"HTTP/1.1 200 ";
1473 let lens: Vec<usize> = if cfg!(miri) {
1475 vec![1, 8, 9, 17, 33]
1476 } else {
1477 (1..80).collect()
1478 };
1479 for len in lens {
1480 for pos in 0..len {
1481 for b in [b'\t', b' ', b'~', 0x00, 0x0b, 0x7f, 0x80, 0xff] {
1482 let mut reason = vec![b'a'; len];
1483 reason[pos] = b;
1484 let buf = [PREFIX, &reason, b"\r\n"].concat();
1485 let valid = b == b'\t' || b == b' ' || b == b'~' || b >= 0x80;
1486
1487 let splits: Vec<usize> = if cfg!(miri) {
1489 let at = PREFIX.len() + pos;
1490 vec![PREFIX.len(), at, at + 1, buf.len()]
1491 } else {
1492 (PREFIX.len()..=buf.len()).collect()
1493 };
1494 for split in splits {
1495 let mut resp = Response::default();
1496 let mut st = State::default();
1497 let first = resp.parse_with_state(&buf[..split], &mut st);
1498 let res = if split == buf.len() {
1499 first
1500 } else if first.is_err() {
1501 assert!(!valid, "len {len} pos {pos} byte {b:#x} split {split}");
1502 continue;
1503 } else {
1504 assert_eq!(first, Ok(Status::Partial));
1505 resp.parse_with_state(&buf, &mut st)
1506 };
1507 let ctx = format!("len {len} pos {pos} byte {b:#x} split {split}");
1508 if !valid {
1509 assert_eq!(res, Err(crate::Error::Status), "{ctx}");
1510 } else if b >= 0x80 {
1511 assert_eq!(res, Ok(Status::Complete(buf.len())), "{ctx}");
1512 assert_eq!(resp.reason, SlicePos::default(), "{ctx}");
1513 } else {
1514 assert_eq!(res, Ok(Status::Complete(buf.len())), "{ctx}");
1515 let start = usize::from(pos == 0 && b == b' ');
1517 assert_eq!(
1518 &buf[resp.reason.start..resp.reason.end],
1519 &reason[start..],
1520 "{ctx}"
1521 );
1522 }
1523 }
1524 }
1525 }
1526 }
1527 }
1528
1529 #[test]
1530 fn test_response_reason_ascii_block_boundaries() {
1531 const PREFIX: &[u8] = b"HTTP/1.1 200 ";
1532 for len in [
1533 63, 64, 65, 71, 72, 73, 127, 128, 129, 135, 136, 137, 199, 200, 255, 256,
1534 ] {
1535 for pos in [
1536 None,
1537 Some(0),
1538 Some(7),
1539 Some(8),
1540 Some(63),
1541 Some(64),
1542 Some(len - 1),
1543 ] {
1544 if pos.is_some_and(|pos| pos >= len) {
1545 continue;
1546 }
1547 let mut reason = vec![b'a'; len];
1548 if let Some(pos) = pos {
1549 reason[pos] = 0xff;
1550 }
1551 let mut buf = [PREFIX, &reason, b"\r\n"].concat();
1552 let consumed = buf.len();
1553 let expected = if pos.is_some() {
1554 SlicePos::default()
1555 } else {
1556 SlicePos {
1557 start: PREFIX.len(),
1558 end: PREFIX.len() + len,
1559 }
1560 };
1561 let mut resp = Response::default();
1562 assert_eq!(resp.parse(&buf), Ok(Status::Complete(consumed)));
1563 assert_eq!(resp.reason, expected, "len {len} pos {pos:?}");
1564 check_split(&buf, Response::parse_with_state, Response::parse);
1565
1566 buf.extend_from_slice(b"X-Next: \xff\r\n\r\n");
1567 let mut resp = Response::default();
1568 assert_eq!(resp.parse(&buf), Ok(Status::Complete(consumed)));
1569 assert_eq!(resp.reason, expected, "len {len} pos {pos:?}");
1570 }
1571 }
1572 }
1573
1574 res_par! {
1575 test_response_version_missing_space,
1576 b"HTTP/1.1"
1577 }
1578
1579 res_par! {
1580 test_response_code_missing_space,
1581 b"HTTP/1.1 200"
1582 }
1583
1584 res! {
1585 test_response_partial_parses_headers_as_much_as_it_can,
1586 b"HTTP/1.1 200 OK\r\nServer: yolo\r\n",
1587 |len, version, code, reason, headers, eof| {
1588 assert_eq!(len, 31);
1589 assert_eq!(version, 1);
1590 assert_eq!(code, 200);
1591 assert_eq!(reason, "OK");
1592 assert_eq!(headers.len(), 1);
1593 assert_eq!(headers[0].0, "Server");
1594 assert_eq!(headers[0].1, b"yolo");
1595 assert!(!eof);
1596 }
1597 }
1598
1599 res! {
1600 test_response_empty_lines_prefix_lf_only,
1601 b"\n\nHTTP/1.1 200 OK\n\n",
1602 |len, version, code, reason, headers, eof| {
1603 assert_eq!(len, 19);
1604 assert_eq!(version, 1);
1605 assert_eq!(code, 200);
1606 assert_eq!(reason, "OK");
1607 assert_eq!(headers.len(), 0);
1608 assert!(eof);
1609 }
1610 }
1611
1612 res! {
1613 test_response_no_cr,
1614 b"HTTP/1.0 200\nContent-type: text/html\n\n",
1615 |len, version, code, reason, headers, eof| {
1616 assert_eq!(len, 38);
1617 assert_eq!(version, 0);
1618 assert_eq!(code, 200);
1619 assert_eq!(reason, "");
1620 assert_eq!(headers.len(), 1);
1621 assert_eq!(headers[0].0, "Content-type");
1622 assert_eq!(headers[0].1, b"text/html");
1623 assert!(eof);
1624 }
1625 }
1626
1627 #[test]
1629 fn partial_permutations() {
1630 let req_str = "GET / HTTP/1.1\r\n";
1631 let mut req = Request::default();
1632 for i in 0..req_str.len() {
1633 let status = req.parse(&req_str.as_bytes()[..i]);
1634 assert_eq!(
1635 status,
1636 Ok(Status::Partial),
1637 "partial request line should return partial. \
1638 Portion which failed: '{seg}' (below {i})",
1639 seg = &req_str[..i]
1640 );
1641 }
1642 }
1643
1644 headers_err! {
1645 test_forbid_headers_with_whitespace_between_header_name_and_colon,
1646 b"Access-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n",
1647 Err(Error::HeaderName)
1648 }
1649
1650 headers_err! {
1651 test_forbid_headers_with_obsolete_line_folding_at_end,
1652 b"Line-Folded-Header: hello there\r\n \r\n \r\n\r\n",
1653 Err(Error::HeaderName)
1654 }
1655
1656 headers_err! {
1657 test_forbid_headers_with_obsolete_line_folding_in_middle,
1658 b"Line-Folded-Header: hello \r\n \r\n there\r\n\r\n",
1659 Err(Error::HeaderName)
1660 }
1661
1662 headers_err! {
1663 test_forbid_headers_with_obsolete_line_folding_in_empty_header,
1664 b"Line-Folded-Header: \r\n \r\n \r\n\r\n",
1665 Err(Error::HeaderName)
1666 }
1667
1668 headers_err! {
1669 test_forbid_headers_with_empty_header_name,
1670 b": hello\r\nBread: baguette\r\n\r\n",
1671 Err(Error::HeaderName)
1672 }
1673
1674 headers_err! {
1675 test_forbid_headers_with_empty_header_name_second,
1676 b"Bread: baguette\r\n: hello\r\n\r\n",
1677 Err(Error::HeaderName)
1678 }
1679
1680 #[test]
1681 fn test_chunk_size() {
1682 assert_eq!(parse_chunk_size(b"0\r\n"), Ok(Status::Complete((3, 0))));
1683 assert_eq!(
1684 parse_chunk_size(b"12\r\nchunk"),
1685 Ok(Status::Complete((4, 18)))
1686 );
1687 assert_eq!(
1688 parse_chunk_size(b"3086d\r\n"),
1689 Ok(Status::Complete((7, 198_765)))
1690 );
1691 assert_eq!(
1692 parse_chunk_size(b"3735AB1;foo bar*\r\n"),
1693 Ok(Status::Complete((18, 57_891_505)))
1694 );
1695 assert_eq!(
1696 parse_chunk_size(b"3735ab1 ; baz \r\n"),
1697 Ok(Status::Complete((16, 57_891_505)))
1698 );
1699 assert_eq!(parse_chunk_size(b"77a65\r"), Ok(Status::Partial));
1700 assert_eq!(parse_chunk_size(b"ab"), Ok(Status::Partial));
1701 assert_eq!(
1702 parse_chunk_size(b"567f8a\rfoo"),
1703 Err(crate::InvalidChunkSize)
1704 );
1705 assert_eq!(
1706 parse_chunk_size(b"567f8a\rfoo"),
1707 Err(crate::InvalidChunkSize)
1708 );
1709 assert_eq!(
1710 parse_chunk_size(b"567xf8a\r\n"),
1711 Err(crate::InvalidChunkSize)
1712 );
1713 assert_eq!(
1714 parse_chunk_size(b"ffffffffffffffff\r\n"),
1715 Ok(Status::Complete((18, u64::MAX)))
1716 );
1717 assert_eq!(
1718 parse_chunk_size(b"1ffffffffffffffff\r\n"),
1719 Err(crate::InvalidChunkSize)
1720 );
1721 assert_eq!(
1722 parse_chunk_size(b"Affffffffffffffff\r\n"),
1723 Err(crate::InvalidChunkSize)
1724 );
1725 assert_eq!(
1726 parse_chunk_size(b"fffffffffffffffff\r\n"),
1727 Err(crate::InvalidChunkSize)
1728 );
1729 }
1730
1731 #[test]
1732 fn test_chunk_size_empty() {
1733 for buf in [&b"\r\n"[..], b";a\r\n", b" \r\n", b"\t;a\r\n", b"\r"] {
1734 assert_eq!(
1735 parse_chunk_size(buf),
1736 Err(crate::InvalidChunkSize),
1737 "{buf:?}"
1738 );
1739 }
1740 assert_eq!(parse_chunk_size(b"0\r\n"), Ok(Status::Complete((3, 0))));
1741 }
1742
1743 fn header_split(buf: &[u8]) -> (Result<HeaderParsed>, Header) {
1746 let mut one = Header::default();
1747 let expected = one.parse(buf);
1748 for split in 0..buf.len() {
1749 let mut h = Header::default();
1750 let mut st = State::default();
1751 let first = h.parse_with_state(&buf[..split], &mut st);
1752 if first.is_err() {
1753 assert_eq!(first, expected, "split {split} of {buf:?}");
1754 continue;
1755 }
1756 assert_eq!(first, Ok(Status::Partial), "split {split} of {buf:?}");
1757 let res = h.parse_with_state(buf, &mut st);
1758 assert_eq!(res, expected, "split {split} of {buf:?}");
1759 if expected.is_ok() {
1760 assert_eq!(h, one, "split {split} of {buf:?}");
1761 }
1762 }
1763 (expected, one)
1764 }
1765
1766 #[test]
1767 fn test_header_split_bare_cr() {
1768 assert_eq!(header_split(b"X: a\rb\r\n").0, Err(Error::HeaderValue));
1769 assert_eq!(header_split(b"X:\rb\r\n").0, Err(Error::HeaderValue));
1770 assert_eq!(header_split(b"\rX: b\r\n").0, Err(Error::NewLine));
1771
1772 let (res, h) = header_split(b"X: a b\r\n");
1773 assert_eq!(res, Ok(Status::Complete(HeaderParsed::Header(8))));
1774 assert_eq!(h.value, SlicePos { start: 3, end: 6 });
1775 assert_eq!(
1776 header_split(b"X:\r\n").0,
1777 Ok(Status::Complete(HeaderParsed::Header(4)))
1778 );
1779 assert_eq!(
1780 header_split(b"\r\n").0,
1781 Ok(Status::Complete(HeaderParsed::Eof(2)))
1782 );
1783 }
1784
1785 #[test]
1786 fn test_request_split() {
1787 for buf in [
1788 &b"GET /path HTTP/1.1\r\n"[..],
1789 b"GET /path HTTP/1.1\r\n",
1790 b"PUT /path HTTP/1.0\n",
1791 b"CUSTOM /path HTTP/1.1\r\n",
1792 b"\r\n\nPOST /path HTTP/1.1\r\n",
1793 b"GET /path HTTP/1.1\r\r\n",
1794 b"GET /path HTTP/1.1\rX",
1795 b"\rGET /path HTTP/1.1\r\n",
1796 b"GE\x00T /path HTTP/1.1\r\n",
1797 b"GET /pa\x7fth HTTP/1.1\r\n",
1798 b"GET /path HTTP/2.0\r\n",
1799 ] {
1800 check_split(buf, Request::parse_with_state, Request::parse);
1801 }
1802 }
1803
1804 #[test]
1805 fn test_response_split() {
1806 for buf in [
1807 &b"HTTP/1.1 200 OK\r\n"[..],
1808 b"HTTP/1.0 404 Not Found \n",
1809 b"\r\nHTTP/1.1 200\r\n",
1810 b"HTTP/1.1 200\n",
1811 b"HTTP/1.1 200 \r\n",
1812 b"HTTP/1.1 200 caf\xc3\xa9\r\n",
1813 b"HTTP/1.1 200 OK\r\r\n",
1814 b"HTTP/1.1 200\rX",
1815 b"HTTP/1.1 2x0 OK\r\n",
1816 b"HTTP/1.1\r\n200 OK\r\n",
1817 b"HTTP/1.1 200 O\x00K\r\n",
1818 ] {
1819 check_split(buf, Response::parse_with_state, Response::parse);
1820 }
1821 }
1822
1823 #[test]
1824 fn test_response_empty_reason() {
1825 for buf in [
1826 &b"HTTP/1.1 200\r\n"[..],
1827 b"HTTP/1.1 200 \r\n",
1828 b"HTTP/1.1 200 \r\n",
1829 b"HTTP/1.1 200\n",
1830 b"HTTP/1.1 200 \n",
1831 b"\r\nHTTP/1.1 200 \n",
1832 ] {
1833 let mut resp = Response::default();
1834 assert_eq!(resp.parse(buf), Ok(Status::Complete(buf.len())));
1835 assert_eq!(resp.reason, SlicePos::default());
1836 check_split(buf, Response::parse_with_state, Response::parse);
1837 }
1838 }
1839
1840 fn check_split<T: Default + PartialEq + fmt::Debug>(
1843 buf: &[u8],
1844 parse: fn(&mut T, &[u8], &mut State) -> Result<usize>,
1845 one_shot: fn(&mut T, &[u8]) -> Result<usize>,
1846 ) {
1847 let mut one = T::default();
1848 let expected = one_shot(&mut one, buf);
1849 let check = |res: Result<usize>, v: &T, what: &str| {
1850 assert_eq!(res, expected, "{what} of {buf:?}");
1851 if expected.is_ok() {
1852 assert_eq!(v, &one, "{what} of {buf:?}");
1853 }
1854 };
1855
1856 for split in 0..buf.len() {
1857 let mut v = T::default();
1858 let mut st = State::default();
1859 let first = parse(&mut v, &buf[..split], &mut st);
1860 if first.is_err() {
1861 check(first, &v, &format!("split {split}"));
1862 continue;
1863 }
1864 assert_eq!(first, Ok(Status::Partial), "split {split} of {buf:?}");
1865 let res = parse(&mut v, buf, &mut st);
1866 check(res, &v, &format!("split {split}"));
1867 }
1868
1869 let mut v = T::default();
1870 let mut st = State::default();
1871 for len in 0..=buf.len() {
1872 let res = parse(&mut v, &buf[..len], &mut st);
1873 if res != Ok(Status::Partial) || len == buf.len() {
1874 check(res, &v, &format!("byte by byte, len {len}"));
1875 break;
1876 }
1877 assert!(st.cursor + 9 >= len, "len {len} of {buf:?}");
1880 }
1881 }
1882
1883 #[test]
1884 fn test_request_resumes_long_method() {
1885 let buf = [b'A'; 4096];
1886 let mut req = Request::default();
1887 let mut st = State::default();
1888 assert_eq!(req.parse_with_state(&buf, &mut st), Ok(Status::Partial));
1889 assert_eq!((st.state, st.start, st.cursor), (1, 0, 4096));
1890 }
1891
1892 #[test]
1893 fn test_header_name_chars_at_every_position() {
1894 let (lens, bytes): (&[usize], Vec<u8>) = if cfg!(miri) {
1896 let bytes = [
1898 0x00, b'\t', b' ', b'!', b'"', b'x', b':', b'~', 0x7f, 0x80, 0xff,
1899 ];
1900 (&[1, 16, 17, 33], bytes.to_vec())
1901 } else {
1902 (
1903 &[1, 15, 16, 17, 31, 32, 33, 47, 48, 70],
1904 (0..=255).collect(),
1905 )
1906 };
1907 for &len in lens {
1908 for pos in 1..len {
1909 for &b in &bytes {
1910 let mut buf = vec![b'x'; len];
1911 buf[pos] = b;
1912 buf.extend_from_slice(b": v\r\n");
1913 let res = Header::default().parse(&buf);
1914 if utils::TOKEN_MAP[b as usize] {
1915 assert_eq!(
1916 res,
1917 Ok(Status::Complete(HeaderParsed::Header(len + 5))),
1918 "len {len} pos {pos} byte {b}"
1919 );
1920 } else if b == b':' {
1921 assert!(res.is_ok(), "len {len} pos {pos}");
1922 } else {
1923 assert_eq!(res, Err(Error::HeaderName), "len {len} pos {pos} byte {b}");
1924 }
1925 }
1926 }
1927 }
1928 }
1929
1930 #[test]
1931 fn test_header_state_reuse() {
1932 let buf = b"A: 1\r\nB: 2\r\n\r\nbody";
1933 let mut h = Header::default();
1934 let mut st = State::default();
1935 assert_eq!(
1936 h.parse_with_state(buf, &mut st),
1937 Ok(Status::Complete(HeaderParsed::Header(6)))
1938 );
1939 assert_eq!(
1940 h.parse_with_state(buf, &mut st),
1941 Ok(Status::Complete(HeaderParsed::Header(12)))
1942 );
1943 assert_eq!((h.name.start, h.value.end), (6, 10));
1944 assert_eq!(
1945 h.parse_with_state(buf, &mut st),
1946 Ok(Status::Complete(HeaderParsed::Eof(14)))
1947 );
1948 }
1949
1950 #[test]
1951 fn test_invalid_state() {
1952 for st in [
1953 State {
1954 state: 3,
1955 start: 0,
1956 cursor: 100,
1957 },
1958 State {
1959 state: 1,
1960 start: 2,
1961 cursor: 1,
1962 },
1963 State {
1964 state: 8,
1965 start: 0,
1966 cursor: 0,
1967 },
1968 ] {
1969 let mut s = st;
1970 assert_eq!(
1971 Header::default().parse_with_state(b"abc", &mut s),
1972 Err(Error::HeaderName)
1973 );
1974 let mut s = st;
1975 assert_eq!(
1976 Request::default().parse_with_state(b"abc", &mut s),
1977 Err(Error::Token)
1978 );
1979 let mut s = st;
1980 assert_eq!(
1981 Response::default().parse_with_state(b"abc", &mut s),
1982 Err(Error::Status)
1983 );
1984 }
1985 }
1986
1987 #[test]
1988 fn test_chunk_size_extension_control_chars() {
1989 for buf in [
1990 &b"4;a\nX\r\n"[..],
1991 b"4;a=\"\nX\"\r\n",
1992 b"4;a\x00\r\n",
1993 b"4;a\x7f\r\n",
1994 b"4;\n",
1995 ] {
1996 assert_eq!(
1997 parse_chunk_size(buf),
1998 Err(crate::InvalidChunkSize),
1999 "{buf:?}"
2000 );
2001 }
2002 assert_eq!(
2003 parse_chunk_size(b"4 ;a=\"b\tc \x80\";d\t\r\n"),
2004 Ok(Status::Complete((17, 4)))
2005 );
2006 }
2007
2008 res! {
2009 test_allow_response_with_multiple_space_delimiters,
2010 b"HTTP/1.1 200 OK\r\n\r\n",
2011 |len, version, code, reason, headers, eof| {
2012 assert_eq!(len, 22);
2013 assert_eq!(version, 1);
2014 assert_eq!(code, 200);
2015 assert_eq!(reason, "OK");
2016 assert_eq!(headers.len(), 0);
2017 assert!(eof);
2018 }
2019 }
2020
2021 res_err! {
2024 test_forbid_response_with_weird_whitespace_delimiters,
2025 b"HTTP/1.1 200\rOK\r\n\r\n",
2026 Err(Error::Status)
2027 }
2028
2029 req! {
2030 test_allow_request_with_multiple_space_delimiters,
2031 b"GET / HTTP/1.1\r\n\r\n",
2032 |len, method, path, version, headers, eof| {
2033 assert_eq!(len, 22);
2034 assert_eq!(method, "GET");
2035 assert_eq!(path, "/");
2036 assert_eq!(version, 1);
2037 assert_eq!(headers.len(), 0);
2038 assert!(eof);
2039 }
2040 }
2041
2042 req_err! {
2045 test_forbid_request_with_weird_whitespace_delimiters,
2046 b"GET\r/\rHTTP/1.1\r\n\r\n",
2047 Err(Error::Token)
2048 }
2049
2050 req_err! {
2051 test_request_with_multiple_spaces_and_bad_path,
2052 b"GET /foo ohno HTTP/1.1\r\n\r\n",
2053 Err(Error::Version)
2054 }
2055
2056 req_err! {
2060 test_request_with_del_in_path,
2061 b"GET /foo\x7Fohno HTTP/1.1\r\n\r\n",
2062 Err(Error::Token)
2063 }
2064
2065 res_err! {
2209 test_response_with_spaces_in_code,
2210 b"HTTP/1.1 99 200 OK\r\n\r\n",
2211 Err(Error::Status)
2212 }
2213
2214 headers_err! {
2215 test_headers_with_whitespace_between_header_name_and_colon,
2216 b"Access-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n",
2217 Err(Error::HeaderName)
2218 }
2219
2220 headers_err! {
2221 test_headers_with_invalid_char_between_header_name_and_colon,
2222 b"Access-Control-Allow-Credentials\xFF: true\r\nBread: baguette\r\n\r\n",
2223 Err(Error::HeaderName)
2224 }
2225
2226 headers_err! {
2227 test_ignore_header_line_with_missing_colon_in_response,
2228 b"Access-Control-Allow-Credentials\r\nBread: baguette\r\n\r\n",
2229 Err(Error::HeaderName)
2230 }
2231
2232 headers_err! {
2233 test_headers_header_with_missing_colon_with_folding,
2234 b"Access-Control-Allow-Credentials \r\n hello\r\nBread: baguette\r\n\r\n",
2235 Err(Error::HeaderName)
2236 }
2237
2238 headers_err! {
2239 test_headers_header_with_nul_in_header_name,
2240 b"Access-Control-Allow-Cred\0entials: hello\r\nBread: baguette\r\n\r\n",
2241 Err(Error::HeaderName)
2242 }
2243
2244 headers_err! {
2245 test_header_with_cr_in_header_name,
2246 b"Access-Control-Allow-Cred\rentials: hello\r\nBread: baguette\r\n\r\n",
2247 Err(Error::HeaderName)
2248 }
2249
2250 headers_err! {
2251 test_header_with_nul_in_whitespace_before_colon,
2252 b"Access-Control-Allow-Credentials \0: hello\r\nBread: baguette\r\n\r\n",
2253 Err(Error::HeaderName)
2254 }
2255
2256 headers_err! {
2257 test_header_with_nul_in_value,
2258 b"Access-Control-Allow-Credentials: hell\0o\r\nBread: baguette\r\n\r\n",
2259 Err(Error::HeaderValue)
2260 }
2261
2262 headers_err! {
2263 test_header_with_invalid_char_in_value,
2264 b"Access-Control-Allow-Credentials: hell\x01o\r\nBread: baguette\r\n\r\n",
2265 Err(Error::HeaderValue)
2266 }
2267
2268 headers_err! {
2269 test_header_with_invalid_char_in_value_with_folding,
2270 b"Access-Control-Allow-Credentials: hell\x01o \n world!\r\nBread: baguette\r\n\r\n",
2271 Err(Error::HeaderValue)
2272 }
2273
2274 headers_err! {
2275 test_header_with_space_before_first_header,
2276 b" Space-Before-Header: hello there\r\n\r\n",
2277 Err(Error::HeaderName)
2278 }
2279
2280 res! {
2281 test_response_no_space_after_colon,
2282 b"HTTP/1.1 200 OK\r\nfoo:bar\r\n\r\n",
2283 |len, version, code, reason, headers, eof| {
2284 assert_eq!(len, 28);
2285 assert_eq!(version, 1);
2286 assert_eq!(code, 200);
2287 assert_eq!(reason, "OK");
2288 assert_eq!(headers.len(), 1);
2289 assert_eq!(headers[0].0, "foo");
2290 assert_eq!(headers[0].1, b"bar");
2291 assert!(eof);
2292 }
2293 }
2294
2295 req_err! {
2296 test_request_with_leading_space,
2297 b" GET / HTTP/1.1\r\nfoo:bar\r\n\r\n",
2298 Err(Error::Token)
2299 }
2300
2301 req_err! {
2302 test_request_with_invalid_method,
2303 b"P()ST / HTTP/1.1\r\nfoo:bar\r\n\r\n",
2304 Err(Error::Token)
2305 }
2306
2307 req! {
2308 test_utf8_in_path_ok,
2309 b"GET /test?post=I\xE2\x80\x99msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n",
2310 |len, method, path, version, headers, eof| {
2311 assert_eq!(len, 67);
2312 assert_eq!(method, "GET");
2313 assert_eq!(path, "/test?post=I’msorryIforkedyou");
2314 assert_eq!(version, 1);
2315 assert_eq!(headers.len(), 1);
2316 assert_eq!(headers[0].0, "Host");
2317 assert_eq!(headers[0].1, b"example.org");
2318 assert!(eof);
2319 }
2320 }
2321
2322 #[test]
2323 fn test_bad_utf8_in_path() {
2324 const BUF: &[u8] =
2325 b"GET /test?post=I\xE2msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n";
2326
2327 let mut req = Request::default();
2328 assert!(req.parse(BUF).unwrap().is_complete());
2329 assert!(str::from_utf8(&BUF[req.path.start..req.path.end]).is_err());
2330 }
2331
2332 #[test]
2333 fn test_parse_uri_delimiter() {
2334 for input in [&b"/path "[..], b"/path ", b"/path HTTP/1.1\r\n"] {
2335 assert_eq!(parse_uri(input), Ok(Status::Complete("/path")));
2336 }
2337 assert_eq!(parse_uri(b"/path"), Ok(Status::Partial));
2338 }
2339
2340 #[test]
2341 fn test_parse_uri_utf8() {
2342 assert_eq!(
2343 parse_uri(b"/caf\xc3\xa9 "),
2344 Ok(Status::Complete("/caf\u{e9}"))
2345 );
2346 assert_eq!(parse_uri(b"/\xff "), Err(Error::Token));
2347 assert_eq!(parse_uri(b"/\xc3 "), Err(Error::Token));
2348 }
2349
2350 #[rustfmt::skip]
2351 res! {
2352 test_response_bench,
2353 b"\
2354HTTP/1.0 200 OK\r\n\
2355Date: Wed, 21 Oct 2015 07:28:00 GMT\r\n\
2356Set-Cookie: session=60; user_id=1\r\n\r\n",
2357 |len, version, code, reason, headers, eof| {
2358 assert_eq!(len, 91);
2359 assert_eq!(version, 0);
2360 assert_eq!(code, 200);
2361 assert_eq!(reason, "OK");
2362 assert_eq!(headers.len(), 2);
2363 assert_eq!(headers[0].0, "Date");
2364 assert_eq!(headers[0].1, b"Wed, 21 Oct 2015 07:28:00 GMT");
2365 assert_eq!(headers[1].0, "Set-Cookie");
2366 assert_eq!(headers[1].1, b"session=60; user_id=1");
2367 assert!(eof);
2368 }
2369 }
2370}