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