1use std::collections::BTreeMap;
11use std::fmt;
12use std::net::SocketAddr;
13use std::sync::Arc;
14use std::time::Duration;
15
16use serde::{Deserialize, Serialize};
17use url::Url;
18
19use crate::client::Client;
20use crate::tls::{Certificate, Identity};
21
22#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
23pub struct Auth {
24 pub username: String,
25 pub password: Option<String>,
26}
27
28#[derive(Clone)]
30pub struct ClientConfig {
31 url: Arc<dyn Fn() -> Url + Send + Sync + 'static>,
32 root_certs: Vec<Certificate>,
33 identity: Option<Identity>,
34 auth: Option<Auth>,
35 dns_overrides: BTreeMap<String, Vec<SocketAddr>>,
36}
37
38impl fmt::Debug for ClientConfig {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.debug_struct("ClientConfig")
41 .field("url", &"...")
42 .field("root_certs", &self.root_certs)
43 .field("identity", &self.identity)
44 .field("auth", &self.auth)
45 .field("dns_overrides", &self.dns_overrides)
46 .finish()
47 }
48}
49
50impl ClientConfig {
51 pub fn new(url: Url) -> ClientConfig {
54 ClientConfig {
55 url: Arc::new(move || url.clone()),
56 root_certs: Vec::new(),
57 identity: None,
58 auth: None,
59 dns_overrides: BTreeMap::new(),
60 }
61 }
62
63 pub fn add_root_certificate(mut self, cert: Certificate) -> ClientConfig {
71 self.root_certs.push(cert);
72 self
73 }
74
75 pub fn identity(mut self, identity: Identity) -> ClientConfig {
77 self.identity = Some(identity);
78 self
79 }
80
81 pub fn auth(mut self, username: String, password: Option<String>) -> ClientConfig {
84 self.auth = Some(Auth { username, password });
85 self
86 }
87
88 pub fn resolve_to_addrs(mut self, domain: &str, addrs: &[SocketAddr]) -> ClientConfig {
93 self.dns_overrides.insert(domain.into(), addrs.into());
94 self
95 }
96
97 pub fn dynamic_url<F: Fn() -> Url + Send + Sync + 'static>(
102 mut self,
103 callback: F,
104 ) -> ClientConfig {
105 self.url = Arc::new(callback);
106 self
107 }
108
109 pub fn build(self) -> Result<Client, anyhow::Error> {
111 let mut builder = reqwest::ClientBuilder::new();
112
113 if !self.root_certs.is_empty() {
117 let tls = crate::tls::rustls_config(&self.root_certs, self.identity.as_ref())?;
118 builder = builder.tls_backend_preconfigured(tls);
119 } else if let Some(ident) = self.identity {
120 builder = builder.identity(ident.into());
121 }
122
123 for (domain, addrs) in self.dns_overrides {
124 builder = builder.resolve_to_addrs(&domain, &addrs);
125 }
126
127 let timeout = Duration::from_secs(60);
129
130 let inner = builder
131 .redirect(reqwest::redirect::Policy::none())
132 .timeout(timeout)
133 .build()
134 .map_err(|e| {
135 anyhow::anyhow!("failed to build schema registry HTTP client: {e:#}")
137 })?;
138
139 Client::new(inner, self.url, self.auth, timeout)
140 }
141}