1use std::{
6 cell::RefCell,
7 error::Error,
8 future::Future,
9 sync::{Arc, LazyLock},
10 time::Duration,
11};
12
13use http::{
14 header::{HeaderMap, HeaderName, HeaderValue},
15 method::Method,
16 uri::Uri,
17};
18use metainfo::{METAINFO, MetaInfo};
19use motore::{
20 layer::{Identity, Layer, Stack},
21 service::{BoxService, Service},
22};
23use paste::paste;
24use volo::{
25 client::{MkClient, OneShotService},
26 context::Context,
27 loadbalance::MkLbLayer,
28 net::dial::{DefaultMakeTransport, MakeTransport},
29};
30
31use self::{
32 layer::{
33 Timeout,
34 header::{Host, UserAgent},
35 },
36 loadbalance::{DefaultLb, LbConfig},
37 transport::{
38 pool,
39 protocol::{ClientConfig, ClientTransport, ClientTransportConfig},
40 },
41};
42use crate::{
43 body::Body,
44 context::ClientContext,
45 error::{
46 BoxError, ClientError,
47 client::{Result, builder_error},
48 },
49 request::Request,
50 response::Response,
51};
52
53mod callopt;
54#[cfg(test)]
55mod client_tests;
56#[cfg(feature = "cookie")]
57pub mod cookie;
58pub mod dns;
59pub mod layer;
60pub mod loadbalance;
61mod request_builder;
62pub mod target;
63#[cfg(test)]
64pub mod test_helpers;
65pub mod transport;
66mod utils;
67
68pub use self::{
69 callopt::CallOpt, request_builder::RequestBuilder, target::Target, transport::protocol,
70};
71
72#[doc(hidden)]
73pub mod prelude {
74 pub use super::{Client, ClientBuilder};
75}
76
77pub struct ClientBuilder<IL = Identity, OL = Identity, C = DefaultMkClient, LB = DefaultLb> {
79 http_config: ClientConfig,
80 client_config: ClientTransportConfig,
81 pool_config: pool::Config,
82 connector: DefaultMakeTransport,
83 timeout: Option<Duration>,
84 user_agent: Option<HeaderValue>,
85 host_mode: Host,
86 headers: HeaderMap,
87 inner_layer: IL,
88 outer_layer: OL,
89 mk_client: C,
90 mk_lb: LB,
91 status: Result<()>,
92 #[cfg(feature = "__tls")]
93 tls_config: Option<volo::net::tls::TlsConnector>,
94}
95
96impl ClientBuilder<Identity, Identity, DefaultMkClient, DefaultLb> {
97 pub fn new() -> Self {
99 Self {
100 http_config: Default::default(),
101 client_config: Default::default(),
102 pool_config: pool::Config::default(),
103 connector: Default::default(),
104 timeout: None,
105 user_agent: None,
106 host_mode: Host::Auto,
107 headers: Default::default(),
108 inner_layer: Identity::new(),
109 outer_layer: Identity::new(),
110 mk_client: DefaultMkClient,
111 mk_lb: Default::default(),
112 status: Ok(()),
113 #[cfg(feature = "__tls")]
114 tls_config: None,
115 }
116 }
117}
118
119impl Default for ClientBuilder<Identity, Identity, DefaultMkClient, DefaultLb> {
120 fn default() -> Self {
121 Self::new()
122 }
123}
124
125impl<IL, OL, C, LB, DISC> ClientBuilder<IL, OL, C, LbConfig<LB, DISC>> {
126 pub fn load_balance<NLB>(
128 self,
129 load_balance: NLB,
130 ) -> ClientBuilder<IL, OL, C, LbConfig<NLB, DISC>> {
131 ClientBuilder {
132 http_config: self.http_config,
133 client_config: self.client_config,
134 pool_config: self.pool_config,
135 connector: self.connector,
136 timeout: self.timeout,
137 user_agent: self.user_agent,
138 host_mode: self.host_mode,
139 headers: self.headers,
140 inner_layer: self.inner_layer,
141 outer_layer: self.outer_layer,
142 mk_client: self.mk_client,
143 mk_lb: self.mk_lb.load_balance(load_balance),
144 status: self.status,
145 #[cfg(feature = "__tls")]
146 tls_config: self.tls_config,
147 }
148 }
149
150 pub fn discover<NDISC>(self, discover: NDISC) -> ClientBuilder<IL, OL, C, LbConfig<LB, NDISC>> {
152 ClientBuilder {
153 http_config: self.http_config,
154 client_config: self.client_config,
155 pool_config: self.pool_config,
156 connector: self.connector,
157 timeout: self.timeout,
158 user_agent: self.user_agent,
159 host_mode: self.host_mode,
160 headers: self.headers,
161 inner_layer: self.inner_layer,
162 outer_layer: self.outer_layer,
163 mk_client: self.mk_client,
164 mk_lb: self.mk_lb.discover(discover),
165 status: self.status,
166 #[cfg(feature = "__tls")]
167 tls_config: self.tls_config,
168 }
169 }
170}
171
172impl<IL, OL, C, LB> ClientBuilder<IL, OL, C, LB> {
173 #[doc(hidden)]
175 pub fn client_maker<C2>(self, new_mk_client: C2) -> ClientBuilder<IL, OL, C2, LB> {
176 ClientBuilder {
177 http_config: self.http_config,
178 client_config: self.client_config,
179 pool_config: self.pool_config,
180 connector: self.connector,
181 timeout: self.timeout,
182 user_agent: self.user_agent,
183 host_mode: self.host_mode,
184 headers: self.headers,
185 inner_layer: self.inner_layer,
186 outer_layer: self.outer_layer,
187 mk_client: new_mk_client,
188 mk_lb: self.mk_lb,
189 status: self.status,
190 #[cfg(feature = "__tls")]
191 tls_config: self.tls_config,
192 }
193 }
194
195 pub fn layer_inner<Inner>(self, layer: Inner) -> ClientBuilder<Stack<Inner, IL>, OL, C, LB> {
209 ClientBuilder {
210 http_config: self.http_config,
211 client_config: self.client_config,
212 pool_config: self.pool_config,
213 connector: self.connector,
214 timeout: self.timeout,
215 user_agent: self.user_agent,
216 host_mode: self.host_mode,
217 headers: self.headers,
218 inner_layer: Stack::new(layer, self.inner_layer),
219 outer_layer: self.outer_layer,
220 mk_client: self.mk_client,
221 mk_lb: self.mk_lb,
222 status: self.status,
223 #[cfg(feature = "__tls")]
224 tls_config: self.tls_config,
225 }
226 }
227
228 pub fn layer_inner_front<Inner>(
242 self,
243 layer: Inner,
244 ) -> ClientBuilder<Stack<IL, Inner>, OL, C, LB> {
245 ClientBuilder {
246 http_config: self.http_config,
247 client_config: self.client_config,
248 pool_config: self.pool_config,
249 connector: self.connector,
250 timeout: self.timeout,
251 user_agent: self.user_agent,
252 host_mode: self.host_mode,
253 headers: self.headers,
254 inner_layer: Stack::new(self.inner_layer, layer),
255 outer_layer: self.outer_layer,
256 mk_client: self.mk_client,
257 mk_lb: self.mk_lb,
258 status: self.status,
259 #[cfg(feature = "__tls")]
260 tls_config: self.tls_config,
261 }
262 }
263
264 pub fn layer_outer<Outer>(self, layer: Outer) -> ClientBuilder<IL, Stack<Outer, OL>, C, LB> {
278 ClientBuilder {
279 http_config: self.http_config,
280 client_config: self.client_config,
281 pool_config: self.pool_config,
282 connector: self.connector,
283 timeout: self.timeout,
284 user_agent: self.user_agent,
285 host_mode: self.host_mode,
286 headers: self.headers,
287 inner_layer: self.inner_layer,
288 outer_layer: Stack::new(layer, self.outer_layer),
289 mk_client: self.mk_client,
290 mk_lb: self.mk_lb,
291 status: self.status,
292 #[cfg(feature = "__tls")]
293 tls_config: self.tls_config,
294 }
295 }
296
297 pub fn layer_outer_front<Outer>(
311 self,
312 layer: Outer,
313 ) -> ClientBuilder<IL, Stack<OL, Outer>, C, LB> {
314 ClientBuilder {
315 http_config: self.http_config,
316 client_config: self.client_config,
317 pool_config: self.pool_config,
318 connector: self.connector,
319 timeout: self.timeout,
320 user_agent: self.user_agent,
321 host_mode: self.host_mode,
322 headers: self.headers,
323 inner_layer: self.inner_layer,
324 outer_layer: Stack::new(self.outer_layer, layer),
325 mk_client: self.mk_client,
326 mk_lb: self.mk_lb,
327 status: self.status,
328 #[cfg(feature = "__tls")]
329 tls_config: self.tls_config,
330 }
331 }
332
333 pub fn mk_load_balance<NLB>(self, mk_load_balance: NLB) -> ClientBuilder<IL, OL, C, NLB> {
335 ClientBuilder {
336 http_config: self.http_config,
337 client_config: self.client_config,
338 pool_config: self.pool_config,
339 connector: self.connector,
340 timeout: self.timeout,
341 user_agent: self.user_agent,
342 host_mode: self.host_mode,
343 headers: self.headers,
344 inner_layer: self.inner_layer,
345 outer_layer: self.outer_layer,
346 mk_client: self.mk_client,
347 mk_lb: mk_load_balance,
348 status: self.status,
349 #[cfg(feature = "__tls")]
350 tls_config: self.tls_config,
351 }
352 }
353
354 pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
356 where
357 K: TryInto<HeaderName>,
358 K::Error: Error + Send + Sync + 'static,
359 V: TryInto<HeaderValue>,
360 V::Error: Error + Send + Sync + 'static,
361 {
362 if self.status.is_err() {
363 return self;
364 }
365
366 if let Err(err) = insert_header(&mut self.headers, key, value) {
367 self.status = Err(err);
368 }
369 self
370 }
371
372 #[cfg(feature = "__tls")]
374 #[cfg_attr(docsrs, doc(cfg(any(feature = "rustls", feature = "native-tls"))))]
375 pub fn set_tls_config<T>(&mut self, tls_config: T) -> &mut Self
376 where
377 T: Into<volo::net::tls::TlsConnector>,
378 {
379 self.tls_config = Some(Into::into(tls_config));
380 self
381 }
382
383 pub fn headers(&self) -> &HeaderMap {
385 &self.headers
386 }
387
388 pub fn headers_mut(&mut self) -> &mut HeaderMap {
390 &mut self.headers
391 }
392
393 #[deprecated(
398 since = "0.4.0",
399 note = "`set_title_case_headers` has been removed into `http1_config`"
400 )]
401 #[cfg(feature = "http1")]
402 pub fn set_title_case_headers(&mut self, title_case_headers: bool) -> &mut Self {
403 self.http_config
404 .h1
405 .set_title_case_headers(title_case_headers);
406 self
407 }
408
409 #[deprecated(
423 since = "0.4.0",
424 note = "`set_max_headers` has been removed into `http1_config`"
425 )]
426 #[cfg(feature = "http1")]
427 pub fn set_max_headers(&mut self, max_headers: usize) -> &mut Self {
428 self.http_config.h1.set_max_headers(max_headers);
429 self
430 }
431
432 #[cfg(feature = "http1")]
434 pub fn http1_config(&mut self) -> &mut self::transport::http1::Config {
435 &mut self.http_config.h1
436 }
437
438 #[cfg(feature = "http2")]
440 pub fn http2_config(&mut self) -> &mut self::transport::http2::Config {
441 &mut self.http_config.h2
442 }
443
444 #[doc(hidden)]
446 pub fn stat_enable(&mut self, enable: bool) -> &mut Self {
447 self.client_config.stat_enable = enable;
448 self
449 }
450
451 #[cfg(feature = "__tls")]
455 #[cfg_attr(docsrs, doc(cfg(any(feature = "rustls", feature = "native-tls"))))]
456 pub fn disable_tls(&mut self, disable: bool) -> &mut Self {
457 self.client_config.disable_tls = disable;
458 self
459 }
460
461 pub fn set_pool_idle_timeout(&mut self, timeout: Duration) -> &mut Self {
467 self.pool_config.idle_timeout = timeout;
468 self
469 }
470
471 pub fn set_max_idle_per_host(&mut self, num: usize) -> &mut Self {
478 self.pool_config.max_idle_per_host = num;
479 self
480 }
481
482 pub fn set_connect_timeout(&mut self, timeout: Duration) -> &mut Self {
484 self.connector.set_connect_timeout(Some(timeout));
485 self
486 }
487
488 pub fn set_read_timeout(&mut self, timeout: Duration) -> &mut Self {
490 self.connector.set_read_timeout(Some(timeout));
491 self
492 }
493
494 pub fn set_write_timeout(&mut self, timeout: Duration) -> &mut Self {
496 self.connector.set_write_timeout(Some(timeout));
497 self
498 }
499
500 pub fn set_request_timeout(&mut self, timeout: Duration) -> &mut Self {
502 self.timeout = Some(timeout);
503 self
504 }
505
506 pub fn user_agent<V>(&mut self, val: V) -> &mut Self
511 where
512 V: TryInto<HeaderValue>,
513 V::Error: Error + Send + Sync + 'static,
514 {
515 if self.status.is_err() {
516 return self;
517 }
518 match val.try_into() {
519 Ok(val) => self.user_agent = Some(val),
520 Err(err) => self.status = Err(builder_error(err)),
521 }
522 self
523 }
524
525 pub fn host_mode(&mut self, mode: Host) -> &mut Self {
535 self.host_mode = mode;
536 self
537 }
538
539 pub fn build<InnerReqBody, OuterReqBody, RespBody>(mut self) -> Result<C::Target>
562 where
563 IL: Layer<ClientTransport<InnerReqBody>>,
564 IL::Service: Send + Sync + 'static,
565 LB: MkLbLayer,
566 LB::Layer: Layer<IL::Service>,
567 <LB::Layer as Layer<IL::Service>>::Service: Send + Sync,
568 OL: Layer<<LB::Layer as Layer<IL::Service>>::Service>,
569 OL::Service: Service<
570 ClientContext,
571 Request<OuterReqBody>,
572 Response = Response<RespBody>,
573 Error = ClientError,
574 > + Send
575 + Sync
576 + 'static,
577 C: MkClient<Client<OuterReqBody, RespBody>>,
578 InnerReqBody: Send,
579 OuterReqBody: Send + 'static,
580 RespBody: Send,
581 {
582 let timeout_layer = Timeout;
583 let host_layer = self.host_mode.clone();
584 let ua_layer = match self.user_agent.take() {
585 Some(ua) => UserAgent::new(ua),
586 None => UserAgent::auto(),
587 };
588 self.layer_outer_front(ua_layer)
589 .layer_outer_front(host_layer)
590 .layer_outer_front(timeout_layer)
591 .build_without_extra_layers()
592 }
593
594 pub fn build_without_extra_layers<InnerReqBody, OuterReqBody, RespBody>(
601 self,
602 ) -> Result<C::Target>
603 where
604 IL: Layer<ClientTransport<InnerReqBody>>,
605 IL::Service: Send + Sync + 'static,
606 LB: MkLbLayer,
607 LB::Layer: Layer<IL::Service>,
608 <LB::Layer as Layer<IL::Service>>::Service: Send + Sync,
609 OL: Layer<<LB::Layer as Layer<IL::Service>>::Service>,
610 OL::Service: Service<
611 ClientContext,
612 Request<OuterReqBody>,
613 Response = Response<RespBody>,
614 Error = ClientError,
615 > + Send
616 + Sync
617 + 'static,
618 C: MkClient<Client<OuterReqBody, RespBody>>,
619 InnerReqBody: Send,
620 OuterReqBody: Send + 'static,
621 RespBody: Send,
622 {
623 self.status?;
624
625 let transport = ClientTransport::new(
626 self.http_config,
627 self.client_config,
628 self.pool_config,
629 #[cfg(feature = "__tls")]
630 self.tls_config,
631 );
632 let service = self
633 .outer_layer
634 .layer(self.mk_lb.make().layer(self.inner_layer.layer(transport)));
635 let service = BoxService::new(service);
636
637 let client_inner = ClientInner {
638 service,
639 timeout: self.timeout,
640 headers: self.headers,
641 };
642 let client = Client {
643 inner: Arc::new(client_inner),
644 };
645 Ok(self.mk_client.mk_client(client))
646 }
647}
648
649fn insert_header<K, V>(headers: &mut HeaderMap, key: K, value: V) -> Result<()>
650where
651 K: TryInto<HeaderName>,
652 K::Error: Error + Send + Sync + 'static,
653 V: TryInto<HeaderValue>,
654 V::Error: Error + Send + Sync + 'static,
655{
656 headers.insert(
657 key.try_into().map_err(builder_error)?,
658 value.try_into().map_err(builder_error)?,
659 );
660 Ok(())
661}
662
663struct ClientInner<ReqBody, RespBody> {
664 service: BoxService<ClientContext, Request<ReqBody>, Response<RespBody>, ClientError>,
665 timeout: Option<Duration>,
666 headers: HeaderMap,
667}
668
669pub struct Client<ReqBody = Body, RespBody = Body> {
690 inner: Arc<ClientInner<ReqBody, RespBody>>,
691}
692
693impl Default for Client {
694 fn default() -> Self {
695 ClientBuilder::default().build().unwrap()
696 }
697}
698
699impl<ReqBody, RespBody> Clone for Client<ReqBody, RespBody> {
700 fn clone(&self) -> Self {
701 Self {
702 inner: Arc::clone(&self.inner),
703 }
704 }
705}
706
707macro_rules! method_requests {
708 ($method:ident) => {
709 paste! {
710 #[doc = concat!("Create a request with `", stringify!([<$method:upper>]) ,"` method and the given `uri`.")]
711 pub fn [<$method:lower>]<U>(&self, uri: U) -> RequestBuilder<Self>
712 where
713 U: TryInto<Uri>,
714 U::Error: Into<BoxError>,
715 {
716 self.request(Method::[<$method:upper>], uri)
717 }
718 }
719 };
720}
721
722impl Client {
723 pub fn builder() -> ClientBuilder<Identity, Identity, DefaultMkClient, DefaultLb> {
725 ClientBuilder::new()
726 }
727}
728
729impl<ReqBody, RespBody> Client<ReqBody, RespBody> {
730 pub fn request_builder(&self) -> RequestBuilder<Self> {
732 RequestBuilder::new(self.clone())
733 }
734
735 pub fn request<U>(&self, method: Method, uri: U) -> RequestBuilder<Self>
737 where
738 U: TryInto<Uri>,
739 U::Error: Into<BoxError>,
740 {
741 RequestBuilder::new(self.clone()).method(method).uri(uri)
742 }
743
744 method_requests!(options);
745 method_requests!(get);
746 method_requests!(post);
747 method_requests!(put);
748 method_requests!(delete);
749 method_requests!(head);
750 method_requests!(trace);
751 method_requests!(connect);
752 method_requests!(patch);
753}
754
755impl<ReqBody, RespBody> OneShotService<ClientContext, Request<ReqBody>>
756 for Client<ReqBody, RespBody>
757where
758 ReqBody: Send,
759{
760 type Response = Response<RespBody>;
761 type Error = ClientError;
762
763 async fn call(
764 self,
765 cx: &mut ClientContext,
766 mut req: Request<ReqBody>,
767 ) -> Result<Self::Response, Self::Error> {
768 #[cfg(feature = "__tls")]
769 if cx.target().scheme() == Some(&http::uri::Scheme::HTTPS) {
770 req.extensions_mut().insert(http::uri::Scheme::HTTPS);
772 }
773
774 {
776 let config = cx.rpc_info_mut().config_mut();
777 if config.timeout().is_none() {
779 config.set_timeout(self.inner.timeout);
780 }
781 }
782
783 req.headers_mut().extend(self.inner.headers.clone());
785
786 let has_metainfo = METAINFO.try_with(|_| {}).is_ok();
788
789 let fut = self.inner.service.call(cx, req);
790
791 if has_metainfo {
792 fut.await
793 } else {
794 METAINFO.scope(RefCell::new(MetaInfo::default()), fut).await
795 }
796 }
797}
798
799impl<ReqBody, RespBody> Service<ClientContext, Request<ReqBody>> for Client<ReqBody, RespBody>
800where
801 ReqBody: Send,
802{
803 type Response = Response<RespBody>;
804 type Error = ClientError;
805
806 fn call(
807 &self,
808 cx: &mut ClientContext,
809 req: Request<ReqBody>,
810 ) -> impl Future<Output = Result<Self::Response, Self::Error>> + Send {
811 OneShotService::call(self.clone(), cx, req)
812 }
813}
814
815pub struct DefaultMkClient;
817
818impl<C> MkClient<C> for DefaultMkClient {
819 type Target = C;
820
821 fn mk_client(&self, service: C) -> Self::Target {
822 service
823 }
824}
825
826static CLIENT: LazyLock<Client> = LazyLock::new(Default::default);
827
828pub async fn get<U>(uri: U) -> Result<Response>
830where
831 U: TryInto<Uri>,
832 U::Error: Into<BoxError>,
833{
834 CLIENT.get(uri).send().await
835}