http_net/limits.rs
1//! Budgets that bound what a peer can make a connection buffer or accept.
2
3/// Upper bound for the start-line and header-section budgets. Keeps every
4/// buffer offset inside `u32` and every buffer allocation sane.
5const HARD_CAP: usize = 16 * 1024 * 1024;
6
7/// Bytes of buffer space, beyond the head budget, used to stream body data.
8const BODY_WINDOW: usize = 16 * 1024;
9
10/// The budgets a connection enforces on incoming messages.
11///
12/// Every limit is checked **before** the bytes it governs are buffered or
13/// allocated: a declared `Content-Length` above [`max_body`](Limits::max_body)
14/// is refused on the header line, a chunk size is checked against the
15/// remaining body budget before its data is read, and the connection's input
16/// buffer can never grow past `2 * (max_start_line + max_header_bytes)` plus a
17/// fixed 16 KiB body window (64 KiB with the defaults): room for a pinned
18/// head plus one partial chunk line or trailer section.
19///
20/// The defaults are the budgets of LexerSketch Studio's localhost server:
21///
22/// | Budget | Default |
23/// |---|---|
24/// | request line / status line / chunk-size line | 8 KiB |
25/// | header section (all field lines) | 16 KiB |
26/// | one field line (headers and trailers) | 8 KiB |
27/// | header fields | 100 |
28/// | trailer section, trailer fields | the header budgets |
29/// | interim (1xx) responses per request (client) | 16 |
30/// | body | 1 MiB |
31///
32/// # Examples
33///
34/// ```
35/// use http_net::Limits;
36///
37/// let limits = Limits::new().with_max_body(64 * 1024).with_max_headers(32);
38/// assert_eq!(limits.max_body(), 64 * 1024);
39/// assert_eq!(limits.max_headers(), 32);
40/// assert_eq!(limits.max_start_line(), 8 * 1024);
41/// ```
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
43pub struct Limits {
44 start_line: usize,
45 header_bytes: usize,
46 headers: usize,
47 field_line: usize,
48 /// `None`: the same as the header budget.
49 trailer_bytes: Option<usize>,
50 /// `None`: the same as the header count.
51 trailer_fields: Option<usize>,
52 interim: u16,
53 pipeline_depth: u8,
54 body: u64,
55}
56
57/// The most requests a client may have outstanding at once.
58pub(crate) const MAX_PIPELINE_DEPTH: u8 = 32;
59
60impl Default for Limits {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl Limits {
67 /// The default budgets (see the table on [`Limits`]).
68 ///
69 /// # Examples
70 ///
71 /// ```
72 /// assert_eq!(http_net::Limits::new(), http_net::Limits::default());
73 /// ```
74 #[must_use]
75 pub const fn new() -> Self {
76 Self {
77 start_line: 8 * 1024,
78 header_bytes: 16 * 1024,
79 headers: 100,
80 field_line: 8 * 1024,
81 trailer_bytes: None,
82 trailer_fields: None,
83 interim: 16,
84 pipeline_depth: 1,
85 body: 1024 * 1024,
86 }
87 }
88
89 /// Sets the longest accepted request line, status line, or chunk-size
90 /// line, in bytes including the CRLF. Clamped to `16..=16 MiB`.
91 ///
92 /// # Examples
93 ///
94 /// ```
95 /// let l = http_net::Limits::new().with_max_start_line(1);
96 /// assert_eq!(l.max_start_line(), 16);
97 /// ```
98 #[must_use]
99 pub const fn with_max_start_line(mut self, bytes: usize) -> Self {
100 self.start_line = clamp(bytes, 16, HARD_CAP);
101 self
102 }
103
104 /// Sets the largest accepted header section (every field line plus the
105 /// blank line that ends it), in bytes. Clamped to `2..=16 MiB`.
106 ///
107 /// # Examples
108 ///
109 /// ```
110 /// let l = http_net::Limits::new().with_max_header_bytes(4096);
111 /// assert_eq!(l.max_header_bytes(), 4096);
112 /// ```
113 #[must_use]
114 pub const fn with_max_header_bytes(mut self, bytes: usize) -> Self {
115 self.header_bytes = clamp(bytes, 2, HARD_CAP);
116 self
117 }
118
119 /// Sets the most header fields accepted in one message (trailer fields
120 /// are counted against the same number separately). Clamped to
121 /// `0..=65536`.
122 ///
123 /// # Examples
124 ///
125 /// ```
126 /// let l = http_net::Limits::new().with_max_headers(8);
127 /// assert_eq!(l.max_headers(), 8);
128 /// ```
129 #[must_use]
130 pub const fn with_max_headers(mut self, count: usize) -> Self {
131 self.headers = clamp(count, 0, 65_536);
132 self
133 }
134
135 /// Sets the longest accepted single field line (name, colon, value, and
136 /// CRLF), in headers and trailers alike. Clamped to `4..=16 MiB`. A line
137 /// over it is [`HttpError::FieldLineTooLong`](crate::HttpError::FieldLineTooLong)
138 /// (431), checked before the rest of the line is buffered. The whole
139 /// section is still bounded by
140 /// [`max_header_bytes`](Self::max_header_bytes).
141 ///
142 /// # Examples
143 ///
144 /// ```
145 /// let l = http_net::Limits::new().with_max_field_line(1024);
146 /// assert_eq!(l.max_field_line(), 1024);
147 /// ```
148 #[must_use]
149 pub const fn with_max_field_line(mut self, bytes: usize) -> Self {
150 self.field_line = clamp(bytes, 4, HARD_CAP);
151 self
152 }
153
154 /// Sets the largest accepted trailer section, in bytes (every trailer
155 /// field line plus the blank line that ends it). Clamped to
156 /// `2..=16 MiB`. Until it is set it follows
157 /// [`max_header_bytes`](Self::max_header_bytes).
158 ///
159 /// # Examples
160 ///
161 /// ```
162 /// let l = http_net::Limits::new().with_max_trailer_bytes(512);
163 /// assert_eq!(l.max_trailer_bytes(), 512);
164 /// assert_eq!(http_net::Limits::new().max_trailer_bytes(), 16 * 1024);
165 /// ```
166 #[must_use]
167 pub const fn with_max_trailer_bytes(mut self, bytes: usize) -> Self {
168 self.trailer_bytes = Some(clamp(bytes, 2, HARD_CAP));
169 self
170 }
171
172 /// Sets the most trailer fields accepted in one message. Clamped to
173 /// `0..=65536`. Until it is set it follows
174 /// [`max_headers`](Self::max_headers).
175 ///
176 /// # Examples
177 ///
178 /// ```
179 /// let l = http_net::Limits::new().with_max_trailer_fields(4);
180 /// assert_eq!(l.max_trailer_fields(), 4);
181 /// ```
182 #[must_use]
183 pub const fn with_max_trailer_fields(mut self, count: usize) -> Self {
184 self.trailer_fields = Some(clamp(count, 0, 65_536));
185 self
186 }
187
188 /// Sets the most interim (1xx) responses a client accepts before the
189 /// final response to one request; one more is
190 /// [`HttpError::TooManyInterim`](crate::HttpError::TooManyInterim). The
191 /// interim heads together also count against
192 /// [`max_header_bytes`](Self::max_header_bytes).
193 ///
194 /// # Examples
195 ///
196 /// ```
197 /// let l = http_net::Limits::new().with_max_interim(2);
198 /// assert_eq!(l.max_interim(), 2);
199 /// ```
200 #[must_use]
201 pub const fn with_max_interim(mut self, count: u16) -> Self {
202 self.interim = count;
203 self
204 }
205
206 /// Sets the largest accepted body, in bytes after chunked decoding.
207 ///
208 /// # Examples
209 ///
210 /// ```
211 /// let l = http_net::Limits::new().with_max_body(0);
212 /// assert_eq!(l.max_body(), 0);
213 /// ```
214 #[must_use]
215 pub const fn with_max_body(mut self, bytes: u64) -> Self {
216 self.body = bytes;
217 self
218 }
219
220 /// The longest accepted start line or chunk-size line, CRLF included.
221 ///
222 /// # Examples
223 ///
224 /// ```
225 /// assert_eq!(http_net::Limits::new().max_start_line(), 8192);
226 /// ```
227 #[must_use]
228 pub const fn max_start_line(&self) -> usize {
229 self.start_line
230 }
231
232 /// The largest accepted header section, in bytes.
233 ///
234 /// # Examples
235 ///
236 /// ```
237 /// assert_eq!(http_net::Limits::new().max_header_bytes(), 16384);
238 /// ```
239 #[must_use]
240 pub const fn max_header_bytes(&self) -> usize {
241 self.header_bytes
242 }
243
244 /// The most header fields accepted in one message.
245 ///
246 /// # Examples
247 ///
248 /// ```
249 /// assert_eq!(http_net::Limits::new().max_headers(), 100);
250 /// ```
251 #[must_use]
252 pub const fn max_headers(&self) -> usize {
253 self.headers
254 }
255
256 /// The longest accepted single field line, CRLF included.
257 ///
258 /// # Examples
259 ///
260 /// ```
261 /// assert_eq!(http_net::Limits::new().max_field_line(), 8192);
262 /// ```
263 #[must_use]
264 pub const fn max_field_line(&self) -> usize {
265 self.field_line
266 }
267
268 /// The largest accepted trailer section, in bytes.
269 ///
270 /// # Examples
271 ///
272 /// ```
273 /// let l = http_net::Limits::new().with_max_header_bytes(4096);
274 /// assert_eq!(l.max_trailer_bytes(), 4096);
275 /// ```
276 #[must_use]
277 pub const fn max_trailer_bytes(&self) -> usize {
278 match self.trailer_bytes {
279 Some(n) => n,
280 None => self.header_bytes,
281 }
282 }
283
284 /// The most trailer fields accepted in one message.
285 ///
286 /// # Examples
287 ///
288 /// ```
289 /// assert_eq!(http_net::Limits::new().max_trailer_fields(), 100);
290 /// ```
291 #[must_use]
292 pub const fn max_trailer_fields(&self) -> usize {
293 match self.trailer_fields {
294 Some(n) => n,
295 None => self.headers,
296 }
297 }
298
299 /// The most interim responses a client accepts per request.
300 ///
301 /// # Examples
302 ///
303 /// ```
304 /// assert_eq!(http_net::Limits::new().max_interim(), 16);
305 /// ```
306 #[must_use]
307 pub const fn max_interim(&self) -> u16 {
308 self.interim
309 }
310
311 /// Sets how many requests a client may have outstanding at once
312 /// (pipelining, RFC 9112 section 9.3.2). Clamped to `1..=32`; 1 (the
313 /// default) means one request at a time. Only idempotent requests with
314 /// full bodies are pipelined; see
315 /// [`ClientConn::can_send`](crate::ClientConn::can_send).
316 ///
317 /// # Examples
318 ///
319 /// ```
320 /// let l = http_net::Limits::new().with_pipeline_depth(8);
321 /// assert_eq!(l.pipeline_depth(), 8);
322 /// assert_eq!(http_net::Limits::new().with_pipeline_depth(0).pipeline_depth(), 1);
323 /// assert_eq!(http_net::Limits::new().with_pipeline_depth(200).pipeline_depth(), 32);
324 /// ```
325 #[must_use]
326 pub const fn with_pipeline_depth(mut self, depth: u8) -> Self {
327 self.pipeline_depth = if depth == 0 {
328 1
329 } else if depth > MAX_PIPELINE_DEPTH {
330 MAX_PIPELINE_DEPTH
331 } else {
332 depth
333 };
334 self
335 }
336
337 /// How many requests a client may have outstanding at once.
338 ///
339 /// # Examples
340 ///
341 /// ```
342 /// assert_eq!(http_net::Limits::new().pipeline_depth(), 1);
343 /// ```
344 #[must_use]
345 pub const fn pipeline_depth(&self) -> u8 {
346 self.pipeline_depth
347 }
348
349 /// The largest accepted body, in bytes.
350 ///
351 /// # Examples
352 ///
353 /// ```
354 /// assert_eq!(http_net::Limits::new().max_body(), 1 << 20);
355 /// ```
356 #[must_use]
357 pub const fn max_body(&self) -> u64 {
358 self.body
359 }
360
361 /// The input buffer's hard capacity, `head + max(head,
362 /// max_trailer_bytes) + 16 KiB` where `head = max_start_line +
363 /// max_header_bytes` (64 KiB with the defaults), so a driver can plan
364 /// memory per connection.
365 ///
366 /// The current message's head stays pinned while its body streams, and
367 /// the decoder may need one whole chunk-size line or trailer section on
368 /// top of it, so this always fits whatever the parser is waiting to
369 /// complete. A connection's memory is this buffer plus `max_headers`
370 /// field records of 16 bytes each (and as many for trailers when they
371 /// are reported).
372 ///
373 /// # Examples
374 ///
375 /// ```
376 /// use http_net::Limits;
377 ///
378 /// assert_eq!(Limits::new().buffer_capacity(), 64 * 1024);
379 /// let small = Limits::new().with_max_start_line(1024).with_max_header_bytes(4096);
380 /// assert_eq!(small.buffer_capacity(), 2 * (1024 + 4096) + 16 * 1024);
381 /// ```
382 #[must_use]
383 pub const fn buffer_capacity(&self) -> usize {
384 let head = self.start_line + self.header_bytes;
385 let trailers = self.max_trailer_bytes();
386 let tail = if trailers > head { trailers } else { head };
387 head + tail + BODY_WINDOW
388 }
389}
390
391const fn clamp(v: usize, lo: usize, hi: usize) -> usize {
392 if v < lo {
393 lo
394 } else if v > hi {
395 hi
396 } else {
397 v
398 }
399}
400
401#[cfg(test)]
402mod tests {
403 use super::*;
404
405 #[test]
406 fn test_limits_clamp_to_hard_bounds() {
407 let l = Limits::new()
408 .with_max_start_line(usize::MAX)
409 .with_max_header_bytes(0)
410 .with_max_headers(usize::MAX);
411 assert_eq!(l.max_start_line(), HARD_CAP);
412 assert_eq!(l.max_header_bytes(), 2);
413 assert_eq!(l.max_headers(), 65_536);
414 assert!(l.buffer_capacity() <= u32::MAX as usize);
415 let l = l.with_max_trailer_bytes(usize::MAX).with_max_field_line(0);
416 assert!(l.buffer_capacity() <= u32::MAX as usize);
417 assert_eq!(l.max_field_line(), 4);
418 }
419
420 #[test]
421 fn test_buffer_capacity_covers_a_larger_trailer_section() {
422 let base = Limits::new();
423 assert_eq!(base.buffer_capacity(), 64 * 1024);
424 let big = base.with_max_trailer_bytes(100 * 1024);
425 assert_eq!(big.buffer_capacity(), 24 * 1024 + 100 * 1024 + 16 * 1024);
426 let small = base.with_max_trailer_bytes(10);
427 assert_eq!(small.buffer_capacity(), 64 * 1024);
428 }
429}