wtransport/config.rs
1//!
2//! This module defines configurations for the WebTransport server and client.
3//!
4//! It provides builders for creating server and client configurations with various options.
5//!
6//! The module includes:
7//! - [`ServerConfig`]: Configuration for the WebTransport server.
8//! - [`ClientConfig`]: Configuration for the WebTransport client.
9//!
10//! Example for creating a server configuration:
11//!
12//! ```no_run
13//! # async fn run() -> anyhow::Result<()> {
14//! use wtransport::Identity;
15//! use wtransport::ServerConfig;
16//!
17//! let server_config = ServerConfig::builder()
18//! .with_bind_default(443)
19//! .with_identity(Identity::load_pemfiles("cert.pem", "key.pem").await?)
20//! .build();
21//!
22//! # Ok(())
23//! # }
24//! ```
25//!
26//! Example for creating a client configuration:
27//!
28//! ```no_run
29//! use wtransport::ClientConfig;
30//!
31//! let client_config = ClientConfig::builder()
32//! .with_bind_default()
33//! .with_native_certs()
34//! .build();
35//! ```
36
37use crate::tls::build_native_cert_store;
38use crate::tls::Identity;
39use socket2::Domain as SocketDomain;
40use socket2::Protocol as SocketProtocol;
41use socket2::Socket;
42use socket2::Type as SocketType;
43use std::fmt::Debug;
44use std::fmt::Display;
45use std::future::Future;
46use std::net::IpAddr;
47use std::net::Ipv4Addr;
48use std::net::Ipv6Addr;
49use std::net::SocketAddr;
50use std::net::SocketAddrV4;
51use std::net::SocketAddrV6;
52use std::net::UdpSocket;
53use std::pin::Pin;
54use std::sync::Arc;
55use std::time::Duration;
56
57/// Alias of [`crate::tls::rustls::ServerConfig`].
58pub type TlsServerConfig = rustls::ServerConfig;
59
60/// Alias of [`crate::tls::rustls::ClientConfig`].
61pub type TlsClientConfig = rustls::ClientConfig;
62
63/// Alias of [`crate::quinn::TransportConfig`].
64#[cfg(feature = "quinn")]
65#[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
66pub type QuicTransportConfig = quinn::TransportConfig;
67
68/// Alias of [`crate::quinn::ServerConfig`].
69#[cfg(feature = "quinn")]
70#[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
71pub type QuicServerConfig = quinn::ServerConfig;
72
73/// Alias of [`crate::quinn::ClientConfig`].
74#[cfg(feature = "quinn")]
75#[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
76pub type QuicClientConfig = quinn::ClientConfig;
77
78/// Configuration for IP address socket bind.
79#[derive(Debug, Copy, Clone)]
80pub enum IpBindConfig {
81 /// Bind to LOCALHOST IPv4 address (no IPv6).
82 LocalV4,
83
84 /// Bind to LOCALHOST IPv6 address (no IPv4).
85 LocalV6,
86
87 /// Bind to LOCALHOST both IPv4 and IPv6 address (dual stack, if supported).
88 LocalDual,
89
90 /// Bind to `INADDR_ANY` IPv4 address (no IPv6).
91 InAddrAnyV4,
92
93 /// Bind to `INADDR_ANY` IPv6 address (no IPv4).
94 InAddrAnyV6,
95
96 /// Bind to `INADDR_ANY` both IPv4 and IPv6 address (dual stack, if supported).
97 InAddrAnyDual,
98}
99
100impl IpBindConfig {
101 fn into_ip(self) -> IpAddr {
102 match self {
103 IpBindConfig::LocalV4 => Ipv4Addr::LOCALHOST.into(),
104 IpBindConfig::LocalV6 => Ipv6Addr::LOCALHOST.into(),
105 IpBindConfig::LocalDual => Ipv6Addr::LOCALHOST.into(),
106 IpBindConfig::InAddrAnyV4 => Ipv4Addr::UNSPECIFIED.into(),
107 IpBindConfig::InAddrAnyV6 => Ipv6Addr::UNSPECIFIED.into(),
108 IpBindConfig::InAddrAnyDual => Ipv6Addr::UNSPECIFIED.into(),
109 }
110 }
111
112 fn into_dual_stack_config(self) -> Ipv6DualStackConfig {
113 match self {
114 IpBindConfig::LocalV4 | IpBindConfig::InAddrAnyV4 => Ipv6DualStackConfig::OsDefault,
115 IpBindConfig::LocalV6 | IpBindConfig::InAddrAnyV6 => Ipv6DualStackConfig::Deny,
116 IpBindConfig::LocalDual | IpBindConfig::InAddrAnyDual => Ipv6DualStackConfig::Allow,
117 }
118 }
119}
120
121/// Configuration for IPv6 dual stack.
122#[derive(Debug, Copy, Clone)]
123pub enum Ipv6DualStackConfig {
124 /// Do not configure dual stack. Use OS's default.
125 OsDefault,
126
127 /// Deny dual stack. This is equivalent to `IPV6_V6ONLY`.
128 ///
129 /// Socket will only bind for IPv6 (IPv4 port will still be available).
130 Deny,
131
132 /// Allow dual stack.
133 ///
134 /// Please note that not all configurations/platforms support dual stack.
135 Allow,
136}
137
138/// Invalid idle timeout.
139pub struct InvalidIdleTimeout;
140
141/// Server configuration.
142///
143/// You can create an instance of `ServerConfig` using its builder pattern by calling
144/// the [`builder()`](Self::builder) method.
145/// Once you have an instance, you can further customize it by chaining method calls
146/// to set various configuration options.
147///
148/// ## Configuration Builder States
149///
150/// The configuration process follows a *state-based builder pattern*, where the server
151/// configuration progresses through *3* states.
152///
153/// ### 1. `WantsBindAddress`
154///
155/// The caller must supply a binding address for the server. This is where to specify
156/// the listening port of the server.
157/// The following options are mutually exclusive:
158///
159/// - [`with_bind_default`](ServerConfigBuilder::with_bind_default): the simplest
160/// configuration where only the port will be specified.
161/// - [`with_bind_config`](ServerConfigBuilder::with_bind_config): configures
162/// binding to an address determined by a configuration preset.
163/// - [`with_bind_address`](ServerConfigBuilder::with_bind_address): configures
164/// binding to a custom-specified socket address.
165/// - [`with_bind_address_v6`](ServerConfigBuilder::with_bind_address_v6): configures
166/// binding to a custom-specified socket address for *IPv6*, along with the [dual stack
167/// configuration](Ipv6DualStackConfig).
168/// - [`with_bind_socket`](ServerConfigBuilder::with_bind_socket): configures
169/// binding directly to a custom-specified socket.
170///
171/// Only one of these options can be selected during the client configuration process.
172///
173/// #### Examples:
174///
175/// ```
176/// use wtransport::ServerConfig;
177///
178/// // Configuration for accepting incoming connection on port 443
179/// ServerConfig::builder().with_bind_default(443);
180/// ```
181///
182/// ### 2. `WantsIdentity`
183///
184/// The caller must supply a TLS identity for the server.
185///
186/// - [`with_identity`](ServerConfigBuilder::with_identity): configures
187/// a TLS [`Identity`] for the server.
188/// - [`with_custom_tls`](ServerConfigBuilder::with_custom_tls): sets the TLS
189/// server configuration manually.
190/// - [`with_custom_transport`](ServerConfigBuilder::with_custom_transport): sets the QUIC
191/// transport configuration manually (using default TLS).
192/// - [`with_custom_tls_and_transport`](ServerConfigBuilder::with_custom_tls_and_transport): sets both
193/// a custom TLS and QUIC transport configuration.
194/// - [`build_with_quic_config`](ServerConfigBuilder::build_with_quic_config): directly builds
195/// [`ServerConfig`] providing both TLS and QUIC transport configuration given by
196/// [`quic_config`](QuicServerConfig).
197///
198/// #### Examples:
199/// ```
200/// # use anyhow::Result;
201/// use wtransport::Identity;
202/// use wtransport::ServerConfig;
203///
204/// # async fn run() -> Result<()> {
205/// ServerConfig::builder()
206/// .with_bind_default(443)
207/// .with_identity(Identity::load_pemfiles("cert.pem", "key.pem").await?);
208/// # Ok(())
209/// # }
210/// ```
211///
212/// ### 3. `WantsTransportConfigServer`
213///
214/// The caller can supply *additional* transport configurations.
215/// Multiple options can be given at this stage. Once the configuration is completed, it is possible
216/// to finalize with the method [`build()`](ServerConfigBuilder::build).
217///
218/// All these options can be omitted in the configuration; default values will be used.
219///
220/// - [`max_idle_timeout`](ServerConfigBuilder::max_idle_timeout)
221/// - [`keep_alive_interval`](ServerConfigBuilder::keep_alive_interval)
222/// - [`allow_migration`](ServerConfigBuilder::allow_migration)
223///
224/// #### Examples:
225/// ```
226/// # use anyhow::Result;
227/// use wtransport::ServerConfig;
228/// use wtransport::Identity;
229/// use std::time::Duration;
230///
231/// # async fn run() -> Result<()> {
232/// let server_config = ServerConfig::builder()
233/// .with_bind_default(443)
234/// .with_identity(Identity::load_pemfiles("cert.pem", "key.pem").await?)
235/// .keep_alive_interval(Some(Duration::from_secs(3)))
236/// .build();
237/// # Ok(())
238/// # }
239#[derive(Debug)]
240pub struct ServerConfig {
241 pub(crate) bind_address_config: BindAddressConfig,
242 pub(crate) endpoint_config: quinn::EndpointConfig,
243 pub(crate) quic_config: quinn::ServerConfig,
244}
245
246impl ServerConfig {
247 /// Creates a builder to build up the server configuration.
248 ///
249 /// For more information, see the [`ServerConfigBuilder`] documentation.
250 pub fn builder() -> ServerConfigBuilder<states::WantsBindAddress> {
251 ServerConfigBuilder::default()
252 }
253
254 /// Returns a reference to the inner QUIC endpoint configuration.
255 #[cfg(feature = "quinn")]
256 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
257 pub fn quic_endpoint_config(&self) -> &quinn::EndpointConfig {
258 &self.endpoint_config
259 }
260
261 /// Returns a mutable reference to the inner QUIC endpoint configuration.
262 #[cfg(feature = "quinn")]
263 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
264 pub fn quic_endpoint_config_mut(&mut self) -> &mut quinn::EndpointConfig {
265 &mut self.endpoint_config
266 }
267
268 /// Returns a reference to the inner QUIC configuration.
269 #[cfg(feature = "quinn")]
270 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
271 pub fn quic_config(&self) -> &quinn::ServerConfig {
272 &self.quic_config
273 }
274
275 /// Returns a mutable reference to the inner QUIC configuration.
276 #[cfg(feature = "quinn")]
277 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
278 pub fn quic_config_mut(&mut self) -> &mut quinn::ServerConfig {
279 &mut self.quic_config
280 }
281}
282
283/// Server builder configuration.
284///
285/// The builder might have different state at compile time.
286///
287/// # Examples:
288/// ```no_run
289/// # async fn run() -> anyhow::Result<()> {
290/// # use std::net::Ipv4Addr;
291/// # use std::net::SocketAddr;
292/// # use wtransport::Identity;
293/// # use wtransport::ServerConfig;
294/// let config = ServerConfig::builder()
295/// .with_bind_default(4433)
296/// .with_identity(Identity::load_pemfiles("cert.pem", "key.pem").await?);
297/// # Ok(())
298/// # }
299/// ```
300#[must_use]
301pub struct ServerConfigBuilder<State>(State);
302
303impl ServerConfigBuilder<states::WantsBindAddress> {
304 /// Configures for accepting incoming connections binding ANY IP (allowing IP dual-stack).
305 ///
306 /// `listening_port` is the port where the server will accept incoming connections.
307 ///
308 /// This is equivalent to: [`Self::with_bind_config`] with [`IpBindConfig::InAddrAnyDual`].
309 pub fn with_bind_default(
310 self,
311 listening_port: u16,
312 ) -> ServerConfigBuilder<states::WantsIdentity> {
313 self.with_bind_config(IpBindConfig::InAddrAnyDual, listening_port)
314 }
315
316 /// Sets the binding (local) socket address with a specific [`IpBindConfig`].
317 ///
318 /// `listening_port` is the port where the server will accept incoming connections.
319 pub fn with_bind_config(
320 self,
321 ip_bind_config: IpBindConfig,
322 listening_port: u16,
323 ) -> ServerConfigBuilder<states::WantsIdentity> {
324 let ip_address: IpAddr = ip_bind_config.into_ip();
325
326 match ip_address {
327 IpAddr::V4(ip) => self.with_bind_address(SocketAddr::new(ip.into(), listening_port)),
328 IpAddr::V6(ip) => self.with_bind_address_v6(
329 SocketAddrV6::new(ip, listening_port, 0, 0),
330 ip_bind_config.into_dual_stack_config(),
331 ),
332 }
333 }
334
335 /// Sets the binding (local) socket address for the endpoint.
336 pub fn with_bind_address(
337 self,
338 address: SocketAddr,
339 ) -> ServerConfigBuilder<states::WantsIdentity> {
340 ServerConfigBuilder(states::WantsIdentity {
341 bind_address_config: BindAddressConfig::from(address),
342 })
343 }
344
345 /// Sets the binding (local) socket address for the endpoint with Ipv6 address.
346 ///
347 /// `dual_stack_config` allows/denies dual stack port binding.
348 pub fn with_bind_address_v6(
349 self,
350 address: SocketAddrV6,
351 dual_stack_config: Ipv6DualStackConfig,
352 ) -> ServerConfigBuilder<states::WantsIdentity> {
353 ServerConfigBuilder(states::WantsIdentity {
354 bind_address_config: BindAddressConfig::AddressV6(address, dual_stack_config),
355 })
356 }
357
358 /// Configures the server to bind to a pre-existing [`UdpSocket`].
359 ///
360 /// This allows the server to use an already created socket, which may be beneficial
361 /// for scenarios where socket reuse or specific socket configuration is needed.
362 pub fn with_bind_socket(self, socket: UdpSocket) -> ServerConfigBuilder<states::WantsIdentity> {
363 ServerConfigBuilder(states::WantsIdentity {
364 bind_address_config: BindAddressConfig::Socket(socket),
365 })
366 }
367}
368
369impl ServerConfigBuilder<states::WantsIdentity> {
370 /// Configures TLS with safe defaults and a TLS [`Identity`].
371 ///
372 /// # Example
373 /// ```no_run
374 /// use wtransport::Identity;
375 /// use wtransport::ServerConfig;
376 /// # use anyhow::Result;
377 ///
378 /// # async fn run() -> Result<()> {
379 /// let identity = Identity::load_pemfiles("cert.pem", "key.pem").await?;
380 ///
381 /// let server_config = ServerConfig::builder()
382 /// .with_bind_default(4433)
383 /// .with_identity(identity)
384 /// .build();
385 /// # Ok(())
386 /// # }
387 /// ```
388 pub fn with_identity(
389 self,
390 identity: Identity,
391 ) -> ServerConfigBuilder<states::WantsTransportConfigServer> {
392 use crate::tls::server::build_default_tls_config;
393
394 let tls_config = build_default_tls_config(identity);
395 let quic_endpoint_config = quinn::EndpointConfig::default();
396 let quic_transport_config = quinn::TransportConfig::default();
397
398 self.with(tls_config, quic_endpoint_config, quic_transport_config)
399 }
400
401 /// Allows for manual configuration of a custom TLS setup using a provided
402 /// [`rustls::ServerConfig`], which must support
403 /// [`rustls::CipherSuite::TLS13_AES_128_GCM_SHA256`]. A suitable configuration
404 /// can be obtained using the `ring` crypto provider with a set of versions containing
405 /// [`rustls::version::TLS13`].
406 ///
407 /// This method is provided for advanced users who need fine-grained control over the
408 /// TLS configuration. It allows you to pass a preconfigured [`rustls::ServerConfig`]
409 /// instance to customize the TLS settings according to your specific requirements.
410 ///
411 /// Generally, it is recommended to use the [`with_identity`](Self::with_identity) method
412 /// to configure TLS with safe defaults and an TLS [`Identity`].
413 ///
414 /// # Example
415 ///
416 /// ```no_run
417 /// use wtransport::tls::rustls;
418 /// use wtransport::ServerConfig;
419 ///
420 /// // Create a custom rustls::ServerConfig with specific TLS settings
421 /// let custom_tls_config = rustls::ServerConfig::builder();
422 /// // Customize TLS settings here...
423 /// # let custom_tls_config = custom_tls_config
424 /// # .with_no_client_auth()
425 /// # .with_single_cert(todo!(), todo!()).unwrap();
426 ///
427 /// // Create a ServerConfigBuilder with the custom TLS configuration
428 /// let server_config = ServerConfig::builder()
429 /// .with_bind_default(4433)
430 /// .with_custom_tls(custom_tls_config)
431 /// .build();
432 /// ```
433 pub fn with_custom_tls(
434 self,
435 tls_config: TlsServerConfig,
436 ) -> ServerConfigBuilder<states::WantsTransportConfigServer> {
437 let quic_endpoint_config = quinn::EndpointConfig::default();
438 let quic_transport_config = quinn::TransportConfig::default();
439
440 self.with(tls_config, quic_endpoint_config, quic_transport_config)
441 }
442
443 /// Configures the server with a custom QUIC transport configuration and a default TLS setup
444 /// using the provided [`Identity`].
445 ///
446 /// This method is useful for scenarios where you need to customize the transport settings
447 /// while relying on a default TLS configuration built from an [`Identity`]. It gives you
448 /// control over the transport layer while maintaining safe and standard TLS settings.
449 ///
450 /// **See**: [`with_identity`](Self::with_identity)
451 /// for a simpler configuration option that does not require custom transport settings.
452 ///
453 /// # Parameters
454 ///
455 /// - `identity`: A reference to an [`Identity`] that contains the server's certificate and
456 /// private key. This will be used to generate the default TLS configuration.
457 /// - `quic_transport_config`: A custom [`QuicTransportConfig`] instance that allows you to specify
458 /// various QUIC transport-layer settings according to your requirements.
459 ///
460 /// # Example
461 ///
462 /// ```
463 /// use wtransport::config::QuicTransportConfig;
464 /// use wtransport::Identity;
465 /// use wtransport::ServerConfig;
466 ///
467 /// // Generate a server identity (self signed certificate and private key)
468 /// let identity = Identity::self_signed(["localhost", "127.0.0.1", "::1"]).unwrap();
469 ///
470 /// // Create a custom QuicTransportConfig with specific settings
471 /// let mut custom_transport_config = QuicTransportConfig::default();
472 /// custom_transport_config.datagram_send_buffer_size(1024);
473 ///
474 /// // Create a ServerConfigBuilder with the custom transport configuration and default TLS settings
475 /// let server_config = ServerConfig::builder()
476 /// .with_bind_default(4433)
477 /// .with_custom_transport(identity, custom_transport_config)
478 /// .build();
479 /// ```
480 #[cfg(feature = "quinn")]
481 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
482 pub fn with_custom_transport(
483 self,
484 identity: Identity,
485 quic_transport_config: QuicTransportConfig,
486 ) -> ServerConfigBuilder<states::WantsTransportConfigServer> {
487 use crate::tls::server::build_default_tls_config;
488
489 let tls_config = build_default_tls_config(identity);
490 let quic_endpoint_config = quinn::EndpointConfig::default();
491
492 self.with(tls_config, quic_endpoint_config, quic_transport_config)
493 }
494
495 /// Configures the server with both a custom TLS configuration and a custom QUIC transport
496 /// configuration.
497 ///
498 /// This method is designed for advanced users who require full control over both the TLS
499 /// and transport settings. It allows you to pass a preconfigured [`TlsServerConfig`] and
500 /// a custom [`QuicTransportConfig`] to fine-tune both layers of the server configuration.
501 ///
502 /// # Parameters
503 ///
504 /// - `tls_config`: A custom [`TlsServerConfig`] instance that allows you to specify
505 /// detailed TLS settings, such as ciphersuites, certificate verification, and more. It must
506 /// support TLS 1.3 (see the documentation of [`Self::with_custom_tls`]).
507 /// - `quic_transport_config`: A custom [`QuicTransportConfig`] instance that allows you to specify
508 /// various QUIC transport-layer settings according to your requirements.
509 #[cfg(feature = "quinn")]
510 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
511 pub fn with_custom_tls_and_transport(
512 self,
513 tls_config: TlsServerConfig,
514 quic_transport_config: QuicTransportConfig,
515 ) -> ServerConfigBuilder<states::WantsTransportConfigServer> {
516 let quic_endpoint_config = quinn::EndpointConfig::default();
517 self.with(tls_config, quic_endpoint_config, quic_transport_config)
518 }
519
520 /// Directly builds [`ServerConfig`] skipping TLS and transport configuration.
521 ///
522 /// Both TLS and transport configuration is given by [`quic_config`](QuicServerConfig).
523 #[cfg(feature = "quinn")]
524 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
525 pub fn build_with_quic_config(self, quic_config: QuicServerConfig) -> ServerConfig {
526 ServerConfig {
527 bind_address_config: self.0.bind_address_config,
528 endpoint_config: quinn::EndpointConfig::default(),
529 quic_config,
530 }
531 }
532
533 fn with(
534 self,
535 tls_config: TlsServerConfig,
536 endpoint_config: quinn::EndpointConfig,
537 transport_config: quinn::TransportConfig,
538 ) -> ServerConfigBuilder<states::WantsTransportConfigServer> {
539 ServerConfigBuilder(states::WantsTransportConfigServer {
540 bind_address_config: self.0.bind_address_config,
541 tls_config,
542 endpoint_config,
543 transport_config,
544 migration: true,
545 })
546 }
547}
548
549impl ServerConfigBuilder<states::WantsTransportConfigServer> {
550 /// Completes configuration process.
551 ///
552 /// # Panics
553 ///
554 /// See the documentation of [`Self::with_custom_tls`] for the TLS 1.3 requirement.
555 #[must_use]
556 pub fn build(self) -> ServerConfig {
557 let crypto: Arc<quinn::crypto::rustls::QuicServerConfig> = Arc::new(
558 quinn::crypto::rustls::QuicServerConfig::try_from(self.0.tls_config)
559 .expect("CipherSuite::TLS13_AES_128_GCM_SHA256 missing"),
560 );
561
562 let mut quic_config = quinn::ServerConfig::with_crypto(crypto);
563
564 quic_config.transport_config(Arc::new(self.0.transport_config));
565 quic_config.migration(self.0.migration);
566
567 ServerConfig {
568 bind_address_config: self.0.bind_address_config,
569 endpoint_config: self.0.endpoint_config,
570 quic_config,
571 }
572 }
573
574 /// Maximum duration of inactivity to accept before timing out the connection.
575 ///
576 /// The true idle timeout is the minimum of this and the peer's own max idle timeout. `None`
577 /// represents an infinite timeout.
578 ///
579 /// **WARNING**: If a peer or its network path malfunctions or acts maliciously, an infinite
580 /// idle timeout can result in permanently hung futures!
581 pub fn max_idle_timeout(
582 mut self,
583 idle_timeout: Option<Duration>,
584 ) -> Result<Self, InvalidIdleTimeout> {
585 let idle_timeout = idle_timeout
586 .map(quinn::IdleTimeout::try_from)
587 .transpose()
588 .map_err(|_| InvalidIdleTimeout)?;
589
590 self.0.transport_config.max_idle_timeout(idle_timeout);
591
592 Ok(self)
593 }
594
595 /// Period of inactivity before sending a keep-alive packet
596 ///
597 /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.
598 ///
599 /// `None` to disable, which is the default. Only one side of any given connection needs keep-alive
600 /// enabled for the connection to be preserved. Must be set lower than the
601 /// [`max_idle_timeout`](Self::max_idle_timeout) of both peers to be effective.
602 pub fn keep_alive_interval(mut self, interval: Option<Duration>) -> Self {
603 self.0.transport_config.keep_alive_interval(interval);
604 self
605 }
606
607 /// Whether to allow clients to migrate to new addresses.
608 ///
609 /// Improves behavior for clients that move between different internet connections or suffer NAT
610 /// rebinding. Enabled by default.
611 pub fn allow_migration(mut self, value: bool) -> Self {
612 self.0.migration = value;
613 self
614 }
615}
616
617/// Client configuration.
618///
619///
620/// You can create an instance of `ClientConfig` using its builder pattern by calling
621/// the [`builder()`](Self::builder) method.
622/// Once you have an instance, you can further customize it by chaining method calls
623/// to set various configuration options.
624///
625/// ## Configuration Builder States
626///
627/// The configuration process follows a *state-based builder pattern*, where the client
628/// configuration progresses through *3* states.
629///
630/// ### 1. `WantsBindAddress`
631///
632/// The caller must supply a binding address for the client.
633/// The following options are mutually exclusive:
634///
635/// - [`with_bind_default`](ClientConfigBuilder::with_bind_default): configures to use
636/// the default bind address. This is generally the *default* choice for a client.
637/// - [`with_bind_config`](ClientConfigBuilder::with_bind_config): configures
638/// binding to an address determined by a configuration preset.
639/// - [`with_bind_address`](ClientConfigBuilder::with_bind_address): configures
640/// binding to a custom-specified socket address.
641/// - [`with_bind_address_v6`](ClientConfigBuilder::with_bind_address_v6): configures
642/// binding to a custom-specified socket address for *IPv6*, along with the [dual stack
643/// configuration](Ipv6DualStackConfig).
644/// - [`with_bind_socket`](ClientConfigBuilder::with_bind_socket): configures
645/// binding directly to a custom-specified socket.
646///
647/// Only one of these options can be selected during the client configuration process.
648///
649/// #### Examples:
650///
651/// ```
652/// use wtransport::ClientConfig;
653///
654/// ClientConfig::builder().with_bind_default();
655/// ```
656///
657/// ### 2. `WantsRootStore`
658///
659/// The caller must supply a TLS root store configuration for server certificate validation.
660/// The following options are mutually exclusive:
661///
662/// - [`with_native_certs`](ClientConfigBuilder::with_native_certs): configures to use
663/// root certificates found in the platform's native certificate store. This is the *default*
664/// configuration as it uses root store installed on the current machine.
665/// - [`with_server_certificate_hashes`][cert_hashes]: configures the client to accept
666/// *some* certificates mapped to hashes. This can be used to connect to self signed
667/// certificates securely, where the hash of the certificate is shared in advance
668/// through some other mechanism (such as an invite link).
669/// - (**insecure**) [`with_no_cert_validation`](ClientConfigBuilder::with_no_cert_validation):
670/// configure to skip server certificate validation. This might be handy for testing purpose
671/// to accept *self-signed* certificate, but you should almost always prefer
672/// [`with_server_certificate_hashes`][cert_hashes] for that use case.
673/// - [`with_custom_tls`](ClientConfigBuilder::with_custom_tls): sets the TLS client
674/// configuration manually.
675/// - [`with_custom_transport`](ClientConfigBuilder::with_custom_transport): sets the QUIC
676/// transport configuration manually (using default TLS).
677/// - [`with_custom_tls_and_transport`](ClientConfigBuilder::with_custom_tls_and_transport): sets both
678/// a custom TLS and QUIC transport configuration.
679/// - [`build_with_quic_config`](ClientConfigBuilder::build_with_quic_config): directly builds
680/// [`ClientConfig`] providing both TLS and QUIC transport configuration given by
681/// [`quic_config`](QuicClientConfig).
682///
683/// Only one of these options can be selected during the client configuration process.
684///
685/// [cert_hashes]: ClientConfigBuilder::with_server_certificate_hashes
686///
687/// #### Examples:
688/// ```
689/// use wtransport::ClientConfig;
690///
691/// ClientConfig::builder()
692/// .with_bind_default()
693/// .with_native_certs();
694/// ```
695///
696/// ### 3. `WantsTransportConfigClient`
697///
698/// The caller can supply *additional* transport configurations.
699/// Multiple options can be given at this stage. Once the configuration is completed, it is possible
700/// to finalize with the method [`build()`](ClientConfigBuilder::build).
701///
702/// All these options can be omitted in the configuration; default values will be used.
703///
704/// - [`max_idle_timeout`](ClientConfigBuilder::max_idle_timeout)
705/// - [`keep_alive_interval`](ClientConfigBuilder::keep_alive_interval)
706/// - [`dns_resolver`](ClientConfigBuilder::dns_resolver)
707///
708/// #### Examples:
709/// ```
710/// use std::time::Duration;
711/// use wtransport::ClientConfig;
712///
713/// let client_config = ClientConfig::builder()
714/// .with_bind_default()
715/// .with_native_certs()
716/// .max_idle_timeout(Some(Duration::from_secs(30)))
717/// .unwrap()
718/// .keep_alive_interval(Some(Duration::from_secs(3)))
719/// .build();
720/// ```
721#[derive(Debug)]
722pub struct ClientConfig {
723 pub(crate) bind_address_config: BindAddressConfig,
724 pub(crate) endpoint_config: quinn::EndpointConfig,
725 pub(crate) quic_config: quinn::ClientConfig,
726 pub(crate) dns_resolver: Arc<dyn DnsResolver + Send + Sync>,
727}
728
729impl ClientConfig {
730 /// Creates a builder to build up the client configuration.
731 ///
732 /// For more information, see the [`ClientConfigBuilder`] documentation.
733 pub fn builder() -> ClientConfigBuilder<states::WantsBindAddress> {
734 ClientConfigBuilder::default()
735 }
736
737 /// Allows setting a custom [`DnsResolver`] for this configuration.
738 ///
739 /// Default resolver is [`TokioDnsResolver`].
740 pub fn set_dns_resolver<R>(&mut self, dns_resolver: R)
741 where
742 R: DnsResolver + Send + Sync + 'static,
743 {
744 self.dns_resolver = Arc::new(dns_resolver);
745 }
746
747 /// Returns a reference to the inner QUIC endpoint configuration.
748 #[cfg(feature = "quinn")]
749 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
750 pub fn quic_endpoint_config(&self) -> &quinn::EndpointConfig {
751 &self.endpoint_config
752 }
753
754 /// Returns a mutable reference to the inner QUIC endpoint configuration.
755 #[cfg(feature = "quinn")]
756 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
757 pub fn quic_endpoint_config_mut(&mut self) -> &mut quinn::EndpointConfig {
758 &mut self.endpoint_config
759 }
760
761 /// Returns a reference to the inner QUIC configuration.
762 #[cfg(feature = "quinn")]
763 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
764 pub fn quic_config(&self) -> &quinn::ClientConfig {
765 &self.quic_config
766 }
767
768 /// Returns a mutable reference to the inner QUIC configuration.
769 #[cfg(feature = "quinn")]
770 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
771 pub fn quic_config_mut(&mut self) -> &mut quinn::ClientConfig {
772 &mut self.quic_config
773 }
774}
775
776impl Default for ClientConfig {
777 fn default() -> Self {
778 ClientConfig::builder()
779 .with_bind_default()
780 .with_native_certs()
781 .build()
782 }
783}
784
785/// Client builder configuration.
786///
787/// The builder might have different state at compile time.
788///
789/// # Example
790/// ```no_run
791/// # use std::net::Ipv4Addr;
792/// # use std::net::SocketAddr;
793/// # use wtransport::ClientConfig;
794/// let config = ClientConfig::builder().with_bind_default();
795/// ```
796#[must_use]
797pub struct ClientConfigBuilder<State>(State);
798
799impl ClientConfigBuilder<states::WantsBindAddress> {
800 /// Configures for connecting binding ANY IP (allowing IP dual-stack).
801 ///
802 /// Bind port will be randomly picked.
803 ///
804 /// This is equivalent to: [`Self::with_bind_config`] with [`IpBindConfig::InAddrAnyDual`].
805 pub fn with_bind_default(self) -> ClientConfigBuilder<states::WantsRootStore> {
806 self.with_bind_config(IpBindConfig::InAddrAnyDual)
807 }
808
809 /// Sets the binding (local) socket address with a specific [`IpBindConfig`].
810 ///
811 /// Bind port will be randomly picked.
812 pub fn with_bind_config(
813 self,
814 ip_bind_config: IpBindConfig,
815 ) -> ClientConfigBuilder<states::WantsRootStore> {
816 let ip_address: IpAddr = ip_bind_config.into_ip();
817
818 match ip_address {
819 IpAddr::V4(ip) => self.with_bind_address(SocketAddr::new(ip.into(), 0)),
820 IpAddr::V6(ip) => self.with_bind_address_v6(
821 SocketAddrV6::new(ip, 0, 0, 0),
822 ip_bind_config.into_dual_stack_config(),
823 ),
824 }
825 }
826
827 /// Sets the binding (local) socket address for the endpoint.
828 pub fn with_bind_address(
829 self,
830 address: SocketAddr,
831 ) -> ClientConfigBuilder<states::WantsRootStore> {
832 ClientConfigBuilder(states::WantsRootStore {
833 bind_address_config: BindAddressConfig::from(address),
834 })
835 }
836
837 /// Sets the binding (local) socket address for the endpoint.
838 ///
839 /// `dual_stack_config` allows/denies dual stack port binding.
840 pub fn with_bind_address_v6(
841 self,
842 address: SocketAddrV6,
843 dual_stack_config: Ipv6DualStackConfig,
844 ) -> ClientConfigBuilder<states::WantsRootStore> {
845 ClientConfigBuilder(states::WantsRootStore {
846 bind_address_config: BindAddressConfig::AddressV6(address, dual_stack_config),
847 })
848 }
849
850 /// Configures the client to bind to a pre-existing [`UdpSocket`].
851 ///
852 /// This allows the client to use an already created socket, which can be useful in cases
853 /// where socket reuse or specific socket configurations are necessary.
854 pub fn with_bind_socket(
855 self,
856 socket: UdpSocket,
857 ) -> ClientConfigBuilder<states::WantsRootStore> {
858 ClientConfigBuilder(states::WantsRootStore {
859 bind_address_config: BindAddressConfig::Socket(socket),
860 })
861 }
862}
863
864impl ClientConfigBuilder<states::WantsRootStore> {
865 /// Configures the client to use native (local) root certificates for server validation.
866 ///
867 /// This method loads trusted root certificates from the system's certificate store,
868 /// ensuring that your client can trust certificates signed by well-known authorities.
869 ///
870 /// It configures safe default TLS configuration.
871 pub fn with_native_certs(self) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
872 use crate::tls::client::build_default_tls_config;
873
874 let tls_config = build_default_tls_config(Arc::new(build_native_cert_store()), None);
875 let endpoint_config = quinn::EndpointConfig::default();
876 let transport_config = quinn::TransportConfig::default();
877
878 self.with(tls_config, endpoint_config, transport_config)
879 }
880
881 /// Configures the client to skip server certificate validation, potentially
882 /// compromising security.
883 ///
884 /// This method is intended for advanced users and should be used with caution. It
885 /// configures the client to bypass server certificate validation during the TLS
886 /// handshake, effectively trusting any server certificate presented, even if it is
887 /// not signed by a trusted certificate authority (CA). Using this method can expose
888 /// your application to security risks.
889 ///
890 /// # Safety Note
891 ///
892 /// Using [`with_no_cert_validation`] should only be considered when you have a
893 /// specific need to disable certificate validation. In most cases, it is strongly
894 /// recommended to validate server certificates using trusted root certificates
895 /// (e.g., [`with_native_certs`]) to ensure secure communication.
896 ///
897 /// However, this method can be useful in testing environments or situations where
898 /// you intentionally want to skip certificate validation for specific use cases.
899 ///
900 /// [`with_native_certs`]: #method.with_native_certs
901 /// [`with_no_cert_validation`]: #method.with_no_cert_validation
902 #[cfg(feature = "dangerous-configuration")]
903 #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-configuration")))]
904 pub fn with_no_cert_validation(
905 self,
906 ) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
907 use crate::tls::client::build_default_tls_config;
908 use crate::tls::client::NoServerVerification;
909 use rustls::RootCertStore;
910
911 let tls_config = build_default_tls_config(
912 Arc::new(RootCertStore::empty()),
913 Some(Arc::new(NoServerVerification::new())),
914 );
915
916 let endpoint_config = quinn::EndpointConfig::default();
917 let transport_config = quinn::TransportConfig::default();
918
919 self.with(tls_config, endpoint_config, transport_config)
920 }
921
922 /// Configures the client to skip *some* server certificates validation.
923 ///
924 /// This method configures the client to accept server certificates
925 /// whose digests match the specified *SHA-256* hashes and fulfill
926 /// some additional constraints (*see notes below*).
927 ///
928 /// This is useful for scenarios where clients need to accept known
929 /// self-signed certificates or certificates from non-standard authorities.
930 ///
931 /// This method configuration is similar to the
932 /// [browser W3C WebTransport API](https://www.w3.org/TR/webtransport/#dom-webtransportoptions-servercertificatehashes).
933 ///
934 /// # Notes
935 ///
936 /// - The current time MUST be within the validity period of the certificate.
937 /// - The total length of the validity period MUST NOT exceed *two* weeks.
938 /// - Only certificates for which the public key algorithm is *ECDSA* with the *secp256r1* are accepted.
939 pub fn with_server_certificate_hashes<I>(
940 self,
941 hashes: I,
942 ) -> ClientConfigBuilder<states::WantsTransportConfigClient>
943 where
944 I: IntoIterator<Item = crate::tls::Sha256Digest>,
945 {
946 use crate::tls::client::build_default_tls_config;
947 use crate::tls::client::ServerHashVerification;
948 use rustls::RootCertStore;
949
950 let tls_config = build_default_tls_config(
951 Arc::new(RootCertStore::empty()),
952 Some(Arc::new(ServerHashVerification::new(hashes))),
953 );
954
955 let endpoint_config = quinn::EndpointConfig::default();
956 let transport_config = quinn::TransportConfig::default();
957
958 self.with(tls_config, endpoint_config, transport_config)
959 }
960
961 /// Allows for manual configuration of a custom TLS setup using a provided
962 /// [`rustls::ClientConfig`], which must support
963 /// [`rustls::CipherSuite::TLS13_AES_128_GCM_SHA256`]. A suitable configuration
964 /// can be obtained using the `ring` crypto provider with a set of versions containing
965 /// [`rustls::version::TLS13`].
966 ///
967 /// This method is provided for advanced users who need fine-grained control over the
968 /// TLS configuration. It allows you to pass a preconfigured [`rustls::ClientConfig`]
969 /// instance to customize the TLS settings according to your specific requirements.
970 ///
971 /// For most use cases, it is recommended to use the [`with_native_certs`](Self::with_native_certs)
972 /// method to configure TLS with safe defaults.
973 pub fn with_custom_tls(
974 self,
975 tls_config: TlsClientConfig,
976 ) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
977 let endpoint_config = quinn::EndpointConfig::default();
978 let transport_config = quinn::TransportConfig::default();
979
980 self.with(tls_config, endpoint_config, transport_config)
981 }
982
983 /// Similar to [`with_native_certs`](Self::with_native_certs), but it allows specifying a custom
984 /// QUIC transport configuration.
985 ///
986 /// # Parameters
987 ///
988 /// - `quic_transport_config`: A custom [`QuicTransportConfig`] instance that allows you to specify
989 /// various QUIC transport-layer settings according to your requirements.
990 ///
991 /// # Example
992 ///
993 /// ```
994 /// use wtransport::config::QuicTransportConfig;
995 /// use wtransport::ClientConfig;
996 ///
997 /// // Create a custom QuicTransportConfig with specific settings
998 /// let mut custom_transport_config = QuicTransportConfig::default();
999 /// custom_transport_config.datagram_send_buffer_size(1024);
1000 ///
1001 /// // Create a ClientConfigBuilder with the custom transport configuration and default TLS settings
1002 /// let client_config = ClientConfig::builder()
1003 /// .with_bind_default()
1004 /// .with_custom_transport(custom_transport_config)
1005 /// .build();
1006 /// ```
1007 #[cfg(feature = "quinn")]
1008 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
1009 pub fn with_custom_transport(
1010 self,
1011 quic_transport_config: QuicTransportConfig,
1012 ) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
1013 use crate::tls::client::build_default_tls_config;
1014
1015 let tls_config = build_default_tls_config(Arc::new(build_native_cert_store()), None);
1016 let quic_endpoint_config = quinn::EndpointConfig::default();
1017
1018 self.with(tls_config, quic_endpoint_config, quic_transport_config)
1019 }
1020
1021 /// Configures the client with both a custom TLS configuration and a custom QUIC transport
1022 /// configuration.
1023 ///
1024 /// This method is designed for advanced users who require full control over both the TLS
1025 /// and transport settings. It allows you to pass a preconfigured [`TlsClientConfig`] and
1026 /// a custom [`QuicTransportConfig`] to fine-tune both layers of the server configuration.
1027 ///
1028 /// # Parameters
1029 ///
1030 /// - `tls_config`: A custom [`TlsClientConfig`] instance that allows you to specify
1031 /// detailed TLS settings, such as ciphersuites, certificate verification, and more. It must
1032 /// support TLS 1.3 (see the documentation of [`Self::with_custom_tls`]).
1033 /// - `quic_transport_config`: A custom [`QuicTransportConfig`] instance that allows you to specify
1034 /// various QUIC transport-layer settings according to your requirements.
1035 #[cfg(feature = "quinn")]
1036 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
1037 pub fn with_custom_tls_and_transport(
1038 self,
1039 tls_config: TlsClientConfig,
1040 quic_transport_config: QuicTransportConfig,
1041 ) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
1042 let quic_endpoint_config = quinn::EndpointConfig::default();
1043 self.with(tls_config, quic_endpoint_config, quic_transport_config)
1044 }
1045
1046 /// Directly builds [`ClientConfig`] skipping TLS and transport configuration.
1047 ///
1048 /// Both TLS and transport configuration is given by [`quic_config`](QuicClientConfig).
1049 #[cfg(feature = "quinn")]
1050 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
1051 pub fn build_with_quic_config(self, quic_config: QuicClientConfig) -> ClientConfig {
1052 ClientConfig {
1053 bind_address_config: self.0.bind_address_config,
1054 endpoint_config: quinn::EndpointConfig::default(),
1055 quic_config,
1056 dns_resolver: Arc::<TokioDnsResolver>::default(),
1057 }
1058 }
1059
1060 fn with(
1061 self,
1062 tls_config: TlsClientConfig,
1063 endpoint_config: quinn::EndpointConfig,
1064 transport_config: quinn::TransportConfig,
1065 ) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
1066 ClientConfigBuilder(states::WantsTransportConfigClient {
1067 bind_address_config: self.0.bind_address_config,
1068 tls_config,
1069 endpoint_config,
1070 transport_config,
1071 dns_resolver: Arc::<TokioDnsResolver>::default(),
1072 })
1073 }
1074}
1075
1076impl ClientConfigBuilder<states::WantsTransportConfigClient> {
1077 /// Completes configuration process.
1078 ///
1079 /// # Panics
1080 ///
1081 /// See the documentation of [`Self::with_custom_tls`] for the TLS 1.3 requirement.
1082 #[must_use]
1083 pub fn build(self) -> ClientConfig {
1084 let crypto = quinn::crypto::rustls::QuicClientConfig::try_from(self.0.tls_config)
1085 .expect("CipherSuite::TLS13_AES_128_GCM_SHA256 missing");
1086
1087 let mut quic_config = quinn::ClientConfig::new(Arc::new(crypto));
1088 quic_config.transport_config(Arc::new(self.0.transport_config));
1089
1090 ClientConfig {
1091 bind_address_config: self.0.bind_address_config,
1092 endpoint_config: self.0.endpoint_config,
1093 quic_config,
1094 dns_resolver: self.0.dns_resolver,
1095 }
1096 }
1097
1098 /// Maximum duration of inactivity to accept before timing out the connection.
1099 ///
1100 /// The true idle timeout is the minimum of this and the peer's own max idle timeout. `None`
1101 /// represents an infinite timeout.
1102 ///
1103 /// **WARNING**: If a peer or its network path malfunctions or acts maliciously, an infinite
1104 /// idle timeout can result in permanently hung futures!
1105 pub fn max_idle_timeout(
1106 mut self,
1107 idle_timeout: Option<Duration>,
1108 ) -> Result<Self, InvalidIdleTimeout> {
1109 let idle_timeout = idle_timeout
1110 .map(quinn::IdleTimeout::try_from)
1111 .transpose()
1112 .map_err(|_| InvalidIdleTimeout)?;
1113
1114 self.0.transport_config.max_idle_timeout(idle_timeout);
1115
1116 Ok(self)
1117 }
1118
1119 /// Period of inactivity before sending a keep-alive packet
1120 ///
1121 /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.
1122 ///
1123 /// `None` to disable, which is the default. Only one side of any given connection needs keep-alive
1124 /// enabled for the connection to be preserved. Must be set lower than the
1125 /// [`max_idle_timeout`](Self::max_idle_timeout) of both peers to be effective.
1126 pub fn keep_alive_interval(mut self, interval: Option<Duration>) -> Self {
1127 self.0.transport_config.keep_alive_interval(interval);
1128 self
1129 }
1130
1131 /// Sets the *DNS* resolver used during [`Endpoint::connect`](crate::Endpoint::connect).
1132 ///
1133 /// Default configuration uses [`TokioDnsResolver`].
1134 pub fn dns_resolver<R>(mut self, dns_resolver: R) -> Self
1135 where
1136 R: DnsResolver + Send + Sync + 'static,
1137 {
1138 self.0.dns_resolver = Arc::new(dns_resolver);
1139 self
1140 }
1141}
1142
1143impl Default for ServerConfigBuilder<states::WantsBindAddress> {
1144 fn default() -> Self {
1145 Self(states::WantsBindAddress {})
1146 }
1147}
1148
1149impl Default for ClientConfigBuilder<states::WantsBindAddress> {
1150 fn default() -> Self {
1151 Self(states::WantsBindAddress {})
1152 }
1153}
1154
1155#[derive(Debug)]
1156pub(crate) enum BindAddressConfig {
1157 AddressV4(SocketAddrV4),
1158 AddressV6(SocketAddrV6, Ipv6DualStackConfig),
1159 Socket(UdpSocket),
1160}
1161
1162impl BindAddressConfig {
1163 pub(crate) fn bind_socket(self) -> std::io::Result<UdpSocket> {
1164 let (bind_address, dual_stack_config) = match self {
1165 BindAddressConfig::AddressV4(address) => {
1166 (SocketAddr::from(address), Ipv6DualStackConfig::OsDefault)
1167 }
1168 BindAddressConfig::AddressV6(address, ipv6_dual_stack_config) => {
1169 (SocketAddr::from(address), ipv6_dual_stack_config)
1170 }
1171 BindAddressConfig::Socket(socket) => {
1172 return Ok(socket);
1173 }
1174 };
1175
1176 let domain = match bind_address {
1177 SocketAddr::V4(_) => SocketDomain::IPV4,
1178 SocketAddr::V6(_) => SocketDomain::IPV6,
1179 };
1180
1181 let socket = Socket::new(domain, SocketType::DGRAM, Some(SocketProtocol::UDP))?;
1182
1183 match dual_stack_config {
1184 Ipv6DualStackConfig::OsDefault => {}
1185 Ipv6DualStackConfig::Deny => socket.set_only_v6(true)?,
1186 Ipv6DualStackConfig::Allow => socket.set_only_v6(false)?,
1187 }
1188
1189 socket.bind(&bind_address.into())?;
1190
1191 Ok(UdpSocket::from(socket))
1192 }
1193}
1194
1195impl From<SocketAddr> for BindAddressConfig {
1196 fn from(value: SocketAddr) -> Self {
1197 match value {
1198 SocketAddr::V4(address) => BindAddressConfig::AddressV4(address),
1199 SocketAddr::V6(address) => {
1200 BindAddressConfig::AddressV6(address, Ipv6DualStackConfig::OsDefault)
1201 }
1202 }
1203 }
1204}
1205
1206/// State-types for client/server builder.
1207pub mod states {
1208 use super::*;
1209
1210 /// Config builder state where the caller must supply binding address.
1211 pub struct WantsBindAddress {}
1212
1213 /// Config builder state where the caller must supply TLS certificate.
1214 pub struct WantsIdentity {
1215 pub(super) bind_address_config: BindAddressConfig,
1216 }
1217
1218 /// Config builder state where the caller must supply TLS root store.
1219 pub struct WantsRootStore {
1220 pub(super) bind_address_config: BindAddressConfig,
1221 }
1222
1223 /// Config builder state where transport properties can be set.
1224 pub struct WantsTransportConfigServer {
1225 pub(super) bind_address_config: BindAddressConfig,
1226 pub(super) tls_config: TlsServerConfig,
1227 pub(super) endpoint_config: quinn::EndpointConfig,
1228 pub(super) transport_config: quinn::TransportConfig,
1229 pub(super) migration: bool,
1230 }
1231
1232 /// Config builder state where transport properties can be set.
1233 pub struct WantsTransportConfigClient {
1234 pub(super) bind_address_config: BindAddressConfig,
1235 pub(super) tls_config: TlsClientConfig,
1236 pub(super) endpoint_config: quinn::EndpointConfig,
1237 pub(super) transport_config: quinn::TransportConfig,
1238 pub(super) dns_resolver: Arc<dyn DnsResolver + Send + Sync>,
1239 }
1240}
1241
1242/// Future resolving domain name.
1243///
1244/// See [`DnsResolver::resolve`].
1245pub trait DnsLookupFuture: Future<Output = std::io::Result<Option<SocketAddr>>> + Send {}
1246
1247impl<F> DnsLookupFuture for F where F: Future<Output = std::io::Result<Option<SocketAddr>>> + Send {}
1248
1249/// A trait for asynchronously resolving domain names to IP addresses using DNS.
1250pub trait DnsResolver: Debug {
1251 /// Resolves a domain name to one IP address.
1252 fn resolve(&self, host: &str) -> Pin<Box<dyn DnsLookupFuture>>;
1253}
1254
1255/// A DNS resolver implementation using the *Tokio* asynchronous runtime.
1256///
1257/// Internally, it uses [`tokio::net::lookup_host`].
1258#[derive(Default)]
1259pub struct TokioDnsResolver;
1260
1261impl DnsResolver for TokioDnsResolver {
1262 fn resolve(&self, host: &str) -> Pin<Box<dyn DnsLookupFuture>> {
1263 let host = host.to_string();
1264
1265 Box::pin(async move { Ok(tokio::net::lookup_host(host).await?.next()) })
1266 }
1267}
1268
1269impl Debug for TokioDnsResolver {
1270 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1271 f.debug_struct("TokioDnsResolver").finish()
1272 }
1273}
1274
1275impl std::error::Error for InvalidIdleTimeout {}
1276
1277impl Debug for InvalidIdleTimeout {
1278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1279 f.write_str("idle timeout value configuration is invalid")
1280 }
1281}
1282
1283impl Display for InvalidIdleTimeout {
1284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1285 Debug::fmt(self, f)
1286 }
1287}