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