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 and IvanGoncharov committed Oct 23, 2024
1 parent 7829148 commit d5f69fc
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 18 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ want = "0.3"
# Optional

libc = { version = "0.2", optional = true }
smallvec = { version = "1.12", features = ["const_generics", "const_new"], optional = true }
socket2 = { version = ">=0.4.7, <0.6.0", optional = true, features = ["all"] }

[dev-dependencies]
Expand Down Expand Up @@ -87,8 +88,8 @@ http1 = []
http2 = ["h2"]

# Client/Server
client = []
server = []
client = ["dep:smallvec"]
server = ["dep:smallvec"]

# `impl Stream` for things
stream = []
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 @@ -302,6 +303,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 @@ -434,6 +436,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 @@ -514,6 +534,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 @@ -53,6 +53,7 @@ where
keep_alive: KA::Busy,
method: None,
h1_parser_config: ParserConfig::default(),
h1_max_headers: None,
#[cfg(all(feature = "server", feature = "runtime"))]
h1_header_read_timeout: None,
#[cfg(all(feature = "server", feature = "runtime"))]
Expand Down Expand Up @@ -125,6 +126,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(all(feature = "server", feature = "runtime"))]
pub(crate) fn set_http1_header_read_timeout(&mut self, val: Duration) {
self.state.h1_header_read_timeout = Some(val);
Expand Down Expand Up @@ -198,6 +203,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(all(feature = "server", feature = "runtime"))]
h1_header_read_timeout: self.state.h1_header_read_timeout,
#[cfg(all(feature = "server", feature = "runtime"))]
Expand Down Expand Up @@ -822,6 +828,7 @@ struct State {
/// a body or not.
method: Option<Method>,
h1_parser_config: ParserConfig,
h1_max_headers: Option<usize>,
#[cfg(all(feature = "server", feature = "runtime"))]
h1_header_read_timeout: Option<Duration>,
#[cfg(all(feature = "server", feature = "runtime"))]
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 @@ -191,6 +191,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(all(feature = "server", feature = "runtime"))]
h1_header_read_timeout: parse_ctx.h1_header_read_timeout,
#[cfg(all(feature = "server", feature = "runtime"))]
Expand Down Expand Up @@ -741,6 +742,7 @@ mod tests {
cached_headers: &mut None,
req_method: &mut None,
h1_parser_config: Default::default(),
h1_max_headers: None,
#[cfg(feature = "runtime")]
h1_header_read_timeout: None,
#[cfg(feature = "runtime")]
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 @@ -76,6 +76,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(all(feature = "server", feature = "runtime"))]
h1_header_read_timeout: Option<Duration>,
#[cfg(all(feature = "server", feature = "runtime"))]
Expand Down
Loading

0 comments on commit d5f69fc

Please sign in to comment.