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