Skip to content

Commit 489f400

Browse files
committed
Add optional max header list size
Prior to parsing headers, both the request and response parsers now check that the remaining buffer length does not exceed the configured max size. If the length does exceed the max size, a new HeadersTooLarge error is returned.
1 parent a3c10fd commit 489f400

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

src/lib.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ pub enum Error {
161161
TooManyHeaders,
162162
/// Invalid byte in HTTP version.
163163
Version,
164+
/// Unparsed headers are larger than the configured max size.
165+
HeadersTooLarge,
164166
}
165167

166168
impl Error {
@@ -174,6 +176,7 @@ impl Error {
174176
Error::Token => "invalid token",
175177
Error::TooManyHeaders => "too many headers",
176178
Error::Version => "invalid HTTP version",
179+
Error::HeadersTooLarge => "headers too large",
177180
}
178181
}
179182
}
@@ -261,6 +264,7 @@ pub struct ParserConfig {
261264
allow_multiple_spaces_in_response_status_delimiters: bool,
262265
ignore_invalid_headers_in_responses: bool,
263266
ignore_invalid_headers_in_requests: bool,
267+
max_header_list_size: Option<usize>,
264268
}
265269

266270
impl ParserConfig {
@@ -440,6 +444,18 @@ impl ParserConfig {
440444
) -> Result<usize> {
441445
response.parse_with_config_and_uninit_headers(buf, self, headers)
442446
}
447+
448+
/// Set the maximum size of all headers.
449+
///
450+
/// The value is based on the size of unparsed header fields, including the
451+
/// length of the name and value in octets, an overhead of 24 octets for
452+
/// each header field and 8 octets for each additional whitespace.
453+
///
454+
/// Default is unlimited.
455+
pub fn max_header_list_size(&mut self, val: usize) -> &mut Self {
456+
self.max_header_list_size = Some(val);
457+
self
458+
}
443459
}
444460

445461
/// A parsed Request.
@@ -513,6 +529,11 @@ impl<'h, 'b> Request<'h, 'b> {
513529
newline!(bytes);
514530

515531
let len = orig_len - bytes.len();
532+
if let Some(max) = config.max_header_list_size {
533+
if len > max {
534+
return Err(Error::HeadersTooLarge);
535+
}
536+
}
516537
let headers_len = complete!(parse_headers_iter_uninit(
517538
&mut headers,
518539
&mut bytes,
@@ -710,6 +731,11 @@ impl<'h, 'b> Response<'h, 'b> {
710731

711732

712733
let len = orig_len - bytes.len();
734+
if let Some(max) = config.max_header_list_size {
735+
if len > max {
736+
return Err(Error::HeadersTooLarge);
737+
}
738+
}
713739
let headers_len = complete!(parse_headers_iter_uninit(
714740
&mut headers,
715741
&mut bytes,
@@ -2498,4 +2524,40 @@ mod tests {
24982524
assert!(REQUEST.as_ptr() <= method.as_ptr());
24992525
assert!(method.as_ptr() <= buf_end);
25002526
}
2527+
2528+
#[test]
2529+
fn test_request_max_header_list_size() {
2530+
const REQUEST: &[u8] = b"GET / HTTP/1.1\r\nHeader: value\r\n\r\n";
2531+
2532+
let mut headers = [EMPTY_HEADER; 1];
2533+
let mut request = Request::new(&mut headers[..]);
2534+
2535+
let result = crate::ParserConfig::default()
2536+
.max_header_list_size(10)
2537+
.parse_request(&mut request, REQUEST);
2538+
assert_eq!(result, Err(crate::Error::HeadersTooLarge));
2539+
2540+
let result = crate::ParserConfig::default()
2541+
.max_header_list_size(17)
2542+
.parse_request(&mut request, REQUEST);
2543+
assert_eq!(result, Ok(Status::Complete(REQUEST.len())));
2544+
}
2545+
2546+
#[test]
2547+
fn test_response_max_header_list_size() {
2548+
const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\nHeader: value\r\n\r\n";
2549+
2550+
let mut headers = [EMPTY_HEADER; 1];
2551+
let mut response = Response::new(&mut headers[..]);
2552+
2553+
let result = crate::ParserConfig::default()
2554+
.max_header_list_size(10)
2555+
.parse_response(&mut response, RESPONSE);
2556+
assert_eq!(result, Err(crate::Error::HeadersTooLarge));
2557+
2558+
let result = crate::ParserConfig::default()
2559+
.max_header_list_size(17)
2560+
.parse_response(&mut response, RESPONSE);
2561+
assert_eq!(result, Ok(Status::Complete(RESPONSE.len())));
2562+
}
25012563
}

0 commit comments

Comments
 (0)