Skip to content

Commit 32805b9

Browse files
committed
feat(http1): support configurable max_headers for http1
Signed-off-by: Yu Li <[email protected]>
1 parent b1ee228 commit 32805b9

File tree

5 files changed

+59
-5
lines changed

5 files changed

+59
-5
lines changed

src/proto/h1/conn.rs

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use http::{HeaderMap, Method, Version};
1414
use httparse::ParserConfig;
1515

1616
use super::io::Buffered;
17+
use super::DEFAULT_MAX_HEADERS;
1718
use super::{Decoder, Encode, EncodedBuf, Encoder, Http1Transaction, ParseContext, Wants};
1819
use crate::body::DecodedLength;
1920
#[cfg(feature = "server")]
@@ -54,6 +55,7 @@ where
5455
keep_alive: KA::Busy,
5556
method: None,
5657
h1_parser_config: ParserConfig::default(),
58+
h1_max_headers: DEFAULT_MAX_HEADERS,
5759
#[cfg(feature = "server")]
5860
h1_header_read_timeout: None,
5961
#[cfg(feature = "server")]
@@ -132,6 +134,10 @@ where
132134
self.state.h09_responses = true;
133135
}
134136

137+
pub(crate) fn set_http1_max_headers(&mut self, val: usize) {
138+
self.state.h1_max_headers = val;
139+
}
140+
135141
#[cfg(feature = "server")]
136142
pub(crate) fn set_http1_header_read_timeout(&mut self, val: Duration) {
137143
self.state.h1_header_read_timeout = Some(val);
@@ -207,6 +213,7 @@ where
207213
cached_headers: &mut self.state.cached_headers,
208214
req_method: &mut self.state.method,
209215
h1_parser_config: self.state.h1_parser_config.clone(),
216+
h1_max_headers: self.state.h1_max_headers,
210217
#[cfg(feature = "server")]
211218
h1_header_read_timeout: self.state.h1_header_read_timeout,
212219
#[cfg(feature = "server")]
@@ -847,6 +854,7 @@ struct State {
847854
/// a body or not.
848855
method: Option<Method>,
849856
h1_parser_config: ParserConfig,
857+
h1_max_headers: usize,
850858
#[cfg(feature = "server")]
851859
h1_header_read_timeout: Option<Duration>,
852860
#[cfg(feature = "server")]

src/proto/h1/io.rs

+3
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")]
@@ -662,6 +663,7 @@ enum WriteStrategy {
662663
mod tests {
663664
use crate::common::io::Compat;
664665
use crate::common::time::Time;
666+
use crate::proto::h1::DEFAULT_MAX_HEADERS;
665667

666668
use super::*;
667669
use std::time::Duration;
@@ -725,6 +727,7 @@ mod tests {
725727
cached_headers: &mut None,
726728
req_method: &mut None,
727729
h1_parser_config: Default::default(),
730+
h1_max_headers: DEFAULT_MAX_HEADERS,
728731
h1_header_read_timeout: None,
729732
h1_header_read_timeout_fut: &mut None,
730733
h1_header_read_timeout_running: &mut false,

src/proto/h1/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub(crate) use self::dispatch::Dispatcher;
1818
pub(crate) use self::encode::{EncodedBuf, Encoder};
1919
//TODO: move out of h1::io
2020
pub(crate) use self::io::MINIMUM_MAX_BUFFER_SIZE;
21+
pub(crate) use self::role::DEFAULT_MAX_HEADERS;
2122

2223
mod conn;
2324
mod decode;
@@ -78,6 +79,7 @@ pub(crate) struct ParseContext<'a> {
7879
cached_headers: &'a mut Option<HeaderMap>,
7980
req_method: &'a mut Option<Method>,
8081
h1_parser_config: ParserConfig,
82+
h1_max_headers: usize,
8183
#[cfg(feature = "server")]
8284
h1_header_read_timeout: Option<Duration>,
8385
#[cfg(feature = "server")]

src/proto/h1/role.rs

+24-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::proto::h1::{
2929
use crate::proto::RequestHead;
3030
use crate::proto::{BodyLength, MessageHead, RequestLine};
3131

32-
const MAX_HEADERS: usize = 100;
32+
pub(crate) const DEFAULT_MAX_HEADERS: usize = 100;
3333
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
3434
#[cfg(feature = "server")]
3535
const MAX_URI_LEN: usize = (u16::MAX - 1) as usize;
@@ -139,9 +139,10 @@ impl Http1Transaction for Server {
139139
// but we *never* read any of it until after httparse has assigned
140140
// values into it. By not zeroing out the stack memory, this saves
141141
// a good ~5% on pipeline benchmarks.
142-
let mut headers_indices = [MaybeUninit::<HeaderIndices>::uninit(); MAX_HEADERS];
142+
let mut headers_indices = vec![MaybeUninit::<HeaderIndices>::uninit(); ctx.h1_max_headers];
143143
{
144-
let mut headers = [MaybeUninit::<httparse::Header<'_>>::uninit(); MAX_HEADERS];
144+
let mut headers =
145+
vec![MaybeUninit::<httparse::Header<'_>>::uninit(); ctx.h1_max_headers];
145146
trace!(bytes = buf.len(), "Request.parse");
146147
let mut req = httparse::Request::new(&mut []);
147148
let bytes = buf.as_ref();
@@ -966,9 +967,11 @@ impl Http1Transaction for Client {
966967

967968
// Loop to skip information status code headers (100 Continue, etc).
968969
loop {
969-
let mut headers_indices = [MaybeUninit::<HeaderIndices>::uninit(); MAX_HEADERS];
970+
let mut headers_indices =
971+
vec![MaybeUninit::<HeaderIndices>::uninit(); ctx.h1_max_headers];
970972
let (len, status, reason, version, headers_len) = {
971-
let mut headers = [MaybeUninit::<httparse::Header<'_>>::uninit(); MAX_HEADERS];
973+
let mut headers =
974+
vec![MaybeUninit::<httparse::Header<'_>>::uninit(); ctx.h1_max_headers];
972975
trace!(bytes = buf.len(), "Response.parse");
973976
let mut res = httparse::Response::new(&mut []);
974977
let bytes = buf.as_ref();
@@ -1610,6 +1613,7 @@ mod tests {
16101613
cached_headers: &mut None,
16111614
req_method: &mut method,
16121615
h1_parser_config: Default::default(),
1616+
h1_max_headers: DEFAULT_MAX_HEADERS,
16131617
h1_header_read_timeout: None,
16141618
h1_header_read_timeout_fut: &mut None,
16151619
h1_header_read_timeout_running: &mut false,
@@ -1641,6 +1645,7 @@ mod tests {
16411645
cached_headers: &mut None,
16421646
req_method: &mut Some(crate::Method::GET),
16431647
h1_parser_config: Default::default(),
1648+
h1_max_headers: DEFAULT_MAX_HEADERS,
16441649
h1_header_read_timeout: None,
16451650
h1_header_read_timeout_fut: &mut None,
16461651
h1_header_read_timeout_running: &mut false,
@@ -1667,6 +1672,7 @@ mod tests {
16671672
cached_headers: &mut None,
16681673
req_method: &mut None,
16691674
h1_parser_config: Default::default(),
1675+
h1_max_headers: DEFAULT_MAX_HEADERS,
16701676
h1_header_read_timeout: None,
16711677
h1_header_read_timeout_fut: &mut None,
16721678
h1_header_read_timeout_running: &mut false,
@@ -1691,6 +1697,7 @@ mod tests {
16911697
cached_headers: &mut None,
16921698
req_method: &mut Some(crate::Method::GET),
16931699
h1_parser_config: Default::default(),
1700+
h1_max_headers: DEFAULT_MAX_HEADERS,
16941701
h1_header_read_timeout: None,
16951702
h1_header_read_timeout_fut: &mut None,
16961703
h1_header_read_timeout_running: &mut false,
@@ -1717,6 +1724,7 @@ mod tests {
17171724
cached_headers: &mut None,
17181725
req_method: &mut Some(crate::Method::GET),
17191726
h1_parser_config: Default::default(),
1727+
h1_max_headers: DEFAULT_MAX_HEADERS,
17201728
h1_header_read_timeout: None,
17211729
h1_header_read_timeout_fut: &mut None,
17221730
h1_header_read_timeout_running: &mut false,
@@ -1747,6 +1755,7 @@ mod tests {
17471755
cached_headers: &mut None,
17481756
req_method: &mut Some(crate::Method::GET),
17491757
h1_parser_config,
1758+
h1_max_headers: DEFAULT_MAX_HEADERS,
17501759
h1_header_read_timeout: None,
17511760
h1_header_read_timeout_fut: &mut None,
17521761
h1_header_read_timeout_running: &mut false,
@@ -1774,6 +1783,7 @@ mod tests {
17741783
cached_headers: &mut None,
17751784
req_method: &mut Some(crate::Method::GET),
17761785
h1_parser_config: Default::default(),
1786+
h1_max_headers: DEFAULT_MAX_HEADERS,
17771787
h1_header_read_timeout: None,
17781788
h1_header_read_timeout_fut: &mut None,
17791789
h1_header_read_timeout_running: &mut false,
@@ -1796,6 +1806,7 @@ mod tests {
17961806
cached_headers: &mut None,
17971807
req_method: &mut None,
17981808
h1_parser_config: Default::default(),
1809+
h1_max_headers: DEFAULT_MAX_HEADERS,
17991810
h1_header_read_timeout: None,
18001811
h1_header_read_timeout_fut: &mut None,
18011812
h1_header_read_timeout_running: &mut false,
@@ -1839,6 +1850,7 @@ mod tests {
18391850
cached_headers: &mut None,
18401851
req_method: &mut None,
18411852
h1_parser_config: Default::default(),
1853+
h1_max_headers: DEFAULT_MAX_HEADERS,
18421854
h1_header_read_timeout: None,
18431855
h1_header_read_timeout_fut: &mut None,
18441856
h1_header_read_timeout_running: &mut false,
@@ -1863,6 +1875,7 @@ mod tests {
18631875
cached_headers: &mut None,
18641876
req_method: &mut None,
18651877
h1_parser_config: Default::default(),
1878+
h1_max_headers: DEFAULT_MAX_HEADERS,
18661879
h1_header_read_timeout: None,
18671880
h1_header_read_timeout_fut: &mut None,
18681881
h1_header_read_timeout_running: &mut false,
@@ -2096,6 +2109,7 @@ mod tests {
20962109
cached_headers: &mut None,
20972110
req_method: &mut Some(Method::GET),
20982111
h1_parser_config: Default::default(),
2112+
h1_max_headers: DEFAULT_MAX_HEADERS,
20992113
h1_header_read_timeout: None,
21002114
h1_header_read_timeout_fut: &mut None,
21012115
h1_header_read_timeout_running: &mut false,
@@ -2120,6 +2134,7 @@ mod tests {
21202134
cached_headers: &mut None,
21212135
req_method: &mut Some(m),
21222136
h1_parser_config: Default::default(),
2137+
h1_max_headers: DEFAULT_MAX_HEADERS,
21232138
h1_header_read_timeout: None,
21242139
h1_header_read_timeout_fut: &mut None,
21252140
h1_header_read_timeout_running: &mut false,
@@ -2144,6 +2159,7 @@ mod tests {
21442159
cached_headers: &mut None,
21452160
req_method: &mut Some(Method::GET),
21462161
h1_parser_config: Default::default(),
2162+
h1_max_headers: DEFAULT_MAX_HEADERS,
21472163
h1_header_read_timeout: None,
21482164
h1_header_read_timeout_fut: &mut None,
21492165
h1_header_read_timeout_running: &mut false,
@@ -2663,6 +2679,7 @@ mod tests {
26632679
cached_headers: &mut None,
26642680
req_method: &mut Some(Method::GET),
26652681
h1_parser_config: Default::default(),
2682+
h1_max_headers: DEFAULT_MAX_HEADERS,
26662683
h1_header_read_timeout: None,
26672684
h1_header_read_timeout_fut: &mut None,
26682685
h1_header_read_timeout_running: &mut false,
@@ -2751,6 +2768,7 @@ mod tests {
27512768
cached_headers: &mut headers,
27522769
req_method: &mut None,
27532770
h1_parser_config: Default::default(),
2771+
h1_max_headers: DEFAULT_MAX_HEADERS,
27542772
h1_header_read_timeout: None,
27552773
h1_header_read_timeout_fut: &mut None,
27562774
h1_header_read_timeout_running: &mut false,
@@ -2795,6 +2813,7 @@ mod tests {
27952813
cached_headers: &mut headers,
27962814
req_method: &mut None,
27972815
h1_parser_config: Default::default(),
2816+
h1_max_headers: DEFAULT_MAX_HEADERS,
27982817
h1_header_read_timeout: None,
27992818
h1_header_read_timeout_fut: &mut None,
28002819
h1_header_read_timeout_running: &mut false,

src/server/conn/http1.rs

+22
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pub struct Builder {
7575
h1_keep_alive: bool,
7676
h1_title_case_headers: bool,
7777
h1_preserve_header_case: bool,
78+
h1_max_headers: Option<usize>,
7879
h1_header_read_timeout: Dur,
7980
h1_writev: Option<bool>,
8081
max_buf_size: Option<usize>,
@@ -242,6 +243,7 @@ impl Builder {
242243
h1_keep_alive: true,
243244
h1_title_case_headers: false,
244245
h1_preserve_header_case: false,
246+
h1_max_headers: None,
245247
h1_header_read_timeout: Dur::Default(Some(Duration::from_secs(30))),
246248
h1_writev: None,
247249
max_buf_size: None,
@@ -294,6 +296,23 @@ impl Builder {
294296
self
295297
}
296298

299+
/// Set the maximum number of headers.
300+
///
301+
/// When a frame is received, the parser will reserve a buffer to store headers for optimal
302+
/// performance.
303+
///
304+
/// If the server receives more headers than the buffer size, it responds to the client with
305+
/// `431 Request Header Fields Too Large`.
306+
///
307+
/// If the client receives more headers than the buffer size, the error
308+
/// "message header too large" is returned.
309+
///
310+
/// Default is 100.
311+
pub fn max_headers(&mut self, val: usize) -> &mut Self {
312+
self.h1_max_headers = Some(val);
313+
self
314+
}
315+
297316
/// Set a timeout for reading client request headers. If a client does not
298317
/// transmit the entire header within this time, the connection is closed.
299318
///
@@ -412,6 +431,9 @@ impl Builder {
412431
if self.h1_preserve_header_case {
413432
conn.set_preserve_header_case();
414433
}
434+
if let Some(max_headers) = self.h1_max_headers {
435+
conn.set_http1_max_headers(max_headers);
436+
}
415437
if let Some(dur) = self
416438
.timer
417439
.check(self.h1_header_read_timeout, "header_read_timeout")

0 commit comments

Comments
 (0)