1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
use fieldwork::Fieldwork;
/// # Performance and security parameters for trillium-http.
///
/// Trillium's http implementation is built with sensible defaults, but applications differ in usage
/// and this escape hatch allows an application to be tuned. It is best to tune these parameters in
/// context of realistic benchmarks for your application.
///
/// Long term, trillium may export several standard defaults for different constraints and
/// application types. In the distant future, these may turn into initial values and trillium will
/// tune itself based on values seen at runtime.
#[derive(Clone, Copy, Debug, Fieldwork)]
#[fieldwork(get, get_mut, set, with, without)]
pub struct HttpConfig {
/// The maximum length allowed before the http body begins for a given request.
///
/// **Default**: `8kb` in bytes
///
/// **Unit**: Byte count
pub(crate) head_max_len: usize,
/// The maximum length of a received body
///
/// This limit applies regardless of whether the body is read all at once or streamed
/// incrementally, and regardless of transfer encoding (chunked or fixed-length). The correct
/// value will be application dependent.
///
/// **Default**: `10mb` in bytes
///
/// **Unit**: Byte count
pub(crate) received_body_max_len: u64,
#[field = false] // this one is private for now
pub(crate) max_headers: usize,
/// The initial buffer allocated for the response.
///
/// Ideally this would be exactly the length of the combined response headers and body, if the
/// body is short. If the value is shorter than the headers plus the body, multiple transport
/// writes will be performed, and if the value is longer, unnecessary memory will be allocated
/// for each conn. Although a tcp packet can be up to 64kb, it is probably better to use a
/// value less than 1.5kb.
///
/// **Default**: `512`
///
/// **Unit**: byte count
pub(crate) response_buffer_len: usize,
/// Maximum size the response buffer may grow to absorb backpressure.
///
/// When the transport cannot accept data as fast as the response body is produced, the buffer
/// absorbs the remainder up to this limit. Once the limit is reached, writes apply
/// backpressure to the body source. This prevents a slow client from causing unbounded memory
/// growth.
///
/// **Default**: `2mb` in bytes
///
/// **Unit**: byte count
pub(crate) response_buffer_max_len: usize,
/// The initial buffer allocated for the request headers.
///
/// Ideally this is the length of the request headers. It will grow nonlinearly until
/// `max_head_len` or the end of the headers are reached, whichever happens first.
///
/// **Default**: `128`
///
/// **Unit**: byte count
pub(crate) request_buffer_initial_len: usize,
/// The number of response headers to allocate space for on conn creation.
///
/// Headers will grow on insertion when they reach this size.
///
/// **Default**: `16`
///
/// **Unit**: Header count
pub(crate) response_header_initial_capacity: usize,
/// A sort of cooperative task yielding knob.
///
/// Decreasing this number will improve tail latencies at a slight cost to total throughput for
/// fast clients. This will have more of an impact on servers that spend a lot of time in IO
/// compared to app handlers.
///
/// **Default**: `16`
///
/// **Unit**: the number of consecutive `Poll::Ready` async writes to perform before yielding
/// the task back to the runtime.
pub(crate) copy_loops_per_yield: usize,
/// The initial buffer capacity allocated when reading a chunked http body to bytes or string.
///
/// Ideally this would be the size of the http body, which is highly application dependent. As
/// with other initial buffer lengths, further allocation will be performed until the necessary
/// length is achieved. A smaller number will result in more vec resizing, and a larger number
/// will result in unnecessary allocation.
///
/// **Default**: `128`
///
/// **Unit**: byte count
pub(crate) received_body_initial_len: usize,
/// Maximum size to pre-allocate based on content-length for buffering a complete request body
///
/// When we receive a fixed-length (not chunked-encoding) body that is smaller than this size,
/// we can allocate a buffer with exactly the right size before we receive the body. However,
/// if this is unbounded, malicious clients can issue headers with large content-length and
/// then keep the connection open without sending any bytes, allowing them to allocate
/// memory faster than their bandwidth usage. This does not limit the ability to receive
/// fixed-length bodies larger than this, but the memory allocation will grow as with
/// chunked bodies. Note that this has no impact on chunked bodies. If this is set higher
/// than the `received_body_max_len`, this parameter has no effect. This parameter only
/// impacts [`ReceivedBody::read_string`](crate::ReceivedBody::read_string) and
/// [`ReceivedBody::read_bytes`](crate::ReceivedBody::read_bytes).
///
/// **Default**: `1mb` in bytes
///
/// **Unit**: Byte count
pub(crate) received_body_max_preallocate: usize,
/// The maximum cumulative size of a header block the peer may send.
///
/// Advertised in `SETTINGS_MAX_FIELD_SECTION_SIZE` on HTTP/3 (RFC 9114 ยง7.2.4.1). Guards
/// against pathological header lists inflating memory per stream during HPACK/QPACK decode.
///
/// **Default**: `32 KiB`
///
/// **Unit**: byte count
pub(crate) max_header_list_size: u64,
/// whether [datagrams](https://www.rfc-editor.org/rfc/rfc9297.html) are enabled for HTTP/3
///
/// This is a protocol-level setting and is communicated to the peer as well as enforced.
///
/// **Default**: false
pub(crate) h3_datagrams_enabled: bool,
/// whether [webtransport](https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http3)
/// (`draft-ietf-webtrans-http3`) is enabled for HTTP/3
///
/// This is a protocol-level setting and is communicated to the peer. You do not need to
/// manually configure this if using
/// [`trillium-webtransport`](https://docs.rs/trillium-webtransport)
///
/// **Default**: false
pub(crate) webtransport_enabled: bool,
/// whether to panic when a response header with an invalid value (containing `\r`, `\n`, or
/// `\0`) is encountered.
///
/// Invalid header values are always skipped to prevent header injection. When this is `true`,
/// Trillium will additionally panic, surfacing the bug loudly. When `false`, the skip is only
/// logged (to the `log` backend) at error level.
///
/// **Default**: `true` when compiled with `debug_assertions` (i.e. debug builds), `false` in
/// release builds. Override to `true` in release if you want strict production behavior, or to
/// `false` in debug if you prefer not to panic during development.
pub(crate) panic_on_invalid_response_headers: bool,
}
impl HttpConfig {
/// Default Config
pub const DEFAULT: Self = HttpConfig {
response_buffer_len: 512,
response_buffer_max_len: 2 * 1024 * 1024,
request_buffer_initial_len: 128,
head_max_len: 8 * 1024,
max_headers: 128,
response_header_initial_capacity: 16,
copy_loops_per_yield: 16,
received_body_max_len: 10 * 1024 * 1024,
received_body_initial_len: 128,
received_body_max_preallocate: 1024 * 1024,
max_header_list_size: 8 * 1024,
h3_datagrams_enabled: false,
webtransport_enabled: false,
panic_on_invalid_response_headers: cfg!(debug_assertions),
};
}
impl Default for HttpConfig {
fn default() -> Self {
HttpConfig::DEFAULT
}
}