Skip to main content

volo_http/client/
mod.rs

1//! Client implementation
2//!
3//! See [`Client`] for more details.
4
5use 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
78/// A builder for configuring an HTTP [`Client`].
79pub 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    /// Create a new client builder.
99    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    /// Set load balancer for the client.
128    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    /// Set service discover for the client.
152    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    /// This is unstable now and may be changed in the future.
175    #[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    /// Add a new inner layer to the client.
197    ///
198    /// The layer's `Service` should be `Send + Sync + Clone + 'static`.
199    ///
200    /// # Order
201    ///
202    /// Assume we already have two layers: foo and bar. We want to add a new layer baz.
203    ///
204    /// The current order is: foo -> bar (the request will come to foo first, and then bar).
205    ///
206    /// After we call `.layer_inner(baz)`, we will get: foo -> bar -> baz.
207    ///
208    /// The overall order for layers is: outer -> LoadBalance -> \[inner\] -> transport.
209    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    /// Add a new inner layer to the client.
230    ///
231    /// The layer's `Service` should be `Send + Sync + Clone + 'static`.
232    ///
233    /// # Order
234    ///
235    /// Assume we already have two layers: foo and bar. We want to add a new layer baz.
236    ///
237    /// The current order is: foo -> bar (the request will come to foo first, and then bar).
238    ///
239    /// After we call `.layer_inner_front(baz)`, we will get: baz -> foo -> bar.
240    ///
241    /// The overall order for layers is: outer -> LoadBalance -> \[inner\] -> transport.
242    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    /// Add a new outer layer to the client.
266    ///
267    /// The layer's `Service` should be `Send + Sync + Clone + 'static`.
268    ///
269    /// # Order
270    ///
271    /// Assume we already have two layers: foo and bar. We want to add a new layer baz.
272    ///
273    /// The current order is: foo -> bar (the request will come to foo first, and then bar).
274    ///
275    /// After we call `.layer_outer(baz)`, we will get: foo -> bar -> baz.
276    ///
277    /// The overall order for layers is: \[outer\] -> Timeout -> LoadBalance -> inner -> transport.
278    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    /// Add a new outer layer to the client.
299    ///
300    /// The layer's `Service` should be `Send + Sync + Clone + 'static`.
301    ///
302    /// # Order
303    ///
304    /// Assume we already have two layers: foo and bar. We want to add a new layer baz.
305    ///
306    /// The current order is: foo -> bar (the request will come to foo first, and then bar).
307    ///
308    /// After we call `.layer_outer_front(baz)`, we will get: baz -> foo -> bar.
309    ///
310    /// The overall order for layers is: \[outer\] -> LoadBalance -> inner -> transport.
311    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    /// Set a new load balance for the client.
335    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    /// Insert a header to the request.
356    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    /// Set tls config for the client.
374    #[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    /// Get a reference to the default headers of the client.
385    pub fn headers(&self) -> &HeaderMap {
386        &self.headers
387    }
388
389    /// Get a mutable reference to the default headers of the client.
390    pub fn headers_mut(&mut self) -> &mut HeaderMap {
391        &mut self.headers
392    }
393
394    /// Set whether HTTP/1 connections will write header names as title case at
395    /// the socket level.
396    ///
397    /// Default is false.
398    #[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    /// Set the maximum number of headers.
411    ///
412    /// When a response is received, the parser will reserve a buffer to store headers for optimal
413    /// performance.
414    ///
415    /// If client receives more headers than the buffer size, the error "message header too large"
416    /// is returned.
417    ///
418    /// Note that headers is allocated on the stack by default, which has higher performance. After
419    /// setting this value, headers will be allocated in heap memory, that is, heap memory
420    /// allocation will occur for each response, and there will be a performance drop of about 5%.
421    ///
422    /// Default is 100.
423    #[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    /// Get configuration of http1 part.
434    #[cfg(feature = "http1")]
435    pub fn http1_config(&mut self) -> &mut self::transport::http1::Config {
436        &mut self.http_config.h1
437    }
438
439    /// Get configuration of http2 part.
440    #[cfg(feature = "http2")]
441    pub fn http2_config(&mut self) -> &mut self::transport::http2::Config {
442        &mut self.http_config.h2
443    }
444
445    /// This is unstable now and may be changed in the future.
446    #[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    /// Disable TLS for the client.
453    ///
454    /// Default is false, when TLS related feature is enabled, TLS is enabled by default.
455    #[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    /// Set idle timeout of connection pool.
463    ///
464    /// If a connection is idle for more than the timeout, the connection will be dropped.
465    ///
466    /// Default is 20 seconds.
467    pub fn set_pool_idle_timeout(&mut self, timeout: Duration) -> &mut Self {
468        self.pool_config.idle_timeout = timeout;
469        self
470    }
471
472    /// Set the maximum number of idle connections per host.
473    ///
474    /// If the number of idle connections on a host exceeds this value, the connection pool will
475    /// refuse to add new idle connections.
476    ///
477    /// Default is 10240.
478    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    /// Set the maximum idle time for a connection.
484    pub fn set_connect_timeout(&mut self, timeout: Duration) -> &mut Self {
485        self.connector.set_connect_timeout(Some(timeout));
486        self
487    }
488
489    /// Set the maximum idle time for reading data from the connection.
490    pub fn set_read_timeout(&mut self, timeout: Duration) -> &mut Self {
491        self.connector.set_read_timeout(Some(timeout));
492        self
493    }
494
495    /// Set the maximum idle time for writing data to the connection.
496    pub fn set_write_timeout(&mut self, timeout: Duration) -> &mut Self {
497        self.connector.set_write_timeout(Some(timeout));
498        self
499    }
500
501    /// Set the maximum idle time for the whole request.
502    pub fn set_request_timeout(&mut self, timeout: Duration) -> &mut Self {
503        self.timeout = Some(timeout);
504        self
505    }
506
507    /// Set default `User-Agent` in request header.
508    ///
509    /// If there is `User-Agent` given, a default `User-Agent` will be generated by crate name and
510    /// version.
511    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    /// Set mode of client setting `Host` in headers.
527    ///
528    /// This mode only works when building client by [`ClientBuilder::build`],
529    /// [`ClientBuilder::build_without_extra_layers`] will ignore this config.
530    ///
531    /// For more configurations, refer to [`Host`].
532    ///
533    /// Default is [`Host::Auto`], it will generate a `Host` by target domain name or address if
534    /// there is no `Host` in request headers.
535    pub fn host_mode(&mut self, mode: Host) -> &mut Self {
536        self.host_mode = mode;
537        self
538    }
539
540    /// Build the HTTP client with default configurations.
541    ///
542    /// This method will insert some default layers: [`Timeout`], [`UserAgent`] and [`Host`], and
543    /// the final calling sequence will be as follows:
544    ///
545    /// - Outer:
546    ///   - [`Timeout`]: Apply timeout from [`ClientBuilder::set_request_timeout`] or
547    ///     [`CallOpt::with_timeout`]. Note that without this layer, timeout from [`Client`] or
548    ///     [`CallOpt`] will not work.
549    ///   - [`Host`]: Insert `Host` to request headers. [`Host::Auto`] will be applied by default,
550    ///     it will insert a `Host` generated from current [`Target`] if there is no `Host` in
551    ///     headers.
552    ///   - [`UserAgent`]: Insert `User-Agent` into the request header, it takes the given value
553    ///     from [`ClientBuilder::user_agent`] or generates a value based on the current package
554    ///     name and version. If `User-Agent` already exists, this layer does nothing.
555    ///   - Other outer layers
556    /// - LoadBalance ([`LbConfig`] with [`DnsResolver`] by default)
557    /// - Inner layers
558    ///   - Other inner layers
559    /// - Transport through network or unix domain socket.
560    ///
561    /// [`DnsResolver`]: crate::client::dns::DnsResolver
562    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    /// Build the HTTP client without inserting any extra layers.
596    ///
597    /// This method is provided for advanced users, some features may not work properly without the
598    /// default layers,
599    ///
600    /// See [`ClientBuilder::build`] for more details.
601    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
670/// An Client for sending HTTP requests and handling HTTP responses.
671///
672/// # Examples
673///
674/// ```no_run
675/// use volo_http::{body::BodyConversion, client::Client};
676///
677/// # tokio_test::block_on(async {
678/// let client = Client::builder().build().unwrap();
679/// let resp = client
680///     .get("http://httpbin.org/get")
681///     .send()
682///     .await
683///     .expect("failed to send request")
684///     .into_string()
685///     .await
686///     .expect("failed to convert response to string");
687/// println!("{resp:?}");
688/// # })
689/// ```
690pub 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    /// Create a new client builder.
725    pub fn builder() -> ClientBuilder<Identity, Identity, DefaultMkClient, DefaultLb> {
726        ClientBuilder::new()
727    }
728}
729
730impl<ReqBody, RespBody> Client<ReqBody, RespBody> {
731    /// Create a builder for building a request.
732    pub fn request_builder(&self) -> RequestBuilder<Self> {
733        RequestBuilder::new(self.clone())
734    }
735
736    /// Create a builder for building a request with the specified method and URI.
737    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            // save scheme in request
772            req.extensions_mut().insert(http::uri::Scheme::HTTPS);
773        }
774
775        // set timeout
776        {
777            let config = cx.rpc_info_mut().config_mut();
778            // We should check it here because CallOptService must be outer of the client service
779            if config.timeout().is_none() {
780                config.set_timeout(self.inner.timeout);
781            }
782        }
783
784        // extend headermap
785        req.headers_mut().extend(self.inner.headers.clone());
786
787        // apply metainfo if it does not exist
788        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
816/// A dummy [`MkClient`] that does not have any functionality
817pub 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
829/// Create a GET request to the specified URI.
830pub 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}