conjure_runtime_rustls_platform_verifier/
builder.rs1#[cfg(not(target_arch = "wasm32"))]
16use crate::blocking;
17use crate::client::ClientState;
18use crate::config::{ProxyConfig, SecurityConfig, ServiceConfig};
19use crate::weak_cache::Cached;
20use crate::{Client, HostMetricsRegistry, UserAgent};
21use arc_swap::ArcSwap;
22use conjure_error::Error;
23use conjure_http::client::ConjureRuntime;
24use rustls::client::ResolvesClientCert;
25use std::borrow::Cow;
26use std::sync::Arc;
27use std::time::Duration;
28#[cfg(not(target_arch = "wasm32"))]
29use tokio::runtime::Handle;
30use url::Url;
31use witchcraft_metrics::MetricRegistry;
32
33const MESH_PREFIX: &str = "mesh-";
34
35pub struct Builder<T = Complete>(T);
37
38pub struct ServiceStage(());
40
41pub struct UserAgentStage {
43 service: String,
44}
45
46#[derive(Clone, PartialEq, Eq, Hash)]
47pub(crate) struct CachedConfig {
48 service: String,
49 user_agent: UserAgent,
50 uris: Vec<Url>,
51 security: SecurityConfig,
52 proxy: ProxyConfig,
53 connect_timeout: Duration,
54 read_timeout: Duration,
55 write_timeout: Duration,
56 backoff_slot_size: Duration,
57 max_num_retries: u32,
58 client_qos: ClientQos,
59 server_qos: ServerQos,
60 service_error: ServiceError,
61 idempotency: Idempotency,
62 node_selection_strategy: NodeSelectionStrategy,
63 override_host_index: Option<usize>,
64}
65
66#[derive(Clone)]
67pub(crate) struct UncachedConfig {
68 pub(crate) metrics: Option<Arc<MetricRegistry>>,
69 pub(crate) host_metrics: Option<Arc<HostMetricsRegistry>>,
70 #[cfg(not(target_arch = "wasm32"))]
71 pub(crate) blocking_handle: Option<Handle>,
72 pub(crate) conjure_runtime: Arc<ConjureRuntime>,
73 pub(crate) client_cert_resolver: Option<Arc<dyn ResolvesClientCert>>,
74}
75
76pub struct Complete {
78 cached: CachedConfig,
79 uncached: UncachedConfig,
80}
81
82impl Default for Builder<ServiceStage> {
83 #[inline]
84 fn default() -> Self {
85 Builder::new()
86 }
87}
88
89impl Builder<ServiceStage> {
90 #[inline]
92 pub fn new() -> Self {
93 Builder(ServiceStage(()))
94 }
95
96 #[inline]
100 pub fn service(self, service: &str) -> Builder<UserAgentStage> {
101 Builder(UserAgentStage {
102 service: service.to_string(),
103 })
104 }
105}
106
107impl Builder<UserAgentStage> {
108 #[inline]
110 pub fn user_agent(self, user_agent: UserAgent) -> Builder {
111 Builder(Complete {
112 cached: CachedConfig {
113 service: self.0.service,
114 user_agent,
115 uris: vec![],
116 security: SecurityConfig::builder().build(),
117 proxy: ProxyConfig::Direct,
118 connect_timeout: Duration::from_secs(10),
119 read_timeout: Duration::from_secs(5 * 60),
120 write_timeout: Duration::from_secs(5 * 60),
121 backoff_slot_size: Duration::from_millis(250),
122 max_num_retries: 4,
123 client_qos: ClientQos::Enabled,
124 server_qos: ServerQos::AutomaticRetry,
125 service_error: ServiceError::WrapInNewError,
126 idempotency: Idempotency::ByMethod,
127 node_selection_strategy: NodeSelectionStrategy::PinUntilError,
128 override_host_index: None,
129 },
130 uncached: UncachedConfig {
131 metrics: None,
132 host_metrics: None,
133 #[cfg(not(target_arch = "wasm32"))]
134 blocking_handle: None,
135 conjure_runtime: Arc::new(ConjureRuntime::new()),
136 client_cert_resolver: None,
137 },
138 })
139 }
140}
141
142#[cfg(test)]
143impl Builder {
144 pub(crate) fn for_test() -> Self {
145 use crate::Agent;
146
147 Builder::new()
148 .service("test")
149 .user_agent(UserAgent::new(Agent::new("test", "0.0.0")))
150 }
151}
152
153impl Builder<Complete> {
154 pub(crate) fn cached_config(&self) -> &CachedConfig {
155 &self.0.cached
156 }
157
158 #[inline]
160 pub fn from_config(mut self, config: &ServiceConfig) -> Self {
161 self = self.uris(config.uris().to_vec());
162
163 if let Some(security) = config.security() {
164 self = self.security(security.clone());
165 }
166
167 if let Some(proxy) = config.proxy() {
168 self = self.proxy(proxy.clone());
169 }
170
171 if let Some(connect_timeout) = config.connect_timeout() {
172 self = self.connect_timeout(connect_timeout);
173 }
174
175 if let Some(read_timeout) = config.read_timeout() {
176 self = self.read_timeout(read_timeout);
177 }
178
179 if let Some(write_timeout) = config.write_timeout() {
180 self = self.write_timeout(write_timeout);
181 }
182
183 if let Some(backoff_slot_size) = config.backoff_slot_size() {
184 self = self.backoff_slot_size(backoff_slot_size);
185 }
186
187 if let Some(max_num_retries) = config.max_num_retries() {
188 self = self.max_num_retries(max_num_retries);
189 }
190
191 self
192 }
193
194 #[inline]
196 pub fn get_service(&self) -> &str {
197 &self.0.cached.service
198 }
199
200 #[inline]
202 pub fn get_user_agent(&self) -> &UserAgent {
203 &self.0.cached.user_agent
204 }
205
206 #[inline]
210 pub fn uri(mut self, uri: Url) -> Self {
211 self.0.cached.uris.push(uri);
212 self
213 }
214
215 #[inline]
219 pub fn uris(mut self, uris: Vec<Url>) -> Self {
220 self.0.cached.uris = uris;
221 self
222 }
223
224 #[inline]
226 pub fn get_uris(&self) -> &[Url] {
227 &self.0.cached.uris
228 }
229
230 #[inline]
234 pub fn security(mut self, security: SecurityConfig) -> Self {
235 self.0.cached.security = security;
236 self
237 }
238
239 #[inline]
241 pub fn get_security(&self) -> &SecurityConfig {
242 &self.0.cached.security
243 }
244
245 #[inline]
249 pub fn proxy(mut self, proxy: ProxyConfig) -> Self {
250 self.0.cached.proxy = proxy;
251 self
252 }
253
254 #[inline]
256 pub fn get_proxy(&self) -> &ProxyConfig {
257 &self.0.cached.proxy
258 }
259
260 #[inline]
264 pub fn connect_timeout(mut self, connect_timeout: Duration) -> Self {
265 self.0.cached.connect_timeout = connect_timeout;
266 self
267 }
268
269 #[inline]
271 pub fn get_connect_timeout(&self) -> Duration {
272 self.0.cached.connect_timeout
273 }
274
275 #[inline]
281 pub fn read_timeout(mut self, read_timeout: Duration) -> Self {
282 self.0.cached.read_timeout = read_timeout;
283 self
284 }
285
286 #[inline]
288 pub fn get_read_timeout(&self) -> Duration {
289 self.0.cached.read_timeout
290 }
291
292 #[inline]
298 pub fn write_timeout(mut self, write_timeout: Duration) -> Self {
299 self.0.cached.write_timeout = write_timeout;
300 self
301 }
302
303 #[inline]
305 pub fn get_write_timeout(&self) -> Duration {
306 self.0.cached.write_timeout
307 }
308
309 #[inline]
316 pub fn backoff_slot_size(mut self, backoff_slot_size: Duration) -> Self {
317 self.0.cached.backoff_slot_size = backoff_slot_size;
318 self
319 }
320
321 #[inline]
323 pub fn get_backoff_slot_size(&self) -> Duration {
324 self.0.cached.backoff_slot_size
325 }
326
327 #[inline]
331 pub fn max_num_retries(mut self, max_num_retries: u32) -> Self {
332 self.0.cached.max_num_retries = max_num_retries;
333 self
334 }
335
336 #[inline]
338 pub fn get_max_num_retries(&self) -> u32 {
339 self.0.cached.max_num_retries
340 }
341
342 #[inline]
346 pub fn client_qos(mut self, client_qos: ClientQos) -> Self {
347 self.0.cached.client_qos = client_qos;
348 self
349 }
350
351 #[inline]
353 pub fn get_client_qos(&self) -> ClientQos {
354 self.0.cached.client_qos
355 }
356
357 #[inline]
361 pub fn server_qos(mut self, server_qos: ServerQos) -> Self {
362 self.0.cached.server_qos = server_qos;
363 self
364 }
365
366 #[inline]
368 pub fn get_server_qos(&self) -> ServerQos {
369 self.0.cached.server_qos
370 }
371
372 #[inline]
376 pub fn service_error(mut self, service_error: ServiceError) -> Self {
377 self.0.cached.service_error = service_error;
378 self
379 }
380
381 #[inline]
383 pub fn get_service_error(&self) -> ServiceError {
384 self.0.cached.service_error
385 }
386
387 #[inline]
393 pub fn idempotency(mut self, idempotency: Idempotency) -> Self {
394 self.0.cached.idempotency = idempotency;
395 self
396 }
397
398 #[inline]
400 pub fn get_idempotency(&self) -> Idempotency {
401 self.0.cached.idempotency
402 }
403
404 #[inline]
408 pub fn node_selection_strategy(
409 mut self,
410 node_selection_strategy: NodeSelectionStrategy,
411 ) -> Self {
412 self.0.cached.node_selection_strategy = node_selection_strategy;
413 self
414 }
415
416 #[inline]
418 pub fn get_node_selection_strategy(&self) -> NodeSelectionStrategy {
419 self.0.cached.node_selection_strategy
420 }
421
422 #[inline]
426 pub fn metrics(mut self, metrics: Arc<MetricRegistry>) -> Self {
427 self.0.uncached.metrics = Some(metrics);
428 self
429 }
430
431 #[inline]
433 pub fn get_metrics(&self) -> Option<&Arc<MetricRegistry>> {
434 self.0.uncached.metrics.as_ref()
435 }
436
437 #[inline]
441 pub fn host_metrics(mut self, host_metrics: Arc<HostMetricsRegistry>) -> Self {
442 self.0.uncached.host_metrics = Some(host_metrics);
443 self
444 }
445
446 #[inline]
448 pub fn get_host_metrics(&self) -> Option<&Arc<HostMetricsRegistry>> {
449 self.0.uncached.host_metrics.as_ref()
450 }
451
452 #[inline]
456 pub fn conjure_runtime(mut self, conjure_runtime: Arc<ConjureRuntime>) -> Self {
457 self.0.uncached.conjure_runtime = conjure_runtime;
458 self
459 }
460
461 pub fn get_conjure_runtime(&self) -> &Arc<ConjureRuntime> {
463 &self.0.uncached.conjure_runtime
464 }
465
466 #[inline]
470 pub fn client_cert_resolver(mut self, resolver: Arc<dyn ResolvesClientCert>) -> Self {
471 self.0.uncached.client_cert_resolver = Some(resolver);
472 self
473 }
474
475 #[inline]
477 pub fn get_client_cert_resolver(&self) -> Option<&Arc<dyn ResolvesClientCert>> {
478 self.0.uncached.client_cert_resolver.as_ref()
479 }
480
481 #[inline]
483 pub fn override_host_index(mut self, override_host_index: usize) -> Self {
484 self.0.cached.override_host_index = Some(override_host_index);
485 self
486 }
487
488 #[inline]
490 pub fn get_override_host_index(&self) -> Option<usize> {
491 self.0.cached.override_host_index
492 }
493
494 pub(crate) fn mesh_mode(&self) -> bool {
495 self.0
496 .cached
497 .uris
498 .iter()
499 .any(|uri| uri.scheme().starts_with(MESH_PREFIX))
500 }
501
502 pub(crate) fn postprocessed_uris(&self) -> Result<Cow<'_, [Url]>, Error> {
503 if self.mesh_mode() {
504 if self.0.cached.uris.len() != 1 {
505 return Err(Error::internal_safe("mesh mode expects exactly one URI")
506 .with_safe_param("uris", &self.0.cached.uris));
507 }
508
509 let uri = self.0.cached.uris[0]
510 .as_str()
511 .strip_prefix(MESH_PREFIX)
512 .unwrap()
513 .parse()
514 .unwrap();
515
516 Ok(Cow::Owned(vec![uri]))
517 } else {
518 Ok(Cow::Borrowed(&self.0.cached.uris))
519 }
520 }
521
522 pub fn build(&self) -> Result<Client, Error> {
524 let state = ClientState::new(self)?;
525 Ok(Client::new(
526 Arc::new(ArcSwap::new(Arc::new(Cached::uncached(state)))),
527 None,
528 ))
529 }
530}
531
532#[cfg(not(target_arch = "wasm32"))]
533impl Builder<Complete> {
534 #[inline]
540 pub fn blocking_handle(mut self, blocking_handle: Handle) -> Self {
541 self.0.uncached.blocking_handle = Some(blocking_handle);
542 self
543 }
544
545 #[inline]
547 pub fn get_blocking_handle(&self) -> Option<&Handle> {
548 self.0.uncached.blocking_handle.as_ref()
549 }
550
551 pub fn build_blocking(&self) -> Result<blocking::Client, Error> {
553 self.build().map(|client| blocking::Client {
554 client,
555 handle: self.0.uncached.blocking_handle.clone(),
556 })
557 }
558}
559
560#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
562#[non_exhaustive]
563pub enum ClientQos {
564 Enabled,
568
569 DangerousDisableSympatheticClientQos,
574}
575
576#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
580#[non_exhaustive]
581pub enum ServerQos {
582 AutomaticRetry,
586
587 Propagate429And503ToCaller,
592}
593
594#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
598#[non_exhaustive]
599pub enum ServiceError {
600 WrapInNewError,
607
608 PropagateToCaller,
613}
614
615#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
617#[non_exhaustive]
618pub enum Idempotency {
619 Always,
621
622 ByMethod,
627
628 Never,
630}
631
632#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
634#[non_exhaustive]
635pub enum NodeSelectionStrategy {
636 PinUntilError,
644
645 PinUntilErrorWithoutReshuffle,
647
648 Balanced,
650}