tokio_quiche/settings/quic.rs
1// Copyright (C) 2025, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10//
11// * Redistributions in binary form must reproduce the above copyright
12// notice, this list of conditions and the following disclaimer in the
13// documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use foundations::settings::settings;
28use serde_with::serde_as;
29use serde_with::DurationMilliSeconds;
30use std::time::Duration;
31
32/// QUIC configuration parameters.
33#[serde_as]
34#[settings]
35#[non_exhaustive]
36pub struct QuicSettings {
37 /// Configures the list of supported application protocols.
38 ///
39 /// Defaults to `[b"h3"]`.
40 #[serde(skip, default = "QuicSettings::default_alpn")]
41 pub alpn: Vec<Vec<u8>>,
42
43 /// Configures whether to enable DATAGRAM frame support. H3 connections
44 /// copy this setting from the underlying QUIC connection.
45 ///
46 /// Defaults to `true`.
47 #[serde(default = "QuicSettings::default_enable_dgram")]
48 pub enable_dgram: bool,
49
50 /// Max queue length for received DATAGRAM frames.
51 ///
52 /// Defaults to `2^16`.
53 #[serde(default = "QuicSettings::default_dgram_max_queue_len")]
54 pub dgram_recv_max_queue_len: usize,
55
56 /// Max queue length for sending DATAGRAM frames.
57 ///
58 /// Defaults to `2^16`.
59 #[serde(default = "QuicSettings::default_dgram_max_queue_len")]
60 pub dgram_send_max_queue_len: usize,
61
62 /// Configures whether to enable early data (0-RTT) support. Currently only
63 /// supported for servers.
64 ///
65 /// Defaults to `false`.
66 pub enable_early_data: bool,
67
68 /// Sets the `initial_max_data` transport parameter.
69 ///
70 /// Defaults to 10 MB.
71 #[serde(default = "QuicSettings::default_initial_max_data")]
72 pub initial_max_data: u64,
73
74 /// Sets the `initial_max_stream_data_bidi_local` transport parameter.
75 ///
76 /// Defaults to 1 MB.
77 #[serde(default = "QuicSettings::default_initial_max_stream_data")]
78 pub initial_max_stream_data_bidi_local: u64,
79
80 /// Sets the `initial_max_stream_data_bidi_remote` transport parameter.
81 ///
82 /// Defaults to 1 MB.
83 #[serde(default = "QuicSettings::default_initial_max_stream_data")]
84 pub initial_max_stream_data_bidi_remote: u64,
85
86 /// Sets the `initial_max_stream_data_uni` transport parameter.
87 ///
88 /// Defaults to 1 MB.
89 #[serde(default = "QuicSettings::default_initial_max_stream_data")]
90 pub initial_max_stream_data_uni: u64,
91
92 /// Sets the `initial_max_streams_bidi` transport parameter.
93 ///
94 /// Defaults to `100`.
95 #[serde(default = "QuicSettings::default_initial_max_streams")]
96 pub initial_max_streams_bidi: u64,
97
98 /// Sets the `initial_max_streams_uni` transport parameter.
99 ///
100 /// Defaults to `100`.
101 #[serde(default = "QuicSettings::default_initial_max_streams")]
102 pub initial_max_streams_uni: u64,
103
104 /// Configures the max idle timeout of the connection in milliseconds. The
105 /// real idle timeout is the minimum of this and the peer's
106 /// `max_idle_timeout`.
107 ///
108 /// Defaults to 56 seconds.
109 #[serde(
110 rename = "max_idle_timeout_ms",
111 default = "QuicSettings::default_max_idle_timeout"
112 )]
113 #[serde_as(as = "Option<DurationMilliSeconds>")]
114 pub max_idle_timeout: Option<Duration>,
115
116 /// Configures whether the local endpoint supports active connection
117 /// migration.
118 ///
119 /// Defaults to `true` (meaning disabled).
120 #[serde(default = "QuicSettings::default_disable_active_migration")]
121 pub disable_active_migration: bool,
122
123 /// Sets the `active_connection_id_limit` transport parameter.
124 ///
125 /// Defaults to 2. Note that values less than 2 will be ignored.
126 #[serde(default = "QuicSettings::default_active_connection_id_limit")]
127 pub active_connection_id_limit: u64,
128
129 /// Sets the maximum incoming UDP payload size.
130 ///
131 /// Defaults to 1350 bytes.
132 #[serde(default = "QuicSettings::default_max_recv_udp_payload_size")]
133 pub max_recv_udp_payload_size: usize,
134
135 /// Sets the maximum outgoing UDP payload size.
136 ///
137 /// Defaults to 1350 bytes.
138 #[serde(default = "QuicSettings::default_max_send_udp_payload_size")]
139 pub max_send_udp_payload_size: usize,
140
141 /// Whether to validate client IPs in QUIC initials.
142 ///
143 /// If set to `true`, any received QUIC initial will immediately spawn a
144 /// connection and start crypto operations for the handshake. Otherwise,
145 /// the client is asked to execute a stateless retry first (the default).
146 pub disable_client_ip_validation: bool,
147
148 /// Path to a file in which TLS secrets will be logged in
149 /// [SSLKEYLOGFILE format](https://tlswg.org/sslkeylogfile/draft-ietf-tls-keylogfile.html).
150 pub keylog_file: Option<String>,
151
152 /// Path to a directory where QLOG files will be saved.
153 pub qlog_dir: Option<String>,
154
155 /// Congestion control algorithm to use.
156 ///
157 /// For available values, see
158 /// [`CongestionControlAlgorithm`](quiche::CongestionControlAlgorithm).
159 ///
160 /// Defaults to `cubic`.
161 #[serde(default = "QuicSettings::default_cc_algorithm")]
162 pub cc_algorithm: String,
163
164 /// The default initial congestion window size in terms of packet count.
165 ///
166 /// Defaults to 10.
167 #[serde(default = "QuicSettings::default_initial_congestion_window_packets")]
168 pub initial_congestion_window_packets: usize,
169
170 /// Configures whether to enable relaxed loss detection on spurious loss.
171 ///
172 /// Defaults to `false`.
173 pub enable_relaxed_loss_threshold: bool,
174
175 /// Configures whether to do path MTU discovery.
176 ///
177 /// Defaults to `false`.
178 pub discover_path_mtu: bool,
179
180 /// Configures the maximum number of PMTUD probe attempts before treating
181 /// a size as failed.
182 ///
183 /// Defaults to 3 per [RFC 8899 Section 5.1.2](https://datatracker.ietf.org/doc/html/rfc8899#section-5.1.2).
184 /// If 0 is passed, the default value is used.
185 #[serde(default = "QuicSettings::default_pmtud_max_probes")]
186 pub pmtud_max_probes: u8,
187
188 /// Whether to use HyStart++ (only with `cubic` and `reno` CC).
189 ///
190 /// Defaults to `true`.
191 #[serde(default = "QuicSettings::default_enable_hystart")]
192 pub enable_hystart: bool,
193
194 /// Optionally enables pacing for outgoing packets.
195 ///
196 /// Note: this also requires pacing-compatible
197 /// [`SocketCapabilities`](crate::socket::SocketCapabilities).
198 pub enable_pacing: bool,
199
200 /// Sets the max value for pacing rate.
201 ///
202 /// By default, there is no limit.
203 pub max_pacing_rate: Option<u64>,
204
205 /// Optionally enables expensive versions of the
206 /// `accepted_initial_quic_packet_count`
207 /// and `rejected_initial_quic_packet_count` metrics.
208 ///
209 /// The expensive versions add a label for the peer IP subnet (`/24` for
210 /// IPv4, `/32` for IPv6). They thus generate many more time series if
211 /// peers are arbitrary eyeballs from the global Internet.
212 pub enable_expensive_packet_count_metrics: bool,
213
214 /// Forwards [`quiche`] logs into the logging system currently used by
215 /// [`foundations`].
216 ///
217 /// Defaults to `false`.
218 ///
219 /// # Warning
220 /// This should **only be used for local debugging**. `quiche` can emit lots
221 /// (and lots, and lots) of logs (the TRACE level emits a log record for
222 /// every packet and frame) and you can very easily overwhelm your
223 /// logging pipeline.
224 pub capture_quiche_logs: bool,
225
226 /// A timeout for the QUIC handshake, in milliseconds.
227 ///
228 /// Disabled by default.
229 #[serde(rename = "handshake_timeout_ms")]
230 #[serde_as(as = "Option<DurationMilliSeconds>")]
231 pub handshake_timeout: Option<Duration>,
232
233 /// The maximum number of newly-created connections that will be queued for
234 /// the application to receive. Not applicable to client-side usage.
235 ///
236 /// Defaults to 1024 connections.
237 #[serde(default = "QuicSettings::default_listen_backlog")]
238 pub listen_backlog: usize,
239
240 /// Whether or not to verify the peer's certificate.
241 ///
242 /// Defaults to `false`, meaning no peer verification is performed. Note
243 /// that clients should usually set this value to `true` - see
244 /// [`verify_peer()`] for more.
245 ///
246 /// [`verify_peer()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.verify_peer
247 pub verify_peer: bool,
248
249 /// The maximum size of the receiver connection flow control window.
250 ///
251 /// Defaults to 24MB.
252 #[serde(default = "QuicSettings::default_max_connection_window")]
253 pub max_connection_window: u64,
254
255 /// The maximum size of the receiveer stream flow control window.
256 ///
257 /// Defaults to 16MB.
258 #[serde(default = "QuicSettings::default_max_stream_window")]
259 pub max_stream_window: u64,
260
261 /// If true, send an advisory STREAMS_BLOCKED frame when the
262 /// application's local stream creation attempts fail due to the
263 /// peer advertised MAX_STREAMS limit.
264 ///
265 /// Defaults to false.
266 pub enable_send_streams_blocked: bool,
267
268 /// Configures whether to send GREASE values.
269 ///
270 /// Defaults to true.
271 #[serde(default = "QuicSettings::default_grease")]
272 pub grease: bool,
273
274 /// Sets the anti-amplification limit factor.
275 ///
276 /// Defaults to 3.
277 #[serde(default = "QuicSettings::default_amplification_factor")]
278 pub max_amplification_factor: usize,
279
280 /// Sets the send capacity factor.
281 ///
282 /// A factor greater than 1 allows the connection to buffer more outbound
283 /// data than can be sent at this moment. This can improve throughput by
284 /// reducing time spent waiting for new data.
285 ///
286 /// Defaults to 1.
287 #[serde(default = "QuicSettings::default_send_capacity_factor")]
288 pub send_capacity_factor: f64,
289
290 /// Sets the `ack_delay_exponent` transport parameter.
291 ///
292 /// Defaults to 3.
293 #[serde(default = "QuicSettings::default_ack_delay_exponent")]
294 pub ack_delay_exponent: u64,
295
296 /// Sets the `max_ack_delay` transport parameter.
297 ///
298 /// Defaults to 25.
299 #[serde(default = "QuicSettings::default_max_ack_delay")]
300 pub max_ack_delay: u64,
301
302 /// Configures the max number of queued received PATH_CHALLENGE frames.
303 ///
304 /// Defaults to 3.
305 #[serde(default = "QuicSettings::default_max_path_challenge_recv_queue_len")]
306 pub max_path_challenge_recv_queue_len: usize,
307
308 /// Sets the initial stateless reset token.
309 ///
310 /// Note that this applies only to server-side connections - on client-side
311 /// connections, this is a no-op.
312 ///
313 /// Defaults to `None`.
314 pub stateless_reset_token: Option<u128>,
315
316 /// Sets whether the QUIC connection should avoid reusing DCIDs over
317 /// different paths.
318 ///
319 /// Defaults to `false`. See [`set_disable_dcid_reuse()`] for more.
320 ///
321 /// [`set_disable_dcid_reuse()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.disable_dcid_reuse
322 pub disable_dcid_reuse: bool,
323
324 /// Specifies the number of bytes used to track unknown transport
325 /// parameters.
326 ///
327 /// Defaults to `None`, e.g., unknown transport parameters will not be
328 /// tracked. See [`enable_track_unknown_transport_parameters()`] for
329 /// more.
330 ///
331 /// [`enable_track_unknown_transport_parameters()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.enable_track_unknown_transport_parameters
332 pub track_unknown_transport_parameters: Option<usize>,
333}
334
335impl QuicSettings {
336 #[inline]
337 fn default_alpn() -> Vec<Vec<u8>> {
338 quiche::h3::APPLICATION_PROTOCOL
339 .iter()
340 .map(|v| v.to_vec())
341 .collect()
342 }
343
344 #[inline]
345 fn default_enable_dgram() -> bool {
346 true
347 }
348
349 #[inline]
350 fn default_dgram_max_queue_len() -> usize {
351 65536
352 }
353
354 #[inline]
355 fn default_initial_max_data() -> u64 {
356 10_000_000
357 }
358
359 #[inline]
360 fn default_initial_max_stream_data() -> u64 {
361 1_000_000
362 }
363
364 #[inline]
365 fn default_initial_max_streams() -> u64 {
366 100
367 }
368
369 #[inline]
370 fn default_max_idle_timeout() -> Option<Duration> {
371 Some(Duration::from_secs(56))
372 }
373
374 #[inline]
375 fn default_max_recv_udp_payload_size() -> usize {
376 1350
377 }
378
379 #[inline]
380 fn default_max_send_udp_payload_size() -> usize {
381 1350
382 }
383
384 #[inline]
385 fn default_disable_active_migration() -> bool {
386 true
387 }
388
389 #[inline]
390 fn default_cc_algorithm() -> String {
391 "cubic".to_string()
392 }
393
394 #[inline]
395 fn default_initial_congestion_window_packets() -> usize {
396 10
397 }
398
399 #[inline]
400 fn default_enable_hystart() -> bool {
401 true
402 }
403
404 #[inline]
405 fn default_listen_backlog() -> usize {
406 // Given a worst-case 1 minute handshake timeout and up to 4096 concurrent
407 // handshakes, we will dequeue at least 70 connections per second.
408 // This means this backlog size limits the queueing latency to
409 // ~15s.
410 1024
411 }
412
413 #[inline]
414 fn default_max_connection_window() -> u64 {
415 24 * 1024 * 1024
416 }
417
418 #[inline]
419 fn default_max_stream_window() -> u64 {
420 16 * 1024 * 1024
421 }
422
423 #[inline]
424 fn default_grease() -> bool {
425 true
426 }
427
428 #[inline]
429 fn default_amplification_factor() -> usize {
430 3
431 }
432
433 #[inline]
434 fn default_send_capacity_factor() -> f64 {
435 1.0
436 }
437
438 #[inline]
439 fn default_ack_delay_exponent() -> u64 {
440 3
441 }
442
443 #[inline]
444 fn default_max_ack_delay() -> u64 {
445 25
446 }
447
448 #[inline]
449 fn default_active_connection_id_limit() -> u64 {
450 2
451 }
452
453 #[inline]
454 fn default_max_path_challenge_recv_queue_len() -> usize {
455 3
456 }
457
458 #[inline]
459 fn default_pmtud_max_probes() -> u8 {
460 3
461 }
462}
463
464#[cfg(test)]
465mod test {
466 use super::QuicSettings;
467 use std::time::Duration;
468
469 #[test]
470 fn timeouts_parse_as_milliseconds() {
471 let quic = serde_json::from_str::<QuicSettings>(
472 r#"{ "handshake_timeout_ms": 5000, "max_idle_timeout_ms": 7000 }"#,
473 )
474 .unwrap();
475
476 assert_eq!(quic.handshake_timeout.unwrap(), Duration::from_secs(5));
477 assert_eq!(quic.max_idle_timeout.unwrap(), Duration::from_secs(7));
478 }
479}