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