Skip to main content

ntex_httparse/
lib.rs

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
11//! # httparse
12//!
13//! A push library for parsing HTTP/1.x requests and responses.
14//!
15//! The focus is on speed and safety. Unsafe code is used to keep parsing fast,
16//! but unsafety is contained in a submodule, with invariants enforced. The
17//! parsing internals use an `Iterator` instead of direct indexing, while
18//! skipping bounds checks.
19//!
20//! SIMD optimizations are enabled automatically when available.
21//! If building an executable to be run on multiple platforms, and thus
22//! not passing `target_feature` or `target_cpu` flags to the compiler,
23//! runtime detection can still detect SSE4.2 or AVX2 support to provide
24//! massive wins.
25//!
26//! If compiling for a specific target, remembering to include
27//! `-C target_cpu=native` allows the detection to become compile time checks,
28//! making it *even* faster.
29
30use 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/// An error in parsing.
46#[derive(Copy, Clone, PartialEq, Eq, Debug)]
47pub enum Error {
48    /// Invalid byte in header name.
49    HeaderName,
50    /// Invalid byte in header value.
51    HeaderValue,
52    /// Invalid byte in new line.
53    NewLine,
54    /// Invalid byte in Response status.
55    Status,
56    /// Invalid byte where token is required.
57    Token,
58    /// Parsed more headers than provided buffer can contain.
59    TooManyHeaders,
60    /// Invalid byte in HTTP version.
61    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/// An error in parsing a chunk size.
93#[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
102/// A Result of any parsing action.
103///
104/// If the input is invalid, an `Error` will be returned. Note that incomplete
105/// data is not considered invalid, and so will not return an error, but rather
106/// a `Ok(Status::Partial)`.
107pub type Result<T> = result::Result<Status<T>, Error>;
108
109/// The result of a successful parse pass.
110///
111/// `Complete` is used when the buffer contained the complete value.
112/// `Partial` is used when parsing did not reach the end of the expected value,
113/// but no invalid data was found.
114#[derive(Copy, Clone, Eq, PartialEq, Debug)]
115pub enum Status<T> {
116    /// The completed result.
117    Complete(T),
118    /// A partial result.
119    Partial,
120}
121
122impl<T> Status<T> {
123    /// Convenience method to check if status is complete.
124    #[inline]
125    pub fn is_complete(&self) -> bool {
126        match *self {
127            Status::Complete(..) => true,
128            Status::Partial => false,
129        }
130    }
131
132    /// Convenience method to check if status is partial.
133    #[inline]
134    pub fn is_partial(&self) -> bool {
135        match *self {
136            Status::Complete(..) => false,
137            Status::Partial => true,
138        }
139    }
140
141    /// Convenience method to unwrap a Complete value. Panics if the status is
142    /// `Partial`.
143    #[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    // state
155    pub state: u8,
156    // bytes
157    pub start: usize,
158    pub cursor: usize,
159}
160
161#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
162/// A slice position.
163pub 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/// A parsed Request.
176///
177/// # Example
178///
179/// ```no_run
180/// let buf = b"GET /404 HTTP/1.1\r\nHost:";
181/// let mut req = ntex_httparse::Request::default();
182/// if let Ok(ntex_httparse::Status::Complete(consumed)) = req.parse(buf) {
183///     // check router for path.
184///     // /404 doesn't exist? we could stop parsing
185///     let _ = req.path;
186/// }
187/// ```
188#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
189pub struct Request {
190    /// Parsed request's method.
191    pub method: SlicePos,
192    /// Parsed request's path.
193    pub path: SlicePos,
194    /// Parsed request's http version.
195    pub version: u8,
196}
197
198impl Request {
199    #[inline]
200    /// Parse request
201    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    /// Parse request
208    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/// A parsed Response.
238#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
239pub struct Response {
240    /// Parsed response's http version.
241    pub version: u8,
242    /// Parsed response's code.
243    pub code: u16,
244    /// Parsed response's reason (start position, length).
245    pub reason: SlicePos,
246}
247
248impl Response {
249    #[inline]
250    /// Parse response code and reason
251    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        // version
258        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        // code
265        self.code = complete!(parse_code(&mut bytes));
266
267        // RFC7230 says there must be 'SP' and then reason-phrase, but admits
268        // its only for legacy reasons. With the reason-phrase completely
269        // optional (and preferred to be omitted) in HTTP2, we'll just
270        // handle any response that doesn't include a reason-phrase, because
271        // it's more lenient, and we don't care anyways.
272        //
273        // So, a SP means parse a reason-phrase.
274        // A newline means go to headers.
275        // Anything else we'll say is a malformed status.
276        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]
299// WARNING: Exported for internal benchmarks, not fit for public consumption
300pub 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    // SAFETY: parse_method_inner verifies validity of method
306    let m = unsafe { str::from_utf8_unchecked(&src[s.start..s.end]) };
307    Ok(Status::Complete(m))
308}
309
310#[inline]
311// WARNING: Exported for internal benchmarks, not fit for public consumption
312fn 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            // we matched "GET " which has 4 bytes and is ASCII
321            bytes.advance(4); // advance cursor past "GET "
322            let method = bytes.slice_position(1);
323            complete!(utils::skip_spaces(bytes));
324            Ok(Status::Complete(method))
325        }
326        // If `bytes.peek_n...` returns a Some([u8; 4]),
327        // then we are assured that `bytes` contains at least 4 bytes.
328        // Thus `bytes.len() >= 4`,
329        // and it is safe to peek at byte 4 with `bytes.peek_ahead(4)`.
330        Some(POST) if bytes.peek_ahead(4) == Some(b' ') => {
331            // we matched "POST " which has 5 bytes
332            bytes.advance(5); // advance cursor past "POST "
333            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                // First char must be a token char, it can't be a space which would indicate an empty token.
341                return Err(Error::Token);
342            }
343
344            loop {
345                let b = next!(bytes);
346                if b == b' ' {
347                    return Ok(Status::Complete(
348                        // SAFETY: all bytes up till `i` must have been `is_method_token` and therefore also utf-8.
349                        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/// From [RFC 7230](https://tools.ietf.org/html/rfc7230):
360///
361/// > ```notrust
362/// > reason-phrase  = *( HTAB / SP / VCHAR / obs-text )
363/// > HTAB           = %x09        ; horizontal tab
364/// > VCHAR          = %x21-7E     ; visible (printing) characters
365/// > obs-text       = %x80-FF
366/// > ```
367///
368/// > A.2.  Changes from RFC 2616
369/// >
370/// > Non-US-ASCII content in header fields and the reason phrase
371/// > has been obsoleted and made opaque (the TEXT rule was removed).
372#[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                // SAFETY: (1) calling bytes.slice_skip(2) is safe, because at least two next! calls
381                // advance the bytes iterator.
382                // (2) calling from_utf8_unchecked is safe, because the bytes returned by slice_skip
383                // were validated to be allowed US-ASCII chars by the other arms of the if/else or
384                // otherwise `seen_obs_text` is true and an empty string is returned instead.
385                if seen_obs_text {
386                    // obs-text characters were found, so return the fallback empty string
387                    bytes.commit();
388                    SlicePos::default()
389                } else {
390                    // all bytes up till `i` must have been HTAB / SP / VCHAR
391                    bytes.slice_position(2)
392                },
393            ));
394        } else if b == b'\n' {
395            return Ok(Status::Complete(
396                // SAFETY: (1) calling bytes.slice_skip(1) is safe, because at least one next! call
397                // advance the bytes iterator.
398                // (2) see (2) of safety comment above.
399                if seen_obs_text {
400                    // obs-text characters were found, so return the fallback empty string
401                    bytes.commit();
402                    SlicePos::default()
403                } else {
404                    // all bytes up till `i` must have been HTAB / SP / VCHAR
405                    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)]
418/// Parse request path
419pub 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]
434// WARNING: Exported for internal benchmarks, not fit for public consumption
435fn 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        // URI must have at least one char
442        if start == b_end {
443            return Err(Error::Token);
444        }
445
446        // SAFETY: all bytes up till `i` must have been `is_token` and therefore also utf-8.
447        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
466/// Parse a buffer of bytes as a chunk size.
467///
468/// The return value, if complete and successful, includes the index of the
469/// buffer that parsing stopped at, and the size of the following chunk.
470///
471/// # Example
472///
473/// ```
474/// let buf = b"4\r\nRust\r\n0\r\n\r\n";
475/// assert_eq!(ntex_httparse::parse_chunk_size(buf),
476///            Ok(ntex_httparse::Status::Complete((3, 4))));
477/// ```
478pub 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                    // actually unreachable!(), because count stops the loop at 15 digits before
496                    // we can reach u64::MAX / RADIX == 0xfffffffffffffff, which requires 15 hex
497                    // digits. This stops mirai reporting a false alarm regarding the `size *=
498                    // RADIX` multiplication below.
499                    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            // If we weren't in the extension yet, the ";" signals its start
520            b';' if !in_ext => {
521                in_ext = true;
522                in_chunk_size = false;
523            }
524            // "Linear white space" is ignored between the chunk size and the
525            // extension separator token (";") due to the "implied *LWS rule".
526            b'\t' | b' ' if !in_ext && !in_chunk_size => {}
527            // LWS can follow the chunk size, but no more digits can come
528            b'\t' | b' ' if in_chunk_size => in_chunk_size = false,
529            // We allow any arbitrary octet once we are in the extension, since
530            // they all get ignored anyway. According to the HTTP spec, valid
531            // extensions would have a more strict syntax:
532            //     (token ["=" (token | quoted-string)])
533            // but we gain nothing by rejecting an otherwise valid chunk size.
534            _ if in_ext => {}
535            // Finally, if we aren't in the extension and we're reading any
536            // other octet, the chunk size line is invalid!
537            _ => 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                    // SAFETY: Request::parse() validates path
582                    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 the scalar parsing
762        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 the sse42 parsing
778        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 the avx2 parsing
794        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 the avx2 parsing
810        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    // A single byte which is part of a method is not invalid
851    req_par! {
852        test_request_one_byte_method,
853        b"G"
854    }
855
856    // A subset of a method is a partial method, not invalid
857    req_par! {
858        test_request_partial_method,
859        b"GE"
860    }
861
862    // A method, without the delimiting space, is a partial request
863    req_par! {
864        test_request_method_no_delimiter,
865        b"GET"
866    }
867
868    // Regression test: assert that a partial read with just the method and
869    // space results in a partial, rather than a token error from uri parsing.
870    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                // SAFETY: Request::parse() validates reason
1105                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            // Empty string fallback in case of obs-text
1243            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    /// Check all subset permutations of a partial request line with no headers
1309    #[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    // /// This is technically allowed by the spec, but we only support multiple spaces as an option,
1426    // /// not stray `\r`s.
1427    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    // /// This is technically allowed by the spec, but we only support multiple spaces as an option,
1447    // /// not stray `\r`s.
1448    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    // // This test ensure there is an error when there is a DEL character in the path
1461    // // since we allow all char from 0x21 code except DEL, this test ensure that DEL
1462    // // is not allowed in the path
1463    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    // #[test]
1470    // #[cfg_attr(miri, ignore)] // Miri is too slow for this test
1471    // fn test_all_utf8_char_in_paths() {
1472    //     // two code points
1473    //     for i in 128..256 {
1474    //         for j in 128..256 {
1475    //             let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1476    //             let mut request = Request::new(&mut headers[..]);
1477    //             let bytes = [i as u8, j as u8];
1478
1479    //             match core::str::from_utf8(&bytes) {
1480    //                 Ok(s) => {
1481    //                     let first_line = format!("GET /{} HTTP/1.1\r\n\r\n", s);
1482    //                     let result = crate::ParserConfig::default()
1483    //                         .allow_multiple_spaces_in_request_line_delimiters(true)
1484    //                         .parse_request(&mut request, first_line.as_bytes());
1485
1486    //                     assert_eq!(
1487    //                         result,
1488    //                         Ok(Status::Complete(20)),
1489    //                         "failed for utf8 char i: {}, j: {}",
1490    //                         i,
1491    //                         j
1492    //                     );
1493    //                 }
1494    //                 Err(_) => {
1495    //                     let mut first_line = b"GET /".to_vec();
1496    //                     first_line.extend(&bytes);
1497    //                     first_line.extend(b" HTTP/1.1\r\n\r\n");
1498
1499    //                     let result = crate::ParserConfig::default()
1500    //                         .allow_multiple_spaces_in_request_line_delimiters(true)
1501    //                         .parse_request(&mut request, first_line.as_slice());
1502
1503    //                     assert_eq!(
1504    //                         result,
1505    //                         Err(crate::Error::Token),
1506    //                         "failed for utf8 char i: {}, j: {}",
1507    //                         i,
1508    //                         j
1509    //                     );
1510    //                 }
1511    //             };
1512
1513    //             // three code points starting from 0xe0
1514    //             if i < 0xe0 {
1515    //                 continue;
1516    //             }
1517
1518    //             for k in 128..256 {
1519    //                 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1520    //                 let mut request = Request::new(&mut headers[..]);
1521    //                 let bytes = [i as u8, j as u8, k as u8];
1522
1523    //                 match core::str::from_utf8(&bytes) {
1524    //                     Ok(s) => {
1525    //                         let first_line = format!("GET /{} HTTP/1.1\r\n\r\n", s);
1526    //                         let result = crate::ParserConfig::default()
1527    //                             .allow_multiple_spaces_in_request_line_delimiters(true)
1528    //                             .parse_request(&mut request, first_line.as_bytes());
1529
1530    //                         assert_eq!(
1531    //                             result,
1532    //                             Ok(Status::Complete(21)),
1533    //                             "failed for utf8 char i: {}, j: {}, k: {}",
1534    //                             i,
1535    //                             j,
1536    //                             k
1537    //                         );
1538    //                     }
1539    //                     Err(_) => {
1540    //                         let mut first_line = b"GET /".to_vec();
1541    //                         first_line.extend(&bytes);
1542    //                         first_line.extend(b" HTTP/1.1\r\n\r\n");
1543
1544    //                         let result = crate::ParserConfig::default()
1545    //                             .allow_multiple_spaces_in_request_line_delimiters(true)
1546    //                             .parse_request(&mut request, first_line.as_slice());
1547
1548    //                         assert_eq!(
1549    //                             result,
1550    //                             Err(crate::Error::Token),
1551    //                             "failed for utf8 char i: {}, j: {}, k: {}",
1552    //                             i,
1553    //                             j,
1554    //                             k
1555    //                         );
1556    //                     }
1557    //                 };
1558
1559    //                 // four code points starting from 0xf0
1560    //                 if i < 0xf0 {
1561    //                     continue;
1562    //                 }
1563
1564    //                 for l in 128..256 {
1565    //                     let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1566    //                     let mut request = Request::new(&mut headers[..]);
1567    //                     let bytes = [i as u8, j as u8, k as u8, l as u8];
1568
1569    //                     match core::str::from_utf8(&bytes) {
1570    //                         Ok(s) => {
1571    //                             let first_line = format!("GET /{} HTTP/1.1\r\n\r\n", s);
1572    //                             let result = crate::ParserConfig::default()
1573    //                                 .allow_multiple_spaces_in_request_line_delimiters(true)
1574    //                                 .parse_request(&mut request, first_line.as_bytes());
1575
1576    //                             assert_eq!(
1577    //                                 result,
1578    //                                 Ok(Status::Complete(22)),
1579    //                                 "failed for utf8 char i: {}, j: {}, k: {}, l: {}",
1580    //                                 i,
1581    //                                 j,
1582    //                                 k,
1583    //                                 l
1584    //                             );
1585    //                         }
1586    //                         Err(_) => {
1587    //                             let mut first_line = b"GET /".to_vec();
1588    //                             first_line.extend(&bytes);
1589    //                             first_line.extend(b" HTTP/1.1\r\n\r\n");
1590
1591    //                             let result = crate::ParserConfig::default()
1592    //                                 .allow_multiple_spaces_in_request_line_delimiters(true)
1593    //                                 .parse_request(&mut request, first_line.as_slice());
1594
1595    //                             assert_eq!(
1596    //                                 result,
1597    //                                 Err(crate::Error::Token),
1598    //                                 "failed for utf8 char i: {}, j: {}, k: {}, l: {}",
1599    //                                 i,
1600    //                                 j,
1601    //                                 k,
1602    //                                 l
1603    //                             );
1604    //                         }
1605    //                     };
1606    //                 }
1607    //             }
1608    //         }
1609    //     }
1610    // }
1611
1612    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}