1#[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 std::borrow::Cow;
25use std::sync::Arc;
26use std::time::Duration;
27#[cfg(not(target_arch = "wasm32"))]
28use tokio::runtime::Handle;
29use url::Url;
30use witchcraft_metrics::MetricRegistry;
31
32const MESH_PREFIX: &str = "mesh-";
33
34pub struct Builder<T = Complete>(T);
36
37pub struct ServiceStage(());
39
40pub struct UserAgentStage {
42 service: String,
43}
44
45#[derive(Clone, PartialEq, Eq, Hash)]
46pub(crate) struct CachedConfig {
47 service: String,
48 user_agent: UserAgent,
49 uris: Vec<Url>,
50 security: SecurityConfig,
51 proxy: ProxyConfig,
52 connect_timeout: Duration,
53 read_timeout: Duration,
54 write_timeout: Duration,
55 backoff_slot_size: Duration,
56 max_num_retries: u32,
57 client_qos: ClientQos,
58 server_qos: ServerQos,
59 service_error: ServiceError,
60 idempotency: Idempotency,
61 node_selection_strategy: NodeSelectionStrategy,
62 override_host_index: Option<usize>,
63}
64
65#[derive(Clone)]
66pub(crate) struct UncachedConfig {
67 pub(crate) metrics: Option<Arc<MetricRegistry>>,
68 pub(crate) host_metrics: Option<Arc<HostMetricsRegistry>>,
69 #[cfg(not(target_arch = "wasm32"))]
70 pub(crate) blocking_handle: Option<Handle>,
71 pub(crate) conjure_runtime: Arc<ConjureRuntime>,
72}
73
74pub struct Complete {
76 cached: CachedConfig,
77 uncached: UncachedConfig,
78}
79
80impl Default for Builder<ServiceStage> {
81 #[inline]
82 fn default() -> Self {
83 Builder::new()
84 }
85}
86
87impl Builder<ServiceStage> {
88 #[inline]
90 pub fn new() -> Self {
91 Builder(ServiceStage(()))
92 }
93
94 #[inline]
98 pub fn service(self, service: &str) -> Builder<UserAgentStage> {
99 Builder(UserAgentStage {
100 service: service.to_string(),
101 })
102 }
103}
104
105impl Builder<UserAgentStage> {
106 #[inline]
108 pub fn user_agent(self, user_agent: UserAgent) -> Builder {
109 Builder(Complete {
110 cached: CachedConfig {
111 service: self.0.service,
112 user_agent,
113 uris: vec![],
114 security: SecurityConfig::builder().build(),
115 proxy: ProxyConfig::Direct,
116 connect_timeout: Duration::from_secs(10),
117 read_timeout: Duration::from_secs(5 * 60),
118 write_timeout: Duration::from_secs(5 * 60),
119 backoff_slot_size: Duration::from_millis(250),
120 max_num_retries: 4,
121 client_qos: ClientQos::Enabled,
122 server_qos: ServerQos::AutomaticRetry,
123 service_error: ServiceError::WrapInNewError,
124 idempotency: Idempotency::ByMethod,
125 node_selection_strategy: NodeSelectionStrategy::PinUntilError,
126 override_host_index: None,
127 },
128 uncached: UncachedConfig {
129 metrics: None,
130 host_metrics: None,
131 #[cfg(not(target_arch = "wasm32"))]
132 blocking_handle: None,
133 conjure_runtime: Arc::new(ConjureRuntime::new()),
134 },
135 })
136 }
137}
138
139#[cfg(test)]
140impl Builder {
141 pub(crate) fn for_test() -> Self {
142 use crate::Agent;
143
144 Builder::new()
145 .service("test")
146 .user_agent(UserAgent::new(Agent::new("test", "0.0.0")))
147 }
148}
149
150impl Builder<Complete> {
151 pub(crate) fn cached_config(&self) -> &CachedConfig {
152 &self.0.cached
153 }
154
155 #[inline]
157 pub fn from_config(mut self, config: &ServiceConfig) -> Self {
158 self = self.uris(config.uris().to_vec());
159
160 if let Some(security) = config.security() {
161 self = self.security(security.clone());
162 }
163
164 if let Some(proxy) = config.proxy() {
165 self = self.proxy(proxy.clone());
166 }
167
168 if let Some(connect_timeout) = config.connect_timeout() {
169 self = self.connect_timeout(connect_timeout);
170 }
171
172 if let Some(read_timeout) = config.read_timeout() {
173 self = self.read_timeout(read_timeout);
174 }
175
176 if let Some(write_timeout) = config.write_timeout() {
177 self = self.write_timeout(write_timeout);
178 }
179
180 if let Some(backoff_slot_size) = config.backoff_slot_size() {
181 self = self.backoff_slot_size(backoff_slot_size);
182 }
183
184 if let Some(max_num_retries) = config.max_num_retries() {
185 self = self.max_num_retries(max_num_retries);
186 }
187
188 self
189 }
190
191 #[inline]
193 pub fn get_service(&self) -> &str {
194 &self.0.cached.service
195 }
196
197 #[inline]
199 pub fn get_user_agent(&self) -> &UserAgent {
200 &self.0.cached.user_agent
201 }
202
203 #[inline]
207 pub fn uri(mut self, uri: Url) -> Self {
208 self.0.cached.uris.push(uri);
209 self
210 }
211
212 #[inline]
216 pub fn uris(mut self, uris: Vec<Url>) -> Self {
217 self.0.cached.uris = uris;
218 self
219 }
220
221 #[inline]
223 pub fn get_uris(&self) -> &[Url] {
224 &self.0.cached.uris
225 }
226
227 #[inline]
231 pub fn security(mut self, security: SecurityConfig) -> Self {
232 self.0.cached.security = security;
233 self
234 }
235
236 #[inline]
238 pub fn get_security(&self) -> &SecurityConfig {
239 &self.0.cached.security
240 }
241
242 #[inline]
246 pub fn proxy(mut self, proxy: ProxyConfig) -> Self {
247 self.0.cached.proxy = proxy;
248 self
249 }
250
251 #[inline]
253 pub fn get_proxy(&self) -> &ProxyConfig {
254 &self.0.cached.proxy
255 }
256
257 #[inline]
261 pub fn connect_timeout(mut self, connect_timeout: Duration) -> Self {
262 self.0.cached.connect_timeout = connect_timeout;
263 self
264 }
265
266 #[inline]
268 pub fn get_connect_timeout(&self) -> Duration {
269 self.0.cached.connect_timeout
270 }
271
272 #[inline]
278 pub fn read_timeout(mut self, read_timeout: Duration) -> Self {
279 self.0.cached.read_timeout = read_timeout;
280 self
281 }
282
283 #[inline]
285 pub fn get_read_timeout(&self) -> Duration {
286 self.0.cached.read_timeout
287 }
288
289 #[inline]
295 pub fn write_timeout(mut self, write_timeout: Duration) -> Self {
296 self.0.cached.write_timeout = write_timeout;
297 self
298 }
299
300 #[inline]
302 pub fn get_write_timeout(&self) -> Duration {
303 self.0.cached.write_timeout
304 }
305
306 #[inline]
313 pub fn backoff_slot_size(mut self, backoff_slot_size: Duration) -> Self {
314 self.0.cached.backoff_slot_size = backoff_slot_size;
315 self
316 }
317
318 #[inline]
320 pub fn get_backoff_slot_size(&self) -> Duration {
321 self.0.cached.backoff_slot_size
322 }
323
324 #[inline]
328 pub fn max_num_retries(mut self, max_num_retries: u32) -> Self {
329 self.0.cached.max_num_retries = max_num_retries;
330 self
331 }
332
333 #[inline]
335 pub fn get_max_num_retries(&self) -> u32 {
336 self.0.cached.max_num_retries
337 }
338
339 #[inline]
343 pub fn client_qos(mut self, client_qos: ClientQos) -> Self {
344 self.0.cached.client_qos = client_qos;
345 self
346 }
347
348 #[inline]
350 pub fn get_client_qos(&self) -> ClientQos {
351 self.0.cached.client_qos
352 }
353
354 #[inline]
358 pub fn server_qos(mut self, server_qos: ServerQos) -> Self {
359 self.0.cached.server_qos = server_qos;
360 self
361 }
362
363 #[inline]
365 pub fn get_server_qos(&self) -> ServerQos {
366 self.0.cached.server_qos
367 }
368
369 #[inline]
373 pub fn service_error(mut self, service_error: ServiceError) -> Self {
374 self.0.cached.service_error = service_error;
375 self
376 }
377
378 #[inline]
380 pub fn get_service_error(&self) -> ServiceError {
381 self.0.cached.service_error
382 }
383
384 #[inline]
390 pub fn idempotency(mut self, idempotency: Idempotency) -> Self {
391 self.0.cached.idempotency = idempotency;
392 self
393 }
394
395 #[inline]
397 pub fn get_idempotency(&self) -> Idempotency {
398 self.0.cached.idempotency
399 }
400
401 #[inline]
405 pub fn node_selection_strategy(
406 mut self,
407 node_selection_strategy: NodeSelectionStrategy,
408 ) -> Self {
409 self.0.cached.node_selection_strategy = node_selection_strategy;
410 self
411 }
412
413 #[inline]
415 pub fn get_node_selection_strategy(&self) -> NodeSelectionStrategy {
416 self.0.cached.node_selection_strategy
417 }
418
419 #[inline]
423 pub fn metrics(mut self, metrics: Arc<MetricRegistry>) -> Self {
424 self.0.uncached.metrics = Some(metrics);
425 self
426 }
427
428 #[inline]
430 pub fn get_metrics(&self) -> Option<&Arc<MetricRegistry>> {
431 self.0.uncached.metrics.as_ref()
432 }
433
434 #[inline]
438 pub fn host_metrics(mut self, host_metrics: Arc<HostMetricsRegistry>) -> Self {
439 self.0.uncached.host_metrics = Some(host_metrics);
440 self
441 }
442
443 #[inline]
445 pub fn get_host_metrics(&self) -> Option<&Arc<HostMetricsRegistry>> {
446 self.0.uncached.host_metrics.as_ref()
447 }
448
449 #[inline]
453 pub fn conjure_runtime(mut self, conjure_runtime: Arc<ConjureRuntime>) -> Self {
454 self.0.uncached.conjure_runtime = conjure_runtime;
455 self
456 }
457
458 pub fn get_conjure_runtime(&self) -> &Arc<ConjureRuntime> {
460 &self.0.uncached.conjure_runtime
461 }
462
463 #[inline]
465 pub fn override_host_index(mut self, override_host_index: usize) -> Self {
466 self.0.cached.override_host_index = Some(override_host_index);
467 self
468 }
469
470 #[inline]
472 pub fn get_override_host_index(&self) -> Option<usize> {
473 self.0.cached.override_host_index
474 }
475
476 pub(crate) fn mesh_mode(&self) -> bool {
477 self.0
478 .cached
479 .uris
480 .iter()
481 .any(|uri| uri.scheme().starts_with(MESH_PREFIX))
482 }
483
484 pub(crate) fn postprocessed_uris(&self) -> Result<Cow<'_, [Url]>, Error> {
485 if self.mesh_mode() {
486 if self.0.cached.uris.len() != 1 {
487 return Err(Error::internal_safe("mesh mode expects exactly one URI")
488 .with_safe_param("uris", &self.0.cached.uris));
489 }
490
491 let uri = self.0.cached.uris[0]
492 .as_str()
493 .strip_prefix(MESH_PREFIX)
494 .unwrap()
495 .parse()
496 .unwrap();
497
498 Ok(Cow::Owned(vec![uri]))
499 } else {
500 Ok(Cow::Borrowed(&self.0.cached.uris))
501 }
502 }
503
504 pub fn build(&self) -> Result<Client, Error> {
506 let state = ClientState::new(self)?;
507 Ok(Client::new(
508 Arc::new(ArcSwap::new(Arc::new(Cached::uncached(state)))),
509 None,
510 ))
511 }
512}
513
514#[cfg(not(target_arch = "wasm32"))]
515impl Builder<Complete> {
516 #[inline]
522 pub fn blocking_handle(mut self, blocking_handle: Handle) -> Self {
523 self.0.uncached.blocking_handle = Some(blocking_handle);
524 self
525 }
526
527 #[inline]
529 pub fn get_blocking_handle(&self) -> Option<&Handle> {
530 self.0.uncached.blocking_handle.as_ref()
531 }
532
533 pub fn build_blocking(&self) -> Result<blocking::Client, Error> {
535 self.build().map(|client| blocking::Client {
536 client,
537 handle: self.0.uncached.blocking_handle.clone(),
538 })
539 }
540}
541
542#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
544#[non_exhaustive]
545pub enum ClientQos {
546 Enabled,
550
551 DangerousDisableSympatheticClientQos,
556}
557
558#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
562#[non_exhaustive]
563pub enum ServerQos {
564 AutomaticRetry,
568
569 Propagate429And503ToCaller,
574}
575
576#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
580#[non_exhaustive]
581pub enum ServiceError {
582 WrapInNewError,
589
590 PropagateToCaller,
595}
596
597#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
599#[non_exhaustive]
600pub enum Idempotency {
601 Always,
603
604 ByMethod,
609
610 Never,
612}
613
614#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
616#[non_exhaustive]
617pub enum NodeSelectionStrategy {
618 PinUntilError,
626
627 PinUntilErrorWithoutReshuffle,
629
630 Balanced,
632}