1#![cfg_attr(not(feature = "std"), no_std)]
2#![deny(missing_docs)]
3#![cfg_attr(test, deny(warnings))]
4
5//! # httparse
6//!
7//! A push library for parsing HTTP/1.x requests and responses.
8//!
9//! The focus is on speed and safety. Unsafe code is used to keep parsing fast,
10//! but unsafety is contained in a submodule, with invariants enforced. The
11//! parsing internals use an `Iterator` instead of direct indexing, while
12//! skipping bounds checks.
13//!
14//! With Rust 1.27.0 or later, support for SIMD is enabled automatically.
15//! If building an executable to be run on multiple platforms, and thus
16//! not passing `target_feature` or `target_cpu` flags to the compiler,
17//! runtime detection can still detect SSE4.2 or AVX2 support to provide
18//! massive wins.
19//!
20//! If compiling for a specific target, remembering to include
21//! `-C target_cpu=native` allows the detection to become compile time checks,
22//! making it *even* faster.
23
24use core::{fmt, result, str};
25use core::mem::{self, MaybeUninit};
26
27use crate::iter::Bytes;
28
29mod iter;
30#[macro_use] mod macros;
31mod simd;
32
33/// Determines if byte is a token char.
34///
35/// > ```notrust
36/// > token = 1*tchar
37/// >
38/// > tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
39/// > / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
40/// > / DIGIT / ALPHA
41/// > ; any VCHAR, except delimiters
42/// > ```
43#[inline]
44fn is_token(b: u8) -> bool {
45 b > 0x1F && b < 0x7F
46}
47
48// ASCII codes to accept URI string.
49// i.e. A-Z a-z 0-9 !#$%&'*+-._();:@=,/?[]~^
50// TODO: Make a stricter checking for URI string?
51static URI_MAP: [bool; 256] = byte_map![
52 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53// \0 \n
54 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55// commands
56 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
57// \w ! " # $ % & ' ( ) * + , - . /
58 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
59// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
60 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
61// @ A B C D E F G H I J K L M N O
62 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
63// P Q R S T U V W X Y Z [ \ ] ^ _
64 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
65// ` a b c d e f g h i j k l m n o
66 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
67// p q r s t u v w x y z { | } ~ del
68// ====== Extended ASCII (aka. obs-text) ======
69 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
70 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
71 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77];
78
79#[inline]
80fn is_uri_token(b: u8) -> bool {
81 URI_MAP[b as usize]
82}
83
84static HEADER_NAME_MAP: [bool; 256] = byte_map![
85 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
87 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
88 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
89 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
90 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
91 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
92 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
93 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
94 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
95 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
96 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
97 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
98 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
100 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
101];
102
103#[inline]
104fn is_header_name_token(b: u8) -> bool {
105 HEADER_NAME_MAP[b as usize]
106}
107
108static HEADER_VALUE_MAP: [bool; 256] = byte_map![
109 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
112 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
118 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
119 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
120 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
121 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
122 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
123 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
124 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
125];
126
127
128#[inline]
129fn is_header_value_token(b: u8) -> bool {
130 HEADER_VALUE_MAP[b as usize]
131}
132
133/// An error in parsing.
134#[derive(Copy, Clone, PartialEq, Eq, Debug)]
135pub enum Error {
136 /// Invalid byte in header name.
137 HeaderName,
138 /// Invalid byte in header value.
139 HeaderValue,
140 /// Invalid byte in new line.
141 NewLine,
142 /// Invalid byte in Response status.
143 Status,
144 /// Invalid byte where token is required.
145 Token,
146 /// Parsed more headers than provided buffer can contain.
147 TooManyHeaders,
148 /// Invalid byte in HTTP version.
149 Version,
150}
151
152impl Error {
153 #[inline]
154 fn description_str(&self) -> &'static str {
155 match *self {
156 Error::HeaderName => "invalid header name",
157 Error::HeaderValue => "invalid header value",
158 Error::NewLine => "invalid new line",
159 Error::Status => "invalid response status",
160 Error::Token => "invalid token",
161 Error::TooManyHeaders => "too many headers",
162 Error::Version => "invalid HTTP version",
163 }
164 }
165}
166
167impl fmt::Display for Error {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 f.write_str(self.description_str())
170 }
171}
172
173#[cfg(feature = "std")]
174impl std::error::Error for Error {
175 fn description(&self) -> &str {
176 self.description_str()
177 }
178}
179
180/// An error in parsing a chunk size.
181// Note: Move this into the error enum once v2.0 is released.
182#[derive(Debug, PartialEq, Eq)]
183pub struct InvalidChunkSize;
184
185impl fmt::Display for InvalidChunkSize {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 f.write_str(data:"invalid chunk size")
188 }
189}
190
191/// A Result of any parsing action.
192///
193/// If the input is invalid, an `Error` will be returned. Note that incomplete
194/// data is not considered invalid, and so will not return an error, but rather
195/// a `Ok(Status::Partial)`.
196pub type Result<T> = result::Result<Status<T>, Error>;
197
198/// The result of a successful parse pass.
199///
200/// `Complete` is used when the buffer contained the complete value.
201/// `Partial` is used when parsing did not reach the end of the expected value,
202/// but no invalid data was found.
203#[derive(Copy, Clone, Eq, PartialEq, Debug)]
204pub enum Status<T> {
205 /// The completed result.
206 Complete(T),
207 /// A partial result.
208 Partial
209}
210
211impl<T> Status<T> {
212 /// Convenience method to check if status is complete.
213 #[inline]
214 pub fn is_complete(&self) -> bool {
215 match *self {
216 Status::Complete(..) => true,
217 Status::Partial => false
218 }
219 }
220
221 /// Convenience method to check if status is partial.
222 #[inline]
223 pub fn is_partial(&self) -> bool {
224 match *self {
225 Status::Complete(..) => false,
226 Status::Partial => true
227 }
228 }
229
230 /// Convenience method to unwrap a Complete value. Panics if the status is
231 /// `Partial`.
232 #[inline]
233 pub fn unwrap(self) -> T {
234 match self {
235 Status::Complete(t) => t,
236 Status::Partial => panic!("Tried to unwrap Status::Partial")
237 }
238 }
239}
240
241/// Parser configuration.
242#[derive(Clone, Debug, Default)]
243pub struct ParserConfig {
244 allow_spaces_after_header_name_in_responses: bool,
245 allow_obsolete_multiline_headers_in_responses: bool,
246 allow_multiple_spaces_in_request_line_delimiters: bool,
247 allow_multiple_spaces_in_response_status_delimiters: bool,
248 ignore_invalid_headers_in_responses: bool,
249}
250
251impl ParserConfig {
252 /// Sets whether spaces and tabs should be allowed after header names in responses.
253 pub fn allow_spaces_after_header_name_in_responses(
254 &mut self,
255 value: bool,
256 ) -> &mut Self {
257 self.allow_spaces_after_header_name_in_responses = value;
258 self
259 }
260
261 /// Sets whether multiple spaces are allowed as delimiters in request lines.
262 ///
263 /// # Background
264 ///
265 /// The [latest version of the HTTP/1.1 spec][spec] allows implementations to parse multiple
266 /// whitespace characters in place of the `SP` delimiters in the request line, including:
267 ///
268 /// > SP, HTAB, VT (%x0B), FF (%x0C), or bare CR
269 ///
270 /// This option relaxes the parser to allow for multiple spaces, but does *not* allow the
271 /// request line to contain the other mentioned whitespace characters.
272 ///
273 /// [spec]: https://httpwg.org/http-core/draft-ietf-httpbis-messaging-latest.html#rfc.section.3.p.3
274 pub fn allow_multiple_spaces_in_request_line_delimiters(&mut self, value: bool) -> &mut Self {
275 self.allow_multiple_spaces_in_request_line_delimiters = value;
276 self
277 }
278
279 /// Whether multiple spaces are allowed as delimiters in request lines.
280 pub fn multiple_spaces_in_request_line_delimiters_are_allowed(&self) -> bool {
281 self.allow_multiple_spaces_in_request_line_delimiters
282 }
283
284 /// Sets whether multiple spaces are allowed as delimiters in response status lines.
285 ///
286 /// # Background
287 ///
288 /// The [latest version of the HTTP/1.1 spec][spec] allows implementations to parse multiple
289 /// whitespace characters in place of the `SP` delimiters in the response status line,
290 /// including:
291 ///
292 /// > SP, HTAB, VT (%x0B), FF (%x0C), or bare CR
293 ///
294 /// This option relaxes the parser to allow for multiple spaces, but does *not* allow the status
295 /// line to contain the other mentioned whitespace characters.
296 ///
297 /// [spec]: https://httpwg.org/http-core/draft-ietf-httpbis-messaging-latest.html#rfc.section.4.p.3
298 pub fn allow_multiple_spaces_in_response_status_delimiters(&mut self, value: bool) -> &mut Self {
299 self.allow_multiple_spaces_in_response_status_delimiters = value;
300 self
301 }
302
303 /// Whether multiple spaces are allowed as delimiters in response status lines.
304 pub fn multiple_spaces_in_response_status_delimiters_are_allowed(&self) -> bool {
305 self.allow_multiple_spaces_in_response_status_delimiters
306 }
307
308 /// Sets whether obsolete multiline headers should be allowed.
309 ///
310 /// This is an obsolete part of HTTP/1. Use at your own risk. If you are
311 /// building an HTTP library, the newlines (`\r` and `\n`) should be
312 /// replaced by spaces before handing the header value to the user.
313 ///
314 /// # Example
315 ///
316 /// ```rust
317 /// let buf = b"HTTP/1.1 200 OK\r\nFolded-Header: hello\r\n there \r\n\r\n";
318 /// let mut headers = [httparse::EMPTY_HEADER; 16];
319 /// let mut response = httparse::Response::new(&mut headers);
320 ///
321 /// let res = httparse::ParserConfig::default()
322 /// .allow_obsolete_multiline_headers_in_responses(true)
323 /// .parse_response(&mut response, buf);
324 ///
325 /// assert_eq!(res, Ok(httparse::Status::Complete(buf.len())));
326 ///
327 /// assert_eq!(response.headers.len(), 1);
328 /// assert_eq!(response.headers[0].name, "Folded-Header");
329 /// assert_eq!(response.headers[0].value, b"hello\r\n there");
330 /// ```
331 pub fn allow_obsolete_multiline_headers_in_responses(
332 &mut self,
333 value: bool,
334 ) -> &mut Self {
335 self.allow_obsolete_multiline_headers_in_responses = value;
336 self
337 }
338
339 /// Whether obsolete multiline headers should be allowed.
340 pub fn obsolete_multiline_headers_in_responses_are_allowed(&self) -> bool {
341 self.allow_obsolete_multiline_headers_in_responses
342 }
343
344 /// Parses a request with the given config.
345 pub fn parse_request<'headers, 'buf>(
346 &self,
347 request: &mut Request<'headers, 'buf>,
348 buf: &'buf [u8],
349 ) -> Result<usize> {
350 request.parse_with_config(buf, self)
351 }
352
353 /// Parses a request with the given config and buffer for headers
354 pub fn parse_request_with_uninit_headers<'headers, 'buf>(
355 &self,
356 request: &mut Request<'headers, 'buf>,
357 buf: &'buf [u8],
358 headers: &'headers mut [MaybeUninit<Header<'buf>>],
359 ) -> Result<usize> {
360 request.parse_with_config_and_uninit_headers(buf, self, headers)
361 }
362
363 /// Sets whether invalid header lines should be silently ignored in responses.
364 ///
365 /// This mimicks the behaviour of major browsers. You probably don't want this.
366 /// You should only want this if you are implementing a proxy whose main
367 /// purpose is to sit in front of browsers whose users access arbitrary content
368 /// which may be malformed, and they expect everything that works without
369 /// the proxy to keep working with the proxy.
370 ///
371 /// This option will prevent `ParserConfig::parse_response` from returning
372 /// an error encountered when parsing a header, except if the error was caused
373 /// by the character NUL (ASCII code 0), as Chrome specifically always reject
374 /// those, or if the error was caused by a lone character `\r`, as Firefox and
375 /// Chrome behave differently in that case.
376 ///
377 /// The ignorable errors are:
378 /// * empty header names;
379 /// * characters that are not allowed in header names, except for `\0` and `\r`;
380 /// * when `allow_spaces_after_header_name_in_responses` is not enabled,
381 /// spaces and tabs between the header name and the colon;
382 /// * missing colon between header name and value;
383 /// * when `allow_obsolete_multiline_headers_in_responses` is not enabled,
384 /// headers using obsolete line folding.
385 /// * characters that are not allowed in header values except for `\0` and `\r`.
386 ///
387 /// If an ignorable error is encountered, the parser tries to find the next
388 /// line in the input to resume parsing the rest of the headers. As lines
389 /// contributing to a header using obsolete line folding always start
390 /// with whitespace, those will be ignored too. An error will be emitted
391 /// nonetheless if it finds `\0` or a lone `\r` while looking for the
392 /// next line.
393 pub fn ignore_invalid_headers_in_responses(
394 &mut self,
395 value: bool,
396 ) -> &mut Self {
397 self.ignore_invalid_headers_in_responses = value;
398 self
399 }
400
401 /// Parses a response with the given config.
402 pub fn parse_response<'headers, 'buf>(
403 &self,
404 response: &mut Response<'headers, 'buf>,
405 buf: &'buf [u8],
406 ) -> Result<usize> {
407 response.parse_with_config(buf, self)
408 }
409
410 /// Parses a response with the given config and buffer for headers
411 pub fn parse_response_with_uninit_headers<'headers, 'buf>(
412 &self,
413 response: &mut Response<'headers, 'buf>,
414 buf: &'buf [u8],
415 headers: &'headers mut [MaybeUninit<Header<'buf>>],
416 ) -> Result<usize> {
417 response.parse_with_config_and_uninit_headers(buf, self, headers)
418 }
419}
420
421/// A parsed Request.
422///
423/// The optional values will be `None` if a parse was not complete, and did not
424/// parse the associated property. This allows you to inspect the parts that
425/// could be parsed, before reading more, in case you wish to exit early.
426///
427/// # Example
428///
429/// ```no_run
430/// let buf = b"GET /404 HTTP/1.1\r\nHost:";
431/// let mut headers = [httparse::EMPTY_HEADER; 16];
432/// let mut req = httparse::Request::new(&mut headers);
433/// let res = req.parse(buf).unwrap();
434/// if res.is_partial() {
435/// match req.path {
436/// Some(ref path) => {
437/// // check router for path.
438/// // /404 doesn't exist? we could stop parsing
439/// },
440/// None => {
441/// // must read more and parse again
442/// }
443/// }
444/// }
445/// ```
446#[derive(Debug, Eq, PartialEq)]
447pub struct Request<'headers, 'buf> {
448 /// The request method, such as `GET`.
449 pub method: Option<&'buf str>,
450 /// The request path, such as `/about-us`.
451 pub path: Option<&'buf str>,
452 /// The request minor version, such as `1` for `HTTP/1.1`.
453 pub version: Option<u8>,
454 /// The request headers.
455 pub headers: &'headers mut [Header<'buf>]
456}
457
458impl<'h, 'b> Request<'h, 'b> {
459 /// Creates a new Request, using a slice of headers you allocate.
460 #[inline]
461 pub fn new(headers: &'h mut [Header<'b>]) -> Request<'h, 'b> {
462 Request {
463 method: None,
464 path: None,
465 version: None,
466 headers,
467 }
468 }
469
470 fn parse_with_config_and_uninit_headers(
471 &mut self,
472 buf: &'b [u8],
473 config: &ParserConfig,
474 mut headers: &'h mut [MaybeUninit<Header<'b>>],
475 ) -> Result<usize> {
476 let orig_len = buf.len();
477 let mut bytes = Bytes::new(buf);
478 complete!(skip_empty_lines(&mut bytes));
479 const GET: [u8; 4] = *b"GET ";
480 const POST: [u8; 4] = *b"POST";
481 let method = match bytes.peek_n::<[u8; 4]>(4) {
482 Some(GET) => {
483 unsafe {
484 bytes.advance_and_commit(4);
485 }
486 "GET"
487 }
488 Some(POST) if bytes.peek_ahead(4) == Some(b' ') => {
489 unsafe {
490 bytes.advance_and_commit(5);
491 }
492 "POST"
493 }
494 _ => complete!(parse_token(&mut bytes)),
495 };
496 self.method = Some(method);
497 if config.allow_multiple_spaces_in_request_line_delimiters {
498 complete!(skip_spaces(&mut bytes));
499 }
500 self.path = Some(complete!(parse_uri(&mut bytes)));
501 if config.allow_multiple_spaces_in_request_line_delimiters {
502 complete!(skip_spaces(&mut bytes));
503 }
504 self.version = Some(complete!(parse_version(&mut bytes)));
505 newline!(bytes);
506
507 let len = orig_len - bytes.len();
508 let headers_len = complete!(parse_headers_iter_uninit(
509 &mut headers,
510 &mut bytes,
511 &ParserConfig::default(),
512 ));
513 /* SAFETY: see `parse_headers_iter_uninit` guarantees */
514 self.headers = unsafe { assume_init_slice(headers) };
515
516 Ok(Status::Complete(len + headers_len))
517 }
518
519 /// Try to parse a buffer of bytes into the Request,
520 /// except use an uninitialized slice of `Header`s.
521 ///
522 /// For more information, see `parse`
523 pub fn parse_with_uninit_headers(
524 &mut self,
525 buf: &'b [u8],
526 headers: &'h mut [MaybeUninit<Header<'b>>],
527 ) -> Result<usize> {
528 self.parse_with_config_and_uninit_headers(buf, &Default::default(), headers)
529 }
530
531 fn parse_with_config(&mut self, buf: &'b [u8], config: &ParserConfig) -> Result<usize> {
532 let headers = mem::replace(&mut self.headers, &mut []);
533
534 /* SAFETY: see `parse_headers_iter_uninit` guarantees */
535 unsafe {
536 let headers: *mut [Header<'_>] = headers;
537 let headers = headers as *mut [MaybeUninit<Header<'_>>];
538 match self.parse_with_config_and_uninit_headers(buf, config, &mut *headers) {
539 Ok(Status::Complete(idx)) => Ok(Status::Complete(idx)),
540 other => {
541 // put the original headers back
542 self.headers = &mut *(headers as *mut [Header<'_>]);
543 other
544 },
545 }
546 }
547 }
548
549 /// Try to parse a buffer of bytes into the Request.
550 ///
551 /// Returns byte offset in `buf` to start of HTTP body.
552 pub fn parse(&mut self, buf: &'b [u8]) -> Result<usize> {
553 self.parse_with_config(buf, &Default::default())
554 }
555}
556
557#[inline]
558fn skip_empty_lines(bytes: &mut Bytes<'_>) -> Result<()> {
559 loop {
560 let b: Option = bytes.peek();
561 match b {
562 Some(b'\r') => {
563 // there's `\r`, so it's safe to bump 1 pos
564 unsafe { bytes.bump() };
565 expect!(bytes.next() == b'\n' => Err(Error::NewLine));
566 },
567 Some(b'\n') => {
568 // there's `\n`, so it's safe to bump 1 pos
569 unsafe { bytes.bump(); }
570 },
571 Some(..) => {
572 bytes.slice();
573 return Ok(Status::Complete(()));
574 },
575 None => return Ok(Status::Partial)
576 }
577 }
578}
579
580#[inline]
581fn skip_spaces(bytes: &mut Bytes<'_>) -> Result<()> {
582 loop {
583 let b: Option = bytes.peek();
584 match b {
585 Some(b' ') => {
586 // there's ` `, so it's safe to bump 1 pos
587 unsafe { bytes.bump() };
588 }
589 Some(..) => {
590 bytes.slice();
591 return Ok(Status::Complete(()));
592 }
593 None => return Ok(Status::Partial),
594 }
595 }
596}
597
598/// A parsed Response.
599///
600/// See `Request` docs for explanation of optional values.
601#[derive(Debug, Eq, PartialEq)]
602pub struct Response<'headers, 'buf> {
603 /// The response minor version, such as `1` for `HTTP/1.1`.
604 pub version: Option<u8>,
605 /// The response code, such as `200`.
606 pub code: Option<u16>,
607 /// The response reason-phrase, such as `OK`.
608 ///
609 /// Contains an empty string if the reason-phrase was missing or contained invalid characters.
610 pub reason: Option<&'buf str>,
611 /// The response headers.
612 pub headers: &'headers mut [Header<'buf>]
613}
614
615impl<'h, 'b> Response<'h, 'b> {
616 /// Creates a new `Response` using a slice of `Header`s you have allocated.
617 #[inline]
618 pub fn new(headers: &'h mut [Header<'b>]) -> Response<'h, 'b> {
619 Response {
620 version: None,
621 code: None,
622 reason: None,
623 headers,
624 }
625 }
626
627 /// Try to parse a buffer of bytes into this `Response`.
628 pub fn parse(&mut self, buf: &'b [u8]) -> Result<usize> {
629 self.parse_with_config(buf, &ParserConfig::default())
630 }
631
632 fn parse_with_config(&mut self, buf: &'b [u8], config: &ParserConfig) -> Result<usize> {
633 let headers = mem::replace(&mut self.headers, &mut []);
634
635 unsafe {
636 let headers: *mut [Header<'_>] = headers;
637 let headers = headers as *mut [MaybeUninit<Header<'_>>];
638 match self.parse_with_config_and_uninit_headers(buf, config, &mut *headers) {
639 Ok(Status::Complete(idx)) => Ok(Status::Complete(idx)),
640 other => {
641 // put the original headers back
642 self.headers = &mut *(headers as *mut [Header<'_>]);
643 other
644 },
645 }
646 }
647 }
648
649 fn parse_with_config_and_uninit_headers(
650 &mut self,
651 buf: &'b [u8],
652 config: &ParserConfig,
653 mut headers: &'h mut [MaybeUninit<Header<'b>>],
654 ) -> Result<usize> {
655 let orig_len = buf.len();
656 let mut bytes = Bytes::new(buf);
657
658 complete!(skip_empty_lines(&mut bytes));
659 self.version = Some(complete!(parse_version(&mut bytes)));
660 space!(bytes or Error::Version);
661 if config.allow_multiple_spaces_in_response_status_delimiters {
662 complete!(skip_spaces(&mut bytes));
663 }
664 self.code = Some(complete!(parse_code(&mut bytes)));
665
666 // RFC7230 says there must be 'SP' and then reason-phrase, but admits
667 // its only for legacy reasons. With the reason-phrase completely
668 // optional (and preferred to be omitted) in HTTP2, we'll just
669 // handle any response that doesn't include a reason-phrase, because
670 // it's more lenient, and we don't care anyways.
671 //
672 // So, a SP means parse a reason-phrase.
673 // A newline means go to headers.
674 // Anything else we'll say is a malformed status.
675 match next!(bytes) {
676 b' ' => {
677 if config.allow_multiple_spaces_in_response_status_delimiters {
678 complete!(skip_spaces(&mut bytes));
679 }
680 bytes.slice();
681 self.reason = Some(complete!(parse_reason(&mut bytes)));
682 },
683 b'\r' => {
684 expect!(bytes.next() == b'\n' => Err(Error::Status));
685 bytes.slice();
686 self.reason = Some("");
687 },
688 b'\n' => {
689 bytes.slice();
690 self.reason = Some("");
691 }
692 _ => return Err(Error::Status),
693 }
694
695
696 let len = orig_len - bytes.len();
697 let headers_len = complete!(parse_headers_iter_uninit(
698 &mut headers,
699 &mut bytes,
700 config
701 ));
702 /* SAFETY: see `parse_headers_iter_uninit` guarantees */
703 self.headers = unsafe { assume_init_slice(headers) };
704 Ok(Status::Complete(len + headers_len))
705 }
706}
707
708/// Represents a parsed header.
709#[derive(Copy, Clone, Eq, PartialEq)]
710pub struct Header<'a> {
711 /// The name portion of a header.
712 ///
713 /// A header name must be valid ASCII-US, so it's safe to store as a `&str`.
714 pub name: &'a str,
715 /// The value portion of a header.
716 ///
717 /// While headers **should** be ASCII-US, the specification allows for
718 /// values that may not be, and so the value is stored as bytes.
719 pub value: &'a [u8],
720}
721
722impl<'a> fmt::Debug for Header<'a> {
723 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
724 let mut f: DebugStruct<'_, '_> = f.debug_struct(name:"Header");
725 f.field(name:"name", &self.name);
726 if let Ok(value: &str) = str::from_utf8(self.value) {
727 f.field(name:"value", &value);
728 } else {
729 f.field(name:"value", &self.value);
730 }
731 f.finish()
732 }
733}
734
735/// An empty header, useful for constructing a `Header` array to pass in for
736/// parsing.
737///
738/// # Example
739///
740/// ```
741/// let headers = [httparse::EMPTY_HEADER; 64];
742/// ```
743pub const EMPTY_HEADER: Header<'static> = Header { name: "", value: b"" };
744
745#[inline]
746fn parse_version(bytes: &mut Bytes<'_>) -> Result<u8> {
747 if let Some(eight: [u8; 8]) = bytes.peek_n::<[u8; 8]>(8) {
748 unsafe { bytes.advance(8); }
749 return match &eight {
750 b"HTTP/1.0" => Ok(Status::Complete(0)),
751 b"HTTP/1.1" => Ok(Status::Complete(1)),
752 _ => Err(Error::Version),
753 }
754 }
755
756 // else (but not in `else` because of borrow checker)
757
758 // If there aren't at least 8 bytes, we still want to detect early
759 // if this is a valid version or not. If it is, we'll return Partial.
760 expect!(bytes.next() == b'H' => Err(Error::Version));
761 expect!(bytes.next() == b'T' => Err(Error::Version));
762 expect!(bytes.next() == b'T' => Err(Error::Version));
763 expect!(bytes.next() == b'P' => Err(Error::Version));
764 expect!(bytes.next() == b'/' => Err(Error::Version));
765 expect!(bytes.next() == b'1' => Err(Error::Version));
766 expect!(bytes.next() == b'.' => Err(Error::Version));
767 Ok(Status::Partial)
768}
769
770/// From [RFC 7230](https://tools.ietf.org/html/rfc7230):
771///
772/// > ```notrust
773/// > reason-phrase = *( HTAB / SP / VCHAR / obs-text )
774/// > HTAB = %x09 ; horizontal tab
775/// > VCHAR = %x21-7E ; visible (printing) characters
776/// > obs-text = %x80-FF
777/// > ```
778///
779/// > A.2. Changes from RFC 2616
780/// >
781/// > Non-US-ASCII content in header fields and the reason phrase
782/// > has been obsoleted and made opaque (the TEXT rule was removed).
783#[inline]
784fn parse_reason<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> {
785 let mut seen_obs_text = false;
786 loop {
787 let b = next!(bytes);
788 if b == b'\r' {
789 expect!(bytes.next() == b'\n' => Err(Error::Status));
790 return Ok(Status::Complete(unsafe {
791 let bytes = bytes.slice_skip(2);
792 if !seen_obs_text {
793 // all bytes up till `i` must have been HTAB / SP / VCHAR
794 str::from_utf8_unchecked(bytes)
795 } else {
796 // obs-text characters were found, so return the fallback empty string
797 ""
798 }
799 }));
800 } else if b == b'\n' {
801 return Ok(Status::Complete(unsafe {
802 let bytes = bytes.slice_skip(1);
803 if !seen_obs_text {
804 // all bytes up till `i` must have been HTAB / SP / VCHAR
805 str::from_utf8_unchecked(bytes)
806 } else {
807 // obs-text characters were found, so return the fallback empty string
808 ""
809 }
810 }));
811 } else if !(b == 0x09 || b == b' ' || (0x21..=0x7E).contains(&b) || b >= 0x80) {
812 return Err(Error::Status);
813 } else if b >= 0x80 {
814 seen_obs_text = true;
815 }
816 }
817}
818
819#[inline]
820fn parse_token<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> {
821 let b: u8 = next!(bytes);
822 if !is_token(b) {
823 // First char must be a token char, it can't be a space which would indicate an empty token.
824 return Err(Error::Token);
825 }
826
827 loop {
828 let b: u8 = next!(bytes);
829 if b == b' ' {
830 return Ok(Status::Complete(unsafe {
831 // all bytes up till `i` must have been `is_token`.
832 str::from_utf8_unchecked(bytes.slice_skip(1))
833 }));
834 } else if !is_token(b) {
835 return Err(Error::Token);
836 }
837 }
838}
839
840#[inline]
841fn parse_uri<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> {
842 let b: u8 = next!(bytes);
843 if !is_uri_token(b) {
844 // First char must be a URI char, it can't be a space which would indicate an empty path.
845 return Err(Error::Token);
846 }
847
848 simd::match_uri_vectored(bytes);
849
850 loop {
851 let b: u8 = next!(bytes);
852 if b == b' ' {
853 return Ok(Status::Complete(unsafe {
854 // all bytes up till `i` must have been `is_token`.
855 str::from_utf8_unchecked(bytes.slice_skip(1))
856 }));
857 } else if !is_uri_token(b) {
858 return Err(Error::Token);
859 }
860 }
861}
862
863
864#[inline]
865fn parse_code(bytes: &mut Bytes<'_>) -> Result<u16> {
866 let hundreds: u8 = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
867 let tens: u8 = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
868 let ones: u8 = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status));
869
870 Ok(Status::Complete((hundreds - b'0') as u16 * 100 +
871 (tens - b'0') as u16 * 10 +
872 (ones - b'0') as u16))
873}
874
875/// Parse a buffer of bytes as headers.
876///
877/// The return value, if complete and successful, includes the index of the
878/// buffer that parsing stopped at, and a sliced reference to the parsed
879/// headers. The length of the slice will be equal to the number of properly
880/// parsed headers.
881///
882/// # Example
883///
884/// ```
885/// let buf = b"Host: foo.bar\nAccept: */*\n\nblah blah";
886/// let mut headers = [httparse::EMPTY_HEADER; 4];
887/// assert_eq!(httparse::parse_headers(buf, &mut headers),
888/// Ok(httparse::Status::Complete((27, &[
889/// httparse::Header { name: "Host", value: b"foo.bar" },
890/// httparse::Header { name: "Accept", value: b"*/*" }
891/// ][..]))));
892/// ```
893pub fn parse_headers<'b: 'h, 'h>(
894 src: &'b [u8],
895 mut dst: &'h mut [Header<'b>],
896) -> Result<(usize, &'h [Header<'b>])> {
897 let mut iter: Bytes<'_> = Bytes::new(slice:src);
898 let pos: usize = complete!(parse_headers_iter(&mut dst, &mut iter, &ParserConfig::default()));
899 Ok(Status::Complete((pos, dst)))
900}
901
902#[inline]
903fn parse_headers_iter<'a, 'b>(
904 headers: &mut &mut [Header<'a>],
905 bytes: &'b mut Bytes<'a>,
906 config: &ParserConfig,
907) -> Result<usize> {
908 parse_headers_iter_uninit(
909 /* SAFETY: see `parse_headers_iter_uninit` guarantees */
910 headers:unsafe { deinit_slice_mut(headers) },
911 bytes,
912 config,
913 )
914}
915
916unsafe fn deinit_slice_mut<'a, 'b, T>(s: &'a mut &'b mut [T]) -> &'a mut &'b mut [MaybeUninit<T>] {
917 let s: *mut &mut [T] = s;
918 let s: *mut &mut [MaybeUninit] = s as *mut &mut [MaybeUninit<T>];
919 &mut *s
920}
921unsafe fn assume_init_slice<T>(s: &mut [MaybeUninit<T>]) -> &mut [T] {
922 let s: *mut [MaybeUninit<T>] = s;
923 let s: *mut [T] = s as *mut [T];
924 &mut *s
925}
926
927/* Function which parsers headers into uninitialized buffer.
928 *
929 * Guarantees that it doesn't write garbage, so casting
930 * &mut &mut [Header] -> &mut &mut [MaybeUninit<Header>]
931 * is safe here.
932 *
933 * Also it promises `headers` get shrunk to number of initialized headers,
934 * so casting the other way around after calling this function is safe
935 */
936fn parse_headers_iter_uninit<'a, 'b>(
937 headers: &mut &mut [MaybeUninit<Header<'a>>],
938 bytes: &'b mut Bytes<'a>,
939 config: &ParserConfig,
940) -> Result<usize> {
941
942 /* Flow of this function is pretty complex, especially with macros,
943 * so this struct makes sure we shrink `headers` to only parsed ones.
944 * Comparing to previous code, this only may introduce some additional
945 * instructions in case of early return */
946 struct ShrinkOnDrop<'r1, 'r2, 'a> {
947 headers: &'r1 mut &'r2 mut [MaybeUninit<Header<'a>>],
948 num_headers: usize,
949 }
950
951 impl<'r1, 'r2, 'a> Drop for ShrinkOnDrop<'r1, 'r2, 'a> {
952 fn drop(&mut self) {
953 let headers = mem::replace(self.headers, &mut []);
954
955 /* SAFETY: num_headers is the number of initialized headers */
956 let headers = unsafe { headers.get_unchecked_mut(..self.num_headers) };
957
958 *self.headers = headers;
959 }
960 }
961
962 let mut autoshrink = ShrinkOnDrop {
963 headers,
964 num_headers: 0,
965 };
966 let mut count: usize = 0;
967 let mut result = Err(Error::TooManyHeaders);
968
969 let mut iter = autoshrink.headers.iter_mut();
970
971 macro_rules! maybe_continue_after_obsolete_line_folding {
972 ($bytes:ident, $label:lifetime) => {
973 if config.allow_obsolete_multiline_headers_in_responses {
974 match $bytes.peek() {
975 None => {
976 // Next byte may be a space, in which case that header
977 // is using obsolete line folding, so we may have more
978 // whitespace to skip after colon.
979 return Ok(Status::Partial);
980 }
981 Some(b' ') | Some(b'\t') => {
982 // The space will be consumed next iteration.
983 continue $label;
984 }
985 _ => {
986 // There is another byte after the end of the line,
987 // but it's not whitespace, so it's probably another
988 // header or the final line return. This header is thus
989 // empty.
990 },
991 }
992 }
993 }
994 }
995
996 'headers: loop {
997 // Return the error `$err` if `ignore_invalid_headers_in_responses`
998 // is false, otherwise find the end of the current line and resume
999 // parsing on the next one.
1000 macro_rules! handle_invalid_char {
1001 ($bytes:ident, $b:ident, $err:ident) => {
1002 if !config.ignore_invalid_headers_in_responses {
1003 return Err(Error::$err);
1004 }
1005
1006 let mut b = $b;
1007
1008 loop {
1009 if b == b'\r' {
1010 expect!(bytes.next() == b'\n' => Err(Error::$err));
1011 break;
1012 }
1013 if b == b'\n' {
1014 break;
1015 }
1016 if b == b'\0' {
1017 return Err(Error::$err);
1018 }
1019 b = next!($bytes);
1020 }
1021
1022 count += $bytes.pos();
1023 $bytes.slice();
1024
1025 continue 'headers;
1026 };
1027 }
1028
1029 // a newline here means the head is over!
1030 let b = next!(bytes);
1031 if b == b'\r' {
1032 expect!(bytes.next() == b'\n' => Err(Error::NewLine));
1033 result = Ok(Status::Complete(count + bytes.pos()));
1034 break;
1035 }
1036 if b == b'\n' {
1037 result = Ok(Status::Complete(count + bytes.pos()));
1038 break;
1039 }
1040 if !is_header_name_token(b) {
1041 handle_invalid_char!(bytes, b, HeaderName);
1042 }
1043
1044 // parse header name until colon
1045 let header_name: &str = 'name: loop {
1046 let mut b = next!(bytes);
1047
1048 if is_header_name_token(b) {
1049 continue 'name;
1050 }
1051
1052 count += bytes.pos();
1053 let name = unsafe {
1054 str::from_utf8_unchecked(bytes.slice_skip(1))
1055 };
1056
1057 if b == b':' {
1058 break 'name name;
1059 }
1060
1061 if config.allow_spaces_after_header_name_in_responses {
1062 while b == b' ' || b == b'\t' {
1063 b = next!(bytes);
1064
1065 if b == b':' {
1066 count += bytes.pos();
1067 bytes.slice();
1068 break 'name name;
1069 }
1070 }
1071 }
1072
1073 handle_invalid_char!(bytes, b, HeaderName);
1074 };
1075
1076 let mut b;
1077
1078 let value_slice = 'value: loop {
1079 // eat white space between colon and value
1080 'whitespace_after_colon: loop {
1081 b = next!(bytes);
1082 if b == b' ' || b == b'\t' {
1083 count += bytes.pos();
1084 bytes.slice();
1085 continue 'whitespace_after_colon;
1086 }
1087 if is_header_value_token(b) {
1088 break 'whitespace_after_colon;
1089 }
1090
1091 if b == b'\r' {
1092 expect!(bytes.next() == b'\n' => Err(Error::HeaderValue));
1093 } else if b != b'\n' {
1094 handle_invalid_char!(bytes, b, HeaderValue);
1095 }
1096
1097 maybe_continue_after_obsolete_line_folding!(bytes, 'whitespace_after_colon);
1098
1099 count += bytes.pos();
1100 let whitespace_slice = bytes.slice();
1101
1102 // This produces an empty slice that points to the beginning
1103 // of the whitespace.
1104 break 'value &whitespace_slice[0..0];
1105 }
1106
1107 'value_lines: loop {
1108 // parse value till EOL
1109
1110 simd::match_header_value_vectored(bytes);
1111
1112 'value_line: loop {
1113 if let Some(bytes8) = bytes.peek_n::<[u8; 8]>(8) {
1114 macro_rules! check {
1115 ($bytes:ident, $i:literal) => ({
1116 b = $bytes[$i];
1117 if !is_header_value_token(b) {
1118 unsafe { bytes.advance($i + 1); }
1119 break 'value_line;
1120 }
1121 });
1122 }
1123
1124 check!(bytes8, 0);
1125 check!(bytes8, 1);
1126 check!(bytes8, 2);
1127 check!(bytes8, 3);
1128 check!(bytes8, 4);
1129 check!(bytes8, 5);
1130 check!(bytes8, 6);
1131 check!(bytes8, 7);
1132 unsafe { bytes.advance(8); }
1133
1134 continue 'value_line;
1135 }
1136
1137 b = next!(bytes);
1138 if !is_header_value_token(b) {
1139 break 'value_line;
1140 }
1141 }
1142
1143 //found_ctl
1144 let skip = if b == b'\r' {
1145 expect!(bytes.next() == b'\n' => Err(Error::HeaderValue));
1146 2
1147 } else if b == b'\n' {
1148 1
1149 } else {
1150 handle_invalid_char!(bytes, b, HeaderValue);
1151 };
1152
1153 maybe_continue_after_obsolete_line_folding!(bytes, 'value_lines);
1154
1155 count += bytes.pos();
1156 // having just checked that a newline exists, it's safe to skip it.
1157 unsafe {
1158 break 'value bytes.slice_skip(skip);
1159 }
1160 }
1161 };
1162
1163 let uninit_header = match iter.next() {
1164 Some(header) => header,
1165 None => break 'headers
1166 };
1167
1168 // trim trailing whitespace in the header
1169 let header_value = if let Some(last_visible) = value_slice
1170 .iter()
1171 .rposition(|b| *b != b' ' && *b != b'\t' && *b != b'\r' && *b != b'\n')
1172 {
1173 // There is at least one non-whitespace character.
1174 &value_slice[0..last_visible+1]
1175 } else {
1176 // There is no non-whitespace character. This can only happen when value_slice is
1177 // empty.
1178 value_slice
1179 };
1180
1181 *uninit_header = MaybeUninit::new(Header {
1182 name: header_name,
1183 value: header_value,
1184 });
1185 autoshrink.num_headers += 1;
1186 }
1187
1188 result
1189}
1190
1191/// Parse a buffer of bytes as a chunk size.
1192///
1193/// The return value, if complete and successful, includes the index of the
1194/// buffer that parsing stopped at, and the size of the following chunk.
1195///
1196/// # Example
1197///
1198/// ```
1199/// let buf = b"4\r\nRust\r\n0\r\n\r\n";
1200/// assert_eq!(httparse::parse_chunk_size(buf),
1201/// Ok(httparse::Status::Complete((3, 4))));
1202/// ```
1203pub fn parse_chunk_size(buf: &[u8])
1204 -> result::Result<Status<(usize, u64)>, InvalidChunkSize> {
1205 const RADIX: u64 = 16;
1206 let mut bytes = Bytes::new(buf);
1207 let mut size = 0;
1208 let mut in_chunk_size = true;
1209 let mut in_ext = false;
1210 let mut count = 0;
1211 loop {
1212 let b = next!(bytes);
1213 match b {
1214 b'0' ..= b'9' if in_chunk_size => {
1215 if count > 15 {
1216 return Err(InvalidChunkSize);
1217 }
1218 count += 1;
1219 size *= RADIX;
1220 size += (b - b'0') as u64;
1221 },
1222 b'a' ..= b'f' if in_chunk_size => {
1223 if count > 15 {
1224 return Err(InvalidChunkSize);
1225 }
1226 count += 1;
1227 size *= RADIX;
1228 size += (b + 10 - b'a') as u64;
1229 }
1230 b'A' ..= b'F' if in_chunk_size => {
1231 if count > 15 {
1232 return Err(InvalidChunkSize);
1233 }
1234 count += 1;
1235 size *= RADIX;
1236 size += (b + 10 - b'A') as u64;
1237 }
1238 b'\r' => {
1239 match next!(bytes) {
1240 b'\n' => break,
1241 _ => return Err(InvalidChunkSize),
1242 }
1243 }
1244 // If we weren't in the extension yet, the ";" signals its start
1245 b';' if !in_ext => {
1246 in_ext = true;
1247 in_chunk_size = false;
1248 }
1249 // "Linear white space" is ignored between the chunk size and the
1250 // extension separator token (";") due to the "implied *LWS rule".
1251 b'\t' | b' ' if !in_ext && !in_chunk_size => {}
1252 // LWS can follow the chunk size, but no more digits can come
1253 b'\t' | b' ' if in_chunk_size => in_chunk_size = false,
1254 // We allow any arbitrary octet once we are in the extension, since
1255 // they all get ignored anyway. According to the HTTP spec, valid
1256 // extensions would have a more strict syntax:
1257 // (token ["=" (token | quoted-string)])
1258 // but we gain nothing by rejecting an otherwise valid chunk size.
1259 _ if in_ext => {}
1260 // Finally, if we aren't in the extension and we're reading any
1261 // other octet, the chunk size line is invalid!
1262 _ => return Err(InvalidChunkSize),
1263 }
1264 }
1265 Ok(Status::Complete((bytes.pos(), size)))
1266}
1267
1268#[cfg(test)]
1269mod tests {
1270 use super::{Request, Response, Status, EMPTY_HEADER, parse_chunk_size};
1271
1272 const NUM_OF_HEADERS: usize = 4;
1273
1274 macro_rules! req {
1275 ($name:ident, $buf:expr, |$arg:ident| $body:expr) => (
1276 req! {$name, $buf, Ok(Status::Complete($buf.len())), |$arg| $body }
1277 );
1278 ($name:ident, $buf:expr, $len:expr, |$arg:ident| $body:expr) => (
1279 #[test]
1280 fn $name() {
1281 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1282 let mut req = Request::new(&mut headers[..]);
1283 let status = req.parse($buf.as_ref());
1284 assert_eq!(status, $len);
1285 closure(req);
1286
1287 fn closure($arg: Request) {
1288 $body
1289 }
1290 }
1291 )
1292 }
1293
1294 req! {
1295 test_request_simple,
1296 b"GET / HTTP/1.1\r\n\r\n",
1297 |req| {
1298 assert_eq!(req.method.unwrap(), "GET");
1299 assert_eq!(req.path.unwrap(), "/");
1300 assert_eq!(req.version.unwrap(), 1);
1301 assert_eq!(req.headers.len(), 0);
1302 }
1303 }
1304
1305 req! {
1306 test_request_simple_with_query_params,
1307 b"GET /thing?data=a HTTP/1.1\r\n\r\n",
1308 |req| {
1309 assert_eq!(req.method.unwrap(), "GET");
1310 assert_eq!(req.path.unwrap(), "/thing?data=a");
1311 assert_eq!(req.version.unwrap(), 1);
1312 assert_eq!(req.headers.len(), 0);
1313 }
1314 }
1315
1316 req! {
1317 test_request_simple_with_whatwg_query_params,
1318 b"GET /thing?data=a^ HTTP/1.1\r\n\r\n",
1319 |req| {
1320 assert_eq!(req.method.unwrap(), "GET");
1321 assert_eq!(req.path.unwrap(), "/thing?data=a^");
1322 assert_eq!(req.version.unwrap(), 1);
1323 assert_eq!(req.headers.len(), 0);
1324 }
1325 }
1326
1327 req! {
1328 test_request_headers,
1329 b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \r\n\r\n",
1330 |req| {
1331 assert_eq!(req.method.unwrap(), "GET");
1332 assert_eq!(req.path.unwrap(), "/");
1333 assert_eq!(req.version.unwrap(), 1);
1334 assert_eq!(req.headers.len(), 2);
1335 assert_eq!(req.headers[0].name, "Host");
1336 assert_eq!(req.headers[0].value, b"foo.com");
1337 assert_eq!(req.headers[1].name, "Cookie");
1338 assert_eq!(req.headers[1].value, b"");
1339 }
1340 }
1341
1342 req! {
1343 test_request_headers_optional_whitespace,
1344 b"GET / HTTP/1.1\r\nHost: \tfoo.com\t \r\nCookie: \t \r\n\r\n",
1345 |req| {
1346 assert_eq!(req.method.unwrap(), "GET");
1347 assert_eq!(req.path.unwrap(), "/");
1348 assert_eq!(req.version.unwrap(), 1);
1349 assert_eq!(req.headers.len(), 2);
1350 assert_eq!(req.headers[0].name, "Host");
1351 assert_eq!(req.headers[0].value, b"foo.com");
1352 assert_eq!(req.headers[1].name, "Cookie");
1353 assert_eq!(req.headers[1].value, b"");
1354 }
1355 }
1356
1357 req! {
1358 // test the scalar parsing
1359 test_request_header_value_htab_short,
1360 b"GET / HTTP/1.1\r\nUser-Agent: some\tagent\r\n\r\n",
1361 |req| {
1362 assert_eq!(req.method.unwrap(), "GET");
1363 assert_eq!(req.path.unwrap(), "/");
1364 assert_eq!(req.version.unwrap(), 1);
1365 assert_eq!(req.headers.len(), 1);
1366 assert_eq!(req.headers[0].name, "User-Agent");
1367 assert_eq!(req.headers[0].value, b"some\tagent");
1368 }
1369 }
1370
1371 req! {
1372 // test the sse42 parsing
1373 test_request_header_value_htab_med,
1374 b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\tagent\r\n\r\n",
1375 |req| {
1376 assert_eq!(req.method.unwrap(), "GET");
1377 assert_eq!(req.path.unwrap(), "/");
1378 assert_eq!(req.version.unwrap(), 1);
1379 assert_eq!(req.headers.len(), 1);
1380 assert_eq!(req.headers[0].name, "User-Agent");
1381 assert_eq!(req.headers[0].value, b"1234567890some\tagent");
1382 }
1383 }
1384
1385 req! {
1386 // test the avx2 parsing
1387 test_request_header_value_htab_long,
1388 b"GET / HTTP/1.1\r\nUser-Agent: 1234567890some\t1234567890agent1234567890\r\n\r\n",
1389 |req| {
1390 assert_eq!(req.method.unwrap(), "GET");
1391 assert_eq!(req.path.unwrap(), "/");
1392 assert_eq!(req.version.unwrap(), 1);
1393 assert_eq!(req.headers.len(), 1);
1394 assert_eq!(req.headers[0].name, "User-Agent");
1395 assert_eq!(req.headers[0].value, &b"1234567890some\t1234567890agent1234567890"[..]);
1396 }
1397 }
1398
1399 req! {
1400 test_request_headers_max,
1401 b"GET / HTTP/1.1\r\nA: A\r\nB: B\r\nC: C\r\nD: D\r\n\r\n",
1402 |req| {
1403 assert_eq!(req.headers.len(), NUM_OF_HEADERS);
1404 }
1405 }
1406
1407 req! {
1408 test_request_multibyte,
1409 b"GET / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: \xe3\x81\xb2\xe3/1.0\r\n\r\n",
1410 |req| {
1411 assert_eq!(req.method.unwrap(), "GET");
1412 assert_eq!(req.path.unwrap(), "/");
1413 assert_eq!(req.version.unwrap(), 1);
1414 assert_eq!(req.headers.len(), 2);
1415 assert_eq!(req.headers[0].name, "Host");
1416 assert_eq!(req.headers[0].value, b"foo.com");
1417 assert_eq!(req.headers[1].name, "User-Agent");
1418 assert_eq!(req.headers[1].value, b"\xe3\x81\xb2\xe3/1.0");
1419 }
1420 }
1421
1422
1423 req! {
1424 test_request_partial,
1425 b"GET / HTTP/1.1\r\n\r", Ok(Status::Partial),
1426 |_req| {}
1427 }
1428
1429 req! {
1430 test_request_partial_version,
1431 b"GET / HTTP/1.", Ok(Status::Partial),
1432 |_req| {}
1433 }
1434
1435 req! {
1436 test_request_partial_parses_headers_as_much_as_it_can,
1437 b"GET / HTTP/1.1\r\nHost: yolo\r\n",
1438 Ok(crate::Status::Partial),
1439 |req| {
1440 assert_eq!(req.method.unwrap(), "GET");
1441 assert_eq!(req.path.unwrap(), "/");
1442 assert_eq!(req.version.unwrap(), 1);
1443 assert_eq!(req.headers.len(), NUM_OF_HEADERS); // doesn't slice since not Complete
1444 assert_eq!(req.headers[0].name, "Host");
1445 assert_eq!(req.headers[0].value, b"yolo");
1446 }
1447 }
1448
1449 req! {
1450 test_request_newlines,
1451 b"GET / HTTP/1.1\nHost: foo.bar\n\n",
1452 |_r| {}
1453 }
1454
1455 req! {
1456 test_request_empty_lines_prefix,
1457 b"\r\n\r\nGET / HTTP/1.1\r\n\r\n",
1458 |req| {
1459 assert_eq!(req.method.unwrap(), "GET");
1460 assert_eq!(req.path.unwrap(), "/");
1461 assert_eq!(req.version.unwrap(), 1);
1462 assert_eq!(req.headers.len(), 0);
1463 }
1464 }
1465
1466 req! {
1467 test_request_empty_lines_prefix_lf_only,
1468 b"\n\nGET / HTTP/1.1\n\n",
1469 |req| {
1470 assert_eq!(req.method.unwrap(), "GET");
1471 assert_eq!(req.path.unwrap(), "/");
1472 assert_eq!(req.version.unwrap(), 1);
1473 assert_eq!(req.headers.len(), 0);
1474 }
1475 }
1476
1477 req! {
1478 test_request_path_backslash,
1479 b"\n\nGET /\\?wayne\\=5 HTTP/1.1\n\n",
1480 |req| {
1481 assert_eq!(req.method.unwrap(), "GET");
1482 assert_eq!(req.path.unwrap(), "/\\?wayne\\=5");
1483 assert_eq!(req.version.unwrap(), 1);
1484 assert_eq!(req.headers.len(), 0);
1485 }
1486 }
1487
1488 req! {
1489 test_request_with_invalid_token_delimiter,
1490 b"GET\n/ HTTP/1.1\r\nHost: foo.bar\r\n\r\n",
1491 Err(crate::Error::Token),
1492 |_r| {}
1493 }
1494
1495
1496 req! {
1497 test_request_with_invalid_but_short_version,
1498 b"GET / HTTP/1!",
1499 Err(crate::Error::Version),
1500 |_r| {}
1501 }
1502
1503 req! {
1504 test_request_with_empty_method,
1505 b" / HTTP/1.1\r\n\r\n",
1506 Err(crate::Error::Token),
1507 |_r| {}
1508 }
1509
1510 req! {
1511 test_request_with_empty_path,
1512 b"GET HTTP/1.1\r\n\r\n",
1513 Err(crate::Error::Token),
1514 |_r| {}
1515 }
1516
1517 req! {
1518 test_request_with_empty_method_and_path,
1519 b" HTTP/1.1\r\n\r\n",
1520 Err(crate::Error::Token),
1521 |_r| {}
1522 }
1523
1524 macro_rules! res {
1525 ($name:ident, $buf:expr, |$arg:ident| $body:expr) => (
1526 res! {$name, $buf, Ok(Status::Complete($buf.len())), |$arg| $body }
1527 );
1528 ($name:ident, $buf:expr, $len:expr, |$arg:ident| $body:expr) => (
1529 #[test]
1530 fn $name() {
1531 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1532 let mut res = Response::new(&mut headers[..]);
1533 let status = res.parse($buf.as_ref());
1534 assert_eq!(status, $len);
1535 closure(res);
1536
1537 fn closure($arg: Response) {
1538 $body
1539 }
1540 }
1541 )
1542 }
1543
1544 res! {
1545 test_response_simple,
1546 b"HTTP/1.1 200 OK\r\n\r\n",
1547 |res| {
1548 assert_eq!(res.version.unwrap(), 1);
1549 assert_eq!(res.code.unwrap(), 200);
1550 assert_eq!(res.reason.unwrap(), "OK");
1551 }
1552 }
1553
1554 res! {
1555 test_response_newlines,
1556 b"HTTP/1.0 403 Forbidden\nServer: foo.bar\n\n",
1557 |_r| {}
1558 }
1559
1560 res! {
1561 test_response_reason_missing,
1562 b"HTTP/1.1 200 \r\n\r\n",
1563 |res| {
1564 assert_eq!(res.version.unwrap(), 1);
1565 assert_eq!(res.code.unwrap(), 200);
1566 assert_eq!(res.reason.unwrap(), "");
1567 }
1568 }
1569
1570 res! {
1571 test_response_reason_missing_no_space,
1572 b"HTTP/1.1 200\r\n\r\n",
1573 |res| {
1574 assert_eq!(res.version.unwrap(), 1);
1575 assert_eq!(res.code.unwrap(), 200);
1576 assert_eq!(res.reason.unwrap(), "");
1577 }
1578 }
1579
1580 res! {
1581 test_response_reason_missing_no_space_with_headers,
1582 b"HTTP/1.1 200\r\nFoo: bar\r\n\r\n",
1583 |res| {
1584 assert_eq!(res.version.unwrap(), 1);
1585 assert_eq!(res.code.unwrap(), 200);
1586 assert_eq!(res.reason.unwrap(), "");
1587 assert_eq!(res.headers.len(), 1);
1588 assert_eq!(res.headers[0].name, "Foo");
1589 assert_eq!(res.headers[0].value, b"bar");
1590 }
1591 }
1592
1593 res! {
1594 test_response_reason_with_space_and_tab,
1595 b"HTTP/1.1 101 Switching Protocols\t\r\n\r\n",
1596 |res| {
1597 assert_eq!(res.version.unwrap(), 1);
1598 assert_eq!(res.code.unwrap(), 101);
1599 assert_eq!(res.reason.unwrap(), "Switching Protocols\t");
1600 }
1601 }
1602
1603 static RESPONSE_REASON_WITH_OBS_TEXT_BYTE: &[u8] = b"HTTP/1.1 200 X\xFFZ\r\n\r\n";
1604 res! {
1605 test_response_reason_with_obsolete_text_byte,
1606 RESPONSE_REASON_WITH_OBS_TEXT_BYTE,
1607 |res| {
1608 assert_eq!(res.version.unwrap(), 1);
1609 assert_eq!(res.code.unwrap(), 200);
1610 // Empty string fallback in case of obs-text
1611 assert_eq!(res.reason.unwrap(), "");
1612 }
1613 }
1614
1615 res! {
1616 test_response_reason_with_nul_byte,
1617 b"HTTP/1.1 200 \x00\r\n\r\n",
1618 Err(crate::Error::Status),
1619 |_res| {}
1620 }
1621
1622 res! {
1623 test_response_version_missing_space,
1624 b"HTTP/1.1",
1625 Ok(Status::Partial),
1626 |_res| {}
1627 }
1628
1629 res! {
1630 test_response_code_missing_space,
1631 b"HTTP/1.1 200",
1632 Ok(Status::Partial),
1633 |_res| {}
1634 }
1635
1636 res! {
1637 test_response_partial_parses_headers_as_much_as_it_can,
1638 b"HTTP/1.1 200 OK\r\nServer: yolo\r\n",
1639 Ok(crate::Status::Partial),
1640 |res| {
1641 assert_eq!(res.version.unwrap(), 1);
1642 assert_eq!(res.code.unwrap(), 200);
1643 assert_eq!(res.reason.unwrap(), "OK");
1644 assert_eq!(res.headers.len(), NUM_OF_HEADERS); // doesn't slice since not Complete
1645 assert_eq!(res.headers[0].name, "Server");
1646 assert_eq!(res.headers[0].value, b"yolo");
1647 }
1648 }
1649
1650 res! {
1651 test_response_empty_lines_prefix_lf_only,
1652 b"\n\nHTTP/1.1 200 OK\n\n",
1653 |_res| {}
1654 }
1655
1656 res! {
1657 test_response_no_cr,
1658 b"HTTP/1.0 200\nContent-type: text/html\n\n",
1659 |res| {
1660 assert_eq!(res.version.unwrap(), 0);
1661 assert_eq!(res.code.unwrap(), 200);
1662 assert_eq!(res.reason.unwrap(), "");
1663 assert_eq!(res.headers.len(), 1);
1664 assert_eq!(res.headers[0].name, "Content-type");
1665 assert_eq!(res.headers[0].value, b"text/html");
1666 }
1667 }
1668
1669 static RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON: &[u8] =
1670 b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n";
1671
1672 #[test]
1673 fn test_forbid_response_with_whitespace_between_header_name_and_colon() {
1674 let mut headers = [EMPTY_HEADER; 2];
1675 let mut response = Response::new(&mut headers[..]);
1676 let result = response.parse(RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON);
1677
1678 assert_eq!(result, Err(crate::Error::HeaderName));
1679 }
1680
1681 #[test]
1682 fn test_allow_response_with_whitespace_between_header_name_and_colon() {
1683 let mut headers = [EMPTY_HEADER; 2];
1684 let mut response = Response::new(&mut headers[..]);
1685 let result = crate::ParserConfig::default()
1686 .allow_spaces_after_header_name_in_responses(true)
1687 .parse_response(&mut response, RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON);
1688
1689 assert_eq!(result, Ok(Status::Complete(77)));
1690 assert_eq!(response.version.unwrap(), 1);
1691 assert_eq!(response.code.unwrap(), 200);
1692 assert_eq!(response.reason.unwrap(), "OK");
1693 assert_eq!(response.headers.len(), 2);
1694 assert_eq!(response.headers[0].name, "Access-Control-Allow-Credentials");
1695 assert_eq!(response.headers[0].value, &b"true"[..]);
1696 assert_eq!(response.headers[1].name, "Bread");
1697 assert_eq!(response.headers[1].value, &b"baguette"[..]);
1698 }
1699
1700 #[test]
1701 fn test_ignore_header_line_with_whitespaces_after_header_name() {
1702 let mut headers = [EMPTY_HEADER; 2];
1703 let mut response = Response::new(&mut headers[..]);
1704 let result = crate::ParserConfig::default()
1705 .ignore_invalid_headers_in_responses(true)
1706 .parse_response(&mut response, RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON);
1707
1708 assert_eq!(result, Ok(Status::Complete(77)));
1709 assert_eq!(response.version.unwrap(), 1);
1710 assert_eq!(response.code.unwrap(), 200);
1711 assert_eq!(response.reason.unwrap(), "OK");
1712 assert_eq!(response.headers.len(), 1);
1713 assert_eq!(response.headers[0].name, "Bread");
1714 assert_eq!(response.headers[0].value, &b"baguette"[..]);
1715 }
1716
1717 static REQUEST_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON: &[u8] =
1718 b"GET / HTTP/1.1\r\nHost : localhost\r\n\r\n";
1719
1720 #[test]
1721 fn test_forbid_request_with_whitespace_between_header_name_and_colon() {
1722 let mut headers = [EMPTY_HEADER; 1];
1723 let mut request = Request::new(&mut headers[..]);
1724 let result = request.parse(REQUEST_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON);
1725
1726 assert_eq!(result, Err(crate::Error::HeaderName));
1727 }
1728
1729 static RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_START: &[u8] =
1730 b"HTTP/1.1 200 OK\r\nLine-Folded-Header: \r\n \r\n hello there\r\n\r\n";
1731
1732 #[test]
1733 fn test_forbid_response_with_obsolete_line_folding_at_start() {
1734 let mut headers = [EMPTY_HEADER; 1];
1735 let mut response = Response::new(&mut headers[..]);
1736 let result = response.parse(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_START);
1737
1738 assert_eq!(result, Err(crate::Error::HeaderName));
1739 }
1740
1741 #[test]
1742 fn test_allow_response_with_obsolete_line_folding_at_start() {
1743 let mut headers = [EMPTY_HEADER; 1];
1744 let mut response = Response::new(&mut headers[..]);
1745 let result = crate::ParserConfig::default()
1746 .allow_obsolete_multiline_headers_in_responses(true)
1747 .parse_response(&mut response, RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_START);
1748
1749 assert_eq!(result, Ok(Status::Complete(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_START.len())));
1750 assert_eq!(response.version.unwrap(), 1);
1751 assert_eq!(response.code.unwrap(), 200);
1752 assert_eq!(response.reason.unwrap(), "OK");
1753 assert_eq!(response.headers.len(), 1);
1754 assert_eq!(response.headers[0].name, "Line-Folded-Header");
1755 assert_eq!(response.headers[0].value, &b"hello there"[..]);
1756 }
1757
1758 static RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_END: &[u8] =
1759 b"HTTP/1.1 200 OK\r\nLine-Folded-Header: hello there\r\n \r\n \r\n\r\n";
1760
1761 #[test]
1762 fn test_forbid_response_with_obsolete_line_folding_at_end() {
1763 let mut headers = [EMPTY_HEADER; 1];
1764 let mut response = Response::new(&mut headers[..]);
1765 let result = response.parse(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_END);
1766
1767 assert_eq!(result, Err(crate::Error::HeaderName));
1768 }
1769
1770 #[test]
1771 fn test_allow_response_with_obsolete_line_folding_at_end() {
1772 let mut headers = [EMPTY_HEADER; 1];
1773 let mut response = Response::new(&mut headers[..]);
1774 let result = crate::ParserConfig::default()
1775 .allow_obsolete_multiline_headers_in_responses(true)
1776 .parse_response(&mut response, RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_END);
1777
1778 assert_eq!(result, Ok(Status::Complete(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_END.len())));
1779 assert_eq!(response.version.unwrap(), 1);
1780 assert_eq!(response.code.unwrap(), 200);
1781 assert_eq!(response.reason.unwrap(), "OK");
1782 assert_eq!(response.headers.len(), 1);
1783 assert_eq!(response.headers[0].name, "Line-Folded-Header");
1784 assert_eq!(response.headers[0].value, &b"hello there"[..]);
1785 }
1786
1787 static RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_MIDDLE: &[u8] =
1788 b"HTTP/1.1 200 OK\r\nLine-Folded-Header: hello \r\n \r\n there\r\n\r\n";
1789
1790 #[test]
1791 fn test_forbid_response_with_obsolete_line_folding_in_middle() {
1792 let mut headers = [EMPTY_HEADER; 1];
1793 let mut response = Response::new(&mut headers[..]);
1794 let result = response.parse(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_MIDDLE);
1795
1796 assert_eq!(result, Err(crate::Error::HeaderName));
1797 }
1798
1799 #[test]
1800 fn test_allow_response_with_obsolete_line_folding_in_middle() {
1801 let mut headers = [EMPTY_HEADER; 1];
1802 let mut response = Response::new(&mut headers[..]);
1803 let result = crate::ParserConfig::default()
1804 .allow_obsolete_multiline_headers_in_responses(true)
1805 .parse_response(&mut response, RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_MIDDLE);
1806
1807 assert_eq!(result, Ok(Status::Complete(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_MIDDLE.len())));
1808 assert_eq!(response.version.unwrap(), 1);
1809 assert_eq!(response.code.unwrap(), 200);
1810 assert_eq!(response.reason.unwrap(), "OK");
1811 assert_eq!(response.headers.len(), 1);
1812 assert_eq!(response.headers[0].name, "Line-Folded-Header");
1813 assert_eq!(response.headers[0].value, &b"hello \r\n \r\n there"[..]);
1814 }
1815
1816 static RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_EMPTY_HEADER: &[u8] =
1817 b"HTTP/1.1 200 OK\r\nLine-Folded-Header: \r\n \r\n \r\n\r\n";
1818
1819 #[test]
1820 fn test_forbid_response_with_obsolete_line_folding_in_empty_header() {
1821 let mut headers = [EMPTY_HEADER; 1];
1822 let mut response = Response::new(&mut headers[..]);
1823 let result = response.parse(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_EMPTY_HEADER);
1824
1825 assert_eq!(result, Err(crate::Error::HeaderName));
1826 }
1827
1828 #[test]
1829 fn test_allow_response_with_obsolete_line_folding_in_empty_header() {
1830 let mut headers = [EMPTY_HEADER; 1];
1831 let mut response = Response::new(&mut headers[..]);
1832 let result = crate::ParserConfig::default()
1833 .allow_obsolete_multiline_headers_in_responses(true)
1834 .parse_response(&mut response, RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_EMPTY_HEADER);
1835
1836 assert_eq!(result, Ok(Status::Complete(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_EMPTY_HEADER.len())));
1837 assert_eq!(response.version.unwrap(), 1);
1838 assert_eq!(response.code.unwrap(), 200);
1839 assert_eq!(response.reason.unwrap(), "OK");
1840 assert_eq!(response.headers.len(), 1);
1841 assert_eq!(response.headers[0].name, "Line-Folded-Header");
1842 assert_eq!(response.headers[0].value, &b""[..]);
1843 }
1844
1845 #[test]
1846 fn test_chunk_size() {
1847 assert_eq!(parse_chunk_size(b"0\r\n"), Ok(Status::Complete((3, 0))));
1848 assert_eq!(parse_chunk_size(b"12\r\nchunk"), Ok(Status::Complete((4, 18))));
1849 assert_eq!(parse_chunk_size(b"3086d\r\n"), Ok(Status::Complete((7, 198765))));
1850 assert_eq!(parse_chunk_size(b"3735AB1;foo bar*\r\n"), Ok(Status::Complete((18, 57891505))));
1851 assert_eq!(parse_chunk_size(b"3735ab1 ; baz \r\n"), Ok(Status::Complete((16, 57891505))));
1852 assert_eq!(parse_chunk_size(b"77a65\r"), Ok(Status::Partial));
1853 assert_eq!(parse_chunk_size(b"ab"), Ok(Status::Partial));
1854 assert_eq!(parse_chunk_size(b"567f8a\rfoo"), Err(crate::InvalidChunkSize));
1855 assert_eq!(parse_chunk_size(b"567f8a\rfoo"), Err(crate::InvalidChunkSize));
1856 assert_eq!(parse_chunk_size(b"567xf8a\r\n"), Err(crate::InvalidChunkSize));
1857 assert_eq!(parse_chunk_size(b"ffffffffffffffff\r\n"), Ok(Status::Complete((18, std::u64::MAX))));
1858 assert_eq!(parse_chunk_size(b"1ffffffffffffffff\r\n"), Err(crate::InvalidChunkSize));
1859 assert_eq!(parse_chunk_size(b"Affffffffffffffff\r\n"), Err(crate::InvalidChunkSize));
1860 assert_eq!(parse_chunk_size(b"fffffffffffffffff\r\n"), Err(crate::InvalidChunkSize));
1861 }
1862
1863 static RESPONSE_WITH_MULTIPLE_SPACE_DELIMITERS: &[u8] =
1864 b"HTTP/1.1 200 OK\r\n\r\n";
1865
1866 #[test]
1867 fn test_forbid_response_with_multiple_space_delimiters() {
1868 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1869 let mut response = Response::new(&mut headers[..]);
1870 let result = response.parse(RESPONSE_WITH_MULTIPLE_SPACE_DELIMITERS);
1871
1872 assert_eq!(result, Err(crate::Error::Status));
1873 }
1874
1875 #[test]
1876 fn test_allow_response_with_multiple_space_delimiters() {
1877 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1878 let mut response = Response::new(&mut headers[..]);
1879 let result = crate::ParserConfig::default()
1880 .allow_multiple_spaces_in_response_status_delimiters(true)
1881 .parse_response(&mut response, RESPONSE_WITH_MULTIPLE_SPACE_DELIMITERS);
1882
1883 assert_eq!(result, Ok(Status::Complete(RESPONSE_WITH_MULTIPLE_SPACE_DELIMITERS.len())));
1884 assert_eq!(response.version.unwrap(), 1);
1885 assert_eq!(response.code.unwrap(), 200);
1886 assert_eq!(response.reason.unwrap(), "OK");
1887 assert_eq!(response.headers.len(), 0);
1888 }
1889
1890 /// This is technically allowed by the spec, but we only support multiple spaces as an option,
1891 /// not stray `\r`s.
1892 static RESPONSE_WITH_WEIRD_WHITESPACE_DELIMITERS: &[u8] =
1893 b"HTTP/1.1 200\rOK\r\n\r\n";
1894
1895 #[test]
1896 fn test_forbid_response_with_weird_whitespace_delimiters() {
1897 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1898 let mut response = Response::new(&mut headers[..]);
1899 let result = response.parse(RESPONSE_WITH_WEIRD_WHITESPACE_DELIMITERS);
1900
1901 assert_eq!(result, Err(crate::Error::Status));
1902 }
1903
1904 #[test]
1905 fn test_still_forbid_response_with_weird_whitespace_delimiters() {
1906 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1907 let mut response = Response::new(&mut headers[..]);
1908 let result = crate::ParserConfig::default()
1909 .allow_multiple_spaces_in_response_status_delimiters(true)
1910 .parse_response(&mut response, RESPONSE_WITH_WEIRD_WHITESPACE_DELIMITERS);
1911 assert_eq!(result, Err(crate::Error::Status));
1912 }
1913
1914 static REQUEST_WITH_MULTIPLE_SPACE_DELIMITERS: &[u8] =
1915 b"GET / HTTP/1.1\r\n\r\n";
1916
1917 #[test]
1918 fn test_forbid_request_with_multiple_space_delimiters() {
1919 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1920 let mut request = Request::new(&mut headers[..]);
1921 let result = request.parse(REQUEST_WITH_MULTIPLE_SPACE_DELIMITERS);
1922
1923 assert_eq!(result, Err(crate::Error::Token));
1924 }
1925
1926 #[test]
1927 fn test_allow_request_with_multiple_space_delimiters() {
1928 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1929 let mut request = Request::new(&mut headers[..]);
1930 let result = crate::ParserConfig::default()
1931 .allow_multiple_spaces_in_request_line_delimiters(true)
1932 .parse_request(&mut request, REQUEST_WITH_MULTIPLE_SPACE_DELIMITERS);
1933
1934 assert_eq!(result, Ok(Status::Complete(REQUEST_WITH_MULTIPLE_SPACE_DELIMITERS.len())));
1935 assert_eq!(request.method.unwrap(), "GET");
1936 assert_eq!(request.path.unwrap(), "/");
1937 assert_eq!(request.version.unwrap(), 1);
1938 assert_eq!(request.headers.len(), 0);
1939 }
1940
1941 /// This is technically allowed by the spec, but we only support multiple spaces as an option,
1942 /// not stray `\r`s.
1943 static REQUEST_WITH_WEIRD_WHITESPACE_DELIMITERS: &[u8] =
1944 b"GET\r/\rHTTP/1.1\r\n\r\n";
1945
1946 #[test]
1947 fn test_forbid_request_with_weird_whitespace_delimiters() {
1948 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1949 let mut request = Request::new(&mut headers[..]);
1950 let result = request.parse(REQUEST_WITH_WEIRD_WHITESPACE_DELIMITERS);
1951
1952 assert_eq!(result, Err(crate::Error::Token));
1953 }
1954
1955 #[test]
1956 fn test_still_forbid_request_with_weird_whitespace_delimiters() {
1957 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1958 let mut request = Request::new(&mut headers[..]);
1959 let result = crate::ParserConfig::default()
1960 .allow_multiple_spaces_in_request_line_delimiters(true)
1961 .parse_request(&mut request, REQUEST_WITH_WEIRD_WHITESPACE_DELIMITERS);
1962 assert_eq!(result, Err(crate::Error::Token));
1963 }
1964
1965 static REQUEST_WITH_MULTIPLE_SPACES_AND_BAD_PATH: &[u8] = b"GET /foo>ohno HTTP/1.1\r\n\r\n";
1966
1967 #[test]
1968 fn test_request_with_multiple_spaces_and_bad_path() {
1969 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1970 let mut request = Request::new(&mut headers[..]);
1971 let result = crate::ParserConfig::default()
1972 .allow_multiple_spaces_in_request_line_delimiters(true)
1973 .parse_request(&mut request, REQUEST_WITH_MULTIPLE_SPACES_AND_BAD_PATH);
1974 assert_eq!(result, Err(crate::Error::Token));
1975 }
1976
1977 static RESPONSE_WITH_SPACES_IN_CODE: &[u8] = b"HTTP/1.1 99 200 OK\r\n\r\n";
1978
1979 #[test]
1980 fn test_response_with_spaces_in_code() {
1981 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
1982 let mut response = Response::new(&mut headers[..]);
1983 let result = crate::ParserConfig::default()
1984 .allow_multiple_spaces_in_response_status_delimiters(true)
1985 .parse_response(&mut response, RESPONSE_WITH_SPACES_IN_CODE);
1986 assert_eq!(result, Err(crate::Error::Status));
1987 }
1988
1989 #[test]
1990 fn test_response_with_empty_header_name() {
1991 const RESPONSE: &[u8] =
1992 b"HTTP/1.1 200 OK\r\n: hello\r\nBread: baguette\r\n\r\n";
1993
1994 let mut headers = [EMPTY_HEADER; 2];
1995 let mut response = Response::new(&mut headers[..]);
1996
1997 let result = crate::ParserConfig::default()
1998 .allow_spaces_after_header_name_in_responses(true)
1999 .parse_response(&mut response, RESPONSE);
2000 assert_eq!(result, Err(crate::Error::HeaderName));
2001
2002 let result = crate::ParserConfig::default()
2003 .ignore_invalid_headers_in_responses(true)
2004 .parse_response(&mut response, RESPONSE);
2005 assert_eq!(result, Ok(Status::Complete(45)));
2006
2007 assert_eq!(response.version.unwrap(), 1);
2008 assert_eq!(response.code.unwrap(), 200);
2009 assert_eq!(response.reason.unwrap(), "OK");
2010 assert_eq!(response.headers.len(), 1);
2011 assert_eq!(response.headers[0].name, "Bread");
2012 assert_eq!(response.headers[0].value, &b"baguette"[..]);
2013 }
2014
2015 #[test]
2016 fn test_request_with_whitespace_between_header_name_and_colon() {
2017 const REQUEST: &[u8] =
2018 b"GET / HTTP/1.1\r\nAccess-Control-Allow-Credentials : true\r\nBread: baguette\r\n\r\n";
2019
2020 let mut headers = [EMPTY_HEADER; 2];
2021 let mut request = Request::new(&mut headers[..]);
2022
2023 let result = crate::ParserConfig::default()
2024 .allow_spaces_after_header_name_in_responses(true)
2025 .parse_request(&mut request, REQUEST);
2026 assert_eq!(result, Err(crate::Error::HeaderName));
2027
2028 let result = crate::ParserConfig::default()
2029
2030 .ignore_invalid_headers_in_responses(true)
2031 .parse_request(&mut request, REQUEST);
2032 assert_eq!(result, Err(crate::Error::HeaderName));
2033 }
2034
2035 #[test]
2036 fn test_response_with_invalid_char_between_header_name_and_colon() {
2037 const RESPONSE: &[u8] =
2038 b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials\xFF : true\r\nBread: baguette\r\n\r\n";
2039
2040 let mut headers = [EMPTY_HEADER; 2];
2041 let mut response = Response::new(&mut headers[..]);
2042
2043 let result = crate::ParserConfig::default()
2044 .allow_spaces_after_header_name_in_responses(true)
2045 .parse_response(&mut response, RESPONSE);
2046 assert_eq!(result, Err(crate::Error::HeaderName));
2047
2048 let result = crate::ParserConfig::default()
2049 .ignore_invalid_headers_in_responses(true)
2050 .parse_response(&mut response, RESPONSE);
2051
2052 assert_eq!(result, Ok(Status::Complete(79)));
2053 assert_eq!(response.version.unwrap(), 1);
2054 assert_eq!(response.code.unwrap(), 200);
2055 assert_eq!(response.reason.unwrap(), "OK");
2056 assert_eq!(response.headers.len(), 1);
2057 assert_eq!(response.headers[0].name, "Bread");
2058 assert_eq!(response.headers[0].value, &b"baguette"[..]);
2059 }
2060
2061 #[test]
2062 fn test_ignore_header_line_with_missing_colon() {
2063 const RESPONSE: &[u8] =
2064 b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials\r\nBread: baguette\r\n\r\n";
2065
2066 let mut headers = [EMPTY_HEADER; 2];
2067 let mut response = Response::new(&mut headers[..]);
2068
2069 let result = crate::ParserConfig::default()
2070 .parse_response(&mut response, RESPONSE);
2071 assert_eq!(result, Err(crate::Error::HeaderName));
2072
2073 let result = crate::ParserConfig::default()
2074 .ignore_invalid_headers_in_responses(true)
2075 .parse_response(&mut response, RESPONSE);
2076 assert_eq!(result, Ok(Status::Complete(70)));
2077
2078 assert_eq!(response.version.unwrap(), 1);
2079 assert_eq!(response.code.unwrap(), 200);
2080 assert_eq!(response.reason.unwrap(), "OK");
2081 assert_eq!(response.headers.len(), 1);
2082 assert_eq!(response.headers[0].name, "Bread");
2083 assert_eq!(response.headers[0].value, &b"baguette"[..]);
2084 }
2085
2086 #[test]
2087 fn test_header_with_missing_colon_with_folding() {
2088 const RESPONSE: &[u8] =
2089 b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials \r\n hello\r\nBread: baguette\r\n\r\n";
2090
2091 let mut headers = [EMPTY_HEADER; 2];
2092 let mut response = Response::new(&mut headers[..]);
2093
2094 let result = crate::ParserConfig::default()
2095 .allow_obsolete_multiline_headers_in_responses(true)
2096 .allow_spaces_after_header_name_in_responses(true)
2097 .parse_response(&mut response, RESPONSE);
2098 assert_eq!(result, Err(crate::Error::HeaderName));
2099
2100 let result = crate::ParserConfig::default()
2101 .ignore_invalid_headers_in_responses(true)
2102 .parse_response(&mut response, RESPONSE);
2103 assert_eq!(result, Ok(Status::Complete(81)));
2104
2105 assert_eq!(response.version.unwrap(), 1);
2106 assert_eq!(response.code.unwrap(), 200);
2107 assert_eq!(response.reason.unwrap(), "OK");
2108 assert_eq!(response.headers.len(), 1);
2109 assert_eq!(response.headers[0].name, "Bread");
2110 assert_eq!(response.headers[0].value, &b"baguette"[..]);
2111 }
2112
2113 #[test]
2114 fn test_header_with_nul_in_header_name() {
2115 const RESPONSE: &[u8] =
2116 b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Cred\0entials: hello\r\nBread: baguette\r\n\r\n";
2117
2118 let mut headers = [EMPTY_HEADER; 2];
2119 let mut response = Response::new(&mut headers[..]);
2120
2121 let result = crate::ParserConfig::default()
2122 .parse_response(&mut response, RESPONSE);
2123 assert_eq!(result, Err(crate::Error::HeaderName));
2124
2125 let result = crate::ParserConfig::default()
2126 .ignore_invalid_headers_in_responses(true)
2127 .parse_response(&mut response, RESPONSE);
2128 assert_eq!(result, Err(crate::Error::HeaderName));
2129 }
2130
2131 #[test]
2132 fn test_header_with_cr_in_header_name() {
2133 const RESPONSE: &[u8] =
2134 b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Cred\rentials: hello\r\nBread: baguette\r\n\r\n";
2135
2136 let mut headers = [EMPTY_HEADER; 2];
2137 let mut response = Response::new(&mut headers[..]);
2138
2139 let result = crate::ParserConfig::default()
2140 .parse_response(&mut response, RESPONSE);
2141 assert_eq!(result, Err(crate::Error::HeaderName));
2142
2143 let result = crate::ParserConfig::default()
2144 .ignore_invalid_headers_in_responses(true)
2145 .parse_response(&mut response, RESPONSE);
2146 assert_eq!(result, Err(crate::Error::HeaderName));
2147 }
2148
2149 #[test]
2150 fn test_header_with_nul_in_whitespace_before_colon() {
2151 const RESPONSE: &[u8] =
2152 b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials \0: hello\r\nBread: baguette\r\n\r\n";
2153
2154 let mut headers = [EMPTY_HEADER; 2];
2155 let mut response = Response::new(&mut headers[..]);
2156
2157 let result = crate::ParserConfig::default()
2158 .allow_spaces_after_header_name_in_responses(true)
2159 .parse_response(&mut response, RESPONSE);
2160 assert_eq!(result, Err(crate::Error::HeaderName));
2161
2162 let result = crate::ParserConfig::default()
2163 .allow_spaces_after_header_name_in_responses(true)
2164 .ignore_invalid_headers_in_responses(true)
2165 .parse_response(&mut response, RESPONSE);
2166 assert_eq!(result, Err(crate::Error::HeaderName));
2167 }
2168
2169 #[test]
2170 fn test_header_with_nul_in_value() {
2171 const RESPONSE: &[u8] =
2172 b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials: hell\0o\r\nBread: baguette\r\n\r\n";
2173
2174 let mut headers = [EMPTY_HEADER; 2];
2175 let mut response = Response::new(&mut headers[..]);
2176
2177 let result = crate::ParserConfig::default()
2178 .parse_response(&mut response, RESPONSE);
2179 assert_eq!(result, Err(crate::Error::HeaderValue));
2180
2181 let result = crate::ParserConfig::default()
2182 .ignore_invalid_headers_in_responses(true)
2183 .parse_response(&mut response, RESPONSE);
2184 assert_eq!(result, Err(crate::Error::HeaderValue));
2185 }
2186
2187 #[test]
2188 fn test_header_with_invalid_char_in_value() {
2189 const RESPONSE: &[u8] =
2190 b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials: hell\x01o\r\nBread: baguette\r\n\r\n";
2191
2192 let mut headers = [EMPTY_HEADER; 2];
2193 let mut response = Response::new(&mut headers[..]);
2194
2195 let result = crate::ParserConfig::default()
2196 .parse_response(&mut response, RESPONSE);
2197 assert_eq!(result, Err(crate::Error::HeaderValue));
2198
2199 let result = crate::ParserConfig::default()
2200 .ignore_invalid_headers_in_responses(true)
2201 .parse_response(&mut response, RESPONSE);
2202 assert_eq!(result, Ok(Status::Complete(78)));
2203
2204 assert_eq!(response.version.unwrap(), 1);
2205 assert_eq!(response.code.unwrap(), 200);
2206 assert_eq!(response.reason.unwrap(), "OK");
2207 assert_eq!(response.headers.len(), 1);
2208 assert_eq!(response.headers[0].name, "Bread");
2209 assert_eq!(response.headers[0].value, &b"baguette"[..]);
2210 }
2211
2212 #[test]
2213 fn test_header_with_invalid_char_in_value_with_folding() {
2214 const RESPONSE: &[u8] =
2215 b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials: hell\x01o \n world!\r\nBread: baguette\r\n\r\n";
2216
2217 let mut headers = [EMPTY_HEADER; 2];
2218 let mut response = Response::new(&mut headers[..]);
2219
2220 let result = crate::ParserConfig::default()
2221 .parse_response(&mut response, RESPONSE);
2222 assert_eq!(result, Err(crate::Error::HeaderValue));
2223
2224 let result = crate::ParserConfig::default()
2225 .ignore_invalid_headers_in_responses(true)
2226 .parse_response(&mut response, RESPONSE);
2227 assert_eq!(result, Ok(Status::Complete(88)));
2228
2229 assert_eq!(response.version.unwrap(), 1);
2230 assert_eq!(response.code.unwrap(), 200);
2231 assert_eq!(response.reason.unwrap(), "OK");
2232 assert_eq!(response.headers.len(), 1);
2233 assert_eq!(response.headers[0].name, "Bread");
2234 assert_eq!(response.headers[0].value, &b"baguette"[..]);
2235 }
2236}
2237