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};
31
32mod iter;
33#[macro_use]
34mod macros;
35mod headers;
36mod simd;
37mod utils;
38mod version;
39
40pub use crate::headers::{Header, HeaderParsed};
41pub use crate::version::parse_version;
42
43use crate::iter::Bytes;
44
45#[derive(Copy, Clone, PartialEq, Eq, Debug)]
47pub enum Error {
48 HeaderName,
50 HeaderValue,
52 NewLine,
54 Status,
56 Token,
58 TooManyHeaders,
60 Version,
62}
63
64impl Error {
65 #[inline]
66 fn description_str(self) -> &'static str {
67 match self {
68 Error::HeaderName => "invalid header name",
69 Error::HeaderValue => "invalid header value",
70 Error::NewLine => "invalid new line",
71 Error::Status => "invalid response status",
72 Error::Token => "invalid token",
73 Error::TooManyHeaders => "too many headers",
74 Error::Version => "invalid HTTP version",
75 }
76 }
77}
78
79impl fmt::Display for Error {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 f.write_str(self.description_str())
82 }
83}
84
85#[cfg(feature = "std")]
86impl std::error::Error for Error {
87 fn description(&self) -> &str {
88 self.description_str()
89 }
90}
91
92#[derive(Debug, PartialEq, Eq)]
94pub struct InvalidChunkSize;
95
96impl fmt::Display for InvalidChunkSize {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 f.write_str("invalid chunk size")
99 }
100}
101
102pub type Result<T> = result::Result<Status<T>, Error>;
108
109#[derive(Copy, Clone, Eq, PartialEq, Debug)]
115pub enum Status<T> {
116 Complete(T),
118 Partial,
120}
121
122impl<T> Status<T> {
123 #[inline]
125 pub fn is_complete(&self) -> bool {
126 match *self {
127 Status::Complete(..) => true,
128 Status::Partial => false,
129 }
130 }
131
132 #[inline]
134 pub fn is_partial(&self) -> bool {
135 match *self {
136 Status::Complete(..) => false,
137 Status::Partial => true,
138 }
139 }
140
141 #[inline]
144 pub fn unwrap(self) -> T {
145 match self {
146 Status::Complete(t) => t,
147 Status::Partial => panic!("Tried to unwrap Status::Partial"),
148 }
149 }
150}
151
152#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
153pub struct State {
154 pub state: u8,
156 pub start: usize,
158 pub cursor: usize,
159}
160
161#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
162pub struct SlicePos {
164 pub start: usize,
165 pub end: usize,
166}
167
168impl SlicePos {
169 pub(crate) fn reset(&mut self) {
170 self.start = 0;
171 self.end = 0;
172 }
173}
174
175#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
189pub struct Request {
190 pub method: SlicePos,
192 pub path: SlicePos,
194 pub version: u8,
196}
197
198impl Request {
199 #[inline]
200 pub fn parse(&mut self, src: &[u8]) -> Result<usize> {
202 let mut st = State::default();
203 self.parse_with_state(src, &mut st)
204 }
205
206 #[inline]
207 pub fn parse_with_state(&mut self, src: &[u8], st: &mut State) -> Result<usize> {
209 if st.state == 0 {
210 let mut tmp = State::default();
211 let mut bytes = Bytes::new(src, &mut tmp);
212 self.method = complete!(parse_method_inner(&mut bytes));
213 st.state = 1;
214 st.start = bytes.st.start;
215 st.cursor = bytes.st.cursor;
216 }
217 if st.state == 1 {
218 let mut bytes = Bytes::new(src, st);
219 self.path = complete!(parse_uri_inner(&mut bytes));
220 bytes.st.state = 2;
221 }
222 if st.state == 2 {
223 let mut tmp = *st;
224 let mut bytes = Bytes::new(src, &mut tmp);
225 self.version = complete!(version::parse_version_inner(&mut bytes));
226 st.state = 3;
227 st.start = bytes.st.start;
228 st.cursor = bytes.st.cursor;
229 }
230
231 let mut bytes = Bytes::new(src, st);
232 newline!(bytes);
233 Ok(Status::Complete(bytes.cursor()))
234 }
235}
236
237#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
239pub struct Response {
240 pub version: u8,
242 pub code: u16,
244 pub reason: SlicePos,
246}
247
248impl Response {
249 #[inline]
250 pub fn parse(&mut self, src: &[u8]) -> Result<usize> {
252 let mut st = State::default();
253 let mut bytes = Bytes::new(src, &mut st);
254
255 complete!(utils::skip_empty_lines(&mut bytes));
256
257 self.version = complete!(version::parse_version_inner(&mut bytes));
259 complete!(utils::skip_empty_lines(&mut bytes));
260 expect!(bytes.next() == b' ' => Err(Error::Version));
261 bytes.commit();
262 complete!(utils::skip_spaces(&mut bytes));
263
264 self.code = complete!(parse_code(&mut bytes));
266
267 self.reason = match next!(bytes) {
277 b' ' => {
278 complete!(utils::skip_spaces(&mut bytes));
279 bytes.commit();
280 complete!(parse_reason(&mut bytes))
281 }
282 b'\r' => {
283 expect!(bytes.next() == b'\n' => Err(Error::Status));
284 bytes.commit();
285 SlicePos::default()
286 }
287 b'\n' => {
288 bytes.commit();
289 SlicePos::default()
290 }
291 _ => return Err(Error::Status),
292 };
293
294 Ok(Status::Complete(bytes.cursor()))
295 }
296}
297
298#[inline]
299pub fn parse_method(src: &[u8]) -> Result<&str> {
301 let s = complete!(parse_method_inner(&mut Bytes::new(
302 src,
303 &mut State::default()
304 )));
305 let m = unsafe { str::from_utf8_unchecked(&src[s.start..s.end]) };
307 Ok(Status::Complete(m))
308}
309
310#[inline]
311fn parse_method_inner(bytes: &mut Bytes<'_, '_>) -> Result<SlicePos> {
313 const GET: [u8; 4] = *b"GET ";
314 const POST: [u8; 4] = *b"POST";
315
316 complete!(utils::skip_empty_lines(bytes));
317
318 match bytes.peek_n::<4>() {
319 Some(GET) => {
320 bytes.advance(4); let method = bytes.slice_position(1);
323 complete!(utils::skip_spaces(bytes));
324 Ok(Status::Complete(method))
325 }
326 Some(POST) if bytes.peek_ahead(4) == Some(b' ') => {
331 bytes.advance(5); let method = bytes.slice_position(1);
334 complete!(utils::skip_spaces(bytes));
335 Ok(Status::Complete(method))
336 }
337 _ => {
338 let b = next!(bytes);
339 if !utils::is_method_token(b) {
340 return Err(Error::Token);
342 }
343
344 loop {
345 let b = next!(bytes);
346 if b == b' ' {
347 return Ok(Status::Complete(
348 bytes.slice_position(1),
350 ));
351 } else if !utils::is_method_token(b) {
352 return Err(Error::Token);
353 }
354 }
355 }
356 }
357}
358
359#[inline]
373fn parse_reason(bytes: &mut Bytes<'_, '_>) -> Result<SlicePos> {
374 let mut seen_obs_text = false;
375 loop {
376 let b = next!(bytes);
377 if b == b'\r' {
378 expect!(bytes.next() == b'\n' => Err(Error::Status));
379 return Ok(Status::Complete(
380 if seen_obs_text {
386 bytes.commit();
388 SlicePos::default()
389 } else {
390 bytes.slice_position(2)
392 },
393 ));
394 } else if b == b'\n' {
395 return Ok(Status::Complete(
396 if seen_obs_text {
400 bytes.commit();
402 SlicePos::default()
403 } else {
404 bytes.slice_position(1)
406 },
407 ));
408 } else if !(b == 0x09 || b == b' ' || (0x21..=0x7E).contains(&b) || b >= 0x80) {
409 return Err(Error::Status);
410 } else if b >= 0x80 {
411 seen_obs_text = true;
412 }
413 }
414}
415
416#[inline]
417#[allow(missing_docs)]
418pub fn parse_uri(src: &[u8]) -> Result<&str> {
420 let mut st = State::default();
421 let mut bytes = Bytes::new(src, &mut st);
422 if let Status::Complete(pos) = parse_uri_inner(&mut bytes)? {
423 if let Ok(path) = simdutf8::basic::from_utf8(&src[pos.start..pos.end]) {
424 Ok(Status::Complete(path))
425 } else {
426 Err(Error::Token)
427 }
428 } else {
429 Ok(Status::Partial)
430 }
431}
432
433#[inline]
434fn parse_uri_inner(bytes: &mut Bytes<'_, '_>) -> Result<SlicePos> {
436 let start = bytes.start();
437 simd::match_uri_vectored(bytes);
438 let b_end = bytes.cursor();
439
440 if next!(bytes) == b' ' {
441 if start == b_end {
443 return Err(Error::Token);
444 }
445
446 let end = bytes.cursor() - 1;
448 complete!(utils::skip_spaces(bytes));
449 Ok(Status::Complete(SlicePos { start, end }))
450 } else {
451 Err(Error::Token)
452 }
453}
454
455#[inline]
456fn parse_code(bytes: &mut Bytes<'_, '_>) -> Result<u16> {
457 let hundreds = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
458 let tens = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
459 let ones = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
460
461 Ok(Status::Complete(
462 (hundreds - b'0') as u16 * 100 + (tens - b'0') as u16 * 10 + (ones - b'0') as u16,
463 ))
464}
465
466pub fn parse_chunk_size(buf: &[u8]) -> result::Result<Status<(usize, u64)>, InvalidChunkSize> {
479 const RADIX: u64 = 16;
480 let mut st = State::default();
481 let mut bytes = Bytes::new(buf, &mut st);
482 let mut size = 0;
483 let mut in_chunk_size = true;
484 let mut in_ext = false;
485 let mut count = 0;
486 loop {
487 let b = next!(bytes);
488 match b {
489 b'0'..=b'9' if in_chunk_size => {
490 if count > 15 {
491 return Err(InvalidChunkSize);
492 }
493 count += 1;
494 if cfg!(debug_assertions) && size > (u64::MAX / RADIX) {
495 return Err(InvalidChunkSize);
500 }
501 size *= RADIX;
502 size += (b - b'0') as u64;
503 }
504 b'a'..=b'f' | b'A'..=b'F' if in_chunk_size => {
505 if count > 15 {
506 return Err(InvalidChunkSize);
507 }
508 count += 1;
509 if cfg!(debug_assertions) && size > (u64::MAX / RADIX) {
510 return Err(InvalidChunkSize);
511 }
512 size *= RADIX;
513 size += ((b | 0x20) + 10 - b'a') as u64;
514 }
515 b'\r' => match next!(bytes) {
516 b'\n' => break,
517 _ => return Err(InvalidChunkSize),
518 },
519 b';' if !in_ext => {
521 in_ext = true;
522 in_chunk_size = false;
523 }
524 b'\t' | b' ' if !in_ext && !in_chunk_size => {}
527 b'\t' | b' ' if in_chunk_size => in_chunk_size = false,
529 _ if in_ext => {}
535 _ => return Err(InvalidChunkSize),
538 }
539 }
540 Ok(Status::Complete((bytes.cursor(), size)))
541}
542
543#[cfg(test)]
544mod tests {
545 #![allow(clippy::items_after_statements)]
546 use super::*;
547
548 macro_rules! req {
549 ($name:ident, $buf:expr, |$len:ident, $method:ident, $path:ident, $version:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
550 #[test]
551 fn $name() {
552 let mut req = Request::default();
553 let mut b = $buf.as_ref();
554 if let Ok(Status::Complete(l)) = req.parse(b) {
555 let mut consumed = l;
556 let mut headers = Vec::new();
557 let mut header = Header::default();
558 let mut headers_eof = false;
559 b = &b[consumed..];
560
561 while let Status::Complete(hdr) = header.parse(b).unwrap() {
562 match hdr {
563 HeaderParsed::Header(l) => {
564 consumed += l;
565 let name = String::from_utf8(Vec::from(
566 &b[header.name.start..header.name.end],
567 ))
568 .unwrap();
569 let value = Vec::from(&b[header.value.start..header.value.end]);
570 headers.push((name, value));
571 b = &b[l..];
572 }
573 HeaderParsed::Eof(l) => {
574 consumed += l;
575 headers_eof = true;
576 break;
577 }
578 }
579 }
580
581 let (path, method) = unsafe {
583 (
584 str::from_utf8_unchecked(&$buf.as_ref()[req.path.start..req.path.end]),
585 str::from_utf8_unchecked(
586 &$buf.as_ref()[req.method.start..req.method.end],
587 ),
588 )
589 };
590
591 closure(consumed, method, path, req.version, headers, headers_eof);
592 } else {
593 panic!()
594 }
595
596 fn closure(
597 $len: usize,
598 $method: &str,
599 $path: &str,
600 $version: u8,
601 $headers: Vec<(String, Vec<u8>)>,
602 $headers_eof: bool,
603 ) {
604 $body
605 }
606 }
607 };
608 }
609
610 macro_rules! headers {
611 ($name:ident, $buf:expr, |$len:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
612 #[test]
613 fn $name() {
614 let mut b = $buf.as_ref();
615 let mut consumed = 0;
616 let mut headers = Vec::new();
617 let mut header = Header::default();
618 let mut headers_eof = false;
619
620 while let Status::Complete(hdr) = header.parse(b).unwrap() {
621 match hdr {
622 HeaderParsed::Header(l) => {
623 consumed += l;
624 let name = String::from_utf8(Vec::from(
625 &b[header.name.start..header.name.end],
626 ))
627 .unwrap();
628 let value = Vec::from(&b[header.value.start..header.value.end]);
629 headers.push((name, value));
630 b = &b[l..];
631 }
632 HeaderParsed::Eof(l) => {
633 consumed += l;
634 headers_eof = true;
635 break;
636 }
637 }
638 }
639 closure(consumed, headers, headers_eof);
640
641 fn closure($len: usize, $headers: Vec<(String, Vec<u8>)>, $headers_eof: bool) {
642 $body
643 }
644 }
645 };
646 }
647
648 macro_rules! req_err {
649 ($name:ident, $buf:expr, $err:expr) => {
650 #[test]
651 fn $name() {
652 assert_eq!(Request::default().parse($buf.as_ref()), $err);
653 }
654 };
655 }
656
657 macro_rules! req_par {
658 ($name:ident, $buf:expr) => {
659 #[test]
660 fn $name() {
661 assert_eq!(Request::default().parse($buf.as_ref()), Ok(Status::Partial));
662 }
663 };
664 }
665
666 macro_rules! headers_err {
667 ($name:ident, $buf:expr, $err:expr) => {
668 #[test]
669 fn $name() {
670 let mut consumed = 0;
671 let mut header = Header::default();
672
673 let result = loop {
674 match header.parse(&$buf.as_ref()[consumed..]) {
675 Ok(Status::Complete(HeaderParsed::Header(l))) => {
676 consumed += l;
677 }
678 Ok(_) => break Ok(()),
679 Err(e) => break Err(e),
680 }
681 };
682 assert_eq!(result, $err);
683 }
684 };
685 }
686
687 req! {
688 test_request_simple,
689 b"GET / HTTP/1.1\r\n\r\n",
690 |len, method, path, version, headers, eof| {
691 assert_eq!(len, 18);
692 assert_eq!(method, "GET");
693 assert_eq!(path, "/");
694 assert_eq!(version, 1);
695 assert_eq!(headers.len(), 0);
696 assert!(eof);
697 }
698 }
699
700 req! {
701 test_request_simple_with_query_params,
702 b"GET /thing?data=a HTTP/1.1\r\n\r\n",
703 |len, method, path, version, headers, eof| {
704 assert_eq!(len, 30);
705 assert_eq!(method, "GET");
706 assert_eq!(path, "/thing?data=a");
707 assert_eq!(version, 1);
708 assert_eq!(headers.len(), 0);
709 assert!(eof);
710 }
711 }
712
713 req! {
714 test_request_simple_with_whatwg_query_params,
715 b"GET /thing?data=a^ HTTP/1.1\r\n\r\n",
716 |len, method, path, version, headers, eof| {
717 assert_eq!(len, 31);
718 assert_eq!(method, "GET");
719 assert_eq!(path, "/thing?data=a^");
720 assert_eq!(version, 1);
721 assert_eq!(headers.len(), 0);
722 assert!(eof);
723 }
724 }
725
726 req! {
727 test_request_headers,
728 b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \r\n\r\n ",
729 |len, method, path, version, headers, eof| {
730 assert_eq!(len, 43);
731 assert_eq!(method, "GET");
732 assert_eq!(path, "/");
733 assert_eq!(version, 1);
734 assert_eq!(headers.len(), 2);
735 assert_eq!(headers[0].0, "Host");
736 assert_eq!(headers[0].1, b"foo.com");
737 assert_eq!(headers[1].0, "Cookie");
738 assert_eq!(headers[1].1, b"");
739 assert!(eof);
740 }
741 }
742
743 req! {
744 test_request_headers_optional_whitespace,
745 b"GET / HTTP/1.1\r\nHost: \tfoo.com\t \r\nCookie: \t \r\n\r\n",
746 |len, method, path, version, headers, eof| {
747 assert_eq!(len, 48);
748 assert_eq!(method, "GET");
749 assert_eq!(path, "/");
750 assert_eq!(version, 1);
751 assert_eq!(headers.len(), 2);
752 assert_eq!(headers[0].0, "Host");
753 assert_eq!(headers[0].1, b"foo.com");
754 assert_eq!(headers[1].0, "Cookie");
755 assert_eq!(headers[1].1, b"");
756 assert!(eof);
757 }
758 }
759
760 req! {
761 test_request_header_value_htab_short,
763 b"GET / HTTP/1.1\r\nUser-Agent: some\tagent\r\n\r\n",
764 |len, method, path, version, headers, eof| {
765 assert_eq!(len, 42);
766 assert_eq!(method, "GET");
767 assert_eq!(path, "/");
768 assert_eq!(version, 1);
769 assert_eq!(headers.len(), 1);
770 assert_eq!(headers[0].0, "User-Agent");
771 assert_eq!(headers[0].1, b"some\tagent");
772 assert!(eof);
773 }
774 }
775
776 req! {
777 test_request_header_value_htab_med,
779 b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\tagent\r\n\r\n",
780 |len, method, path, version, headers, eof| {
781 assert_eq!(len, 52);
782 assert_eq!(method, "GET");
783 assert_eq!(path, "/");
784 assert_eq!(version, 1);
785 assert_eq!(headers.len(), 1);
786 assert_eq!(headers[0].0, "User-Agent");
787 assert_eq!(headers[0].1, b"1234567890some\tagent");
788 assert!(eof);
789 }
790 }
791
792 req! {
793 test_request_header_value_htab_long,
795 b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\t1234567890agent1234567890\r\n\r\n",
796 |len, method, path, version, headers, eof| {
797 assert_eq!(len, 72);
798 assert_eq!(method, "GET");
799 assert_eq!(path, "/");
800 assert_eq!(version, 1);
801 assert_eq!(headers.len(), 1);
802 assert_eq!(headers[0].0, "User-Agent");
803 assert_eq!(headers[0].1, &b"1234567890some\t1234567890agent1234567890"[..]);
804 assert!(eof);
805 }
806 }
807
808 req! {
809 test_request_header_no_space_after_colon,
811 b"GET / HTTP/1.1\r\nUser-Agent:omg-no-space1234567890some1234567890agent1234567890\r\n\r\n",
812 |len, method, path, version, headers, eof| {
813 assert_eq!(len, 82);
814 assert_eq!(method, "GET");
815 assert_eq!(path, "/");
816 assert_eq!(version, 1);
817 assert_eq!(headers.len(), 1);
818 assert_eq!(headers[0].0, "User-Agent");
819 assert_eq!(headers[0].1, &b"omg-no-space1234567890some1234567890agent1234567890"[..]);
820 assert!(eof);
821 }
822 }
823
824 req! {
825 test_request_headers_max,
826 b"GET / HTTP/1.1\r\nA: A\r\nB: B\r\nC: C\r\nD: D\r\n\r\n",
827 |_len, _method, _path, _verion, headers, eof| {
828 assert_eq!(headers.len(), 4);
829 assert!(eof);
830 }
831 }
832
833 req! {
834 test_request_multibyte,
835 b"GET / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: \xe3\x81\xb2\xe3/1.0\r\n\r\n",
836 |len, method, path, version, headers, eof| {
837 assert_eq!(len, 55);
838 assert_eq!(method, "GET");
839 assert_eq!(path, "/");
840 assert_eq!(version, 1);
841 assert_eq!(headers.len(), 2);
842 assert_eq!(headers[0].0, "Host");
843 assert_eq!(headers[0].1, b"foo.com");
844 assert_eq!(headers[1].0, "User-Agent");
845 assert_eq!(headers[1].1, b"\xe3\x81\xb2\xe3/1.0");
846 assert!(eof);
847 }
848 }
849
850 req_par! {
852 test_request_one_byte_method,
853 b"G"
854 }
855
856 req_par! {
858 test_request_partial_method,
859 b"GE"
860 }
861
862 req_par! {
864 test_request_method_no_delimiter,
865 b"GET"
866 }
867
868 req_par! {
871 test_request_method_only,
872 b"GET "
873 }
874
875 req! {
876 test_request_partial,
877 b"GET / HTTP/1.1\r\n\r",
878 |len, method, path, version, headers, eof| {
879 assert_eq!(len, b"GET / HTTP/1.1\r\n\r".len() - 1);
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_par! {
889 test_request_partial_version,
890 b"GET / HTTP/1."
891 }
892
893 req_par! {
894 test_request_method_path_no_delimiter,
895 b"GET /"
896 }
897
898 req_par! {
899 test_request_method_path_only,
900 b"GET / "
901 }
902
903 req! {
904 test_request_partial_parses_headers_as_much_as_it_can,
905 b"GET / HTTP/1.1\r\nHost: yolo\r\n",
906 |len, method, path, version, headers, eof| {
907 assert_eq!(len, 28);
908 assert_eq!(method, "GET");
909 assert_eq!(path, "/");
910 assert_eq!(version, 1);
911 assert_eq!(headers.len(), 1);
912 assert_eq!(headers[0].0, "Host");
913 assert_eq!(headers[0].1, b"yolo");
914 assert!(!eof);
915 }
916 }
917
918 req! {
919 test_request_newlines,
920 b"GET / HTTP/1.1\nHost: foo.bar\n\n",
921 |_len, _method, _path, _verion, _headers, eof| {
922 assert!(eof);
923 }
924 }
925
926 req! {
927 test_request_empty_lines_prefix,
928 b"\r\n\r\nGET / HTTP/1.1\r\n\r\n",
929 |len, method, path, version, headers, eof| {
930 assert_eq!(len, 22);
931 assert_eq!(method, "GET");
932 assert_eq!(path, "/");
933 assert_eq!(version, 1);
934 assert_eq!(headers.len(), 0);
935 assert!(eof);
936 }
937 }
938
939 req! {
940 test_request_empty_lines_prefix_lf_only,
941 b"\n\nGET / HTTP/1.1\n\n",
942 |len, method, path, version, headers, eof| {
943 assert_eq!(len, 18);
944 assert_eq!(method, "GET");
945 assert_eq!(path, "/");
946 assert_eq!(version, 1);
947 assert_eq!(headers.len(), 0);
948 assert!(eof);
949 }
950 }
951
952 req! {
953 test_request_path_backslash,
954 b"\n\nGET /\\?wayne\\=5 HTTP/1.1\n\n",
955 |len, method, path, version, headers, eof| {
956 assert_eq!(len, 28);
957 assert_eq!(method, "GET");
958 assert_eq!(path, "/\\?wayne\\=5");
959 assert_eq!(version, 1);
960 assert_eq!(headers.len(), 0);
961 assert!(eof);
962 }
963 }
964
965 req_err! {
966 test_request_with_invalid_token_delimiter,
967 b"GET\n/ HTTP/1.1\r\nHost: foo.bar\r\n\r\n",
968 Err(Error::Token)
969 }
970
971 req_err! {
972 test_request_with_invalid_but_short_version,
973 b"GET / HTTP/1!",
974 Err(Error::Version)
975 }
976
977 req_err! {
978 test_request_with_empty_method,
979 b" / HTTP/1.1\r\n\r\n",
980 Err(Error::Token)
981 }
982
983 req_err! {
984 test_request_with_empty_path,
985 b"GET HTTP/1.1\r\n\r\n",
986 Err(Error::Token)
987 }
988
989 req_err! {
990 test_request_with_empty_method_and_path,
991 b" HTTP/1.1\r\n\r\n",
992 Err(Error::Token)
993 }
994
995 headers! {
996 test_headers_optional_whitespace,
997 b"Host: \tfoo.com\t \r\nCookie: \t \r\n",
998 |len, headers, eof| {
999 assert_eq!(len, 30);
1000 assert_eq!(headers.len(), 2);
1001 assert_eq!(headers[0].0, "Host");
1002 assert_eq!(headers[0].1, b"foo.com");
1003 assert_eq!(headers[1].0, "Cookie");
1004 assert_eq!(headers[1].1, b"");
1005 assert!(!eof);
1006 }
1007 }
1008
1009 #[test]
1010 fn test_headers_with_state() {
1011 const B1: &[u8] = b"Host";
1012 const B2: &[u8] = b"Host: \t";
1013 const B3: &[u8] = b"Host: \tfoo.com\t ";
1014 const B4: &[u8] = b"Host: \tfoo.com\t \r\nCoo";
1015 const B5: &[u8] = b"Host: \tfoo.com\t \r\nCookie: \t \r\n\r\n";
1016
1017 let mut st = State::default();
1018 let mut header = Header::default();
1019 assert!(header.parse_with_state(B1, &mut st).unwrap().is_partial());
1020 assert_eq!(st.state, 1);
1021 assert_eq!(st.start, 0);
1022 assert_eq!(st.cursor, 4);
1023 assert_eq!(header.name, SlicePos { start: 0, end: 0 });
1024 assert!(header.parse_with_state(B2, &mut st).unwrap().is_partial());
1025 assert_eq!(st.state, 2);
1026 assert_eq!(st.start, 0);
1027 assert_eq!(st.cursor, 7);
1028 assert_eq!(header.name, SlicePos { start: 0, end: 4 });
1029 assert_eq!(&B5[header.name.start..header.name.end], b"Host");
1030
1031 assert!(header.parse_with_state(B3, &mut st).unwrap().is_partial());
1032 assert_eq!(st.state, 3);
1033 assert_eq!(st.start, 0);
1034 assert_eq!(st.cursor, 16);
1035 assert_eq!(header.name, SlicePos { start: 0, end: 4 });
1036 assert_eq!(header.value, SlicePos { start: 7, end: 0 });
1037
1038 assert!(header.parse_with_state(B4, &mut st).unwrap().is_complete());
1039 assert_eq!(st.state, 0);
1040 assert_eq!(st.start, 18);
1041 assert_eq!(st.cursor, 18);
1042 assert_eq!(header.value, SlicePos { start: 7, end: 14 });
1043 assert_eq!(&B5[header.value.start..header.value.end], b"foo.com");
1044
1045 assert!(header.parse_with_state(B5, &mut st).unwrap().is_complete());
1046 assert_eq!(st.state, 0);
1047 assert_eq!(st.start, 30);
1048 assert_eq!(st.cursor, 30);
1049 assert_eq!(header.name, SlicePos { start: 18, end: 24 });
1050 assert_eq!(header.value, SlicePos { start: 0, end: 0 });
1051 assert_eq!(&B5[header.name.start..header.name.end], b"Cookie");
1052 assert_eq!(&B5[header.value.start..header.value.end], b"");
1053
1054 assert!(header.parse_with_state(B5, &mut st).unwrap().is_complete());
1055 assert_eq!(st.state, 0);
1056 assert_eq!(st.start, 30);
1057 assert_eq!(st.cursor, 32);
1058 }
1059
1060 headers_err! {
1061 test_headers_with_obsolete_line_folding_at_start,
1062 b"Line-Folded-Header: \r\n \r\n hello there\r\n\r\n",
1063 Err(Error::HeaderName)
1064 }
1065
1066 headers_err! {
1067 test_header_with_invalid_name,
1068 b"Host : foo.bar\r\n\r\n",
1069 Err(Error::HeaderName)
1070 }
1071
1072 macro_rules! res {
1073 ($name:ident, $buf:expr, |$len:ident, $version:ident, $code:ident, $reason:ident, $headers:ident, $headers_eof:ident| $body:expr) => {
1074 #[test]
1075 fn $name() {
1076 let mut b = $buf.as_ref();
1077 let mut res = Response::default();
1078 let mut consumed = res.parse($buf.as_ref()).unwrap().unwrap();
1079 let mut headers = Vec::new();
1080 let mut header = Header::default();
1081 let mut headers_eof = false;
1082 b = &b[consumed..];
1083
1084 while let Status::Complete(hdr) = header.parse(b).unwrap() {
1085 match hdr {
1086 HeaderParsed::Header(l) => {
1087 consumed += l;
1088 let name = String::from_utf8(Vec::from(
1089 &b[header.name.start..header.name.end],
1090 ))
1091 .unwrap();
1092 let value = Vec::from(&b[header.value.start..header.value.end]);
1093 headers.push((name, value));
1094 b = &b[l..];
1095 }
1096 HeaderParsed::Eof(l) => {
1097 consumed += l;
1098 headers_eof = true;
1099 break;
1100 }
1101 }
1102 }
1103
1104 let reason = unsafe {
1106 str::from_utf8_unchecked(&$buf.as_ref()[res.reason.start..res.reason.end])
1107 };
1108
1109 closure(
1110 consumed,
1111 res.version,
1112 res.code,
1113 reason,
1114 headers,
1115 headers_eof,
1116 );
1117
1118 fn closure(
1119 $len: usize,
1120 $version: u8,
1121 $code: u16,
1122 $reason: &str,
1123 $headers: Vec<(String, Vec<u8>)>,
1124 $headers_eof: bool,
1125 ) {
1126 $body
1127 }
1128 }
1129 };
1130 }
1131
1132 macro_rules! res_err {
1133 ($name:ident, $buf:expr, $err:expr) => {
1134 #[test]
1135 fn $name() {
1136 assert_eq!(Response::default().parse($buf.as_ref()), $err);
1137 }
1138 };
1139 }
1140
1141 macro_rules! res_par {
1142 ($name:ident, $buf:expr) => {
1143 #[test]
1144 fn $name() {
1145 assert_eq!(
1146 Response::default().parse($buf.as_ref()),
1147 Ok(Status::Partial)
1148 );
1149 }
1150 };
1151 }
1152
1153 res! {
1154 test_response_simple,
1155 b"HTTP/1.1 200 OK\r\n\r\n",
1156 |len, version, code, reason, headers, eof| {
1157 assert_eq!(len, 19);
1158 assert_eq!(version, 1);
1159 assert_eq!(code, 200);
1160 assert_eq!(reason, "OK");
1161 assert_eq!(headers.len(), 0);
1162 assert!(eof);
1163 }
1164 }
1165
1166 res! {
1167 test_response_newlines,
1168 b"HTTP/1.0 403 Forbidden\nServer: foo.bar\n\n",
1169 |len, version, code, reason, headers, eof| {
1170 assert_eq!(len, 40);
1171 assert_eq!(version, 0);
1172 assert_eq!(code, 403);
1173 assert_eq!(reason, "Forbidden");
1174 assert_eq!(headers.len(), 1);
1175 assert_eq!(headers[0].0, "Server");
1176 assert_eq!(headers[0].1, b"foo.bar");
1177 assert!(eof);
1178 }
1179 }
1180
1181 res! {
1182 test_response_reason_missing,
1183 b"HTTP/1.1 200 \r\n\r\n",
1184 |len, version, code, reason, headers, eof| {
1185 assert_eq!(len, 17);
1186 assert_eq!(version, 1);
1187 assert_eq!(code, 200);
1188 assert_eq!(reason, "");
1189 assert_eq!(headers.len(), 0);
1190 assert!(eof);
1191 }
1192 }
1193
1194 res! {
1195 test_response_reason_missing_no_space,
1196 b"HTTP/1.1 200\r\n\r\n",
1197 |len, version, code, reason, headers, eof| {
1198 assert_eq!(len, 16);
1199 assert_eq!(version, 1);
1200 assert_eq!(code, 200);
1201 assert_eq!(reason, "");
1202 assert_eq!(headers.len(), 0);
1203 assert!(eof);
1204 }
1205 }
1206
1207 res! {
1208 test_response_reason_missing_no_space_with_headers,
1209 b"HTTP/1.1 200\r\nFoo: bar\r\n\r\n",
1210 |len, version, code, reason, headers, eof| {
1211 assert_eq!(len, 26);
1212 assert_eq!(version, 1);
1213 assert_eq!(code, 200);
1214 assert_eq!(reason, "");
1215 assert_eq!(headers.len(), 1);
1216 assert_eq!(headers[0].0, "Foo");
1217 assert_eq!(headers[0].1, b"bar");
1218 assert!(eof);
1219 }
1220 }
1221
1222 res! {
1223 test_response_reason_with_space_and_tab,
1224 b"HTTP/1.1 101 Switching Protocols\t\r\n\r\n",
1225 |len, version, code, reason, headers, eof| {
1226 assert_eq!(len, 37);
1227 assert_eq!(version, 1);
1228 assert_eq!(code, 101);
1229 assert_eq!(reason, "Switching Protocols\t");
1230 assert_eq!(headers.len(), 0);
1231 assert!(eof);
1232 }
1233 }
1234
1235 res! {
1236 test_response_reason_with_obsolete_text_byte,
1237 b"HTTP/1.1 200 X\xFFZ\r\n\r\n",
1238 |len, version, code, reason, headers, eof| {
1239 assert_eq!(len, 20);
1240 assert_eq!(version, 1);
1241 assert_eq!(code, 200);
1242 assert_eq!(reason, "");
1244 assert_eq!(headers.len(), 0);
1245 assert!(eof);
1246 }
1247 }
1248
1249 res_err! {
1250 test_response_reason_with_nul_byte,
1251 b"HTTP/1.1 200 \x00\r\n\r\n",
1252 Err(crate::Error::Status)
1253 }
1254
1255 res_par! {
1256 test_response_version_missing_space,
1257 b"HTTP/1.1"
1258 }
1259
1260 res_par! {
1261 test_response_code_missing_space,
1262 b"HTTP/1.1 200"
1263 }
1264
1265 res! {
1266 test_response_partial_parses_headers_as_much_as_it_can,
1267 b"HTTP/1.1 200 OK\r\nServer: yolo\r\n",
1268 |len, version, code, reason, headers, eof| {
1269 assert_eq!(len, 31);
1270 assert_eq!(version, 1);
1271 assert_eq!(code, 200);
1272 assert_eq!(reason, "OK");
1273 assert_eq!(headers.len(), 1);
1274 assert_eq!(headers[0].0, "Server");
1275 assert_eq!(headers[0].1, b"yolo");
1276 assert!(!eof);
1277 }
1278 }
1279
1280 res! {
1281 test_response_empty_lines_prefix_lf_only,
1282 b"\n\nHTTP/1.1 200 OK\n\n",
1283 |len, version, code, reason, headers, eof| {
1284 assert_eq!(len, 19);
1285 assert_eq!(version, 1);
1286 assert_eq!(code, 200);
1287 assert_eq!(reason, "OK");
1288 assert_eq!(headers.len(), 0);
1289 assert!(eof);
1290 }
1291 }
1292
1293 res! {
1294 test_response_no_cr,
1295 b"HTTP/1.0 200\nContent-type: text/html\n\n",
1296 |len, version, code, reason, headers, eof| {
1297 assert_eq!(len, 38);
1298 assert_eq!(version, 0);
1299 assert_eq!(code, 200);
1300 assert_eq!(reason, "");
1301 assert_eq!(headers.len(), 1);
1302 assert_eq!(headers[0].0, "Content-type");
1303 assert_eq!(headers[0].1, b"text/html");
1304 assert!(eof);
1305 }
1306 }
1307
1308 #[test]
1310 fn partial_permutations() {
1311 let req_str = "GET / HTTP/1.1\r\n";
1312 let mut req = Request::default();
1313 for i in 0..req_str.len() {
1314 let status = req.parse(&req_str.as_bytes()[..i]);
1315 assert_eq!(
1316 status,
1317 Ok(Status::Partial),
1318 "partial request line should return partial. \
1319 Portion which failed: '{seg}' (below {i})",
1320 seg = &req_str[..i]
1321 );
1322 }
1323 }
1324
1325 headers_err! {
1326 test_forbid_headers_with_whitespace_between_header_name_and_colon,
1327 b"Access-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n",
1328 Err(Error::HeaderName)
1329 }
1330
1331 headers_err! {
1332 test_forbid_headers_with_obsolete_line_folding_at_end,
1333 b"Line-Folded-Header: hello there\r\n \r\n \r\n\r\n",
1334 Err(Error::HeaderName)
1335 }
1336
1337 headers_err! {
1338 test_forbid_headers_with_obsolete_line_folding_in_middle,
1339 b"Line-Folded-Header: hello \r\n \r\n there\r\n\r\n",
1340 Err(Error::HeaderName)
1341 }
1342
1343 headers_err! {
1344 test_forbid_headers_with_obsolete_line_folding_in_empty_header,
1345 b"Line-Folded-Header: \r\n \r\n \r\n\r\n",
1346 Err(Error::HeaderName)
1347 }
1348
1349 headers_err! {
1350 test_forbid_headers_with_empty_header_name,
1351 b": hello\r\nBread: baguette\r\n\r\n",
1352 Err(Error::HeaderName)
1353 }
1354
1355 headers_err! {
1356 test_forbid_headers_with_empty_header_name_second,
1357 b"Bread: baguette\r\n: hello\r\n\r\n",
1358 Err(Error::HeaderName)
1359 }
1360
1361 #[test]
1362 fn test_chunk_size() {
1363 assert_eq!(parse_chunk_size(b"0\r\n"), Ok(Status::Complete((3, 0))));
1364 assert_eq!(
1365 parse_chunk_size(b"12\r\nchunk"),
1366 Ok(Status::Complete((4, 18)))
1367 );
1368 assert_eq!(
1369 parse_chunk_size(b"3086d\r\n"),
1370 Ok(Status::Complete((7, 198_765)))
1371 );
1372 assert_eq!(
1373 parse_chunk_size(b"3735AB1;foo bar*\r\n"),
1374 Ok(Status::Complete((18, 57_891_505)))
1375 );
1376 assert_eq!(
1377 parse_chunk_size(b"3735ab1 ; baz \r\n"),
1378 Ok(Status::Complete((16, 57_891_505)))
1379 );
1380 assert_eq!(parse_chunk_size(b"77a65\r"), Ok(Status::Partial));
1381 assert_eq!(parse_chunk_size(b"ab"), Ok(Status::Partial));
1382 assert_eq!(
1383 parse_chunk_size(b"567f8a\rfoo"),
1384 Err(crate::InvalidChunkSize)
1385 );
1386 assert_eq!(
1387 parse_chunk_size(b"567f8a\rfoo"),
1388 Err(crate::InvalidChunkSize)
1389 );
1390 assert_eq!(
1391 parse_chunk_size(b"567xf8a\r\n"),
1392 Err(crate::InvalidChunkSize)
1393 );
1394 assert_eq!(
1395 parse_chunk_size(b"ffffffffffffffff\r\n"),
1396 Ok(Status::Complete((18, u64::MAX)))
1397 );
1398 assert_eq!(
1399 parse_chunk_size(b"1ffffffffffffffff\r\n"),
1400 Err(crate::InvalidChunkSize)
1401 );
1402 assert_eq!(
1403 parse_chunk_size(b"Affffffffffffffff\r\n"),
1404 Err(crate::InvalidChunkSize)
1405 );
1406 assert_eq!(
1407 parse_chunk_size(b"fffffffffffffffff\r\n"),
1408 Err(crate::InvalidChunkSize)
1409 );
1410 }
1411
1412 res! {
1413 test_allow_response_with_multiple_space_delimiters,
1414 b"HTTP/1.1 200 OK\r\n\r\n",
1415 |len, version, code, reason, headers, eof| {
1416 assert_eq!(len, 22);
1417 assert_eq!(version, 1);
1418 assert_eq!(code, 200);
1419 assert_eq!(reason, "OK");
1420 assert_eq!(headers.len(), 0);
1421 assert!(eof);
1422 }
1423 }
1424
1425 res_err! {
1428 test_forbid_response_with_weird_whitespace_delimiters,
1429 b"HTTP/1.1 200\rOK\r\n\r\n",
1430 Err(Error::Status)
1431 }
1432
1433 req! {
1434 test_allow_request_with_multiple_space_delimiters,
1435 b"GET / HTTP/1.1\r\n\r\n",
1436 |len, method, path, version, headers, eof| {
1437 assert_eq!(len, 22);
1438 assert_eq!(method, "GET");
1439 assert_eq!(path, "/");
1440 assert_eq!(version, 1);
1441 assert_eq!(headers.len(), 0);
1442 assert!(eof);
1443 }
1444 }
1445
1446 req_err! {
1449 test_forbid_request_with_weird_whitespace_delimiters,
1450 b"GET\r/\rHTTP/1.1\r\n\r\n",
1451 Err(Error::Token)
1452 }
1453
1454 req_err! {
1455 test_request_with_multiple_spaces_and_bad_path,
1456 b"GET /foo ohno HTTP/1.1\r\n\r\n",
1457 Err(Error::Version)
1458 }
1459
1460 req_err! {
1464 test_request_with_del_in_path,
1465 b"GET /foo\x7Fohno HTTP/1.1\r\n\r\n",
1466 Err(Error::Token)
1467 }
1468
1469 res_err! {
1613 test_response_with_spaces_in_code,
1614 b"HTTP/1.1 99 200 OK\r\n\r\n",
1615 Err(Error::Status)
1616 }
1617
1618 headers_err! {
1619 test_headers_with_whitespace_between_header_name_and_colon,
1620 b"Access-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n",
1621 Err(Error::HeaderName)
1622 }
1623
1624 headers_err! {
1625 test_headers_with_invalid_char_between_header_name_and_colon,
1626 b"Access-Control-Allow-Credentials\xFF: true\r\nBread: baguette\r\n\r\n",
1627 Err(Error::HeaderName)
1628 }
1629
1630 headers_err! {
1631 test_ignore_header_line_with_missing_colon_in_response,
1632 b"Access-Control-Allow-Credentials\r\nBread: baguette\r\n\r\n",
1633 Err(Error::HeaderName)
1634 }
1635
1636 headers_err! {
1637 test_headers_header_with_missing_colon_with_folding,
1638 b"Access-Control-Allow-Credentials \r\n hello\r\nBread: baguette\r\n\r\n",
1639 Err(Error::HeaderName)
1640 }
1641
1642 headers_err! {
1643 test_headers_header_with_nul_in_header_name,
1644 b"Access-Control-Allow-Cred\0entials: hello\r\nBread: baguette\r\n\r\n",
1645 Err(Error::HeaderName)
1646 }
1647
1648 headers_err! {
1649 test_header_with_cr_in_header_name,
1650 b"Access-Control-Allow-Cred\rentials: hello\r\nBread: baguette\r\n\r\n",
1651 Err(Error::HeaderName)
1652 }
1653
1654 headers_err! {
1655 test_header_with_nul_in_whitespace_before_colon,
1656 b"Access-Control-Allow-Credentials \0: hello\r\nBread: baguette\r\n\r\n",
1657 Err(Error::HeaderName)
1658 }
1659
1660 headers_err! {
1661 test_header_with_nul_in_value,
1662 b"Access-Control-Allow-Credentials: hell\0o\r\nBread: baguette\r\n\r\n",
1663 Err(Error::HeaderValue)
1664 }
1665
1666 headers_err! {
1667 test_header_with_invalid_char_in_value,
1668 b"Access-Control-Allow-Credentials: hell\x01o\r\nBread: baguette\r\n\r\n",
1669 Err(Error::HeaderValue)
1670 }
1671
1672 headers_err! {
1673 test_header_with_invalid_char_in_value_with_folding,
1674 b"Access-Control-Allow-Credentials: hell\x01o \n world!\r\nBread: baguette\r\n\r\n",
1675 Err(Error::HeaderValue)
1676 }
1677
1678 headers_err! {
1679 test_header_with_space_before_first_header,
1680 b" Space-Before-Header: hello there\r\n\r\n",
1681 Err(Error::HeaderName)
1682 }
1683
1684 res! {
1685 test_response_no_space_after_colon,
1686 b"HTTP/1.1 200 OK\r\nfoo:bar\r\n\r\n",
1687 |len, version, code, reason, headers, eof| {
1688 assert_eq!(len, 28);
1689 assert_eq!(version, 1);
1690 assert_eq!(code, 200);
1691 assert_eq!(reason, "OK");
1692 assert_eq!(headers.len(), 1);
1693 assert_eq!(headers[0].0, "foo");
1694 assert_eq!(headers[0].1, b"bar");
1695 assert!(eof);
1696 }
1697 }
1698
1699 req_err! {
1700 test_request_with_leading_space,
1701 b" GET / HTTP/1.1\r\nfoo:bar\r\n\r\n",
1702 Err(Error::Token)
1703 }
1704
1705 req_err! {
1706 test_request_with_invalid_method,
1707 b"P()ST / HTTP/1.1\r\nfoo:bar\r\n\r\n",
1708 Err(Error::Token)
1709 }
1710
1711 req! {
1712 test_utf8_in_path_ok,
1713 b"GET /test?post=I\xE2\x80\x99msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n",
1714 |len, method, path, version, headers, eof| {
1715 assert_eq!(len, 67);
1716 assert_eq!(method, "GET");
1717 assert_eq!(path, "/test?post=I’msorryIforkedyou");
1718 assert_eq!(version, 1);
1719 assert_eq!(headers.len(), 1);
1720 assert_eq!(headers[0].0, "Host");
1721 assert_eq!(headers[0].1, b"example.org");
1722 assert!(eof);
1723 }
1724 }
1725
1726 #[test]
1727 fn test_bad_utf8_in_path() {
1728 const BUF: &[u8] =
1729 b"GET /test?post=I\xE2msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n";
1730
1731 let mut req = Request::default();
1732 assert!(req.parse(BUF).unwrap().is_complete());
1733 assert!(str::from_utf8(&BUF[req.path.start..req.path.end]).is_err());
1734 }
1735
1736 #[rustfmt::skip]
1737 res! {
1738 test_response_bench,
1739 b"\
1740HTTP/1.0 200 OK\r\n\
1741Date: Wed, 21 Oct 2015 07:28:00 GMT\r\n\
1742Set-Cookie: session=60; user_id=1\r\n\r\n",
1743 |len, version, code, reason, headers, eof| {
1744 assert_eq!(len, 91);
1745 assert_eq!(version, 0);
1746 assert_eq!(code, 200);
1747 assert_eq!(reason, "OK");
1748 assert_eq!(headers.len(), 2);
1749 assert_eq!(headers[0].0, "Date");
1750 assert_eq!(headers[0].1, b"Wed, 21 Oct 2015 07:28:00 GMT");
1751 assert_eq!(headers[1].0, "Set-Cookie");
1752 assert_eq!(headers[1].1, b"session=60; user_id=1");
1753 assert!(eof);
1754 }
1755 }
1756}