Skip to main content

tonic/transport/
error.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 std::{error::Error as StdError, fmt};
26
27type Source = Box<dyn StdError + Send + Sync + 'static>;
28
29/// Error's that originate from the client or server;
30pub struct Error {
31    inner: ErrorImpl,
32}
33
34struct ErrorImpl {
35    kind: Kind,
36    source: Option<Source>,
37}
38
39#[derive(Debug)]
40pub(crate) enum Kind {
41    Transport,
42    #[cfg(feature = "channel")]
43    InvalidUri,
44    #[cfg(feature = "channel")]
45    InvalidUserAgent,
46    #[cfg(all(feature = "_tls-any", feature = "channel"))]
47    InvalidTlsConfigForUds,
48}
49
50impl Error {
51    pub(crate) fn new(kind: Kind) -> Self {
52        Self {
53            inner: ErrorImpl { kind, source: None },
54        }
55    }
56
57    pub(crate) fn with(mut self, source: impl Into<Source>) -> Self {
58        self.inner.source = Some(source.into());
59        self
60    }
61
62    pub(crate) fn from_source(source: impl Into<crate::BoxError>) -> Self {
63        Error::new(Kind::Transport).with(source)
64    }
65
66    #[cfg(feature = "channel")]
67    pub(crate) fn new_invalid_uri() -> Self {
68        Error::new(Kind::InvalidUri)
69    }
70
71    #[cfg(feature = "channel")]
72    pub(crate) fn new_invalid_user_agent() -> Self {
73        Error::new(Kind::InvalidUserAgent)
74    }
75
76    fn description(&self) -> &str {
77        match &self.inner.kind {
78            Kind::Transport => "transport error",
79            #[cfg(feature = "channel")]
80            Kind::InvalidUri => "invalid URI",
81            #[cfg(feature = "channel")]
82            Kind::InvalidUserAgent => "user agent is not a valid header value",
83            #[cfg(all(feature = "_tls-any", feature = "channel"))]
84            Kind::InvalidTlsConfigForUds => "cannot apply TLS config for unix domain socket",
85        }
86    }
87}
88
89impl fmt::Debug for Error {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        let mut f = f.debug_tuple("tonic::transport::Error");
92
93        f.field(&self.inner.kind);
94
95        if let Some(source) = &self.inner.source {
96            f.field(source);
97        }
98
99        f.finish()
100    }
101}
102
103impl fmt::Display for Error {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.write_str(self.description())
106    }
107}
108
109impl StdError for Error {
110    fn source(&self) -> Option<&(dyn StdError + 'static)> {
111        self.inner
112            .source
113            .as_ref()
114            .map(|source| &**source as &(dyn StdError + 'static))
115    }
116}