Skip to content

Commit

Permalink
feat(http1): support configurable max_headers(num) to client and se…
Browse files Browse the repository at this point in the history
…rver (#3523)
  • Loading branch information
yukiiiteru authored Jan 25, 2024
1 parent 7177770 commit b114244
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 7 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ httpdate = { version = "1.0", optional = true }
itoa = { version = "1", optional = true }
libc = { version = "0.2", optional = true }
pin-project-lite = { version = "0.2.4", optional = true }
smallvec = { version = "1.12", features = ["const_generics", "const_new"], optional = true }
tracing = { version = "0.1", default-features = false, features = ["std"], optional = true }
want = { version = "0.3", optional = true }

Expand Down Expand Up @@ -80,8 +81,8 @@ http1 = ["dep:futures-channel", "dep:futures-util", "dep:httparse", "dep:itoa"]
http2 = ["dep:futures-channel", "dep:futures-util", "dep:h2"]

# Client/Server
client = ["dep:want", "dep:pin-project-lite"]
server = ["dep:httpdate", "dep:pin-project-lite"]
client = ["dep:want", "dep:pin-project-lite", "dep:smallvec"]
server = ["dep:httpdate", "dep:pin-project-lite", "dep:smallvec"]

# C-API support (currently unstable (no semver))
ffi = ["dep:libc", "dep:http-body-util"]
Expand Down
23 changes: 23 additions & 0 deletions src/client/conn/http1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ pub struct Builder {
h1_writev: Option<bool>,
h1_title_case_headers: bool,
h1_preserve_header_case: bool,
h1_max_headers: Option<usize>,
#[cfg(feature = "ffi")]
h1_preserve_header_order: bool,
h1_read_buf_exact_size: Option<usize>,
Expand Down Expand Up @@ -309,6 +310,7 @@ impl Builder {
h1_parser_config: Default::default(),
h1_title_case_headers: false,
h1_preserve_header_case: false,
h1_max_headers: None,
#[cfg(feature = "ffi")]
h1_preserve_header_order: false,
h1_max_buf_size: None,
Expand Down Expand Up @@ -439,6 +441,24 @@ impl Builder {
self
}

/// Set the maximum number of headers.
///
/// When a response is received, the parser will reserve a buffer to store headers for optimal
/// performance.
///
/// If client receives more headers than the buffer size, the error "message header too large"
/// is returned.
///
/// Note that headers is allocated on the stack by default, which has higher performance. After
/// setting this value, headers will be allocated in heap memory, that is, heap memory
/// allocation will occur for each response, and there will be a performance drop of about 5%.
///
/// Default is 100.
pub fn max_headers(&mut self, val: usize) -> &mut Self {
self.h1_max_headers = Some(val);
self
}

/// Set whether to support preserving original header order.
///
/// Currently, this will record the order in which headers are received, and store this
Expand Down Expand Up @@ -519,6 +539,9 @@ impl Builder {
if opts.h1_preserve_header_case {
conn.set_preserve_header_case();
}
if let Some(max_headers) = opts.h1_max_headers {
conn.set_http1_max_headers(max_headers);
}
#[cfg(feature = "ffi")]
if opts.h1_preserve_header_order {
conn.set_preserve_header_order();
Expand Down
7 changes: 7 additions & 0 deletions src/proto/h1/conn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ where
keep_alive: KA::Busy,
method: None,
h1_parser_config: ParserConfig::default(),
h1_max_headers: None,
#[cfg(feature = "server")]
h1_header_read_timeout: None,
#[cfg(feature = "server")]
Expand Down Expand Up @@ -132,6 +133,10 @@ where
self.state.h09_responses = true;
}

pub(crate) fn set_http1_max_headers(&mut self, val: usize) {
self.state.h1_max_headers = Some(val);
}

#[cfg(feature = "server")]
pub(crate) fn set_http1_header_read_timeout(&mut self, val: Duration) {
self.state.h1_header_read_timeout = Some(val);
Expand Down Expand Up @@ -207,6 +212,7 @@ where
cached_headers: &mut self.state.cached_headers,
req_method: &mut self.state.method,
h1_parser_config: self.state.h1_parser_config.clone(),
h1_max_headers: self.state.h1_max_headers,
#[cfg(feature = "server")]
h1_header_read_timeout: self.state.h1_header_read_timeout,
#[cfg(feature = "server")]
Expand Down Expand Up @@ -847,6 +853,7 @@ struct State {
/// a body or not.
method: Option<Method>,
h1_parser_config: ParserConfig,
h1_max_headers: Option<usize>,
#[cfg(feature = "server")]
h1_header_read_timeout: Option<Duration>,
#[cfg(feature = "server")]
Expand Down
2 changes: 2 additions & 0 deletions src/proto/h1/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ where
cached_headers: parse_ctx.cached_headers,
req_method: parse_ctx.req_method,
h1_parser_config: parse_ctx.h1_parser_config.clone(),
h1_max_headers: parse_ctx.h1_max_headers,
#[cfg(feature = "server")]
h1_header_read_timeout: parse_ctx.h1_header_read_timeout,
#[cfg(feature = "server")]
Expand Down Expand Up @@ -725,6 +726,7 @@ mod tests {
cached_headers: &mut None,
req_method: &mut None,
h1_parser_config: Default::default(),
h1_max_headers: None,
h1_header_read_timeout: None,
h1_header_read_timeout_fut: &mut None,
h1_header_read_timeout_running: &mut false,
Expand Down
1 change: 1 addition & 0 deletions src/proto/h1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ pub(crate) struct ParseContext<'a> {
cached_headers: &'a mut Option<HeaderMap>,
req_method: &'a mut Option<Method>,
h1_parser_config: ParserConfig,
h1_max_headers: Option<usize>,
#[cfg(feature = "server")]
h1_header_read_timeout: Option<Duration>,
#[cfg(feature = "server")]
Expand Down
Loading

0 comments on commit b114244

Please sign in to comment.