Skip to content

Add optional max header list size #163

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ pub enum Error {
TooManyHeaders,
/// Invalid byte in HTTP version.
Version,
/// Unparsed headers are larger than the configured max size.
HeadersTooLarge,
}

impl Error {
Expand All @@ -174,6 +176,7 @@ impl Error {
Error::Token => "invalid token",
Error::TooManyHeaders => "too many headers",
Error::Version => "invalid HTTP version",
Error::HeadersTooLarge => "headers too large",
}
}
}
Expand Down Expand Up @@ -262,6 +265,7 @@ pub struct ParserConfig {
allow_space_before_first_header_name: bool,
ignore_invalid_headers_in_responses: bool,
ignore_invalid_headers_in_requests: bool,
max_header_list_size: Option<usize>,
}

impl ParserConfig {
Expand Down Expand Up @@ -474,6 +478,18 @@ impl ParserConfig {
) -> Result<usize> {
response.parse_with_config_and_uninit_headers(buf, self, headers)
}

/// Set the maximum size of all headers.
///
/// The value is based on the size of unparsed header fields, including the
/// length of the name and value in octets, an overhead of 24 octets for
/// each header field and 8 octets for each additional whitespace.
///
/// Default is unlimited.
pub fn max_header_list_size(&mut self, val: usize) -> &mut Self {
self.max_header_list_size = Some(val);
self
}
}

/// A parsed Request.
Expand Down Expand Up @@ -547,6 +563,11 @@ impl<'h, 'b> Request<'h, 'b> {
newline!(bytes);

let len = orig_len - bytes.len();
if let Some(max) = config.max_header_list_size {
if len > max {
return Err(Error::HeadersTooLarge);
}
}
let headers_len = complete!(parse_headers_iter_uninit(
&mut headers,
&mut bytes,
Expand Down Expand Up @@ -745,6 +766,11 @@ impl<'h, 'b> Response<'h, 'b> {


let len = orig_len - bytes.len();
if let Some(max) = config.max_header_list_size {
if len > max {
return Err(Error::HeadersTooLarge);
}
}
let headers_len = complete!(parse_headers_iter_uninit(
&mut headers,
&mut bytes,
Expand Down Expand Up @@ -2558,7 +2584,7 @@ mod tests {
assert!(method.as_ptr() <= buf_end);
}

static RESPONSE_WITH_SPACE_BEFORE_FIRST_HEADER: &[u8] =
static RESPONSE_WITH_SPACE_BEFORE_FIRST_HEADER: &[u8] =
b"HTTP/1.1 200 OK\r\n Space-Before-Header: hello there\r\n\r\n";

#[test]
Expand Down Expand Up @@ -2591,4 +2617,40 @@ mod tests {
assert_eq!(response.headers[0].name, "Space-Before-Header");
assert_eq!(response.headers[0].value, &b"hello there"[..]);
}

#[test]
fn test_request_max_header_list_size() {
const REQUEST: &[u8] = b"GET / HTTP/1.1\r\nHeader: value\r\n\r\n";

let mut headers = [EMPTY_HEADER; 1];
let mut request = Request::new(&mut headers[..]);

let result = crate::ParserConfig::default()
.max_header_list_size(10)
.parse_request(&mut request, REQUEST);
assert_eq!(result, Err(crate::Error::HeadersTooLarge));

let result = crate::ParserConfig::default()
.max_header_list_size(17)
.parse_request(&mut request, REQUEST);
assert_eq!(result, Ok(Status::Complete(REQUEST.len())));
}

#[test]
fn test_response_max_header_list_size() {
const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nHeader: value\r\n\r\n";

let mut headers = [EMPTY_HEADER; 1];
let mut response = Response::new(&mut headers[..]);

let result = crate::ParserConfig::default()
.max_header_list_size(10)
.parse_response(&mut response, RESPONSE);
assert_eq!(result, Err(crate::Error::HeadersTooLarge));

let result = crate::ParserConfig::default()
.max_header_list_size(17)
.parse_response(&mut response, RESPONSE);
assert_eq!(result, Ok(Status::Complete(RESPONSE.len())));
}
}