use std::sync::Arc;
use std::sync::OnceLock;
use rustls::ClientConfig;
use rustls::RootCertStore;
pub fn shared_client_config() -> Arc<ClientConfig> {
static CONFIG: OnceLock<Arc<ClientConfig>> = OnceLock::new();
CONFIG.get_or_init(build).clone()
}
fn build() -> Arc<ClientConfig> {
let _ =
rustls::crypto::CryptoProvider::install_default(rustls::crypto::ring::default_provider());
let mut roots = RootCertStore::empty();
roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let bundled_added = roots.len();
let native_added = match rustls_native_certs::load_native_certs() {
result if result.errors.is_empty() => {
let mut count = 0usize;
for cert in result.certs {
if roots.add(cert).is_ok() {
count += 1;
}
}
count
}
result => {
eprintln!(
"wire tls: rustls-native-certs reported {} error(s); continuing with bundled webpki roots only",
result.errors.len()
);
let mut count = 0usize;
for cert in result.certs {
if roots.add(cert).is_ok() {
count += 1;
}
}
count
}
};
eprintln!(
"wire tls: trust roots loaded — {bundled_added} webpki + {native_added} native = {} total",
roots.len()
);
let config = ClientConfig::builder()
.with_root_certificates(roots)
.with_no_client_auth();
Arc::new(config)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn shared_client_config_returns_clones_of_same_arc() {
let a = shared_client_config();
let b = shared_client_config();
assert!(
Arc::ptr_eq(&a, &b),
"shared_client_config must return clones of one cached Arc"
);
}
#[test]
fn shared_client_config_has_webpki_roots_loaded() {
let cfg = shared_client_config();
let store_len = cfg
.crypto_provider()
.signature_verification_algorithms
.all
.len();
assert!(store_len > 0, "crypto provider must have verification algs");
}
}