libp2p_swarm/connection/
error.rs

1// Copyright 2018 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21use crate::transport::TransportError;
22use crate::Multiaddr;
23use crate::{ConnectedPoint, PeerId};
24use std::{fmt, io};
25
26/// Errors that can occur in the context of an established `Connection`.
27#[derive(Debug)]
28pub enum ConnectionError<THandlerErr> {
29    /// An I/O error occurred on the connection.
30    // TODO: Eventually this should also be a custom error?
31    IO(io::Error),
32
33    /// The connection keep-alive timeout expired.
34    KeepAliveTimeout,
35
36    /// The connection handler produced an error.
37    #[deprecated(
38        note = "Will be removed together with `ConnectionHandlerEvent::Close`. See <https://github.com/libp2p/rust-libp2p/issues/3591> for details."
39    )]
40    Handler(THandlerErr),
41}
42
43impl<THandlerErr> fmt::Display for ConnectionError<THandlerErr>
44where
45    THandlerErr: fmt::Display,
46{
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        match self {
49            ConnectionError::IO(err) => write!(f, "Connection error: I/O error: {err}"),
50            ConnectionError::KeepAliveTimeout => {
51                write!(f, "Connection closed due to expired keep-alive timeout.")
52            }
53            #[allow(deprecated)]
54            ConnectionError::Handler(err) => write!(f, "Connection error: Handler error: {err}"),
55        }
56    }
57}
58
59impl<THandlerErr> std::error::Error for ConnectionError<THandlerErr>
60where
61    THandlerErr: std::error::Error + 'static,
62{
63    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
64        match self {
65            ConnectionError::IO(err) => Some(err),
66            ConnectionError::KeepAliveTimeout => None,
67            #[allow(deprecated)]
68            ConnectionError::Handler(err) => Some(err),
69        }
70    }
71}
72
73impl<THandlerErr> From<io::Error> for ConnectionError<THandlerErr> {
74    fn from(error: io::Error) -> Self {
75        ConnectionError::IO(error)
76    }
77}
78
79/// Errors that can occur in the context of a pending outgoing `Connection`.
80///
81/// Note: Addresses for an outbound connection are dialed in parallel. Thus, compared to
82/// [`PendingInboundConnectionError`], one or more [`TransportError`]s can occur for a single
83/// connection.
84pub(crate) type PendingOutboundConnectionError =
85    PendingConnectionError<Vec<(Multiaddr, TransportError<io::Error>)>>;
86
87/// Errors that can occur in the context of a pending incoming `Connection`.
88pub(crate) type PendingInboundConnectionError = PendingConnectionError<TransportError<io::Error>>;
89
90/// Errors that can occur in the context of a pending `Connection`.
91#[derive(Debug)]
92pub enum PendingConnectionError<TTransErr> {
93    /// An error occurred while negotiating the transport protocol(s) on a connection.
94    Transport(TTransErr),
95
96    /// Pending connection attempt has been aborted.
97    Aborted,
98
99    /// The peer identity obtained on the connection did not
100    /// match the one that was expected.
101    WrongPeerId {
102        obtained: PeerId,
103        endpoint: ConnectedPoint,
104    },
105
106    /// The connection was dropped because it resolved to our own [`PeerId`].
107    LocalPeerId { endpoint: ConnectedPoint },
108}
109
110impl<T> PendingConnectionError<T> {
111    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> PendingConnectionError<U> {
112        match self {
113            PendingConnectionError::Transport(t) => PendingConnectionError::Transport(f(t)),
114            PendingConnectionError::Aborted => PendingConnectionError::Aborted,
115            PendingConnectionError::WrongPeerId { obtained, endpoint } => {
116                PendingConnectionError::WrongPeerId { obtained, endpoint }
117            }
118            PendingConnectionError::LocalPeerId { endpoint } => {
119                PendingConnectionError::LocalPeerId { endpoint }
120            }
121        }
122    }
123}
124
125impl<TTransErr> fmt::Display for PendingConnectionError<TTransErr>
126where
127    TTransErr: fmt::Display + fmt::Debug,
128{
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        match self {
131            PendingConnectionError::Aborted => write!(f, "Pending connection: Aborted."),
132            PendingConnectionError::Transport(err) => {
133                write!(
134                    f,
135                    "Pending connection: Transport error on connection: {err}"
136                )
137            }
138            PendingConnectionError::WrongPeerId { obtained, endpoint } => {
139                write!(
140                    f,
141                    "Pending connection: Unexpected peer ID {obtained} at {endpoint:?}."
142                )
143            }
144            PendingConnectionError::LocalPeerId { endpoint } => {
145                write!(f, "Pending connection: Local peer ID at {endpoint:?}.")
146            }
147        }
148    }
149}
150
151impl<TTransErr> std::error::Error for PendingConnectionError<TTransErr>
152where
153    TTransErr: std::error::Error + 'static,
154{
155    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
156        match self {
157            PendingConnectionError::Transport(_) => None,
158            PendingConnectionError::WrongPeerId { .. } => None,
159            PendingConnectionError::LocalPeerId { .. } => None,
160            PendingConnectionError::Aborted => None,
161        }
162    }
163}