Skip to content

Commit d5f69fc

Browse files
yukiiiteruIvanGoncharov
authored andcommitted
feat(http1): support configurable max_headers(num) to client and server (hyperium#3523)
1 parent 7829148 commit d5f69fc

File tree

7 files changed

+235
-18
lines changed

7 files changed

+235
-18
lines changed

Cargo.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ want = "0.3"
3939
# Optional
4040

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

4445
[dev-dependencies]
@@ -87,8 +88,8 @@ http1 = []
8788
http2 = ["h2"]
8889

8990
# Client/Server
90-
client = []
91-
server = []
91+
client = ["dep:smallvec"]
92+
server = ["dep:smallvec"]
9293

9394
# `impl Stream` for things
9495
stream = []

src/client/conn/http1.rs

+23
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ pub struct Builder {
115115
h1_writev: Option<bool>,
116116
h1_title_case_headers: bool,
117117
h1_preserve_header_case: bool,
118+
h1_max_headers: Option<usize>,
118119
#[cfg(feature = "ffi")]
119120
h1_preserve_header_order: bool,
120121
h1_read_buf_exact_size: Option<usize>,
@@ -302,6 +303,7 @@ impl Builder {
302303
h1_parser_config: Default::default(),
303304
h1_title_case_headers: false,
304305
h1_preserve_header_case: false,
306+
h1_max_headers: None,
305307
#[cfg(feature = "ffi")]
306308
h1_preserve_header_order: false,
307309
h1_max_buf_size: None,
@@ -434,6 +436,24 @@ impl Builder {
434436
self
435437
}
436438

439+
/// Set the maximum number of headers.
440+
///
441+
/// When a response is received, the parser will reserve a buffer to store headers for optimal
442+
/// performance.
443+
///
444+
/// If client receives more headers than the buffer size, the error "message header too large"
445+
/// is returned.
446+
///
447+
/// Note that headers is allocated on the stack by default, which has higher performance. After
448+
/// setting this value, headers will be allocated in heap memory, that is, heap memory
449+
/// allocation will occur for each response, and there will be a performance drop of about 5%.
450+
///
451+
/// Default is 100.
452+
pub fn max_headers(&mut self, val: usize) -> &mut Self {
453+
self.h1_max_headers = Some(val);
454+
self
455+
}
456+
437457
/// Set whether to support preserving original header order.
438458
///
439459
/// Currently, this will record the order in which headers are received, and store this
@@ -514,6 +534,9 @@ impl Builder {
514534
if opts.h1_preserve_header_case {
515535
conn.set_preserve_header_case();
516536
}
537+
if let Some(max_headers) = opts.h1_max_headers {
538+
conn.set_http1_max_headers(max_headers);
539+
}
517540
#[cfg(feature = "ffi")]
518541
if opts.h1_preserve_header_order {
519542
conn.set_preserve_header_order();

src/proto/h1/conn.rs

+7
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ where
5353
keep_alive: KA::Busy,
5454
method: None,
5555
h1_parser_config: ParserConfig::default(),
56+
h1_max_headers: None,
5657
#[cfg(all(feature = "server", feature = "runtime"))]
5758
h1_header_read_timeout: None,
5859
#[cfg(all(feature = "server", feature = "runtime"))]
@@ -125,6 +126,10 @@ where
125126
self.state.h09_responses = true;
126127
}
127128

129+
pub(crate) fn set_http1_max_headers(&mut self, val: usize) {
130+
self.state.h1_max_headers = Some(val);
131+
}
132+
128133
#[cfg(all(feature = "server", feature = "runtime"))]
129134
pub(crate) fn set_http1_header_read_timeout(&mut self, val: Duration) {
130135
self.state.h1_header_read_timeout = Some(val);
@@ -198,6 +203,7 @@ where
198203
cached_headers: &mut self.state.cached_headers,
199204
req_method: &mut self.state.method,
200205
h1_parser_config: self.state.h1_parser_config.clone(),
206+
h1_max_headers: self.state.h1_max_headers,
201207
#[cfg(all(feature = "server", feature = "runtime"))]
202208
h1_header_read_timeout: self.state.h1_header_read_timeout,
203209
#[cfg(all(feature = "server", feature = "runtime"))]
@@ -822,6 +828,7 @@ struct State {
822828
/// a body or not.
823829
method: Option<Method>,
824830
h1_parser_config: ParserConfig,
831+
h1_max_headers: Option<usize>,
825832
#[cfg(all(feature = "server", feature = "runtime"))]
826833
h1_header_read_timeout: Option<Duration>,
827834
#[cfg(all(feature = "server", feature = "runtime"))]

src/proto/h1/io.rs

+2
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ where
191191
cached_headers: parse_ctx.cached_headers,
192192
req_method: parse_ctx.req_method,
193193
h1_parser_config: parse_ctx.h1_parser_config.clone(),
194+
h1_max_headers: parse_ctx.h1_max_headers,
194195
#[cfg(all(feature = "server", feature = "runtime"))]
195196
h1_header_read_timeout: parse_ctx.h1_header_read_timeout,
196197
#[cfg(all(feature = "server", feature = "runtime"))]
@@ -741,6 +742,7 @@ mod tests {
741742
cached_headers: &mut None,
742743
req_method: &mut None,
743744
h1_parser_config: Default::default(),
745+
h1_max_headers: None,
744746
#[cfg(feature = "runtime")]
745747
h1_header_read_timeout: None,
746748
#[cfg(feature = "runtime")]

src/proto/h1/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub(crate) struct ParseContext<'a> {
7676
cached_headers: &'a mut Option<HeaderMap>,
7777
req_method: &'a mut Option<Method>,
7878
h1_parser_config: ParserConfig,
79+
h1_max_headers: Option<usize>,
7980
#[cfg(all(feature = "server", feature = "runtime"))]
8081
h1_header_read_timeout: Option<Duration>,
8182
#[cfg(all(feature = "server", feature = "runtime"))]

0 commit comments

Comments
 (0)