-
Notifications
You must be signed in to change notification settings - Fork 64
/
error.rs
386 lines (332 loc) · 13.8 KB
/
error.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
// Copyright 2019 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under the MIT license <LICENSE-MIT
// http://opensource.org/licenses/MIT> or the Modified BSD license <LICENSE-BSD
// https://opensource.org/licenses/BSD-3-Clause>, at your option. This file may not be copied,
// modified, or distributed except according to those terms. Please review the Licences for the
// specific language governing permissions and limitations relating to use of the SAFE Network
// Software.
use bytes::Bytes;
use std::{fmt, io, net::SocketAddr};
use thiserror::Error;
/// Errors returned from the endpoint builder ([`crate::EndpointBuilder::server`]).
#[derive(Error, Debug)]
pub enum EndpointError {
///
#[error("Certificate could not be generated for config")]
Certificate(CertificateError),
///
#[error(transparent)]
IoError(#[from] io::Error),
}
/// Various error related to certificates. Should mostly occur due to bugs, not runtime constraints.
#[derive(Error, Debug)]
pub enum CertificateError {
///
#[error("Rcgen internal error generating certificate")]
Rcgen(#[from] rcgen::RcgenError),
///
#[error("Certificate or name validation error")]
WebPki(#[from] webpki::Error),
///
#[error("Rustls internal error")]
Rustls(#[from] rustls::Error),
}
/// Errors that can cause connection loss.
// This is a copy of `quinn::ConnectionError` without the `*Closed` variants, since we want to
// separate them in our interface.
#[derive(Clone, Debug, Error, PartialEq, Eq)]
pub enum ConnectionError {
/// The endpoint has been stopped.
#[error("The endpoint has been stopped")]
Stopped,
/// The number of active connections on the local endpoint is at the limit.
///
/// This limit is imposed by the underlying connection ID generator, which is not currently
/// configurable.
// NOTE: We could make this configurable by exposing a way to set
// quinn_proto::RandomConnectionIdGenerator cid_len
#[error("The number of active connections on the local endpoint is at the limit")]
TooManyConnections,
/// Invalid remote address.
///
/// Examples include attempting to connect to port `0` or using an inappropriate address family.
#[error("Invalid remote address: {0}")]
InvalidAddress(SocketAddr),
/// Internal configuration error.
///
/// This should not occur (if it does, there's a bug!), but it covers possible misconfigurations
/// of the underlying transport library.
#[error("BUG: internal configuration error")]
InternalConfigError(#[source] InternalConfigError),
/// The peer doesn't implement the supported version.
#[error("{}", quinn::ConnectionError::VersionMismatch)]
VersionMismatch,
/// The peer violated the QUIC specification as understood by this implementation.
#[error("{0}")]
TransportError(#[source] quinn_proto::TransportError),
/// The peer is unable to continue processing this connection, usually due to having restarted.
#[error("{}", quinn::ConnectionError::Reset)]
Reset,
/// Communication with the peer has lapsed for longer than the negotiated idle timeout.
#[error("{}", quinn::ConnectionError::TimedOut)]
TimedOut,
/// The connection was closed.
#[error("The connection was closed, {0}")]
Closed(Close),
}
impl From<quinn::ConnectError> for ConnectionError {
fn from(error: quinn::ConnectError) -> Self {
match error {
quinn::ConnectError::EndpointStopping => Self::Stopped,
quinn::ConnectError::TooManyConnections => Self::TooManyConnections,
quinn::ConnectError::InvalidRemoteAddress(addr) => Self::InvalidAddress(addr),
quinn::ConnectError::InvalidDnsName(_)
| quinn::ConnectError::NoDefaultClientConfig
| quinn::ConnectError::UnsupportedVersion => {
// We currently use a hard-coded domain name, so if it's invalid we have a library
// breaking bug. We want to avoid panics though, so we propagate this as an opaque
// error.
Self::InternalConfigError(InternalConfigError(error))
}
}
}
}
impl From<quinn::ConnectionError> for ConnectionError {
fn from(error: quinn::ConnectionError) -> Self {
match error {
quinn::ConnectionError::LocallyClosed => Self::Closed(Close::Local),
quinn::ConnectionError::ApplicationClosed(close) => Self::Closed(Close::Application {
error_code: close.error_code.into_inner(),
reason: close.reason,
}),
quinn::ConnectionError::ConnectionClosed(close) => Self::Closed(Close::Transport {
error_code: TransportErrorCode(close.error_code),
reason: close.reason,
}),
quinn::ConnectionError::VersionMismatch => Self::VersionMismatch,
quinn::ConnectionError::TransportError(error) => Self::TransportError(error),
quinn::ConnectionError::Reset => Self::Reset,
quinn::ConnectionError::TimedOut => Self::TimedOut,
}
}
}
/// An internal configuration error encountered by [`Endpoint`](crate::Endpoint) connect methods.
#[derive(Clone, Debug, Error, PartialEq, Eq)]
#[error(transparent)]
pub struct InternalConfigError(quinn::ConnectError);
/// The reason a connection was closed.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Close {
/// This application closed the connection.
Local,
/// The remote application closed the connection.
Application {
/// The error code supplied by the application.
error_code: u64,
/// The reason supplied by the application.
reason: Bytes,
},
/// The transport layer closed the connection.
///
/// This would indicate an abrupt disconnection or a protocol violation.
Transport {
/// The error code supplied by the QUIC library.
error_code: TransportErrorCode,
/// The reason supplied by the QUIC library.
reason: Bytes,
},
}
impl fmt::Display for Close {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (closed_by, error_code, reason): (_, &dyn fmt::Display, _) = match self {
Self::Local => return write!(f, "we closed the connection"),
Self::Application { error_code, reason } => ("remote application", error_code, reason),
Self::Transport { error_code, reason } => ("transport layer", error_code, reason),
};
write!(
f,
"{} closed the connection (error code: {}, reason: {})",
closed_by,
error_code,
String::from_utf8_lossy(reason)
)
}
}
impl From<quinn::ApplicationClose> for Close {
fn from(close: quinn::ApplicationClose) -> Self {
Self::Application {
error_code: close.error_code.into_inner(),
reason: close.reason,
}
}
}
impl From<quinn::ConnectionClose> for Close {
fn from(close: quinn::ConnectionClose) -> Self {
Self::Transport {
error_code: TransportErrorCode(close.error_code),
reason: close.reason,
}
}
}
/// An opaque error code indicating a transport failure.
///
/// This can be turned to a string via its `Debug` and `Display` impls, but is otherwise opaque.
#[derive(Clone, PartialEq, Eq)]
pub struct TransportErrorCode(quinn_proto::TransportErrorCode);
impl fmt::Debug for TransportErrorCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
impl fmt::Display for TransportErrorCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
/// Errors that can occur when performing an RPC operation.
///
/// QuicP2P uses a number of RPC operations when establishing endpoints, in order to verify public
/// addresses and check for reachability. This error type covers the ways in which these operations
/// can fail, which is a combination of [`SendError`], and [`RecvError`].
#[derive(Debug, Error)]
pub enum RpcError {
/// We did not receive a response within the expected time.
#[error("We did not receive a response within the expected time")]
TimedOut,
/// Failed to send the request.
#[error("Failed to send the request")]
Send(#[from] SendError),
/// Failed to receive the response.
#[error("Failed to receive the response")]
Recv(#[from] RecvError),
}
// Treating `ConnectionError`s as happening on send works because we would only encounter them
// directly (e.g. not part of `SendError` or `RecvError`) when establishing outgoing connections
// before sending.
impl From<ConnectionError> for RpcError {
fn from(error: ConnectionError) -> Self {
Self::Send(error.into())
}
}
impl From<quinn::ConnectionError> for RpcError {
fn from(error: quinn::ConnectionError) -> Self {
Self::Send(error.into())
}
}
impl From<tokio::time::error::Elapsed> for RpcError {
fn from(_: tokio::time::error::Elapsed) -> Self {
Self::TimedOut
}
}
/// Errors that can occur when sending messages.
#[derive(Debug, Error)]
pub enum SendError {
/// The serialised message is too long
#[error("The serialized message is too long ({0} bytes, max: 4 GiB)")]
MessageTooLong(usize),
/// Failed to serialize message.
///
/// This likely indicates a bug in the library, since serializing to bytes should be infallible.
/// Limitations in the serde API mean we cannot verify this statically, and we don't want to
/// introduce potential panics.
#[error("Failed to serialize message")]
Serialization(#[from] bincode::Error),
/// Connection was lost when trying to send a message.
#[error("Connection was lost when trying to send a message")]
ConnectionLost(#[from] ConnectionError),
/// Stream was lost when trying to send a message.
#[error("Stream was lost when trying to send a message")]
StreamLost(#[source] StreamError),
}
impl From<quinn::ConnectionError> for SendError {
fn from(error: quinn::ConnectionError) -> Self {
Self::ConnectionLost(error.into())
}
}
impl From<quinn::WriteError> for SendError {
fn from(error: quinn::WriteError) -> Self {
match error {
quinn::WriteError::Stopped(code) => Self::StreamLost(StreamError::Stopped(code.into())),
quinn::WriteError::ConnectionLost(error) => Self::ConnectionLost(error.into()),
quinn::WriteError::UnknownStream => Self::StreamLost(StreamError::Gone),
quinn::WriteError::ZeroRttRejected => Self::StreamLost(StreamError::Unsupported(
UnsupportedStreamOperation(error.into()),
)),
}
}
}
/// Errors that can occur when receiving messages.
#[derive(Debug, Error)]
pub enum RecvError {
/// Message received has an empty payload
#[error("Message received from peer has an empty payload")]
EmptyMsgPayload,
/// Not enough bytes were received to be a valid message
#[error("Received too few bytes for message")]
NotEnoughBytes,
/// Failed to deserialize message.
#[error("Failed to deserialize message")]
Serialization(#[from] bincode::Error),
/// Message type flag found in message header is invalid
#[error("Invalid message type flag found in message header: {0}")]
InvalidMsgTypeFlag(u8),
/// Type of message received is unexpected
#[error("Type of message received is unexpected: {0}")]
UnexpectedMsgReceived(String),
/// Connection was lost when trying to receive a message.
#[error("Connection was lost when trying to receive a message")]
ConnectionLost(#[from] ConnectionError),
/// Connection was lost when trying to receive a message.
#[error("Error reading to end of stream")]
ReadToEndError(#[from] quinn::ReadToEndError),
/// Stream was lost when trying to receive a message.
#[error("Stream was lost when trying to receive a message")]
StreamLost(#[source] StreamError),
}
impl From<quinn::ConnectionError> for RecvError {
fn from(error: quinn::ConnectionError) -> Self {
Self::ConnectionLost(error.into())
}
}
impl From<quinn::ReadError> for RecvError {
fn from(error: quinn::ReadError) -> Self {
use quinn::ReadError;
match error {
ReadError::Reset(code) => Self::StreamLost(StreamError::Stopped(code.into())),
ReadError::ConnectionLost(error) => Self::ConnectionLost(error.into()),
ReadError::UnknownStream => Self::StreamLost(StreamError::Gone),
ReadError::IllegalOrderedRead | ReadError::ZeroRttRejected => Self::StreamLost(
StreamError::Unsupported(UnsupportedStreamOperation(error.into())),
),
}
}
}
impl From<quinn::ReadExactError> for RecvError {
fn from(error: quinn::ReadExactError) -> Self {
match error {
quinn::ReadExactError::FinishedEarly => Self::NotEnoughBytes,
quinn::ReadExactError::ReadError(error) => error.into(),
}
}
}
/// Errors that can occur when interacting with streams.
#[derive(Debug, Error)]
pub enum StreamError {
/// The peer abandoned the stream.
#[error("The peer abandoned the stream (error code: {0})")]
Stopped(u64),
/// The stream was already stopped, finished, or reset.
#[error("The stream was already stopped, finished, or reset")]
Gone,
/// An error was caused by an unsupported operation.
///
/// Additional stream errors can arise from the use of 0-RTT connections or unordered reads,
/// neither of which are supported by the library.
#[error("An error was caused by an unsupported operation")]
Unsupported(#[source] UnsupportedStreamOperation),
}
/// An error caused by an unsupported operation.
#[derive(Debug, Error)]
#[error(transparent)]
pub struct UnsupportedStreamOperation(Box<dyn std::error::Error + Send + Sync>);