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;
61#[cfg(feature = "multipart")]
62pub mod multipart;
63mod request_builder;
64pub mod sse;
65pub mod target;
66#[cfg(test)]
67pub mod test_helpers;
68pub mod transport;
69mod utils;
70
71pub use self::{
72    callopt::CallOpt, request_builder::RequestBuilder, target::Target, transport::protocol,
73};
74
75#[doc(hidden)]
76pub mod prelude {
77    pub use super::{Client, ClientBuilder};
78}
79
80/// A builder for configuring an HTTP [`Client`].
81pub struct ClientBuilder<IL = Identity, OL = Identity, C = DefaultMkClient, LB = DefaultLb> {
82    http_config: ClientConfig,
83    client_config: ClientTransportConfig,
84    pool_config: pool::Config,
85    connector: DefaultMakeTransport,
86    timeout: Option<Duration>,
87    user_agent: Option<HeaderValue>,
88    host_mode: Host,
89    headers: HeaderMap,
90    inner_layer: IL,
91    outer_layer: OL,
92    mk_client: C,
93    mk_lb: LB,
94    status: Result<()>,
95    #[cfg(feature = "__tls")]
96    tls_config: Option<volo::net::tls::TlsConnector>,
97}
98
99impl ClientBuilder<Identity, Identity, DefaultMkClient, DefaultLb> {
100    /// Create a new client builder.
101    pub fn new() -> Self {
102        Self {
103            http_config: Default::default(),
104            client_config: Default::default(),
105            pool_config: pool::Config::default(),
106            connector: Default::default(),
107            timeout: None,
108            user_agent: None,
109            host_mode: Host::Auto,
110            headers: Default::default(),
111            inner_layer: Identity::new(),
112            outer_layer: Identity::new(),
113            mk_client: DefaultMkClient,
114            mk_lb: Default::default(),
115            status: Ok(()),
116            #[cfg(feature = "__tls")]
117            tls_config: None,
118        }
119    }
120}
121
122impl Default for ClientBuilder<Identity, Identity, DefaultMkClient, DefaultLb> {
123    fn default() -> Self {
124        Self::new()
125    }
126}
127
128impl<IL, OL, C, LB, DISC> ClientBuilder<IL, OL, C, LbConfig<LB, DISC>> {
129    /// Set load balancer for the client.
130    pub fn load_balance<NLB>(
131        self,
132        load_balance: NLB,
133    ) -> ClientBuilder<IL, OL, C, LbConfig<NLB, DISC>> {
134        ClientBuilder {
135            http_config: self.http_config,
136            client_config: self.client_config,
137            pool_config: self.pool_config,
138            connector: self.connector,
139            timeout: self.timeout,
140            user_agent: self.user_agent,
141            host_mode: self.host_mode,
142            headers: self.headers,
143            inner_layer: self.inner_layer,
144            outer_layer: self.outer_layer,
145            mk_client: self.mk_client,
146            mk_lb: self.mk_lb.load_balance(load_balance),
147            status: self.status,
148            #[cfg(feature = "__tls")]
149            tls_config: self.tls_config,
150        }
151    }
152
153    /// Set service discover for the client.
154    pub fn discover<NDISC>(self, discover: NDISC) -> ClientBuilder<IL, OL, C, LbConfig<LB, NDISC>> {
155        ClientBuilder {
156            http_config: self.http_config,
157            client_config: self.client_config,
158            pool_config: self.pool_config,
159            connector: self.connector,
160            timeout: self.timeout,
161            user_agent: self.user_agent,
162            host_mode: self.host_mode,
163            headers: self.headers,
164            inner_layer: self.inner_layer,
165            outer_layer: self.outer_layer,
166            mk_client: self.mk_client,
167            mk_lb: self.mk_lb.discover(discover),
168            status: self.status,
169            #[cfg(feature = "__tls")]
170            tls_config: self.tls_config,
171        }
172    }
173}
174
175impl<IL, OL, C, LB> ClientBuilder<IL, OL, C, LB> {
176    /// This is unstable now and may be changed in the future.
177    #[doc(hidden)]
178    pub fn client_maker<C2>(self, new_mk_client: C2) -> ClientBuilder<IL, OL, C2, LB> {
179        ClientBuilder {
180            http_config: self.http_config,
181            client_config: self.client_config,
182            pool_config: self.pool_config,
183            connector: self.connector,
184            timeout: self.timeout,
185            user_agent: self.user_agent,
186            host_mode: self.host_mode,
187            headers: self.headers,
188            inner_layer: self.inner_layer,
189            outer_layer: self.outer_layer,
190            mk_client: new_mk_client,
191            mk_lb: self.mk_lb,
192            status: self.status,
193            #[cfg(feature = "__tls")]
194            tls_config: self.tls_config,
195        }
196    }
197
198    /// Add a new inner layer to the client.
199    ///
200    /// The layer's `Service` should be `Send + Sync + Clone + 'static`.
201    ///
202    /// # Order
203    ///
204    /// Assume we already have two layers: foo and bar. We want to add a new layer baz.
205    ///
206    /// The current order is: foo -> bar (the request will come to foo first, and then bar).
207    ///
208    /// After we call `.layer_inner(baz)`, we will get: foo -> bar -> baz.
209    ///
210    /// The overall order for layers is: outer -> LoadBalance -> \[inner\] -> transport.
211    pub fn layer_inner<Inner>(self, layer: Inner) -> ClientBuilder<Stack<Inner, IL>, OL, C, LB> {
212        ClientBuilder {
213            http_config: self.http_config,
214            client_config: self.client_config,
215            pool_config: self.pool_config,
216            connector: self.connector,
217            timeout: self.timeout,
218            user_agent: self.user_agent,
219            host_mode: self.host_mode,
220            headers: self.headers,
221            inner_layer: Stack::new(layer, self.inner_layer),
222            outer_layer: self.outer_layer,
223            mk_client: self.mk_client,
224            mk_lb: self.mk_lb,
225            status: self.status,
226            #[cfg(feature = "__tls")]
227            tls_config: self.tls_config,
228        }
229    }
230
231    /// Add a new inner layer to the client.
232    ///
233    /// The layer's `Service` should be `Send + Sync + Clone + 'static`.
234    ///
235    /// # Order
236    ///
237    /// Assume we already have two layers: foo and bar. We want to add a new layer baz.
238    ///
239    /// The current order is: foo -> bar (the request will come to foo first, and then bar).
240    ///
241    /// After we call `.layer_inner_front(baz)`, we will get: baz -> foo -> bar.
242    ///
243    /// The overall order for layers is: outer -> LoadBalance -> \[inner\] -> transport.
244    pub fn layer_inner_front<Inner>(
245        self,
246        layer: Inner,
247    ) -> ClientBuilder<Stack<IL, Inner>, OL, C, LB> {
248        ClientBuilder {
249            http_config: self.http_config,
250            client_config: self.client_config,
251            pool_config: self.pool_config,
252            connector: self.connector,
253            timeout: self.timeout,
254            user_agent: self.user_agent,
255            host_mode: self.host_mode,
256            headers: self.headers,
257            inner_layer: Stack::new(self.inner_layer, layer),
258            outer_layer: self.outer_layer,
259            mk_client: self.mk_client,
260            mk_lb: self.mk_lb,
261            status: self.status,
262            #[cfg(feature = "__tls")]
263            tls_config: self.tls_config,
264        }
265    }
266
267    /// Add a new outer layer to the client.
268    ///
269    /// The layer's `Service` should be `Send + Sync + Clone + 'static`.
270    ///
271    /// # Order
272    ///
273    /// Assume we already have two layers: foo and bar. We want to add a new layer baz.
274    ///
275    /// The current order is: foo -> bar (the request will come to foo first, and then bar).
276    ///
277    /// After we call `.layer_outer(baz)`, we will get: foo -> bar -> baz.
278    ///
279    /// The overall order for layers is: \[outer\] -> Timeout -> LoadBalance -> inner -> transport.
280    pub fn layer_outer<Outer>(self, layer: Outer) -> ClientBuilder<IL, Stack<Outer, OL>, C, LB> {
281        ClientBuilder {
282            http_config: self.http_config,
283            client_config: self.client_config,
284            pool_config: self.pool_config,
285            connector: self.connector,
286            timeout: self.timeout,
287            user_agent: self.user_agent,
288            host_mode: self.host_mode,
289            headers: self.headers,
290            inner_layer: self.inner_layer,
291            outer_layer: Stack::new(layer, self.outer_layer),
292            mk_client: self.mk_client,
293            mk_lb: self.mk_lb,
294            status: self.status,
295            #[cfg(feature = "__tls")]
296            tls_config: self.tls_config,
297        }
298    }
299
300    /// Add a new outer layer to the client.
301    ///
302    /// The layer's `Service` should be `Send + Sync + Clone + 'static`.
303    ///
304    /// # Order
305    ///
306    /// Assume we already have two layers: foo and bar. We want to add a new layer baz.
307    ///
308    /// The current order is: foo -> bar (the request will come to foo first, and then bar).
309    ///
310    /// After we call `.layer_outer_front(baz)`, we will get: baz -> foo -> bar.
311    ///
312    /// The overall order for layers is: \[outer\] -> LoadBalance -> inner -> transport.
313    pub fn layer_outer_front<Outer>(
314        self,
315        layer: Outer,
316    ) -> ClientBuilder<IL, Stack<OL, Outer>, C, LB> {
317        ClientBuilder {
318            http_config: self.http_config,
319            client_config: self.client_config,
320            pool_config: self.pool_config,
321            connector: self.connector,
322            timeout: self.timeout,
323            user_agent: self.user_agent,
324            host_mode: self.host_mode,
325            headers: self.headers,
326            inner_layer: self.inner_layer,
327            outer_layer: Stack::new(self.outer_layer, layer),
328            mk_client: self.mk_client,
329            mk_lb: self.mk_lb,
330            status: self.status,
331            #[cfg(feature = "__tls")]
332            tls_config: self.tls_config,
333        }
334    }
335
336    /// Enable HTTP redirect following for this client.
337    ///
338    /// Redirects are followed by an outer client layer so each redirected target is applied before
339    /// service discovery and load balancing run for the next hop. `0` disables redirect following
340    /// and returns redirect responses as-is.
341    pub fn follow_redirects(
342        self,
343        max_redirects: usize,
344    ) -> ClientBuilder<IL, Stack<OL, layer::FollowRedirect>, C, LB> {
345        self.layer_outer_front(layer::FollowRedirect::new(max_redirects))
346    }
347
348    /// Enable HTTP redirect following only for requests accepted by `predicate`.
349    ///
350    /// The predicate is checked before the initial request and before every follow-up redirect hop.
351    /// Requests rejected by the initial predicate bypass redirect handling without cloning their
352    /// request head for possible replay.
353    pub fn follow_redirects_when<P>(
354        self,
355        max_redirects: usize,
356        predicate: P,
357    ) -> ClientBuilder<IL, Stack<OL, layer::FollowRedirect<P>>, C, LB>
358    where
359        P: layer::RedirectPredicate,
360    {
361        self.layer_outer_front(layer::FollowRedirect::new(max_redirects).when(predicate))
362    }
363
364    /// Set a new load balance for the client.
365    pub fn mk_load_balance<NLB>(self, mk_load_balance: NLB) -> ClientBuilder<IL, OL, C, NLB> {
366        ClientBuilder {
367            http_config: self.http_config,
368            client_config: self.client_config,
369            pool_config: self.pool_config,
370            connector: self.connector,
371            timeout: self.timeout,
372            user_agent: self.user_agent,
373            host_mode: self.host_mode,
374            headers: self.headers,
375            inner_layer: self.inner_layer,
376            outer_layer: self.outer_layer,
377            mk_client: self.mk_client,
378            mk_lb: mk_load_balance,
379            status: self.status,
380            #[cfg(feature = "__tls")]
381            tls_config: self.tls_config,
382        }
383    }
384
385    /// Insert a header to the request.
386    pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
387    where
388        K: TryInto<HeaderName>,
389        K::Error: Error + Send + Sync + 'static,
390        V: TryInto<HeaderValue>,
391        V::Error: Error + Send + Sync + 'static,
392    {
393        if self.status.is_err() {
394            return self;
395        }
396
397        if let Err(err) = insert_header(&mut self.headers, key, value) {
398            self.status = Err(err);
399        }
400        self
401    }
402
403    /// Set tls config for the client.
404    #[cfg(feature = "__tls")]
405    #[cfg_attr(docsrs, doc(cfg(any(feature = "rustls", feature = "native-tls"))))]
406    pub fn set_tls_config<T>(&mut self, tls_config: T) -> &mut Self
407    where
408        T: Into<volo::net::tls::TlsConnector>,
409    {
410        self.tls_config = Some(Into::into(tls_config));
411        self
412    }
413
414    /// Get a reference to the default headers of the client.
415    pub fn headers(&self) -> &HeaderMap {
416        &self.headers
417    }
418
419    /// Get a mutable reference to the default headers of the client.
420    pub fn headers_mut(&mut self) -> &mut HeaderMap {
421        &mut self.headers
422    }
423
424    /// Set whether HTTP/1 connections will write header names as title case at
425    /// the socket level.
426    ///
427    /// Default is false.
428    #[deprecated(
429        since = "0.4.0",
430        note = "`set_title_case_headers` has been removed into `http1_config`"
431    )]
432    #[cfg(feature = "http1")]
433    pub fn set_title_case_headers(&mut self, title_case_headers: bool) -> &mut Self {
434        self.http_config
435            .h1
436            .set_title_case_headers(title_case_headers);
437        self
438    }
439
440    /// Set the maximum number of headers.
441    ///
442    /// When a response is received, the parser will reserve a buffer to store headers for optimal
443    /// performance.
444    ///
445    /// If client receives more headers than the buffer size, the error "message header too large"
446    /// is returned.
447    ///
448    /// Note that headers is allocated on the stack by default, which has higher performance. After
449    /// setting this value, headers will be allocated in heap memory, that is, heap memory
450    /// allocation will occur for each response, and there will be a performance drop of about 5%.
451    ///
452    /// Default is 100.
453    #[deprecated(
454        since = "0.4.0",
455        note = "`set_max_headers` has been removed into `http1_config`"
456    )]
457    #[cfg(feature = "http1")]
458    pub fn set_max_headers(&mut self, max_headers: usize) -> &mut Self {
459        self.http_config.h1.set_max_headers(max_headers);
460        self
461    }
462
463    /// Get configuration of http1 part.
464    #[cfg(feature = "http1")]
465    pub fn http1_config(&mut self) -> &mut self::transport::http1::Config {
466        &mut self.http_config.h1
467    }
468
469    /// Get configuration of http2 part.
470    #[cfg(feature = "http2")]
471    pub fn http2_config(&mut self) -> &mut self::transport::http2::Config {
472        &mut self.http_config.h2
473    }
474
475    /// This is unstable now and may be changed in the future.
476    #[doc(hidden)]
477    pub fn stat_enable(&mut self, enable: bool) -> &mut Self {
478        self.client_config.stat_enable = enable;
479        self
480    }
481
482    /// Disable TLS for the client.
483    ///
484    /// Default is false, when TLS related feature is enabled, TLS is enabled by default.
485    #[cfg(feature = "__tls")]
486    #[cfg_attr(docsrs, doc(cfg(any(feature = "rustls", feature = "native-tls"))))]
487    pub fn disable_tls(&mut self, disable: bool) -> &mut Self {
488        self.client_config.disable_tls = disable;
489        self
490    }
491
492    /// Set idle timeout of connection pool.
493    ///
494    /// If a connection is idle for more than the timeout, the connection will be dropped.
495    ///
496    /// Default is 20 seconds.
497    pub fn set_pool_idle_timeout(&mut self, timeout: Duration) -> &mut Self {
498        self.pool_config.idle_timeout = timeout;
499        self
500    }
501
502    /// Set the maximum number of idle connections per host.
503    ///
504    /// If the number of idle connections on a host exceeds this value, the connection pool will
505    /// refuse to add new idle connections.
506    ///
507    /// Default is 10240.
508    pub fn set_max_idle_per_host(&mut self, num: usize) -> &mut Self {
509        self.pool_config.max_idle_per_host = num;
510        self
511    }
512
513    /// Set the maximum idle time for a connection.
514    pub fn set_connect_timeout(&mut self, timeout: Duration) -> &mut Self {
515        self.connector.set_connect_timeout(Some(timeout));
516        self
517    }
518
519    /// Set the maximum idle time for reading data from the connection.
520    pub fn set_read_timeout(&mut self, timeout: Duration) -> &mut Self {
521        self.connector.set_read_timeout(Some(timeout));
522        self
523    }
524
525    /// Set the maximum idle time for writing data to the connection.
526    pub fn set_write_timeout(&mut self, timeout: Duration) -> &mut Self {
527        self.connector.set_write_timeout(Some(timeout));
528        self
529    }
530
531    /// Set the maximum idle time for the whole request.
532    pub fn set_request_timeout(&mut self, timeout: Duration) -> &mut Self {
533        self.timeout = Some(timeout);
534        self
535    }
536
537    /// Set default `User-Agent` in request header.
538    ///
539    /// If there is `User-Agent` given, a default `User-Agent` will be generated by crate name and
540    /// version.
541    pub fn user_agent<V>(&mut self, val: V) -> &mut Self
542    where
543        V: TryInto<HeaderValue>,
544        V::Error: Error + Send + Sync + 'static,
545    {
546        if self.status.is_err() {
547            return self;
548        }
549        match val.try_into() {
550            Ok(val) => self.user_agent = Some(val),
551            Err(err) => self.status = Err(builder_error(err)),
552        }
553        self
554    }
555
556    /// Set mode of client setting `Host` in headers.
557    ///
558    /// This mode only works when building client by [`ClientBuilder::build`],
559    /// [`ClientBuilder::build_without_extra_layers`] will ignore this config.
560    ///
561    /// For more configurations, refer to [`Host`].
562    ///
563    /// Default is [`Host::Auto`], it will generate a `Host` by target domain name or address if
564    /// there is no `Host` in request headers.
565    pub fn host_mode(&mut self, mode: Host) -> &mut Self {
566        self.host_mode = mode;
567        self
568    }
569
570    /// Build the HTTP client with default configurations.
571    ///
572    /// This method will insert some default layers: [`Timeout`], [`UserAgent`] and [`Host`], and
573    /// the final calling sequence will be as follows:
574    ///
575    /// - Outer:
576    ///   - [`Timeout`]: Apply timeout from [`ClientBuilder::set_request_timeout`] or
577    ///     [`CallOpt::with_timeout`]. Note that without this layer, timeout from [`Client`] or
578    ///     [`CallOpt`] will not work.
579    ///   - [`Host`]: Insert `Host` to request headers. [`Host::Auto`] will be applied by default,
580    ///     it will insert a `Host` generated from current [`Target`] if there is no `Host` in
581    ///     headers.
582    ///   - [`UserAgent`]: Insert `User-Agent` into the request header, it takes the given value
583    ///     from [`ClientBuilder::user_agent`] or generates a value based on the current package
584    ///     name and version. If `User-Agent` already exists, this layer does nothing.
585    ///   - Other outer layers
586    /// - LoadBalance ([`LbConfig`] with [`DnsResolver`] by default)
587    /// - Inner layers
588    ///   - Other inner layers
589    /// - Transport through network or unix domain socket.
590    ///
591    /// [`DnsResolver`]: crate::client::dns::DnsResolver
592    pub fn build<InnerReqBody, OuterReqBody, RespBody>(mut self) -> Result<C::Target>
593    where
594        IL: Layer<ClientTransport<InnerReqBody>>,
595        IL::Service: Send + Sync + 'static,
596        LB: MkLbLayer,
597        LB::Layer: Layer<IL::Service>,
598        <LB::Layer as Layer<IL::Service>>::Service: Send + Sync,
599        OL: Layer<<LB::Layer as Layer<IL::Service>>::Service>,
600        OL::Service: Service<
601                ClientContext,
602                Request<OuterReqBody>,
603                Response = Response<RespBody>,
604                Error = ClientError,
605            > + Send
606            + Sync
607            + 'static,
608        C: MkClient<Client<OuterReqBody, RespBody>>,
609        InnerReqBody: Send,
610        OuterReqBody: Send + 'static,
611        RespBody: Send,
612    {
613        let timeout_layer = Timeout;
614        let host_layer = self.host_mode.clone();
615        let ua_layer = match self.user_agent.take() {
616            Some(ua) => UserAgent::new(ua),
617            None => UserAgent::auto(),
618        };
619        self.layer_outer_front(ua_layer)
620            .layer_outer_front(host_layer)
621            .layer_outer_front(timeout_layer)
622            .build_without_extra_layers()
623    }
624
625    /// Build the HTTP client without inserting any extra layers.
626    ///
627    /// This method is provided for advanced users, some features may not work properly without the
628    /// default layers,
629    ///
630    /// See [`ClientBuilder::build`] for more details.
631    pub fn build_without_extra_layers<InnerReqBody, OuterReqBody, RespBody>(
632        self,
633    ) -> Result<C::Target>
634    where
635        IL: Layer<ClientTransport<InnerReqBody>>,
636        IL::Service: Send + Sync + 'static,
637        LB: MkLbLayer,
638        LB::Layer: Layer<IL::Service>,
639        <LB::Layer as Layer<IL::Service>>::Service: Send + Sync,
640        OL: Layer<<LB::Layer as Layer<IL::Service>>::Service>,
641        OL::Service: Service<
642                ClientContext,
643                Request<OuterReqBody>,
644                Response = Response<RespBody>,
645                Error = ClientError,
646            > + Send
647            + Sync
648            + 'static,
649        C: MkClient<Client<OuterReqBody, RespBody>>,
650        InnerReqBody: Send,
651        OuterReqBody: Send + 'static,
652        RespBody: Send,
653    {
654        self.status?;
655
656        let transport = ClientTransport::new(
657            self.http_config,
658            self.client_config,
659            self.pool_config,
660            #[cfg(feature = "__tls")]
661            self.tls_config,
662        );
663        let service = self
664            .outer_layer
665            .layer(self.mk_lb.make().layer(self.inner_layer.layer(transport)));
666        let service = BoxService::new(service);
667
668        let client_inner = ClientInner {
669            service,
670            timeout: self.timeout,
671            headers: self.headers,
672        };
673        let client = Client {
674            inner: Arc::new(client_inner),
675        };
676        Ok(self.mk_client.mk_client(client))
677    }
678}
679
680fn insert_header<K, V>(headers: &mut HeaderMap, key: K, value: V) -> Result<()>
681where
682    K: TryInto<HeaderName>,
683    K::Error: Error + Send + Sync + 'static,
684    V: TryInto<HeaderValue>,
685    V::Error: Error + Send + Sync + 'static,
686{
687    headers.insert(
688        key.try_into().map_err(builder_error)?,
689        value.try_into().map_err(builder_error)?,
690    );
691    Ok(())
692}
693
694struct ClientInner<ReqBody, RespBody> {
695    service: BoxService<ClientContext, Request<ReqBody>, Response<RespBody>, ClientError>,
696    timeout: Option<Duration>,
697    headers: HeaderMap,
698}
699
700/// An Client for sending HTTP requests and handling HTTP responses.
701///
702/// # Examples
703///
704/// ```no_run
705/// use volo_http::{body::BodyConversion, client::Client};
706///
707/// # tokio_test::block_on(async {
708/// let client = Client::builder().build().unwrap();
709/// let resp = client
710///     .get("http://httpbin.org/get")
711///     .send()
712///     .await
713///     .expect("failed to send request")
714///     .into_string()
715///     .await
716///     .expect("failed to convert response to string");
717/// println!("{resp:?}");
718/// # })
719/// ```
720pub struct Client<ReqBody = Body, RespBody = Body> {
721    inner: Arc<ClientInner<ReqBody, RespBody>>,
722}
723
724impl Default for Client {
725    fn default() -> Self {
726        ClientBuilder::default().build().unwrap()
727    }
728}
729
730impl<ReqBody, RespBody> Clone for Client<ReqBody, RespBody> {
731    fn clone(&self) -> Self {
732        Self {
733            inner: Arc::clone(&self.inner),
734        }
735    }
736}
737
738macro_rules! method_requests {
739    ($method:ident) => {
740        paste! {
741            #[doc = concat!("Create a request with `", stringify!([<$method:upper>]) ,"` method and the given `uri`.")]
742            pub fn [<$method:lower>]<U>(&self, uri: U) -> RequestBuilder<Self>
743            where
744                U: TryInto<Uri>,
745                U::Error: Into<BoxError>,
746            {
747                self.request(Method::[<$method:upper>], uri)
748            }
749        }
750    };
751}
752
753impl Client {
754    /// Create a new client builder.
755    pub fn builder() -> ClientBuilder<Identity, Identity, DefaultMkClient, DefaultLb> {
756        ClientBuilder::new()
757    }
758}
759
760impl<ReqBody, RespBody> Client<ReqBody, RespBody> {
761    /// Create a builder for building a request.
762    pub fn request_builder(&self) -> RequestBuilder<Self> {
763        RequestBuilder::new(self.clone())
764    }
765
766    /// Create a builder for building a request with the specified method and URI.
767    pub fn request<U>(&self, method: Method, uri: U) -> RequestBuilder<Self>
768    where
769        U: TryInto<Uri>,
770        U::Error: Into<BoxError>,
771    {
772        RequestBuilder::new(self.clone()).method(method).uri(uri)
773    }
774
775    method_requests!(options);
776    method_requests!(get);
777    method_requests!(post);
778    method_requests!(put);
779    method_requests!(delete);
780    method_requests!(head);
781    method_requests!(trace);
782    method_requests!(connect);
783    method_requests!(patch);
784}
785
786impl<ReqBody, RespBody> OneShotService<ClientContext, Request<ReqBody>>
787    for Client<ReqBody, RespBody>
788where
789    ReqBody: Send,
790{
791    type Response = Response<RespBody>;
792    type Error = ClientError;
793
794    async fn call(
795        self,
796        cx: &mut ClientContext,
797        mut req: Request<ReqBody>,
798    ) -> Result<Self::Response, Self::Error> {
799        #[cfg(feature = "__tls")]
800        crate::client::layer::utils::update_request_extension(req.extensions_mut(), cx.target());
801
802        // set timeout
803        {
804            let config = cx.rpc_info_mut().config_mut();
805            // We should check it here because CallOptService must be outer of the client service
806            if config.timeout().is_none() {
807                config.set_timeout(self.inner.timeout);
808            }
809        }
810
811        // extend headermap
812        req.headers_mut().extend(self.inner.headers.clone());
813
814        // apply metainfo if it does not exist
815        let has_metainfo = METAINFO.try_with(|_| {}).is_ok();
816
817        let fut = self.inner.service.call(cx, req);
818
819        if has_metainfo {
820            fut.await
821        } else {
822            METAINFO.scope(RefCell::new(MetaInfo::default()), fut).await
823        }
824    }
825}
826
827impl<ReqBody, RespBody> Service<ClientContext, Request<ReqBody>> for Client<ReqBody, RespBody>
828where
829    ReqBody: Send,
830{
831    type Response = Response<RespBody>;
832    type Error = ClientError;
833
834    fn call(
835        &self,
836        cx: &mut ClientContext,
837        req: Request<ReqBody>,
838    ) -> impl Future<Output = Result<Self::Response, Self::Error>> + Send {
839        OneShotService::call(self.clone(), cx, req)
840    }
841}
842
843/// A dummy [`MkClient`] that does not have any functionality
844pub struct DefaultMkClient;
845
846impl<C> MkClient<C> for DefaultMkClient {
847    type Target = C;
848
849    fn mk_client(&self, service: C) -> Self::Target {
850        service
851    }
852}
853
854static CLIENT: LazyLock<Client> = LazyLock::new(Default::default);
855
856/// Create a GET request to the specified URI.
857pub async fn get<U>(uri: U) -> Result<Response>
858where
859    U: TryInto<Uri>,
860    U::Error: Into<BoxError>,
861{
862    CLIENT.get(uri).send().await
863}