iroh_bytes/get/
error.rs

1//! Error returned from get operations
2
3use crate::util::progress::ProgressSendError;
4
5/// Failures for a get operation
6#[derive(Debug, thiserror::Error)]
7pub enum GetError {
8    /// Hash not found.
9    #[error("Hash not found")]
10    NotFound(#[source] anyhow::Error),
11    /// Remote has reset the connection.
12    #[error("Remote has reset the connection")]
13    RemoteReset(#[source] anyhow::Error),
14    /// Remote behaved in a non-compliant way.
15    #[error("Remote behaved in a non-compliant way")]
16    NoncompliantNode(#[source] anyhow::Error),
17
18    /// Network or IO operation failed.
19    #[error("A network or IO operation failed")]
20    Io(#[source] anyhow::Error),
21
22    /// Our download request is invalid.
23    #[error("Our download request is invalid")]
24    BadRequest(#[source] anyhow::Error),
25    /// Operation failed on the local node.
26    #[error("Operation failed on the local node")]
27    LocalFailure(#[source] anyhow::Error),
28}
29
30impl From<ProgressSendError> for GetError {
31    fn from(value: ProgressSendError) -> Self {
32        Self::LocalFailure(value.into())
33    }
34}
35
36impl From<quinn::ConnectionError> for GetError {
37    fn from(value: quinn::ConnectionError) -> Self {
38        // explicit match just to be sure we are taking everything into account
39        match value {
40            e @ quinn::ConnectionError::VersionMismatch => {
41                // > The peer doesn't implement any supported version
42                // unsupported version is likely a long time error, so this peer is not usable
43                GetError::NoncompliantNode(e.into())
44            }
45            e @ quinn::ConnectionError::TransportError(_) => {
46                // > The peer violated the QUIC specification as understood by this implementation
47                // bad peer we don't want to keep around
48                GetError::NoncompliantNode(e.into())
49            }
50            e @ quinn::ConnectionError::ConnectionClosed(_) => {
51                // > The peer's QUIC stack aborted the connection automatically
52                // peer might be disconnecting or otherwise unavailable, drop it
53                GetError::Io(e.into())
54            }
55            e @ quinn::ConnectionError::ApplicationClosed(_) => {
56                // > The peer closed the connection
57                // peer might be disconnecting or otherwise unavailable, drop it
58                GetError::Io(e.into())
59            }
60            e @ quinn::ConnectionError::Reset => {
61                // > The peer is unable to continue processing this connection, usually due to having restarted
62                GetError::RemoteReset(e.into())
63            }
64            e @ quinn::ConnectionError::TimedOut => {
65                // > Communication with the peer has lapsed for longer than the negotiated idle timeout
66                GetError::Io(e.into())
67            }
68            e @ quinn::ConnectionError::LocallyClosed => {
69                // > The local application closed the connection
70                // TODO(@divma): don't see how this is reachable but let's just not use the peer
71                GetError::Io(e.into())
72            }
73        }
74    }
75}
76
77impl From<quinn::ReadError> for GetError {
78    fn from(value: quinn::ReadError) -> Self {
79        match value {
80            e @ quinn::ReadError::Reset(_) => GetError::RemoteReset(e.into()),
81            quinn::ReadError::ConnectionLost(conn_error) => conn_error.into(),
82            quinn::ReadError::UnknownStream
83            | quinn::ReadError::IllegalOrderedRead
84            | quinn::ReadError::ZeroRttRejected => {
85                // all these errors indicate the peer is not usable at this moment
86                GetError::Io(value.into())
87            }
88        }
89    }
90}
91
92impl From<quinn::WriteError> for GetError {
93    fn from(value: quinn::WriteError) -> Self {
94        match value {
95            e @ quinn::WriteError::Stopped(_) => GetError::RemoteReset(e.into()),
96            quinn::WriteError::ConnectionLost(conn_error) => conn_error.into(),
97            quinn::WriteError::UnknownStream | quinn::WriteError::ZeroRttRejected => {
98                // all these errors indicate the peer is not usable at this moment
99                GetError::Io(value.into())
100            }
101        }
102    }
103}
104
105impl From<crate::get::fsm::ConnectedNextError> for GetError {
106    fn from(value: crate::get::fsm::ConnectedNextError) -> Self {
107        use crate::get::fsm::ConnectedNextError::*;
108        match value {
109            e @ PostcardSer(_) => {
110                // serialization errors indicate something wrong with the request itself
111                GetError::BadRequest(e.into())
112            }
113            e @ RequestTooBig => {
114                // request will never be sent, drop it
115                GetError::BadRequest(e.into())
116            }
117            Write(e) => e.into(),
118            e @ Io(_) => {
119                // io errors are likely recoverable
120                GetError::Io(e.into())
121            }
122        }
123    }
124}
125
126impl From<crate::get::fsm::AtBlobHeaderNextError> for GetError {
127    fn from(value: crate::get::fsm::AtBlobHeaderNextError) -> Self {
128        use crate::get::fsm::AtBlobHeaderNextError::*;
129        match value {
130            e @ NotFound => {
131                // > This indicates that the provider does not have the requested data.
132                // peer might have the data later, simply retry it
133                GetError::NotFound(e.into())
134            }
135            Read(e) => e.into(),
136            e @ Io(_) => {
137                // io errors are likely recoverable
138                GetError::Io(e.into())
139            }
140        }
141    }
142}
143
144impl From<crate::get::fsm::DecodeError> for GetError {
145    fn from(value: crate::get::fsm::DecodeError) -> Self {
146        use crate::get::fsm::DecodeError::*;
147
148        match value {
149            e @ NotFound => GetError::NotFound(e.into()),
150            e @ ParentNotFound(_) => GetError::NotFound(e.into()),
151            e @ LeafNotFound(_) => GetError::NotFound(e.into()),
152            e @ ParentHashMismatch(_) => {
153                // TODO(@divma): did the peer sent wrong data? is it corrupted? did we sent a wrong
154                // request?
155                GetError::NoncompliantNode(e.into())
156            }
157            e @ LeafHashMismatch(_) => {
158                // TODO(@divma): did the peer sent wrong data? is it corrupted? did we sent a wrong
159                // request?
160                GetError::NoncompliantNode(e.into())
161            }
162            Read(e) => e.into(),
163            Io(e) => e.into(),
164        }
165    }
166}
167
168impl From<std::io::Error> for GetError {
169    fn from(value: std::io::Error) -> Self {
170        // generally consider io errors recoverable
171        // we might want to revisit this at some point
172        GetError::Io(value.into())
173    }
174}