1use crate::{
2 config::QuinnEndpoint,
3 runtime::{SocketTransport, TrilliumRuntime},
4};
5use std::{
6 fmt::{self, Debug, Formatter},
7 io,
8 net::SocketAddr,
9 sync::Arc,
10};
11use trillium_server_common::{Connector, QuicClientConfig};
12
13pub struct ClientQuicConfig {
30 client_config: quinn::ClientConfig,
31}
32
33impl Debug for ClientQuicConfig {
34 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
35 f.debug_struct("ClientQuicConfig").finish_non_exhaustive()
36 }
37}
38
39impl ClientQuicConfig {
40 #[cfg(feature = "webpki-roots")]
48 pub fn with_webpki_roots() -> Self {
49 let root_store =
50 rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
51 let crypto =
52 rustls::ClientConfig::builder_with_provider(crate::crypto_provider::crypto_provider())
53 .with_safe_default_protocol_versions()
54 .expect("building TLS config with protocol versions")
55 .with_root_certificates(root_store)
56 .with_no_client_auth();
57
58 Self::from_rustls_client_config(crypto)
59 }
60
61 pub fn from_rustls_client_config(mut tls: rustls::ClientConfig) -> Self {
65 if !tls.alpn_protocols.contains(&b"h3".to_vec()) {
66 tls.alpn_protocols.push(b"h3".to_vec());
67 }
68 let quic_tls = quinn::crypto::rustls::QuicClientConfig::try_from(Arc::new(tls))
69 .expect("building QUIC client TLS config");
70 Self::from_quinn_client_config(quinn::ClientConfig::new(Arc::new(quic_tls)))
71 }
72
73 pub fn from_quinn_client_config(config: quinn::ClientConfig) -> Self {
78 Self {
79 client_config: config,
80 }
81 }
82}
83
84impl<C> QuicClientConfig<C> for ClientQuicConfig
85where
86 C: Connector,
87 C::Runtime: Unpin,
88 C::Udp: SocketTransport,
89{
90 type Endpoint = QuinnEndpoint;
91
92 fn bind(&self, addr: SocketAddr, runtime: &C::Runtime) -> io::Result<Self::Endpoint> {
93 let socket = std::net::UdpSocket::bind(addr)?;
94 let quinn_runtime = TrilliumRuntime::<C::Runtime, C::Udp>::new(runtime.clone());
95 let mut endpoint = quinn::Endpoint::new(
96 quinn::EndpointConfig::default(),
97 None, socket,
99 quinn_runtime,
100 )?;
101 endpoint.set_default_client_config(self.client_config.clone());
102 Ok(QuinnEndpoint::new(endpoint))
103 }
104}