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 std::{fmt, io};
22
23use crate::{transport::TransportError, ConnectedPoint, Multiaddr, PeerId};
24
25/// Errors that can occur in the context of an established `Connection`.
26#[derive(Debug)]
27pub enum ConnectionError {
28    /// An I/O error occurred on the connection.
29    // TODO: Eventually this should also be a custom error?
30    IO(io::Error),
31
32    /// The connection keep-alive timeout expired.
33    KeepAliveTimeout,
34}
35
36impl fmt::Display for ConnectionError {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            ConnectionError::IO(err) => write!(f, "Connection error: I/O error: {err}"),
40            ConnectionError::KeepAliveTimeout => {
41                write!(f, "Connection closed due to expired keep-alive timeout.")
42            }
43        }
44    }
45}
46
47impl std::error::Error for ConnectionError {
48    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
49        match self {
50            ConnectionError::IO(err) => Some(err),
51            ConnectionError::KeepAliveTimeout => None,
52        }
53    }
54}
55
56impl From<io::Error> for ConnectionError {
57    fn from(error: io::Error) -> Self {
58        ConnectionError::IO(error)
59    }
60}
61
62/// Errors that can occur in the context of a pending outgoing `Connection`.
63///
64/// Note: Addresses for an outbound connection are dialed in parallel. Thus, compared to
65/// [`PendingInboundConnectionError`], one or more [`TransportError`]s can occur for a single
66/// connection.
67pub(crate) type PendingOutboundConnectionError =
68    PendingConnectionError<Vec<(Multiaddr, TransportError<io::Error>)>>;
69
70/// Errors that can occur in the context of a pending incoming `Connection`.
71pub(crate) type PendingInboundConnectionError = PendingConnectionError<TransportError<io::Error>>;
72
73/// Errors that can occur in the context of a pending `Connection`.
74#[derive(Debug)]
75pub enum PendingConnectionError<TTransErr> {
76    /// An error occurred while negotiating the transport protocol(s) on a connection.
77    Transport(TTransErr),
78
79    /// Pending connection attempt has been aborted.
80    Aborted,
81
82    /// The peer identity obtained on the connection did not
83    /// match the one that was expected.
84    WrongPeerId {
85        obtained: PeerId,
86        endpoint: ConnectedPoint,
87    },
88
89    /// The connection was dropped because it resolved to our own [`PeerId`].
90    LocalPeerId { endpoint: ConnectedPoint },
91}
92
93impl<T> PendingConnectionError<T> {
94    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> PendingConnectionError<U> {
95        match self {
96            PendingConnectionError::Transport(t) => PendingConnectionError::Transport(f(t)),
97            PendingConnectionError::Aborted => PendingConnectionError::Aborted,
98            PendingConnectionError::WrongPeerId { obtained, endpoint } => {
99                PendingConnectionError::WrongPeerId { obtained, endpoint }
100            }
101            PendingConnectionError::LocalPeerId { endpoint } => {
102                PendingConnectionError::LocalPeerId { endpoint }
103            }
104        }
105    }
106}
107
108impl<TTransErr> fmt::Display for PendingConnectionError<TTransErr>
109where
110    TTransErr: fmt::Display + fmt::Debug,
111{
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        match self {
114            PendingConnectionError::Aborted => write!(f, "Pending connection: Aborted."),
115            PendingConnectionError::Transport(err) => {
116                write!(
117                    f,
118                    "Pending connection: Transport error on connection: {err}"
119                )
120            }
121            PendingConnectionError::WrongPeerId { obtained, endpoint } => {
122                write!(
123                    f,
124                    "Pending connection: Unexpected peer ID {obtained} at {endpoint:?}."
125                )
126            }
127            PendingConnectionError::LocalPeerId { endpoint } => {
128                write!(f, "Pending connection: Local peer ID at {endpoint:?}.")
129            }
130        }
131    }
132}
133
134impl<TTransErr> std::error::Error for PendingConnectionError<TTransErr>
135where
136    TTransErr: std::error::Error + 'static,
137{
138    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
139        match self {
140            PendingConnectionError::Transport(_) => None,
141            PendingConnectionError::WrongPeerId { .. } => None,
142            PendingConnectionError::LocalPeerId { .. } => None,
143            PendingConnectionError::Aborted => None,
144        }
145    }
146}