#[cfg(feature = "client")]
use reqwest::Client;
#[cfg(feature = "client")]
use crate::error::Error;
#[derive(Debug, Default, Clone)]
pub struct TlsConfig {
pub client_key_pem: Option<String>,
pub client_cert_pem: Option<String>,
pub root_ca_pems: Vec<String>,
pub accept_invalid_certs: bool,
}
impl TlsConfig {
pub fn insecure() -> Self {
Self {
accept_invalid_certs: true,
..Default::default()
}
}
pub fn from_pem(
client_cert_pem: impl Into<String>,
client_key_pem: impl Into<String>,
root_ca_pems: Vec<String>,
) -> Self {
Self {
client_cert_pem: Some(client_cert_pem.into()),
client_key_pem: Some(client_key_pem.into()),
root_ca_pems,
accept_invalid_certs: false,
}
}
}
#[derive(Debug, Clone)]
pub struct RetryPolicy {
pub max_retries: u32,
pub base_delay_ms: u64,
}
impl Default for RetryPolicy {
fn default() -> Self {
Self {
max_retries: 3,
base_delay_ms: 500,
}
}
}
#[cfg(feature = "client")]
pub fn build_client(config: &TlsConfig) -> Result<Client, Error> {
let mut builder = Client::builder()
.use_rustls_tls()
.danger_accept_invalid_certs(config.accept_invalid_certs)
.timeout(std::time::Duration::from_secs(10));
for pem in &config.root_ca_pems {
let cert = reqwest::Certificate::from_pem(pem.as_bytes())
.map_err(|e| Error::Transport(format!("root CA PEM: {e}")))?;
builder = builder.add_root_certificate(cert);
}
if let (Some(cert_pem), Some(key_pem)) = (&config.client_cert_pem, &config.client_key_pem) {
let combined = format!("{cert_pem}\n{key_pem}");
let identity = reqwest::Identity::from_pem(combined.as_bytes())
.map_err(|e| Error::Transport(format!("mTLS identity PEM: {e}")))?;
builder = builder.identity(identity);
}
builder.build().map_err(|e| Error::Transport(e.to_string()))
}