Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions tonic/src/transport/channel/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
{
let me = dst.try_into().map_err(|e| Error::from_source(e.into()))?;
#[cfg(feature = "_tls-any")]
if let EndpointType::Uri(uri) = &me.uri {

Check warning on line 72 in tonic/src/transport/channel/endpoint.rs

View workflow job for this annotation

GitHub Actions / clippy

this `if` statement can be collapsed
if me.tls.is_none() && uri.scheme() == Some(&http::uri::Scheme::HTTPS) {
return me.tls_config(ClientTlsConfig::new().with_enabled_roots());
}
Expand Down Expand Up @@ -380,7 +380,7 @@
),
..self
}),
EndpointType::Uds(_) => Err(Error::new(error::Kind::InvalidTlsConfigForUds)),
EndpointType::Uds(_) => Err(Error::new(error::ErrorKind::InvalidTlsConfigForUds)),
}
}

Expand Down Expand Up @@ -420,7 +420,7 @@
),
..self
}),
EndpointType::Uds(_) => Err(Error::new(error::Kind::InvalidTlsConfigForUds)),
EndpointType::Uds(_) => Err(Error::new(error::ErrorKind::InvalidTlsConfigForUds)),
}
}

Expand Down
91 changes: 80 additions & 11 deletions tonic/src/transport/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,40 @@ pub struct Error {
}

struct ErrorImpl {
kind: Kind,
kind: ErrorKind,
source: Option<Source>,
}

#[derive(Debug)]
pub(crate) enum Kind {
/// A categorical description of a [`transport::Error`](Error).
///
/// Returned by [`Error::kind`], this enum lets callers programmatically
/// distinguish between the different failure modes of the transport layer
/// without inspecting the error's `Display` output or downcasting its
/// [`source`](std::error::Error::source).
///
/// This enum is marked `#[non_exhaustive]`: new variants may be added in
/// the future without a major version bump, so always include a `_ =>`
/// arm when matching on it.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
/// A generic transport-level failure (I/O, protocol, TLS handshake, etc.).
/// The underlying cause is available via [`std::error::Error::source`].
Transport,
/// The provided value could not be parsed as a valid URI.
#[cfg(feature = "channel")]
InvalidUri,
/// The configured user-agent string is not a valid HTTP header value.
#[cfg(feature = "channel")]
InvalidUserAgent,
/// TLS configuration was applied to an endpoint that uses a Unix domain
/// socket, which is not supported.
#[cfg(all(feature = "_tls-any", feature = "channel"))]
InvalidTlsConfigForUds,
}

impl Error {
pub(crate) fn new(kind: Kind) -> Self {
pub(crate) fn new(kind: ErrorKind) -> Self {
Self {
inner: ErrorImpl { kind, source: None },
}
Expand All @@ -36,28 +53,47 @@ impl Error {
}

pub(crate) fn from_source(source: impl Into<crate::BoxError>) -> Self {
Error::new(Kind::Transport).with(source)
Error::new(ErrorKind::Transport).with(source)
}

#[cfg(feature = "channel")]
pub(crate) fn new_invalid_uri() -> Self {
Error::new(Kind::InvalidUri)
Error::new(ErrorKind::InvalidUri)
}

#[cfg(feature = "channel")]
pub(crate) fn new_invalid_user_agent() -> Self {
Error::new(Kind::InvalidUserAgent)
Error::new(ErrorKind::InvalidUserAgent)
}

/// Returns the [`ErrorKind`] categorizing this error.
///
/// Use this to branch on the failure mode without parsing `Display`
/// output. Always include a `_` arm — [`ErrorKind`] is `#[non_exhaustive]`.
///
/// ```ignore
/// use tonic::transport::{Error, ErrorKind};
///
/// fn classify(err: &Error) {
/// match err.kind() {
/// ErrorKind::Transport => { /* network-level failure */ }
/// _ => { /* configuration or other error */ }
/// }
/// }
/// ```
pub fn kind(&self) -> ErrorKind {
self.inner.kind
}

fn description(&self) -> &str {
match &self.inner.kind {
Kind::Transport => "transport error",
ErrorKind::Transport => "transport error",
#[cfg(feature = "channel")]
Kind::InvalidUri => "invalid URI",
ErrorKind::InvalidUri => "invalid URI",
#[cfg(feature = "channel")]
Kind::InvalidUserAgent => "user agent is not a valid header value",
ErrorKind::InvalidUserAgent => "user agent is not a valid header value",
#[cfg(all(feature = "_tls-any", feature = "channel"))]
Kind::InvalidTlsConfigForUds => "cannot apply TLS config for unix domain socket",
ErrorKind::InvalidTlsConfigForUds => "cannot apply TLS config for unix domain socket",
}
}
}
Expand Down Expand Up @@ -90,3 +126,36 @@ impl StdError for Error {
.map(|source| &**source as &(dyn StdError + 'static))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn from_source_has_transport_kind() {
let inner = std::io::Error::other("boom");
let err = Error::from_source(inner);
assert_eq!(err.kind(), ErrorKind::Transport);
}

#[cfg(feature = "channel")]
#[test]
fn invalid_uri_kind() {
assert_eq!(Error::new_invalid_uri().kind(), ErrorKind::InvalidUri);
}

#[cfg(feature = "channel")]
#[test]
fn invalid_user_agent_kind() {
assert_eq!(
Error::new_invalid_user_agent().kind(),
ErrorKind::InvalidUserAgent,
);
}

#[test]
fn error_kind_is_copy() {
fn assert_copy<T: Copy>() {}
assert_copy::<ErrorKind>();
}
}
2 changes: 1 addition & 1 deletion tonic/src/transport/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ mod tls;
#[doc(inline)]
#[cfg(feature = "channel")]
pub use self::channel::{Channel, Endpoint};
pub use self::error::Error;
pub use self::error::{Error, ErrorKind};
#[doc(inline)]
#[cfg(feature = "server")]
pub use self::server::Server;
Expand Down
Loading