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 body: u64,
54}
55
56impl Default for Limits {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62impl Limits {
63 /// The default budgets (see the table on [`Limits`]).
64 ///
65 /// # Examples
66 ///
67 /// ```
68 /// assert_eq!(http_net::Limits::new(), http_net::Limits::default());
69 /// ```
70 #[must_use]
71 pub const fn new() -> Self {
72 Self {
73 start_line: 8 * 1024,
74 header_bytes: 16 * 1024,
75 headers: 100,
76 field_line: 8 * 1024,
77 trailer_bytes: None,
78 trailer_fields: None,
79 interim: 16,
80 body: 1024 * 1024,
81 }
82 }
83
84 /// Sets the longest accepted request line, status line, or chunk-size
85 /// line, in bytes including the CRLF. Clamped to `16..=16 MiB`.
86 ///
87 /// # Examples
88 ///
89 /// ```
90 /// let l = http_net::Limits::new().with_max_start_line(1);
91 /// assert_eq!(l.max_start_line(), 16);
92 /// ```
93 #[must_use]
94 pub const fn with_max_start_line(mut self, bytes: usize) -> Self {
95 self.start_line = clamp(bytes, 16, HARD_CAP);
96 self
97 }
98
99 /// Sets the largest accepted header section (every field line plus the
100 /// blank line that ends it), in bytes. Clamped to `2..=16 MiB`.
101 ///
102 /// # Examples
103 ///
104 /// ```
105 /// let l = http_net::Limits::new().with_max_header_bytes(4096);
106 /// assert_eq!(l.max_header_bytes(), 4096);
107 /// ```
108 #[must_use]
109 pub const fn with_max_header_bytes(mut self, bytes: usize) -> Self {
110 self.header_bytes = clamp(bytes, 2, HARD_CAP);
111 self
112 }
113
114 /// Sets the most header fields accepted in one message (trailer fields
115 /// are counted against the same number separately). Clamped to
116 /// `0..=65536`.
117 ///
118 /// # Examples
119 ///
120 /// ```
121 /// let l = http_net::Limits::new().with_max_headers(8);
122 /// assert_eq!(l.max_headers(), 8);
123 /// ```
124 #[must_use]
125 pub const fn with_max_headers(mut self, count: usize) -> Self {
126 self.headers = clamp(count, 0, 65_536);
127 self
128 }
129
130 /// Sets the longest accepted single field line (name, colon, value, and
131 /// CRLF), in headers and trailers alike. Clamped to `4..=16 MiB`. A line
132 /// over it is [`HttpError::FieldLineTooLong`](crate::HttpError::FieldLineTooLong)
133 /// (431), checked before the rest of the line is buffered. The whole
134 /// section is still bounded by
135 /// [`max_header_bytes`](Self::max_header_bytes).
136 ///
137 /// # Examples
138 ///
139 /// ```
140 /// let l = http_net::Limits::new().with_max_field_line(1024);
141 /// assert_eq!(l.max_field_line(), 1024);
142 /// ```
143 #[must_use]
144 pub const fn with_max_field_line(mut self, bytes: usize) -> Self {
145 self.field_line = clamp(bytes, 4, HARD_CAP);
146 self
147 }
148
149 /// Sets the largest accepted trailer section, in bytes (every trailer
150 /// field line plus the blank line that ends it). Clamped to
151 /// `2..=16 MiB`. Until it is set it follows
152 /// [`max_header_bytes`](Self::max_header_bytes).
153 ///
154 /// # Examples
155 ///
156 /// ```
157 /// let l = http_net::Limits::new().with_max_trailer_bytes(512);
158 /// assert_eq!(l.max_trailer_bytes(), 512);
159 /// assert_eq!(http_net::Limits::new().max_trailer_bytes(), 16 * 1024);
160 /// ```
161 #[must_use]
162 pub const fn with_max_trailer_bytes(mut self, bytes: usize) -> Self {
163 self.trailer_bytes = Some(clamp(bytes, 2, HARD_CAP));
164 self
165 }
166
167 /// Sets the most trailer fields accepted in one message. Clamped to
168 /// `0..=65536`. Until it is set it follows
169 /// [`max_headers`](Self::max_headers).
170 ///
171 /// # Examples
172 ///
173 /// ```
174 /// let l = http_net::Limits::new().with_max_trailer_fields(4);
175 /// assert_eq!(l.max_trailer_fields(), 4);
176 /// ```
177 #[must_use]
178 pub const fn with_max_trailer_fields(mut self, count: usize) -> Self {
179 self.trailer_fields = Some(clamp(count, 0, 65_536));
180 self
181 }
182
183 /// Sets the most interim (1xx) responses a client accepts before the
184 /// final response to one request; one more is
185 /// [`HttpError::TooManyInterim`](crate::HttpError::TooManyInterim). The
186 /// interim heads together also count against
187 /// [`max_header_bytes`](Self::max_header_bytes).
188 ///
189 /// # Examples
190 ///
191 /// ```
192 /// let l = http_net::Limits::new().with_max_interim(2);
193 /// assert_eq!(l.max_interim(), 2);
194 /// ```
195 #[must_use]
196 pub const fn with_max_interim(mut self, count: u16) -> Self {
197 self.interim = count;
198 self
199 }
200
201 /// Sets the largest accepted body, in bytes after chunked decoding.
202 ///
203 /// # Examples
204 ///
205 /// ```
206 /// let l = http_net::Limits::new().with_max_body(0);
207 /// assert_eq!(l.max_body(), 0);
208 /// ```
209 #[must_use]
210 pub const fn with_max_body(mut self, bytes: u64) -> Self {
211 self.body = bytes;
212 self
213 }
214
215 /// The longest accepted start line or chunk-size line, CRLF included.
216 ///
217 /// # Examples
218 ///
219 /// ```
220 /// assert_eq!(http_net::Limits::new().max_start_line(), 8192);
221 /// ```
222 #[must_use]
223 pub const fn max_start_line(&self) -> usize {
224 self.start_line
225 }
226
227 /// The largest accepted header section, in bytes.
228 ///
229 /// # Examples
230 ///
231 /// ```
232 /// assert_eq!(http_net::Limits::new().max_header_bytes(), 16384);
233 /// ```
234 #[must_use]
235 pub const fn max_header_bytes(&self) -> usize {
236 self.header_bytes
237 }
238
239 /// The most header fields accepted in one message.
240 ///
241 /// # Examples
242 ///
243 /// ```
244 /// assert_eq!(http_net::Limits::new().max_headers(), 100);
245 /// ```
246 #[must_use]
247 pub const fn max_headers(&self) -> usize {
248 self.headers
249 }
250
251 /// The longest accepted single field line, CRLF included.
252 ///
253 /// # Examples
254 ///
255 /// ```
256 /// assert_eq!(http_net::Limits::new().max_field_line(), 8192);
257 /// ```
258 #[must_use]
259 pub const fn max_field_line(&self) -> usize {
260 self.field_line
261 }
262
263 /// The largest accepted trailer section, in bytes.
264 ///
265 /// # Examples
266 ///
267 /// ```
268 /// let l = http_net::Limits::new().with_max_header_bytes(4096);
269 /// assert_eq!(l.max_trailer_bytes(), 4096);
270 /// ```
271 #[must_use]
272 pub const fn max_trailer_bytes(&self) -> usize {
273 match self.trailer_bytes {
274 Some(n) => n,
275 None => self.header_bytes,
276 }
277 }
278
279 /// The most trailer fields accepted in one message.
280 ///
281 /// # Examples
282 ///
283 /// ```
284 /// assert_eq!(http_net::Limits::new().max_trailer_fields(), 100);
285 /// ```
286 #[must_use]
287 pub const fn max_trailer_fields(&self) -> usize {
288 match self.trailer_fields {
289 Some(n) => n,
290 None => self.headers,
291 }
292 }
293
294 /// The most interim responses a client accepts per request.
295 ///
296 /// # Examples
297 ///
298 /// ```
299 /// assert_eq!(http_net::Limits::new().max_interim(), 16);
300 /// ```
301 #[must_use]
302 pub const fn max_interim(&self) -> u16 {
303 self.interim
304 }
305
306 /// The largest accepted body, in bytes.
307 ///
308 /// # Examples
309 ///
310 /// ```
311 /// assert_eq!(http_net::Limits::new().max_body(), 1 << 20);
312 /// ```
313 #[must_use]
314 pub const fn max_body(&self) -> u64 {
315 self.body
316 }
317
318 /// The input buffer's hard capacity, `head + max(head,
319 /// max_trailer_bytes) + 16 KiB` where `head = max_start_line +
320 /// max_header_bytes` (64 KiB with the defaults), so a driver can plan
321 /// memory per connection.
322 ///
323 /// The current message's head stays pinned while its body streams, and
324 /// the decoder may need one whole chunk-size line or trailer section on
325 /// top of it, so this always fits whatever the parser is waiting to
326 /// complete. A connection's memory is this buffer plus `max_headers`
327 /// field records of 16 bytes each (and as many for trailers when they
328 /// are reported).
329 ///
330 /// # Examples
331 ///
332 /// ```
333 /// use http_net::Limits;
334 ///
335 /// assert_eq!(Limits::new().buffer_capacity(), 64 * 1024);
336 /// let small = Limits::new().with_max_start_line(1024).with_max_header_bytes(4096);
337 /// assert_eq!(small.buffer_capacity(), 2 * (1024 + 4096) + 16 * 1024);
338 /// ```
339 #[must_use]
340 pub const fn buffer_capacity(&self) -> usize {
341 let head = self.start_line + self.header_bytes;
342 let trailers = self.max_trailer_bytes();
343 let tail = if trailers > head { trailers } else { head };
344 head + tail + BODY_WINDOW
345 }
346}
347
348const fn clamp(v: usize, lo: usize, hi: usize) -> usize {
349 if v < lo {
350 lo
351 } else if v > hi {
352 hi
353 } else {
354 v
355 }
356}
357
358#[cfg(test)]
359mod tests {
360 use super::*;
361
362 #[test]
363 fn test_limits_clamp_to_hard_bounds() {
364 let l = Limits::new()
365 .with_max_start_line(usize::MAX)
366 .with_max_header_bytes(0)
367 .with_max_headers(usize::MAX);
368 assert_eq!(l.max_start_line(), HARD_CAP);
369 assert_eq!(l.max_header_bytes(), 2);
370 assert_eq!(l.max_headers(), 65_536);
371 assert!(l.buffer_capacity() <= u32::MAX as usize);
372 let l = l.with_max_trailer_bytes(usize::MAX).with_max_field_line(0);
373 assert!(l.buffer_capacity() <= u32::MAX as usize);
374 assert_eq!(l.max_field_line(), 4);
375 }
376
377 #[test]
378 fn test_buffer_capacity_covers_a_larger_trailer_section() {
379 let base = Limits::new();
380 assert_eq!(base.buffer_capacity(), 64 * 1024);
381 let big = base.with_max_trailer_bytes(100 * 1024);
382 assert_eq!(big.buffer_capacity(), 24 * 1024 + 100 * 1024 + 16 * 1024);
383 let small = base.with_max_trailer_bytes(10);
384 assert_eq!(small.buffer_capacity(), 64 * 1024);
385 }
386}