Skip to main content

tonic/transport/channel/
endpoint.rs

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