conjure_runtime_rustls_platform_verifier/
client_factory.rs1#[cfg(not(target_arch = "wasm32"))]
16use crate::blocking;
17use crate::builder::{CachedConfig, UncachedConfig};
18use crate::config::{ServiceConfig, ServicesConfig};
19use crate::weak_cache::{Cached, WeakCache};
20use crate::{Builder, ClientState, Host, PerHostClients};
21use crate::{
22 Client, ClientQos, HostMetricsRegistry, Idempotency, NodeSelectionStrategy, ServerQos,
23 ServiceError, UserAgent,
24};
25use arc_swap::ArcSwap;
26use conjure_error::Error;
27#[cfg(not(target_arch = "wasm32"))]
28use conjure_http::client::Service;
29use conjure_http::client::{AsyncService, ConjureRuntime};
30use conjure_runtime_config::service_config;
31use refreshable::{RefreshHandle, Refreshable};
32use std::borrow::Borrow;
33use std::collections::HashMap;
34use std::sync::Arc;
35#[cfg(not(target_arch = "wasm32"))]
36use tokio::runtime::Handle;
37use witchcraft_log::warn;
38use witchcraft_metrics::MetricRegistry;
39
40const STATE_CACHE_CAPACITY: usize = 10_000;
41
42#[derive(Clone)]
44pub struct ClientFactory<T = Complete>(T);
45
46pub struct ConfigStage(());
48
49pub struct UserAgentStage {
51 config: Arc<Refreshable<ServicesConfig, Error>>,
52}
53
54#[derive(Clone)]
55struct CacheManager {
56 uncached_inner: UncachedConfig,
57 cache: WeakCache<CachedConfig, ClientState>,
58}
59
60impl CacheManager {
61 fn uncached(&self) -> &UncachedConfig {
62 &self.uncached_inner
63 }
64
65 fn uncached_mut(&mut self) -> &mut UncachedConfig {
66 self.cache = WeakCache::new(STATE_CACHE_CAPACITY);
67 &mut self.uncached_inner
68 }
69}
70
71#[derive(Clone)]
73pub struct Complete {
74 config: Arc<Refreshable<ServicesConfig, Error>>,
75 user_agent: UserAgent,
76 client_qos: ClientQos,
77 server_qos: ServerQos,
78 service_error: ServiceError,
79 idempotency: Idempotency,
80 node_selection_strategy: NodeSelectionStrategy,
81 cache_manager: CacheManager,
82}
83
84impl Default for ClientFactory<ConfigStage> {
85 #[inline]
86 fn default() -> Self {
87 ClientFactory::builder()
88 }
89}
90
91impl ClientFactory<ConfigStage> {
92 #[inline]
94 pub fn builder() -> Self {
95 ClientFactory(ConfigStage(()))
96 }
97
98 #[inline]
100 pub fn config(
101 self,
102 config: Refreshable<ServicesConfig, Error>,
103 ) -> ClientFactory<UserAgentStage> {
104 ClientFactory(UserAgentStage {
105 config: Arc::new(config),
106 })
107 }
108}
109
110impl ClientFactory<UserAgentStage> {
111 #[inline]
113 pub fn user_agent(self, user_agent: UserAgent) -> ClientFactory {
114 ClientFactory(Complete {
115 config: self.0.config,
116 user_agent,
117 client_qos: ClientQos::Enabled,
118 server_qos: ServerQos::AutomaticRetry,
119 service_error: ServiceError::WrapInNewError,
120 idempotency: Idempotency::ByMethod,
121 node_selection_strategy: NodeSelectionStrategy::PinUntilError,
122 cache_manager: CacheManager {
123 uncached_inner: UncachedConfig {
124 metrics: None,
125 host_metrics: None,
126 #[cfg(not(target_arch = "wasm32"))]
127 blocking_handle: None,
128 conjure_runtime: Arc::new(ConjureRuntime::new()),
129 client_cert_resolver: None,
130 },
131 cache: WeakCache::new(STATE_CACHE_CAPACITY),
132 },
133 })
134 }
135}
136
137impl ClientFactory {
138 #[inline]
140 pub fn user_agent(mut self, user_agent: UserAgent) -> Self {
141 self.0.user_agent = user_agent;
142 self
143 }
144
145 #[inline]
147 pub fn get_user_agent(&self) -> &UserAgent {
148 &self.0.user_agent
149 }
150
151 #[inline]
155 pub fn client_qos(mut self, client_qos: ClientQos) -> Self {
156 self.0.client_qos = client_qos;
157 self
158 }
159
160 #[inline]
162 pub fn get_client_qos(&self) -> ClientQos {
163 self.0.client_qos
164 }
165
166 #[inline]
170 pub fn server_qos(mut self, server_qos: ServerQos) -> Self {
171 self.0.server_qos = server_qos;
172 self
173 }
174
175 #[inline]
177 pub fn get_server_qos(&self) -> ServerQos {
178 self.0.server_qos
179 }
180
181 #[inline]
185 pub fn service_error(mut self, service_error: ServiceError) -> Self {
186 self.0.service_error = service_error;
187 self
188 }
189
190 #[inline]
192 pub fn get_service_error(&self) -> ServiceError {
193 self.0.service_error
194 }
195
196 #[inline]
202 pub fn idempotency(mut self, idempotency: Idempotency) -> Self {
203 self.0.idempotency = idempotency;
204 self
205 }
206
207 #[inline]
209 pub fn get_idempotency(&self) -> Idempotency {
210 self.0.idempotency
211 }
212
213 #[inline]
217 pub fn node_selection_strategy(
218 mut self,
219 node_selection_strategy: NodeSelectionStrategy,
220 ) -> Self {
221 self.0.node_selection_strategy = node_selection_strategy;
222 self
223 }
224
225 #[inline]
227 pub fn get_node_selection_strategy(&self) -> NodeSelectionStrategy {
228 self.0.node_selection_strategy
229 }
230
231 #[inline]
235 pub fn metrics(mut self, metrics: Arc<MetricRegistry>) -> Self {
236 self.0.cache_manager.uncached_mut().metrics = Some(metrics);
237 self
238 }
239
240 #[inline]
242 pub fn get_metrics(&self) -> Option<&Arc<MetricRegistry>> {
243 self.0.cache_manager.uncached().metrics.as_ref()
244 }
245
246 #[inline]
250 pub fn host_metrics(mut self, host_metrics: Arc<HostMetricsRegistry>) -> Self {
251 self.0.cache_manager.uncached_mut().host_metrics = Some(host_metrics);
252 self
253 }
254
255 #[inline]
257 pub fn get_host_metrics(&self) -> Option<&Arc<HostMetricsRegistry>> {
258 self.0.cache_manager.uncached().host_metrics.as_ref()
259 }
260
261 #[inline]
265 pub fn conjure_runtime(mut self, conjure_runtime: Arc<ConjureRuntime>) -> Self {
266 self.0.cache_manager.uncached_mut().conjure_runtime = conjure_runtime;
267 self
268 }
269
270 pub fn get_conjure_runtime(&self) -> &Arc<ConjureRuntime> {
272 &self.0.cache_manager.uncached().conjure_runtime
273 }
274
275 fn state_builder(&self, service: &str) -> StateBuilder {
276 StateBuilder {
277 service: service.to_string(),
278 user_agent: self.0.user_agent.clone(),
279 client_qos: self.0.client_qos,
280 server_qos: self.0.server_qos,
281 service_error: self.0.service_error,
282 idempotency: self.0.idempotency,
283 node_selection_strategy: self.0.node_selection_strategy,
284 cache_manager: self.0.cache_manager.clone(),
285 }
286 }
287
288 pub fn client<T>(&self, service: &str) -> Result<T, Error>
298 where
299 T: AsyncService<Client>,
300 {
301 self.client_inner(service)
302 .map(|c| T::new(c, &self.0.cache_manager.uncached().conjure_runtime))
303 }
304
305 fn client_inner(&self, service: &str) -> Result<Client, Error> {
306 let service_config = self.0.config.map({
307 let service = service.to_string();
308 move |c| c.merged_service(&service).unwrap_or_default()
309 });
310 let state_builder = self.state_builder(service);
311 Self::raw_client(state_builder, service_config, None)
312 }
313
314 fn raw_client<T>(
315 state_builder: T,
316 service_config: Refreshable<ServiceConfig, Error>,
317 override_host_index: Option<usize>,
318 ) -> Result<Client, Error>
319 where
320 T: Borrow<StateBuilder> + 'static + Sync + Send,
321 {
322 let state = state_builder
323 .borrow()
324 .build(&service_config.get(), override_host_index)?;
325 let state = Arc::new(ArcSwap::new(state));
326
327 let subscription = service_config.try_subscribe({
328 let state = state.clone();
329 move |config| {
330 let new_state = state_builder.borrow().build(config, override_host_index)?;
331 state.store(new_state);
332 Ok(())
333 }
334 })?;
335
336 Ok(Client::new(state, Some(subscription)))
337 }
338
339 pub fn per_host_clients<T>(
347 &self,
348 service: &str,
349 ) -> Result<Refreshable<PerHostClients<T>, Error>, Error>
350 where
351 T: AsyncService<Client> + 'static + Sync + Send,
352 {
353 self.per_host_clients_inner(service, T::new)
354 }
355
356 fn per_host_clients_inner<T>(
357 &self,
358 service: &str,
359 make_client: impl Fn(Client, &Arc<ConjureRuntime>) -> T + 'static + Sync + Send,
360 ) -> Result<Refreshable<PerHostClients<T>, Error>, Error>
361 where
362 T: 'static + Sync + Send,
363 {
364 let state_builder = Arc::new(self.state_builder(service));
365
366 self.0
367 .config
368 .map({
369 let service = service.to_string();
370 move |c| c.merged_service(&service).unwrap_or_default()
371 })
372 .try_map({
373 let mut client_handles = HashMap::<Host, ClientHandle>::new();
374 move |c| {
375 let mut new_client_handles = HashMap::new();
376 let mut clients = HashMap::new();
377 let mut errors = vec![];
378
379 for (i, uri) in c.uris().iter().enumerate() {
380 let host = Host { uri: uri.clone() };
381 let host_config = service_config::Builder::from(c.clone())
382 .uris([uri.clone()])
383 .build();
384
385 let client_handle = match client_handles.remove(&host) {
386 Some(mut client_handle) => {
387 if let Err(es) = client_handle.handle.refresh(host_config) {
388 errors.extend(es);
389 }
390 client_handle
391 }
392 None => {
393 let (host_config, handle) = Refreshable::new(host_config);
394 let client = match Self::raw_client(
395 state_builder.clone(),
396 host_config,
397 Some(i),
398 ) {
399 Ok(state) => state,
400 Err(e) => {
401 errors.push(e);
402 continue;
403 }
404 };
405 ClientHandle { client, handle }
406 }
407 };
408
409 clients.insert(
410 host.clone(),
411 make_client(
412 client_handle.client.clone(),
413 &state_builder.cache_manager.uncached().conjure_runtime,
414 ),
415 );
416 new_client_handles.insert(host, client_handle);
417 }
418
419 client_handles = new_client_handles;
420 match errors.pop() {
421 Some(e) => {
422 for e2 in errors {
423 warn!("error reloading per-host clients", error: e2);
424 }
425 Err(e)
426 }
427 None => Ok(PerHostClients { clients }),
428 }
429 }
430 })
431 }
432}
433
434#[cfg(not(target_arch = "wasm32"))]
435impl ClientFactory {
436 #[inline]
442 pub fn blocking_handle(mut self, blocking_handle: Handle) -> Self {
443 self.0.cache_manager.uncached_mut().blocking_handle = Some(blocking_handle);
444 self
445 }
446
447 #[inline]
449 pub fn get_blocking_handle(&self) -> Option<&Handle> {
450 self.0.cache_manager.uncached().blocking_handle.as_ref()
451 }
452
453 pub fn blocking_client<T>(&self, service: &str) -> Result<T, Error>
463 where
464 T: Service<blocking::Client>,
465 {
466 self.blocking_client_inner(service)
467 .map(|c| T::new(c, &self.0.cache_manager.uncached().conjure_runtime))
468 }
469
470 fn blocking_client_inner(&self, service: &str) -> Result<blocking::Client, Error> {
471 self.client_inner(service).map(|client| blocking::Client {
472 client,
473 handle: self.0.cache_manager.uncached().blocking_handle.clone(),
474 })
475 }
476
477 pub fn blocking_per_host_clients<T>(
485 &self,
486 service: &str,
487 ) -> Result<Refreshable<PerHostClients<T>, Error>, Error>
488 where
489 T: Service<blocking::Client> + 'static + Sync + Send,
490 {
491 self.per_host_clients_inner(service, {
492 let handle = self.0.cache_manager.uncached().blocking_handle.clone();
493 move |client, runtime| {
494 T::new(
495 blocking::Client {
496 client,
497 handle: handle.clone(),
498 },
499 runtime,
500 )
501 }
502 })
503 }
504}
505
506struct ClientHandle {
507 client: Client,
508 handle: RefreshHandle<ServiceConfig, Error>,
509}
510
511struct StateBuilder {
512 service: String,
513 user_agent: UserAgent,
514 client_qos: ClientQos,
515 server_qos: ServerQos,
516 service_error: ServiceError,
517 idempotency: Idempotency,
518 node_selection_strategy: NodeSelectionStrategy,
519 cache_manager: CacheManager,
520}
521
522impl StateBuilder {
523 fn build(
524 &self,
525 config: &ServiceConfig,
526 override_host_index: Option<usize>,
527 ) -> Result<Arc<Cached<CachedConfig, ClientState>>, Error> {
528 let mut builder = Client::builder()
529 .service(&self.service)
530 .user_agent(self.user_agent.clone())
531 .from_config(config)
532 .client_qos(self.client_qos)
533 .server_qos(self.server_qos)
534 .service_error(self.service_error)
535 .idempotency(self.idempotency)
536 .node_selection_strategy(self.node_selection_strategy)
537 .conjure_runtime(self.cache_manager.uncached().conjure_runtime.clone());
538
539 if let Some(metrics) = self.cache_manager.uncached().metrics.clone() {
540 builder = builder.metrics(metrics);
541 }
542
543 if let Some(host_metrics) = self.cache_manager.uncached().host_metrics.clone() {
544 builder = builder.host_metrics(host_metrics);
545 }
546
547 if let Some(override_host_index) = override_host_index {
548 builder = builder.override_host_index(override_host_index);
549 }
550
551 self.cache_manager
552 .cache
553 .get(&builder, Builder::cached_config, ClientState::new)
554 }
555}