1mod callopt;
9pub mod dns;
10mod meta;
11
12use std::{cell::RefCell, marker::PhantomData, sync::Arc, time::Duration};
13
14pub use callopt::CallOpt;
15pub use meta::MetaService;
16use motore::{
17 ServiceExt,
18 layer::{Identity, Layer, Stack},
19 service::{BoxCloneService, Service},
20};
21use volo::{
22 FastStr,
23 client::{MkClient, WithOptService},
24 context::{Endpoint, Role, RpcInfo},
25 discovery::Discover,
26 loadbalance::{MkLbLayer, random::WeightedRandomBalance},
27 net::Address,
28};
29
30use self::{dns::DnsResolver, layer::timeout::TimeoutLayer};
31use crate::{
32 Request, Response, Status,
33 codec::compression::CompressionEncoding,
34 context::{ClientContext, Config},
35 layer::loadbalance::LbConfig,
36 transport::ClientTransport,
37};
38pub mod layer;
39
40pub struct ClientBuilder<IL, OL, C, LB, T, U> {
42 http2_config: Http2Config,
43 rpc_config: Config,
44 callee_name: FastStr,
45 caller_name: FastStr,
46 target: Option<Address>,
48 inner_layer: IL,
49 outer_layer: OL,
50 mk_client: C,
51 mk_lb: LB,
52 _marker: PhantomData<fn(T, U)>,
53
54 #[cfg(feature = "__tls")]
55 tls_config: Option<volo::net::tls::ClientTlsConfig>,
56}
57
58impl<C, T, U>
59 ClientBuilder<
60 Identity,
61 Identity,
62 C,
63 LbConfig<WeightedRandomBalance<<DnsResolver as Discover>::Key>, DnsResolver>,
64 T,
65 U,
66 >
67{
68 pub fn new(service_client: C, service_name: impl AsRef<str>) -> Self {
70 Self {
71 http2_config: Default::default(),
72 rpc_config: Default::default(),
73 callee_name: FastStr::new(service_name),
74 caller_name: "".into(),
75 target: None,
76 inner_layer: Identity::new(),
77 outer_layer: Identity::new(),
78 mk_client: service_client,
79 mk_lb: LbConfig::new(WeightedRandomBalance::new(), DnsResolver::default()),
80 _marker: PhantomData,
81
82 #[cfg(feature = "__tls")]
83 tls_config: None,
84 }
85 }
86}
87
88impl<IL, OL, C, LB, T, U, DISC> ClientBuilder<IL, OL, C, LbConfig<LB, DISC>, T, U> {
89 pub fn load_balance<NLB>(
90 self,
91 load_balance: NLB,
92 ) -> ClientBuilder<IL, OL, C, LbConfig<NLB, DISC>, T, U> {
93 ClientBuilder {
94 http2_config: self.http2_config,
95 rpc_config: self.rpc_config,
96 callee_name: self.callee_name,
97 caller_name: self.caller_name,
98 target: self.target,
99 inner_layer: self.inner_layer,
100 outer_layer: self.outer_layer,
101 mk_client: self.mk_client,
102 mk_lb: self.mk_lb.load_balance(load_balance),
103 _marker: PhantomData,
104
105 #[cfg(feature = "__tls")]
106 tls_config: self.tls_config,
107 }
108 }
109
110 pub fn discover<NDISC>(
111 self,
112 discover: NDISC,
113 ) -> ClientBuilder<IL, OL, C, LbConfig<LB, NDISC>, T, U> {
114 ClientBuilder {
115 http2_config: self.http2_config,
116 rpc_config: self.rpc_config,
117 callee_name: self.callee_name,
118 caller_name: self.caller_name,
119 target: self.target,
120 inner_layer: self.inner_layer,
121 outer_layer: self.outer_layer,
122 mk_client: self.mk_client,
123 mk_lb: self.mk_lb.discover(discover),
124 _marker: PhantomData,
125
126 #[cfg(feature = "__tls")]
127 tls_config: self.tls_config,
128 }
129 }
130}
131
132impl<IL, OL, C, LB, T, U> ClientBuilder<IL, OL, C, LB, T, U> {
133 pub fn rpc_timeout(mut self, timeout: Option<Duration>) -> Self {
139 self.rpc_config.set_rpc_timeout(timeout);
140 self
141 }
142 pub fn http2_init_stream_window_size(mut self, sz: impl Into<u32>) -> Self {
147 self.http2_config.init_stream_window_size = sz.into();
148 self
149 }
150
151 pub fn http2_init_connection_window_size(mut self, sz: impl Into<u32>) -> Self {
155 self.http2_config.init_connection_window_size = sz.into();
156 self
157 }
158
159 pub fn http2_adaptive_window(mut self, enabled: bool) -> Self {
167 self.http2_config.adaptive_window = enabled;
168 self
169 }
170
171 pub fn http2_max_frame_size(mut self, sz: impl Into<u32>) -> Self {
175 self.http2_config.max_frame_size = sz.into();
176 self
177 }
178
179 pub fn http2_keepalive_interval(mut self, interval: impl Into<Option<Duration>>) -> Self {
184 self.http2_config.http2_keepalive_interval = interval.into();
185 self
186 }
187
188 pub fn http2_keepalive_timeout(mut self, timeout: Duration) -> Self {
195 self.http2_config.http2_keepalive_timeout = timeout;
196 self
197 }
198
199 pub fn http2_keepalive_while_idle(mut self, enabled: bool) -> Self {
208 self.http2_config.http2_keepalive_while_idle = enabled;
209 self
210 }
211
212 pub fn http2_max_concurrent_reset_streams(mut self, sz: impl Into<usize>) -> Self {
216 self.http2_config.max_concurrent_reset_streams = sz.into();
217 self
218 }
219
220 pub fn http2_max_send_buf_size(mut self, max: impl Into<usize>) -> Self {
226 self.http2_config.max_send_buf_size = max.into();
227 self
228 }
229
230 #[deprecated(
235 since = "0.9.0",
236 note = "`retry_canceled_requests` has been removed in `hyper`"
237 )]
238 pub fn retry_canceled_requests(self, _enabled: bool) -> Self {
239 self
240 }
241
242 #[deprecated(
246 since = "0.9.0",
247 note = "accepting http1 connection was not supported by `hyper`"
248 )]
249 pub fn accept_http1(self, _accept_http1: bool) -> Self {
250 self
251 }
252
253 pub fn connect_timeout(mut self, timeout: Duration) -> Self {
257 self.rpc_config.connect_timeout = Some(timeout);
258 self
259 }
260
261 pub fn read_timeout(mut self, timeout: Duration) -> Self {
265 self.rpc_config.read_timeout = Some(timeout);
266 self
267 }
268
269 pub fn write_timeout(mut self, timeout: Duration) -> Self {
273 self.rpc_config.write_timeout = Some(timeout);
274 self
275 }
276
277 pub fn caller_name(mut self, name: impl AsRef<str>) -> Self {
281 self.caller_name = FastStr::new(name);
282 self
283 }
284
285 pub fn send_compressions(mut self, config: Vec<CompressionEncoding>) -> Self {
290 self.rpc_config.send_compressions = Some(config);
291 self
292 }
293
294 pub fn accept_compressions(mut self, config: Vec<CompressionEncoding>) -> Self {
299 self.rpc_config.accept_compressions = Some(config);
300 self
301 }
302
303 pub fn mk_load_balance<NLB>(self, mk_load_balance: NLB) -> ClientBuilder<IL, OL, C, NLB, T, U> {
304 ClientBuilder {
305 http2_config: self.http2_config,
306 rpc_config: self.rpc_config,
307 callee_name: self.callee_name,
308 caller_name: self.caller_name,
309 target: self.target,
310 inner_layer: self.inner_layer,
311 outer_layer: self.outer_layer,
312 mk_client: self.mk_client,
313 mk_lb: mk_load_balance,
314 _marker: PhantomData,
315
316 #[cfg(feature = "__tls")]
317 tls_config: self.tls_config,
318 }
319 }
320
321 pub fn address<A: Into<Address>>(mut self, target: A) -> Self {
327 self.target = Some(target.into());
328 self
329 }
330
331 pub fn layer_inner<Inner>(
345 self,
346 layer: Inner,
347 ) -> ClientBuilder<Stack<Inner, IL>, OL, C, LB, T, U> {
348 ClientBuilder {
349 http2_config: self.http2_config,
350 rpc_config: self.rpc_config,
351 callee_name: self.callee_name,
352 caller_name: self.caller_name,
353 target: self.target,
354 inner_layer: Stack::new(layer, self.inner_layer),
355 outer_layer: self.outer_layer,
356 mk_client: self.mk_client,
357 mk_lb: self.mk_lb,
358 _marker: self._marker,
359
360 #[cfg(feature = "__tls")]
361 tls_config: self.tls_config,
362 }
363 }
364
365 pub fn layer_inner_front<Inner>(
379 self,
380 layer: Inner,
381 ) -> ClientBuilder<Stack<IL, Inner>, OL, C, LB, T, U> {
382 ClientBuilder {
383 http2_config: self.http2_config,
384 rpc_config: self.rpc_config,
385 callee_name: self.callee_name,
386 caller_name: self.caller_name,
387 target: self.target,
388 inner_layer: Stack::new(self.inner_layer, layer),
389 outer_layer: self.outer_layer,
390 mk_client: self.mk_client,
391 mk_lb: self.mk_lb,
392 _marker: self._marker,
393
394 #[cfg(feature = "__tls")]
395 tls_config: self.tls_config,
396 }
397 }
398
399 pub fn layer_outer<Outer>(
413 self,
414 layer: Outer,
415 ) -> ClientBuilder<IL, Stack<Outer, OL>, C, LB, T, U> {
416 ClientBuilder {
417 http2_config: self.http2_config,
418 rpc_config: self.rpc_config,
419 callee_name: self.callee_name,
420 caller_name: self.caller_name,
421 target: self.target,
422 inner_layer: self.inner_layer,
423 outer_layer: Stack::new(layer, self.outer_layer),
424 mk_client: self.mk_client,
425 mk_lb: self.mk_lb,
426 _marker: self._marker,
427
428 #[cfg(feature = "__tls")]
429 tls_config: self.tls_config,
430 }
431 }
432
433 pub fn layer_outer_front<Outer>(
447 self,
448 layer: Outer,
449 ) -> ClientBuilder<IL, Stack<OL, Outer>, C, LB, T, U> {
450 ClientBuilder {
451 http2_config: self.http2_config,
452 rpc_config: self.rpc_config,
453 callee_name: self.callee_name,
454 caller_name: self.caller_name,
455 target: self.target,
456 inner_layer: self.inner_layer,
457 outer_layer: Stack::new(self.outer_layer, layer),
458 mk_client: self.mk_client,
459 mk_lb: self.mk_lb,
460 _marker: self._marker,
461
462 #[cfg(feature = "__tls")]
463 tls_config: self.tls_config,
464 }
465 }
466
467 #[cfg(feature = "__tls")]
471 #[cfg_attr(docsrs, doc(cfg(any(feature = "rustls", feature = "native-tls"))))]
472 pub fn tls_config(mut self, tls_config: volo::net::tls::ClientTlsConfig) -> Self {
473 self.tls_config = Some(tls_config);
474 self
475 }
476}
477
478impl<IL, OL, C, LB, T, U> ClientBuilder<IL, OL, C, LB, T, U>
479where
480 C: MkClient<Client<BoxCloneService<ClientContext, Request<T>, Response<U>, Status>>>,
481 LB: MkLbLayer,
482 LB::Layer: Layer<IL::Service>,
483 <LB::Layer as Layer<IL::Service>>::Service:
484 Service<ClientContext, Request<T>, Response = Response<U>> + 'static + Send + Clone + Sync,
485 <<LB::Layer as Layer<IL::Service>>::Service as Service<ClientContext, Request<T>>>::Error:
486 Into<Status>,
487 IL: Layer<MetaService<ClientTransport<U>>>,
488 IL::Service:
489 Service<ClientContext, Request<T>, Response = Response<U>> + 'static + Send + Clone + Sync,
490 <IL::Service as Service<ClientContext, Request<T>>>::Error: Into<Status>,
491 OL:
492 Layer<
493 BoxCloneService<
494 ClientContext,
495 Request<T>,
496 Response<U>,
497 <<LB::Layer as Layer<IL::Service>>::Service as Service<
498 ClientContext,
499 Request<T>,
500 >>::Error,
501 >,
502 >,
503 OL::Service:
504 Service<ClientContext, Request<T>, Response = Response<U>> + 'static + Send + Clone + Sync,
505 <OL::Service as Service<ClientContext, Request<T>>>::Error: Send + Into<Status>,
506 T: 'static + Send,
507{
508 pub fn build(self) -> C::Target {
510 #[cfg(not(feature = "__tls"))]
511 let transport =
512 MetaService::new(ClientTransport::new(&self.http2_config, &self.rpc_config));
513 #[cfg(feature = "__tls")]
514 let transport = match self.tls_config {
515 Some(tls_config) => MetaService::new(ClientTransport::new_with_tls(
516 &self.http2_config,
517 &self.rpc_config,
518 tls_config,
519 )),
520 None => MetaService::new(ClientTransport::new(&self.http2_config, &self.rpc_config)),
521 };
522
523 let transport = self.outer_layer.layer(BoxCloneService::new(
524 self.mk_lb.make().layer(self.inner_layer.layer(transport)),
525 ));
526
527 let transport = transport.map_err(|err| err.into());
528 let transport = TimeoutLayer::new().layer(transport);
529 let transport = BoxCloneService::new(transport);
530
531 self.mk_client.mk_client(Client {
532 inner: Arc::new(ClientInner {
533 callee_name: self.callee_name,
534 caller_name: self.caller_name,
535 rpc_config: self.rpc_config,
536 target: self.target,
537 }),
538 transport,
539 })
540 }
541}
542
543#[derive(Debug)]
544struct ClientInner {
546 callee_name: FastStr,
547 caller_name: FastStr,
548 rpc_config: Config,
549 target: Option<Address>,
550}
551
552#[derive(Clone)]
558pub struct Client<S> {
559 transport: S,
560 inner: Arc<ClientInner>,
561}
562
563impl<S> Client<S> {
564 pub fn make_cx(&self, path: &'static str) -> ClientContext {
565 ClientContext::new(self.make_rpc_info(path))
566 }
567
568 fn make_rpc_info(&self, method: &'static str) -> RpcInfo<Config> {
569 let caller = Endpoint::new(self.inner.caller_name.clone());
570 let mut callee = Endpoint::new(self.inner.callee_name.clone());
571 if let Some(target) = &self.inner.target {
572 callee.set_address(target.clone());
573 }
574 RpcInfo::new(
575 Role::Client,
576 method.into(),
577 caller,
578 callee,
579 self.inner.rpc_config.clone(),
580 )
581 }
582
583 pub fn with_opt<Opt>(self, opt: Opt) -> Client<WithOptService<S, Opt>> {
584 Client {
585 transport: WithOptService::new(self.transport, opt),
586 inner: self.inner,
587 }
588 }
589}
590
591macro_rules! impl_client {
592 (($self: ident, &mut $cx:ident, $req: ident) => async move $e: tt ) => {
593 impl<S, Req: Send + 'static>
594 volo::service::Service<crate::context::ClientContext, Req> for Client<S>
595 where
596 S: volo::service::Service<
597 crate::context::ClientContext,
598 Req,
599 Error = crate::Status,
600 > + Sync
601 + Send
602 + 'static,
603 {
604 type Response = S::Response;
605 type Error = S::Error;
606
607 async fn call(
608 &$self,
609 $cx: &mut crate::context::ClientContext,
610 $req: Req,
611 ) -> Result<Self::Response, Self::Error> {
612 $e
613 }
614 }
615
616 impl<S, Req: Send + 'static>
617 volo::client::OneShotService<crate::context::ClientContext, Req> for Client<S>
618 where
619 S: volo::client::OneShotService<
620 crate::context::ClientContext,
621 Req,
622 Error = crate::Status,
623 > + Sync
624 + Send
625 + 'static,
626 {
627 type Response = S::Response;
628 type Error = S::Error;
629
630 async fn call(
631 $self,
632 $cx: &mut crate::context::ClientContext,
633 $req: Req,
634 ) -> Result<Self::Response, Self::Error> {
635 $e
636 }
637 }
638 };
639}
640
641impl_client!((self, &mut cx, req) => async move {
642 let has_metainfo = metainfo::METAINFO.try_with(|_| {}).is_ok();
643
644 let mk_call = async { self.transport.call(cx, req).await };
645
646 if has_metainfo {
647 mk_call.await
648 } else {
649 metainfo::METAINFO
650 .scope(RefCell::new(metainfo::MetaInfo::default()), mk_call)
651 .await
652 }
653});
654
655const DEFAULT_STREAM_WINDOW_SIZE: u32 = 1024 * 1024 * 2; const DEFAULT_CONN_WINDOW_SIZE: u32 = 1024 * 1024 * 5; const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16; const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 1024; const DEFAULT_KEEPALIVE_TIMEOUT_SECS: Duration = Duration::from_secs(20); const DEFAULT_MAX_CONCURRENT_RESET_STREAMS: usize = 10;
661
662#[derive(Debug, Clone, Copy)]
664pub struct Http2Config {
665 pub(crate) init_stream_window_size: u32,
666 pub(crate) init_connection_window_size: u32,
667 pub(crate) adaptive_window: bool,
668 pub(crate) max_frame_size: u32,
669 pub(crate) http2_keepalive_interval: Option<Duration>,
670 pub(crate) http2_keepalive_timeout: Duration,
671 pub(crate) http2_keepalive_while_idle: bool,
672 pub(crate) max_concurrent_reset_streams: usize,
673 pub(crate) max_send_buf_size: usize,
674}
675
676impl Default for Http2Config {
677 fn default() -> Self {
678 Self {
679 init_stream_window_size: DEFAULT_STREAM_WINDOW_SIZE,
680 init_connection_window_size: DEFAULT_CONN_WINDOW_SIZE,
681 adaptive_window: false,
682 max_frame_size: DEFAULT_MAX_FRAME_SIZE,
683 http2_keepalive_interval: None,
684 http2_keepalive_timeout: DEFAULT_KEEPALIVE_TIMEOUT_SECS,
685 http2_keepalive_while_idle: false,
686 max_concurrent_reset_streams: DEFAULT_MAX_CONCURRENT_RESET_STREAMS,
687 max_send_buf_size: DEFAULT_MAX_SEND_BUF_SIZE,
688 }
689 }
690}