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