1use std::{path::Path, sync::Arc};
22
23use anyhow::{Context as _, Result};
24use once_cell::sync::Lazy;
25
26#[cfg(feature = "rustls-native-certs")]
27pub static NATIVE_ROOTS: Lazy<Arc<[rustls::pki_types::CertificateDer<'static>]>> =
28 Lazy::new(|| {
29 let res = rustls_native_certs::load_native_certs();
30 if !res.errors.is_empty() {
31 tracing::warn!(errors = ?res.errors, "failed to load native root certificate store");
32 }
33 Arc::from(res.certs)
34 });
35
36#[cfg(all(feature = "rustls-native-certs", feature = "oci"))]
37pub static NATIVE_ROOTS_OCI: Lazy<Arc<[oci_client::client::Certificate]>> = Lazy::new(|| {
38 NATIVE_ROOTS
39 .iter()
40 .map(|cert| oci_client::client::Certificate {
41 encoding: oci_client::client::CertificateEncoding::Der,
42 data: cert.to_vec(),
43 })
44 .collect()
45});
46
47#[cfg(all(feature = "rustls-native-certs", feature = "reqwest"))]
48pub static NATIVE_ROOTS_REQWEST: Lazy<Arc<[reqwest::tls::Certificate]>> = Lazy::new(|| {
49 NATIVE_ROOTS
50 .iter()
51 .filter_map(|cert| reqwest::tls::Certificate::from_der(cert.as_ref()).ok())
52 .collect()
53});
54
55pub static DEFAULT_ROOTS: Lazy<Arc<rustls::RootCertStore>> = Lazy::new(|| {
56 #[allow(unused_mut)]
57 let mut ca = rustls::RootCertStore::empty();
58 #[cfg(feature = "rustls-native-certs")]
59 {
60 let (added, ignored) = ca.add_parsable_certificates(NATIVE_ROOTS.iter().cloned());
61 tracing::debug!(added, ignored, "loaded native root certificate store");
62 }
63 #[cfg(feature = "webpki-roots")]
64 ca.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
65 Arc::new(ca)
66});
67
68pub static DEFAULT_CLIENT_CONFIG: Lazy<rustls::ClientConfig> = Lazy::new(|| {
69 rustls::ClientConfig::builder()
70 .with_root_certificates(Arc::clone(&DEFAULT_ROOTS))
71 .with_no_client_auth()
72});
73
74pub static DEFAULT_CLIENT_CONFIG_ARC: Lazy<Arc<rustls::ClientConfig>> =
75 Lazy::new(|| Arc::new(DEFAULT_CLIENT_CONFIG.clone()));
76
77#[cfg(feature = "tokio-rustls")]
78pub static DEFAULT_RUSTLS_CONNECTOR: Lazy<tokio_rustls::TlsConnector> =
79 Lazy::new(|| tokio_rustls::TlsConnector::from(Arc::clone(&DEFAULT_CLIENT_CONFIG_ARC)));
80
81#[cfg(feature = "hyper-rustls")]
82pub static DEFAULT_HYPER_CONNECTOR: Lazy<
83 hyper_rustls::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>,
84> = Lazy::new(|| {
85 hyper_rustls::HttpsConnectorBuilder::new()
86 .with_tls_config(DEFAULT_CLIENT_CONFIG.clone())
87 .https_or_http()
88 .enable_all_versions()
89 .build()
90});
91
92#[cfg(all(feature = "reqwest", feature = "rustls-native-certs"))]
93pub static DEFAULT_REQWEST_CLIENT: Lazy<reqwest::Client> = Lazy::new(|| {
94 reqwest::ClientBuilder::default()
95 .user_agent(REQWEST_USER_AGENT)
96 .with_native_certificates()
97 .build()
98 .expect("failed to build HTTP client")
99});
100
101pub static REQWEST_USER_AGENT: &str =
102 concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
103
104#[cfg(feature = "rustls-native-certs")]
105pub trait NativeRootsExt {
106 fn with_native_certificates(self) -> Self;
107}
108
109#[cfg(all(feature = "reqwest", feature = "rustls-native-certs"))]
110impl NativeRootsExt for reqwest::ClientBuilder {
111 fn with_native_certificates(self) -> Self {
112 NATIVE_ROOTS_REQWEST
113 .iter()
114 .cloned()
115 .fold(self, reqwest::ClientBuilder::add_root_certificate)
116 }
117}
118
119pub fn load_certs_from_paths(
121 paths: &[impl AsRef<Path>],
122) -> Result<Vec<rustls::pki_types::CertificateDer<'static>>> {
123 paths
124 .iter()
125 .map(read_certs_from_path)
126 .flat_map(|result| match result {
127 Ok(vec) => vec.into_iter().map(Ok).collect(),
128 Err(er) => vec![Err(er)],
129 })
130 .collect::<Result<Vec<_>, _>>()
131}
132
133pub fn read_certs_from_path(
137 path: impl AsRef<Path>,
138) -> Result<Vec<rustls::pki_types::CertificateDer<'static>>> {
139 let path = path.as_ref();
140 if !path.is_file() {
142 return Ok(Vec::with_capacity(0));
143 }
144 let mut reader =
145 std::io::BufReader::new(std::fs::File::open(path).with_context(|| {
146 format!("failed to open file at provided path: {}", path.display())
147 })?);
148 Ok(rustls_pemfile::certs(&mut reader).collect::<Result<Vec<_>, _>>()?)
149}