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]
451 pub fn override_host_index(mut self, override_host_index: usize) -> Self {
452 self.0.cached.override_host_index = Some(override_host_index);
453 self
454 }
455
456 #[inline]
458 pub fn get_override_host_index(&self) -> Option<usize> {
459 self.0.cached.override_host_index
460 }
461
462 pub(crate) fn mesh_mode(&self) -> bool {
463 self.0
464 .cached
465 .uris
466 .iter()
467 .any(|uri| uri.scheme().starts_with(MESH_PREFIX))
468 }
469
470 pub(crate) fn postprocessed_uris(&self) -> Result<Cow<'_, [Url]>, Error> {
471 if self.mesh_mode() {
472 if self.0.cached.uris.len() != 1 {
473 return Err(Error::internal_safe("mesh mode expects exactly one URI")
474 .with_safe_param("uris", &self.0.cached.uris));
475 }
476
477 let uri = self.0.cached.uris[0]
478 .as_str()
479 .strip_prefix(MESH_PREFIX)
480 .unwrap()
481 .parse()
482 .unwrap();
483
484 Ok(Cow::Owned(vec![uri]))
485 } else {
486 Ok(Cow::Borrowed(&self.0.cached.uris))
487 }
488 }
489
490 pub fn build(&self) -> Result<Client, Error> {
492 let state = ClientState::new(self)?;
493 Ok(Client::new(
494 Arc::new(ArcSwap::new(Arc::new(Cached::uncached(state)))),
495 None,
496 ))
497 }
498}
499
500#[cfg(not(target_arch = "wasm32"))]
501impl Builder<Complete> {
502 #[inline]
508 pub fn blocking_handle(mut self, blocking_handle: Handle) -> Self {
509 self.0.uncached.blocking_handle = Some(blocking_handle);
510 self
511 }
512
513 #[inline]
515 pub fn get_blocking_handle(&self) -> Option<&Handle> {
516 self.0.uncached.blocking_handle.as_ref()
517 }
518
519 pub fn build_blocking(&self) -> Result<blocking::Client, Error> {
521 self.build().map(|client| blocking::Client {
522 client,
523 handle: self.0.uncached.blocking_handle.clone(),
524 })
525 }
526}
527
528#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
530#[non_exhaustive]
531pub enum ClientQos {
532 Enabled,
536
537 DangerousDisableSympatheticClientQos,
542}
543
544#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
548#[non_exhaustive]
549pub enum ServerQos {
550 AutomaticRetry,
554
555 Propagate429And503ToCaller,
560}
561
562#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
566#[non_exhaustive]
567pub enum ServiceError {
568 WrapInNewError,
575
576 PropagateToCaller,
581}
582
583#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
585#[non_exhaustive]
586pub enum Idempotency {
587 Always,
589
590 ByMethod,
595
596 Never,
598}
599
600#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
602#[non_exhaustive]
603pub enum NodeSelectionStrategy {
604 PinUntilError,
612
613 PinUntilErrorWithoutReshuffle,
615
616 Balanced,
618}