Skip to main content

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}