Skip to main content

tonic/transport/channel/
endpoint.rs

1/*
2 *
3 * Copyright 2025 gRPC authors.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 */
24
25use super::Channel;
26#[cfg(feature = "_tls-any")]
27use super::ClientTlsConfig;
28#[cfg(feature = "_tls-any")]
29use super::service::TlsConnector;
30use super::service::{self, Executor, SharedExec};
31use super::uds_connector::UdsConnector;
32use crate::transport::Error;
33#[cfg(feature = "_tls-any")]
34use crate::transport::error;
35use bytes::Bytes;
36use http::{HeaderValue, uri::Uri};
37use hyper::rt;
38use hyper_util::client::legacy::connect::HttpConnector;
39#[cfg(feature = "_tls-any")]
40use std::sync::Arc;
41use std::{fmt, future::Future, net::IpAddr, pin::Pin, str, str::FromStr, time::Duration};
42#[cfg(feature = "_tls-any")]
43use tokio_rustls::rustls::client::danger::ServerCertVerifier;
44use tower_service::Service;
45
46#[derive(Clone, PartialEq, Eq, Hash)]
47pub(crate) enum EndpointType {
48    Uri(Uri),
49    Uds(String),
50}
51
52/// Channel builder.
53///
54/// This struct is used to build and configure HTTP/2 channels.
55#[derive(Clone)]
56pub struct Endpoint {
57    pub(crate) uri: EndpointType,
58    fallback_uri: Uri,
59    pub(crate) origin: Option<Uri>,
60    pub(crate) user_agent: Option<HeaderValue>,
61    pub(crate) timeout: Option<Duration>,
62    pub(crate) concurrency_limit: Option<usize>,
63    pub(crate) rate_limit: Option<(u64, Duration)>,
64    #[cfg(feature = "_tls-any")]
65    pub(crate) tls: Option<TlsConnector>,
66    pub(crate) buffer_size: Option<usize>,
67    pub(crate) init_stream_window_size: Option<u32>,
68    pub(crate) init_connection_window_size: Option<u32>,
69    pub(crate) max_frame_size: Option<u32>,
70    pub(crate) tcp_keepalive: Option<Duration>,
71    pub(crate) tcp_keepalive_interval: Option<Duration>,
72    pub(crate) tcp_keepalive_retries: Option<u32>,
73    pub(crate) tcp_nodelay: bool,
74    pub(crate) http2_keep_alive_interval: Option<Duration>,
75    pub(crate) http2_keep_alive_timeout: Option<Duration>,
76    pub(crate) http2_keep_alive_while_idle: Option<bool>,
77    pub(crate) http2_header_table_size: Option<u32>,
78    pub(crate) http2_max_header_list_size: Option<u32>,
79    pub(crate) http2_max_concurrent_reset_streams: Option<usize>,
80    pub(crate) http2_reset_stream_duration: Option<Duration>,
81    pub(crate) http2_max_local_error_reset_streams: Option<Option<usize>>,
82    pub(crate) connect_timeout: Option<Duration>,
83    pub(crate) http2_adaptive_window: Option<bool>,
84    pub(crate) local_address: Option<IpAddr>,
85    pub(crate) executor: SharedExec,
86}
87
88impl Endpoint {
89    // FIXME: determine if we want to expose this or not. This is really
90    // just used in codegen for a shortcut.
91    #[doc(hidden)]
92    pub fn new<D>(dst: D) -> Result<Self, Error>
93    where
94        D: TryInto<Self>,
95        D::Error: Into<crate::BoxError>,
96    {
97        let me = dst.try_into().map_err(|e| Error::from_source(e.into()))?;
98        #[cfg(feature = "_tls-any")]
99        if let EndpointType::Uri(uri) = &me.uri
100            && me.tls.is_none()
101            && uri.scheme() == Some(&http::uri::Scheme::HTTPS)
102        {
103            return me.tls_config(ClientTlsConfig::new().with_enabled_roots());
104        }
105        Ok(me)
106    }
107
108    fn new_uri(uri: Uri) -> Self {
109        Self {
110            uri: EndpointType::Uri(uri.clone()),
111            fallback_uri: uri,
112            origin: None,
113            user_agent: None,
114            concurrency_limit: None,
115            rate_limit: None,
116            timeout: None,
117            #[cfg(feature = "_tls-any")]
118            tls: None,
119            buffer_size: None,
120            init_stream_window_size: None,
121            init_connection_window_size: None,
122            max_frame_size: None,
123            tcp_keepalive: None,
124            tcp_keepalive_interval: None,
125            tcp_keepalive_retries: None,
126            tcp_nodelay: true,
127            http2_keep_alive_interval: None,
128            http2_keep_alive_timeout: None,
129            http2_keep_alive_while_idle: None,
130            http2_header_table_size: None,
131            http2_max_header_list_size: None,
132            http2_max_concurrent_reset_streams: None,
133            http2_reset_stream_duration: None,
134            http2_max_local_error_reset_streams: None,
135            connect_timeout: None,
136            http2_adaptive_window: None,
137            executor: SharedExec::tokio(),
138            local_address: None,
139        }
140    }
141
142    fn new_uds(uds_filepath: &str) -> Self {
143        Self {
144            uri: EndpointType::Uds(uds_filepath.to_string()),
145            fallback_uri: Uri::from_static("http://tonic"),
146            origin: None,
147            user_agent: None,
148            concurrency_limit: None,
149            rate_limit: None,
150            timeout: None,
151            #[cfg(feature = "_tls-any")]
152            tls: None,
153            buffer_size: None,
154            init_stream_window_size: None,
155            init_connection_window_size: None,
156            max_frame_size: None,
157            tcp_keepalive: None,
158            tcp_keepalive_interval: None,
159            tcp_keepalive_retries: None,
160            tcp_nodelay: true,
161            http2_keep_alive_interval: None,
162            http2_keep_alive_timeout: None,
163            http2_keep_alive_while_idle: None,
164            http2_header_table_size: None,
165            http2_max_header_list_size: None,
166            http2_max_concurrent_reset_streams: None,
167            http2_reset_stream_duration: None,
168            http2_max_local_error_reset_streams: None,
169            connect_timeout: None,
170            http2_adaptive_window: None,
171            executor: SharedExec::tokio(),
172            local_address: None,
173        }
174    }
175
176    /// Convert an `Endpoint` from a static string.
177    ///
178    /// # Panics
179    ///
180    /// This function panics if the argument is an invalid URI.
181    ///
182    /// ```
183    /// # use tonic::transport::Endpoint;
184    /// Endpoint::from_static("https://example.com");
185    /// ```
186    pub fn from_static(s: &'static str) -> Self {
187        if s.starts_with("unix:") {
188            let uds_filepath = s
189                .strip_prefix("unix://")
190                .or_else(|| s.strip_prefix("unix:"))
191                .expect("Invalid unix domain socket URI");
192            Self::new_uds(uds_filepath)
193        } else {
194            let uri = Uri::from_static(s);
195            Self::new_uri(uri)
196        }
197    }
198
199    /// Convert an `Endpoint` from shared bytes.
200    ///
201    /// ```
202    /// # use tonic::transport::Endpoint;
203    /// Endpoint::from_shared("https://example.com".to_string());
204    /// ```
205    pub fn from_shared(s: impl Into<Bytes>) -> Result<Self, Error> {
206        let s = str::from_utf8(&s.into())
207            .map_err(|e| Error::new_invalid_uri().with(e))?
208            .to_string();
209        if s.starts_with("unix:") {
210            let uds_filepath = s
211                .strip_prefix("unix://")
212                .or_else(|| s.strip_prefix("unix:"))
213                .ok_or(Error::new_invalid_uri())?;
214            Ok(Self::new_uds(uds_filepath))
215        } else {
216            let uri = Uri::from_maybe_shared(s).map_err(|e| Error::new_invalid_uri().with(e))?;
217            Ok(Self::from(uri))
218        }
219    }
220
221    /// Set a custom user-agent header.
222    ///
223    /// `user_agent` will be prepended to Tonic's default user-agent string (`tonic/x.x.x`).
224    /// It must be a value that can be converted into a valid  `http::HeaderValue` or building
225    /// the endpoint will fail.
226    /// ```
227    /// # use tonic::transport::Endpoint;
228    /// # let mut builder = Endpoint::from_static("https://example.com");
229    /// builder.user_agent("Greeter").expect("Greeter should be a valid header value");
230    /// // user-agent: "Greeter tonic/x.x.x"
231    /// ```
232    pub fn user_agent<T>(self, user_agent: T) -> Result<Self, Error>
233    where
234        T: TryInto<HeaderValue>,
235    {
236        user_agent
237            .try_into()
238            .map(|ua| Endpoint {
239                user_agent: Some(ua),
240                ..self
241            })
242            .map_err(|_| Error::new_invalid_user_agent())
243    }
244
245    /// Set a custom origin.
246    ///
247    /// Override the `origin`, mainly useful when you are reaching a Server/LoadBalancer
248    /// which serves multiple services at the same time.
249    /// It will play the role of SNI (Server Name Indication).
250    ///
251    /// ```
252    /// # use tonic::transport::Endpoint;
253    /// # let mut builder = Endpoint::from_static("https://proxy.com");
254    /// builder.origin("https://example.com".parse().expect("http://example.com must be a valid URI"));
255    /// // origin: "https://example.com"
256    /// ```
257    pub fn origin(self, origin: Uri) -> Self {
258        Endpoint {
259            origin: Some(origin),
260            ..self
261        }
262    }
263
264    /// Apply a timeout to each request.
265    ///
266    /// ```
267    /// # use tonic::transport::Endpoint;
268    /// # use std::time::Duration;
269    /// # let mut builder = Endpoint::from_static("https://example.com");
270    /// builder.timeout(Duration::from_secs(5));
271    /// ```
272    ///
273    /// # Notes
274    ///
275    /// This does **not** set the timeout metadata (`grpc-timeout` header) on
276    /// the request, meaning the server will not be informed of this timeout,
277    /// for that use [`Request::set_timeout`].
278    ///
279    /// [`Request::set_timeout`]: crate::Request::set_timeout
280    pub fn timeout(self, dur: Duration) -> Self {
281        Endpoint {
282            timeout: Some(dur),
283            ..self
284        }
285    }
286
287    /// Apply a timeout to connecting to the uri.
288    ///
289    /// Defaults to no timeout.
290    ///
291    /// ```
292    /// # use tonic::transport::Endpoint;
293    /// # use std::time::Duration;
294    /// # let mut builder = Endpoint::from_static("https://example.com");
295    /// builder.connect_timeout(Duration::from_secs(5));
296    /// ```
297    pub fn connect_timeout(self, dur: Duration) -> Self {
298        Endpoint {
299            connect_timeout: Some(dur),
300            ..self
301        }
302    }
303
304    /// Set whether TCP keepalive messages are enabled on accepted connections.
305    ///
306    /// If `None` is specified, keepalive is disabled, otherwise the duration
307    /// specified will be the time to remain idle before sending TCP keepalive
308    /// probes.
309    ///
310    /// Default is no keepalive (`None`)
311    pub fn tcp_keepalive(self, tcp_keepalive: Option<Duration>) -> Self {
312        Endpoint {
313            tcp_keepalive,
314            ..self
315        }
316    }
317
318    /// Set the duration between two successive TCP keepalive retransmissions,
319    /// if acknowledgement to the previous keepalive transmission is not received.
320    ///
321    /// This is only used if `tcp_keepalive` is not None.
322    ///
323    /// Defaults to None, which is the system default.
324    pub fn tcp_keepalive_interval(self, tcp_keepalive_interval: Option<Duration>) -> Self {
325        Endpoint {
326            tcp_keepalive_interval,
327            ..self
328        }
329    }
330
331    /// Set the number of retransmissions to be carried out before declaring that remote end is not available.
332    ///
333    /// This is only used if `tcp_keepalive` is not None.
334    ///
335    /// Defaults to None, which is the system default.
336    pub fn tcp_keepalive_retries(self, tcp_keepalive_retries: Option<u32>) -> Self {
337        Endpoint {
338            tcp_keepalive_retries,
339            ..self
340        }
341    }
342
343    /// Apply a concurrency limit to each request.
344    ///
345    /// ```
346    /// # use tonic::transport::Endpoint;
347    /// # let mut builder = Endpoint::from_static("https://example.com");
348    /// builder.concurrency_limit(256);
349    /// ```
350    pub fn concurrency_limit(self, limit: usize) -> Self {
351        Endpoint {
352            concurrency_limit: Some(limit),
353            ..self
354        }
355    }
356
357    /// Apply a rate limit to each request.
358    ///
359    /// ```
360    /// # use tonic::transport::Endpoint;
361    /// # use std::time::Duration;
362    /// # let mut builder = Endpoint::from_static("https://example.com");
363    /// builder.rate_limit(32, Duration::from_secs(1));
364    /// ```
365    pub fn rate_limit(self, limit: u64, duration: Duration) -> Self {
366        Endpoint {
367            rate_limit: Some((limit, duration)),
368            ..self
369        }
370    }
371
372    /// Sets the [`SETTINGS_INITIAL_WINDOW_SIZE`][spec] option for HTTP2
373    /// stream-level flow control.
374    ///
375    /// Default is 65,535
376    ///
377    /// [spec]: https://httpwg.org/specs/rfc9113.html#InitialWindowSize
378    pub fn initial_stream_window_size(self, sz: impl Into<Option<u32>>) -> Self {
379        Endpoint {
380            init_stream_window_size: sz.into(),
381            ..self
382        }
383    }
384
385    /// Sets the max connection-level flow control for HTTP2
386    ///
387    /// Default is 65,535
388    pub fn initial_connection_window_size(self, sz: impl Into<Option<u32>>) -> Self {
389        Endpoint {
390            init_connection_window_size: sz.into(),
391            ..self
392        }
393    }
394
395    /// Sets the tower service default internal buffer size
396    ///
397    /// Default is 1024
398    pub fn buffer_size(self, sz: impl Into<Option<usize>>) -> Self {
399        Endpoint {
400            buffer_size: sz.into(),
401            ..self
402        }
403    }
404
405    /// Configures TLS for the endpoint.
406    #[cfg(feature = "_tls-any")]
407    pub fn tls_config(self, tls_config: ClientTlsConfig) -> Result<Self, Error> {
408        match &self.uri {
409            EndpointType::Uri(uri) => Ok(Endpoint {
410                tls: Some(
411                    tls_config
412                        .into_tls_connector(uri)
413                        .map_err(Error::from_source)?,
414                ),
415                ..self
416            }),
417            EndpointType::Uds(_) => Err(Error::new(error::Kind::InvalidTlsConfigForUds)),
418        }
419    }
420
421    /// Configures TLS for the endpoint, replacing the default WebPKI server
422    /// certificate verifier with a custom implementation.
423    ///
424    /// **Warning:** A misconfigured verifier can silently disable peer
425    /// validation. Only use this if you understand rustls' verifier contract.
426    ///
427    /// The custom verifier replaces the default verifier, so any
428    /// [`ClientTlsConfig`] method that configures the default verifier —
429    /// [`ca_certificate`](ClientTlsConfig::ca_certificate),
430    /// [`ca_certificates`](ClientTlsConfig::ca_certificates),
431    /// [`trust_anchor`](ClientTlsConfig::trust_anchor),
432    /// [`trust_anchors`](ClientTlsConfig::trust_anchors),
433    /// `with_native_roots`, `with_webpki_roots`, or
434    /// [`with_enabled_roots`](ClientTlsConfig::with_enabled_roots) — must
435    /// not be set on `tls_config`. Mixing produces an error.
436    ///
437    /// SNI ([`domain_name`](ClientTlsConfig::domain_name)), client identity
438    /// ([`identity`](ClientTlsConfig::identity)),
439    /// [`timeout`](ClientTlsConfig::timeout),
440    /// [`use_key_log`](ClientTlsConfig::use_key_log), and
441    /// [`assume_http2`](ClientTlsConfig::assume_http2) continue to apply.
442    #[cfg(feature = "_tls-any")]
443    pub fn tls_config_with_verifier(
444        self,
445        tls_config: ClientTlsConfig,
446        verifier: Arc<dyn ServerCertVerifier>,
447    ) -> Result<Self, Error> {
448        match &self.uri {
449            EndpointType::Uri(uri) => Ok(Endpoint {
450                tls: Some(
451                    tls_config
452                        .into_tls_connector_with_verifier(uri, verifier)
453                        .map_err(Error::from_source)?,
454                ),
455                ..self
456            }),
457            EndpointType::Uds(_) => Err(Error::new(error::Kind::InvalidTlsConfigForUds)),
458        }
459    }
460
461    /// Set the value of `TCP_NODELAY` option for accepted connections. Enabled by default.
462    pub fn tcp_nodelay(self, enabled: bool) -> Self {
463        Endpoint {
464            tcp_nodelay: enabled,
465            ..self
466        }
467    }
468
469    /// Set http2 KEEP_ALIVE_INTERVAL. Uses `hyper`'s default otherwise.
470    pub fn http2_keep_alive_interval(self, interval: Duration) -> Self {
471        Endpoint {
472            http2_keep_alive_interval: Some(interval),
473            ..self
474        }
475    }
476
477    /// Set http2 KEEP_ALIVE_TIMEOUT. Uses `hyper`'s default otherwise.
478    pub fn keep_alive_timeout(self, duration: Duration) -> Self {
479        Endpoint {
480            http2_keep_alive_timeout: Some(duration),
481            ..self
482        }
483    }
484
485    /// Set http2 KEEP_ALIVE_WHILE_IDLE. Uses `hyper`'s default otherwise.
486    pub fn keep_alive_while_idle(self, enabled: bool) -> Self {
487        Endpoint {
488            http2_keep_alive_while_idle: Some(enabled),
489            ..self
490        }
491    }
492
493    /// Sets whether to use an adaptive flow control. Uses `hyper`'s default otherwise.
494    pub fn http2_adaptive_window(self, enabled: bool) -> Self {
495        Endpoint {
496            http2_adaptive_window: Some(enabled),
497            ..self
498        }
499    }
500
501    /// Sets the `SETTINGS_HEADER_TABLE_SIZE` option for HTTP2 connections.
502    ///
503    /// Informs the peer of the maximum size of the header compression
504    /// table used to decode header blocks, in octets.
505    ///
506    /// Default is 4,096.
507    pub fn http2_header_table_size(self, size: u32) -> Self {
508        Endpoint {
509            http2_header_table_size: Some(size),
510            ..self
511        }
512    }
513
514    /// Sets the max size of received header frames.
515    ///
516    /// This will default to whatever the default in hyper is. As of v1.4.1, it is 16 KiB.
517    pub fn http2_max_header_list_size(self, size: u32) -> Self {
518        Endpoint {
519            http2_max_header_list_size: Some(size),
520            ..self
521        }
522    }
523
524    /// Sets the maximum number of locally reset streams that HTTP2 remembers.
525    ///
526    /// HTTP2 ignores frames for a remembered reset stream. A frame for a forgotten one
527    /// counts toward [`Endpoint::http2_max_local_error_reset_streams`].
528    ///
529    /// Uses `hyper`'s default otherwise.
530    #[must_use]
531    pub fn http2_max_concurrent_reset_streams(self, max: usize) -> Self {
532        Endpoint {
533            http2_max_concurrent_reset_streams: Some(max),
534            ..self
535        }
536    }
537
538    /// Sets how long HTTP2 remembers a locally reset stream.
539    ///
540    /// See [`Endpoint::http2_max_concurrent_reset_streams`]. Uses `hyper`'s default otherwise.
541    #[must_use]
542    pub fn http2_reset_stream_duration(self, duration: Duration) -> Self {
543        Endpoint {
544            http2_reset_stream_duration: Some(duration),
545            ..self
546        }
547    }
548
549    /// Sets the maximum number of local error resets before HTTP2 closes the
550    /// connection with `GOAWAY(ENHANCE_YOUR_CALM)`.
551    ///
552    /// Pass `None` to disable the limit. The limit guards against a misbehaving
553    /// peer, so only disable it for trusted peers. Uses `hyper`'s default (1024) otherwise.
554    #[must_use]
555    pub fn http2_max_local_error_reset_streams(self, max: impl Into<Option<usize>>) -> Self {
556        Endpoint {
557            http2_max_local_error_reset_streams: Some(max.into()),
558            ..self
559        }
560    }
561
562    /// Sets the maximum frame size to use for HTTP2.
563    ///
564    /// Passing `None` will do nothing.
565    ///
566    /// If not set, will default from underlying transport.
567    ///
568    /// # Example
569    ///
570    /// ```
571    /// # use tonic::transport::Endpoint;
572    /// # let builder = Endpoint::from_static("https://example.com");
573    /// let endpoint = builder.max_frame_size(1024 * 1024u32);
574    /// ```
575    #[must_use]
576    pub fn max_frame_size(self, frame_size: impl Into<Option<u32>>) -> Self {
577        Endpoint {
578            max_frame_size: frame_size.into(),
579            ..self
580        }
581    }
582
583    /// Sets the executor used to spawn async tasks.
584    ///
585    /// Uses `tokio::spawn` by default.
586    pub fn executor<E>(mut self, executor: E) -> Self
587    where
588        E: Executor<Pin<Box<dyn Future<Output = ()> + Send>>> + Send + Sync + 'static,
589    {
590        self.executor = SharedExec::new(executor);
591        self
592    }
593
594    pub(crate) fn connector<C>(&self, c: C) -> service::Connector<C> {
595        service::Connector::new(
596            c,
597            #[cfg(feature = "_tls-any")]
598            self.tls.clone(),
599        )
600    }
601
602    /// Set the local address.
603    ///
604    /// This sets the IP address the client will use. By default we let hyper select the IP address.
605    pub fn local_address(self, addr: Option<IpAddr>) -> Self {
606        Endpoint {
607            local_address: addr,
608            ..self
609        }
610    }
611
612    pub(crate) fn http_connector(&self) -> service::Connector<HttpConnector> {
613        let mut http = HttpConnector::new();
614        http.enforce_http(false);
615        http.set_nodelay(self.tcp_nodelay);
616        http.set_keepalive(self.tcp_keepalive);
617        http.set_keepalive_interval(self.tcp_keepalive_interval);
618        http.set_keepalive_retries(self.tcp_keepalive_retries);
619        http.set_connect_timeout(self.connect_timeout);
620        http.set_local_address(self.local_address);
621        self.connector(http)
622    }
623
624    pub(crate) fn uds_connector(&self, uds_filepath: &str) -> service::Connector<UdsConnector> {
625        self.connector(UdsConnector::new(uds_filepath))
626    }
627
628    /// Create a channel from this config.
629    pub async fn connect(&self) -> Result<Channel, Error> {
630        match &self.uri {
631            EndpointType::Uri(_) => Channel::connect(self.http_connector(), self.clone()).await,
632            EndpointType::Uds(uds_filepath) => {
633                Channel::connect(self.uds_connector(uds_filepath.as_str()), self.clone()).await
634            }
635        }
636    }
637
638    /// Create a channel from this config.
639    ///
640    /// The channel returned by this method does not attempt to connect to the endpoint until first
641    /// use.
642    pub fn connect_lazy(&self) -> Channel {
643        match &self.uri {
644            EndpointType::Uri(_) => Channel::new(self.http_connector(), self.clone()),
645            EndpointType::Uds(uds_filepath) => {
646                Channel::new(self.uds_connector(uds_filepath.as_str()), self.clone())
647            }
648        }
649    }
650
651    /// Connect with a custom connector.
652    ///
653    /// This allows you to build a [Channel](struct.Channel.html) that uses a non-HTTP transport.
654    /// See the `uds` example for an example on how to use this function to build channel that
655    /// uses a Unix socket transport.
656    ///
657    /// The [`connect_timeout`](Endpoint::connect_timeout) will still be applied.
658    pub async fn connect_with_connector<C>(&self, connector: C) -> Result<Channel, Error>
659    where
660        C: Service<Uri> + Send + 'static,
661        C::Response: rt::Read + rt::Write + Send + Unpin,
662        C::Future: Send,
663        crate::BoxError: From<C::Error> + Send,
664    {
665        let connector = self.connector(connector);
666
667        if let Some(connect_timeout) = self.connect_timeout {
668            let mut connector = hyper_timeout::TimeoutConnector::new(connector);
669            connector.set_connect_timeout(Some(connect_timeout));
670            Channel::connect(connector, self.clone()).await
671        } else {
672            Channel::connect(connector, self.clone()).await
673        }
674    }
675
676    /// Connect with a custom connector lazily.
677    ///
678    /// This allows you to build a [Channel](struct.Channel.html) that uses a non-HTTP transport
679    /// connect to it lazily.
680    ///
681    /// See the `uds` example for an example on how to use this function to build channel that
682    /// uses a Unix socket transport.
683    pub fn connect_with_connector_lazy<C>(&self, connector: C) -> Channel
684    where
685        C: Service<Uri> + Send + 'static,
686        C::Response: rt::Read + rt::Write + Send + Unpin,
687        C::Future: Send,
688        crate::BoxError: From<C::Error> + Send,
689    {
690        let connector = self.connector(connector);
691        if let Some(connect_timeout) = self.connect_timeout {
692            let mut connector = hyper_timeout::TimeoutConnector::new(connector);
693            connector.set_connect_timeout(Some(connect_timeout));
694            Channel::new(connector, self.clone())
695        } else {
696            Channel::new(connector, self.clone())
697        }
698    }
699
700    /// Get the endpoint uri.
701    ///
702    /// ```
703    /// # use tonic::transport::Endpoint;
704    /// # use http::Uri;
705    /// let endpoint = Endpoint::from_static("https://example.com");
706    ///
707    /// assert_eq!(endpoint.uri(), &Uri::from_static("https://example.com"));
708    /// ```
709    pub fn uri(&self) -> &Uri {
710        match &self.uri {
711            EndpointType::Uri(uri) => uri,
712            EndpointType::Uds(_) => &self.fallback_uri,
713        }
714    }
715
716    /// Get the value of `TCP_NODELAY` option for accepted connections.
717    pub fn get_tcp_nodelay(&self) -> bool {
718        self.tcp_nodelay
719    }
720
721    /// Get the connect timeout.
722    pub fn get_connect_timeout(&self) -> Option<Duration> {
723        self.connect_timeout
724    }
725
726    /// Get whether TCP keepalive messages are enabled on accepted connections.
727    ///
728    /// If `None` is specified, keepalive is disabled, otherwise the duration
729    /// specified will be the time to remain idle before sending TCP keepalive
730    /// probes.
731    pub fn get_tcp_keepalive(&self) -> Option<Duration> {
732        self.tcp_keepalive
733    }
734
735    /// Get whether TCP keepalive interval.
736    pub fn get_tcp_keepalive_interval(&self) -> Option<Duration> {
737        self.tcp_keepalive_interval
738    }
739
740    /// Get whether TCP keepalive retries.
741    pub fn get_tcp_keepalive_retries(&self) -> Option<u32> {
742        self.tcp_keepalive_retries
743    }
744}
745
746impl From<Uri> for Endpoint {
747    fn from(uri: Uri) -> Self {
748        Self::new_uri(uri)
749    }
750}
751
752impl TryFrom<Bytes> for Endpoint {
753    type Error = Error;
754
755    fn try_from(t: Bytes) -> Result<Self, Self::Error> {
756        Self::from_shared(t)
757    }
758}
759
760impl TryFrom<String> for Endpoint {
761    type Error = Error;
762
763    fn try_from(t: String) -> Result<Self, Self::Error> {
764        Self::from_shared(t.into_bytes())
765    }
766}
767
768impl TryFrom<&'static str> for Endpoint {
769    type Error = Error;
770
771    fn try_from(t: &'static str) -> Result<Self, Self::Error> {
772        Self::from_shared(t.as_bytes())
773    }
774}
775
776impl fmt::Debug for Endpoint {
777    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
778        f.debug_struct("Endpoint").finish()
779    }
780}
781
782impl FromStr for Endpoint {
783    type Err = Error;
784
785    fn from_str(s: &str) -> Result<Self, Self::Err> {
786        Self::try_from(s.to_string())
787    }
788}