Skip to content

Commit b114244

Browse files
authored
feat(http1): support configurable max_headers(num) to client and server (#3523)
1 parent 7177770 commit b114244

File tree

7 files changed

+235
-7
lines changed

7 files changed

+235
-7
lines changed

Cargo.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ httpdate = { version = "1.0", optional = true }
3636
itoa = { version = "1", optional = true }
3737
libc = { version = "0.2", optional = true }
3838
pin-project-lite = { version = "0.2.4", optional = true }
39+
smallvec = { version = "1.12", features = ["const_generics", "const_new"], optional = true }
3940
tracing = { version = "0.1", default-features = false, features = ["std"], optional = true }
4041
want = { version = "0.3", optional = true }
4142

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

8283
# Client/Server
83-
client = ["dep:want", "dep:pin-project-lite"]
84-
server = ["dep:httpdate", "dep:pin-project-lite"]
84+
client = ["dep:want", "dep:pin-project-lite", "dep:smallvec"]
85+
server = ["dep:httpdate", "dep:pin-project-lite", "dep:smallvec"]
8586

8687
# C-API support (currently unstable (no semver))
8788
ffi = ["dep:libc", "dep:http-body-util"]

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>,
@@ -309,6 +310,7 @@ impl Builder {
309310
h1_parser_config: Default::default(),
310311
h1_title_case_headers: false,
311312
h1_preserve_header_case: false,
313+
h1_max_headers: None,
312314
#[cfg(feature = "ffi")]
313315
h1_preserve_header_order: false,
314316
h1_max_buf_size: None,
@@ -439,6 +441,24 @@ impl Builder {
439441
self
440442
}
441443

444+
/// Set the maximum number of headers.
445+
///
446+
/// When a response is received, the parser will reserve a buffer to store headers for optimal
447+
/// performance.
448+
///
449+
/// If client receives more headers than the buffer size, the error "message header too large"
450+
/// is returned.
451+
///
452+
/// Note that headers is allocated on the stack by default, which has higher performance. After
453+
/// setting this value, headers will be allocated in heap memory, that is, heap memory
454+
/// allocation will occur for each response, and there will be a performance drop of about 5%.
455+
///
456+
/// Default is 100.
457+
pub fn max_headers(&mut self, val: usize) -> &mut Self {
458+
self.h1_max_headers = Some(val);
459+
self
460+
}
461+
442462
/// Set whether to support preserving original header order.
443463
///
444464
/// Currently, this will record the order in which headers are received, and store this
@@ -519,6 +539,9 @@ impl Builder {
519539
if opts.h1_preserve_header_case {
520540
conn.set_preserve_header_case();
521541
}
542+
if let Some(max_headers) = opts.h1_max_headers {
543+
conn.set_http1_max_headers(max_headers);
544+
}
522545
#[cfg(feature = "ffi")]
523546
if opts.h1_preserve_header_order {
524547
conn.set_preserve_header_order();

src/proto/h1/conn.rs

+7
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ where
5454
keep_alive: KA::Busy,
5555
method: None,
5656
h1_parser_config: ParserConfig::default(),
57+
h1_max_headers: None,
5758
#[cfg(feature = "server")]
5859
h1_header_read_timeout: None,
5960
#[cfg(feature = "server")]
@@ -132,6 +133,10 @@ where
132133
self.state.h09_responses = true;
133134
}
134135

136+
pub(crate) fn set_http1_max_headers(&mut self, val: usize) {
137+
self.state.h1_max_headers = Some(val);
138+
}
139+
135140
#[cfg(feature = "server")]
136141
pub(crate) fn set_http1_header_read_timeout(&mut self, val: Duration) {
137142
self.state.h1_header_read_timeout = Some(val);
@@ -207,6 +212,7 @@ where
207212
cached_headers: &mut self.state.cached_headers,
208213
req_method: &mut self.state.method,
209214
h1_parser_config: self.state.h1_parser_config.clone(),
215+
h1_max_headers: self.state.h1_max_headers,
210216
#[cfg(feature = "server")]
211217
h1_header_read_timeout: self.state.h1_header_read_timeout,
212218
#[cfg(feature = "server")]
@@ -847,6 +853,7 @@ struct State {
847853
/// a body or not.
848854
method: Option<Method>,
849855
h1_parser_config: ParserConfig,
856+
h1_max_headers: Option<usize>,
850857
#[cfg(feature = "server")]
851858
h1_header_read_timeout: Option<Duration>,
852859
#[cfg(feature = "server")]

src/proto/h1/io.rs

+2
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ where
184184
cached_headers: parse_ctx.cached_headers,
185185
req_method: parse_ctx.req_method,
186186
h1_parser_config: parse_ctx.h1_parser_config.clone(),
187+
h1_max_headers: parse_ctx.h1_max_headers,
187188
#[cfg(feature = "server")]
188189
h1_header_read_timeout: parse_ctx.h1_header_read_timeout,
189190
#[cfg(feature = "server")]
@@ -725,6 +726,7 @@ mod tests {
725726
cached_headers: &mut None,
726727
req_method: &mut None,
727728
h1_parser_config: Default::default(),
729+
h1_max_headers: None,
728730
h1_header_read_timeout: None,
729731
h1_header_read_timeout_fut: &mut None,
730732
h1_header_read_timeout_running: &mut false,

src/proto/h1/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub(crate) struct ParseContext<'a> {
7878
cached_headers: &'a mut Option<HeaderMap>,
7979
req_method: &'a mut Option<Method>,
8080
h1_parser_config: ParserConfig,
81+
h1_max_headers: Option<usize>,
8182
#[cfg(feature = "server")]
8283
h1_header_read_timeout: Option<Duration>,
8384
#[cfg(feature = "server")]

0 commit comments

Comments
 (0)