Skip to content

Commit

Permalink
Allow ignoring invalid header lines (fixes seanmonstar#61, seanmonsta…
Browse files Browse the repository at this point in the history
  • Loading branch information
nox committed Apr 22, 2022
1 parent 8d4c92a commit 96cda55
Showing 1 changed file with 104 additions and 7 deletions.
111 changes: 104 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,11 @@ impl<T> Status<T> {
#[derive(Clone, Debug, Default)]
pub struct ParserConfig {
allow_spaces_after_header_name_in_responses: bool,
ignore_invalid_header_lines_in_responses: bool,
}

impl ParserConfig {
/// Sets whether spaces should be allowed after header name.
/// Sets whether spaces and tabs should be allowed after header names in responses.
pub fn allow_spaces_after_header_name_in_responses(
&mut self,
value: bool,
Expand All @@ -256,6 +257,15 @@ impl ParserConfig {
self
}

/// Sets whether invalid header lines should be silently ignored in responses.
pub fn ignore_invalid_header_lines_in_responses(
&mut self,
value: bool,
) -> &mut Self {
self.ignore_invalid_header_lines_in_responses = value;
self
}

/// Parses a response with the given config.
pub fn parse_response<'headers, 'buf>(
&self,
Expand Down Expand Up @@ -762,11 +772,6 @@ fn parse_headers_iter_uninit<'a, 'b>(
return Err(Error::HeaderName);
}

let uninit_header = match iter.next() {
Some(header) => header,
None => break 'headers
};

// parse header name until colon
let header_name: &str = 'name: loop {
let mut b = next!(bytes);
Expand Down Expand Up @@ -796,7 +801,23 @@ fn parse_headers_iter_uninit<'a, 'b>(
}
}

return Err(Error::HeaderName);
if !config.ignore_invalid_header_lines_in_responses {
return Err(Error::HeaderName);
}

while b != b'\n' {
b = next!(bytes);
}

count += bytes.pos();
bytes.slice();

continue 'headers;
};

let uninit_header = match iter.next() {
Some(header) => header,
None => break 'headers
};

let mut b;
Expand Down Expand Up @@ -1378,6 +1399,23 @@ mod tests {
assert_eq!(response.headers[1].value, &b"baguette"[..]);
}

#[test]
fn test_ignore_header_line_with_whitespaces_after_header_name() {
let mut headers = [EMPTY_HEADER; 2];
let mut response = Response::new(&mut headers[..]);
let result = ::ParserConfig::default()
.ignore_invalid_header_lines_in_responses(true)
.parse_response(&mut response, RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON);

assert_eq!(result, Ok(Status::Complete(77)));
assert_eq!(response.version.unwrap(), 1);
assert_eq!(response.code.unwrap(), 200);
assert_eq!(response.reason.unwrap(), "OK");
assert_eq!(response.headers.len(), 1);
assert_eq!(response.headers[0].name, "Bread");
assert_eq!(response.headers[0].value, &b"baguette"[..]);
}

static REQUEST_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON: &'static [u8] =
b"GET / HTTP/1.1\r\nHost : localhost\r\n\r\n";

Expand Down Expand Up @@ -1421,4 +1459,63 @@ mod tests {

assert_eq!(result, Err(::Error::HeaderName));
}

#[test]
fn test_ignore_header_line_with_invalid_char_after_header_name() {
let mut headers = [EMPTY_HEADER; 2];
let mut response = Response::new(&mut headers[..]);
let result = ::ParserConfig::default()
.ignore_invalid_header_lines_in_responses(true)
.parse_response(&mut response, RESPONSE_WITH_INVALID_CHAR_BETWEEN_HEADER_NAME_AND_COLON);

assert_eq!(result, Ok(Status::Complete(77)));
assert_eq!(response.version.unwrap(), 1);
assert_eq!(response.code.unwrap(), 200);
assert_eq!(response.reason.unwrap(), "OK");
assert_eq!(response.headers.len(), 1);
assert_eq!(response.headers[0].name, "Bread");
assert_eq!(response.headers[0].value, &b"baguette"[..]);
}

static RESPONSE_WITH_MISSING_COLON: &'static [u8] =
b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials\r\nBread: baguette\r\n\r\n";

#[test]
fn test_ignore_header_line_with_missing_colon() {
let mut headers = [EMPTY_HEADER; 2];
let mut response = Response::new(&mut headers[..]);
let result = ::ParserConfig::default()
.ignore_invalid_header_lines_in_responses(true)
.parse_response(&mut response, RESPONSE_WITH_MISSING_COLON);

assert_eq!(result, Ok(Status::Complete(70)));
assert_eq!(response.version.unwrap(), 1);
assert_eq!(response.code.unwrap(), 200);
assert_eq!(response.reason.unwrap(), "OK");
assert_eq!(response.headers.len(), 1);
assert_eq!(response.headers[0].name, "Bread");
assert_eq!(response.headers[0].value, &b"baguette"[..]);
}

static RESPONSE_WITH_INVALID_CHAR_AFTER_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON: &'static [u8] =
b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials \xFF: true\r\nBread: baguette\r\n\r\n";

#[test]
fn test_ignore_header_line_with_invalid_char_after_whitespace() {
let mut headers = [EMPTY_HEADER; 2];
let mut response = Response::new(&mut headers[..]);
let result = ::ParserConfig::default()
.allow_spaces_after_header_name_in_responses(true)
.ignore_invalid_header_lines_in_responses(true)
.parse_response(&mut response, RESPONSE_WITH_INVALID_CHAR_AFTER_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON);

assert_eq!(result, Ok(Status::Complete(81)));
assert_eq!(response.version.unwrap(), 1);
assert_eq!(response.code.unwrap(), 200);
assert_eq!(response.reason.unwrap(), "OK");
assert_eq!(response.headers.len(), 1);
assert_eq!(response.headers[0].name, "Bread");
assert_eq!(response.headers[0].value, &b"baguette"[..]);
}

}

0 comments on commit 96cda55

Please sign in to comment.