Skip to main content

wtransport_lightyear_patch/
error.rs

1use crate::driver::utils::varint_q2w;
2use crate::driver::DriverError;
3use std::fmt::Display;
4use wtransport_proto::error::ErrorCode;
5use wtransport_proto::varint::VarInt;
6
7/// An enumeration representing various errors that can occur during a WebTransport connection.
8#[derive(thiserror::Error, Debug)]
9pub enum ConnectionError {
10    /// The connection was aborted by the peer (protocol level).
11    #[error("Connection aborted by peer: {0}")]
12    ConnectionClosed(ConnectionClose),
13
14    /// The connection was closed by the peer (application level).
15    #[error("Connection closed by peer: {0}")]
16    ApplicationClosed(ApplicationClose),
17
18    /// The connection was locally closed.
19    #[error("Connection locally closed")]
20    LocallyClosed,
21
22    /// The connection was locally closed because an HTTP3 protocol violation.
23    #[error("Connection locally aborted: {0}")]
24    LocalH3Error(H3Error),
25
26    /// The connection timed out.
27    #[error("Connection timed out")]
28    TimedOut,
29
30    /// The connection was closed because a QUIC protocol error.
31    #[error("QUIC protocol error: {0}")]
32    QuicProto(QuicProtoError),
33}
34
35impl ConnectionError {
36    pub(crate) fn with_driver_error(
37        driver_error: DriverError,
38        quic_connection: &quinn::Connection,
39    ) -> Self {
40        match driver_error {
41            DriverError::Proto(error_code) => Self::local_h3_error(error_code),
42            DriverError::NotConnected => Self::no_connect(quic_connection),
43        }
44    }
45
46    pub(crate) fn no_connect(quic_connection: &quinn::Connection) -> Self {
47        quic_connection
48            .close_reason()
49            .expect("QUIC connection is still alive on close-cast")
50            .into()
51    }
52
53    pub(crate) fn local_h3_error(error_code: ErrorCode) -> Self {
54        ConnectionError::LocalH3Error(H3Error { code: error_code })
55    }
56}
57
58/// An enumeration representing various errors that can occur during a WebTransport client connecting.
59#[derive(thiserror::Error, Debug)]
60pub enum ConnectingError {
61    /// URL provided for connection is not valid.
62    #[error("Invalid URL: {0}")]
63    InvalidUrl(String),
64
65    /// Failure during DNS resolution.
66    #[error("Cannot resolve domain: {0}")]
67    DnsLookup(std::io::Error),
68
69    /// Cannot find any DNS.
70    #[error("No domain found for dns resolution")]
71    DnsNotFound,
72
73    /// Connection error during handshaking.
74    #[error(transparent)]
75    ConnectionError(ConnectionError),
76
77    /// Request rejected.
78    #[error("Server rejected WebTransport session request")]
79    SessionRejected,
80
81    /// Cannot use reserved key for additional headers.
82    #[error("Additional header '{0}' is reserved")]
83    ReservedHeader(String),
84}
85
86impl ConnectingError {
87    pub(crate) fn with_no_connection(quic_connection: &quinn::Connection) -> Self {
88        ConnectingError::ConnectionError(
89            quic_connection
90                .close_reason()
91                .expect("QUIC connection is still alive on close-cast")
92                .into(),
93        )
94    }
95}
96
97/// An error that arise from writing to a stream.
98#[derive(thiserror::Error, Debug)]
99pub enum StreamWriteError {
100    /// Connection has been dropped.
101    #[error("Not connected")]
102    NotConnected,
103
104    /// The peer is no longer accepting data on this stream.
105    #[error("Stream stopped (code: {0})")]
106    Stopped(VarInt),
107
108    /// QUIC protocol error.
109    #[error("QUIC protocol error")]
110    QuicProto,
111}
112
113/// An error that arise from reading from a stream.
114#[derive(thiserror::Error, Debug)]
115pub enum StreamReadError {
116    /// Connection has been dropped.
117    #[error("Not connected")]
118    NotConnected,
119
120    /// The peer abandoned transmitting data on this stream
121    #[error("Stream reset (code: {0})")]
122    Reset(VarInt),
123
124    /// QUIC protocol error.
125    #[error("QUIC protocol error")]
126    QuicProto,
127}
128
129/// An error that arise from reading from a stream.
130#[derive(thiserror::Error, Debug)]
131pub enum StreamReadExactError {
132    /// The stream finished before all bytes were read.
133    #[error("Stream finished too early")]
134    FinishedEarly,
135
136    /// A read error occurred.
137    #[error(transparent)]
138    Read(StreamReadError),
139}
140
141/// An error that arise from sending a datagram.
142#[derive(thiserror::Error, Debug)]
143pub enum SendDatagramError {
144    /// Connection has been dropped.
145    #[error("Not connected")]
146    NotConnected,
147
148    /// The peer does not support receiving datagram frames.
149    #[error("Peer does not support datagrams")]
150    UnsupportedByPeer,
151
152    /// The datagram is larger than the connection can currently accommodate.
153    #[error("Datagram payload too large")]
154    TooLarge,
155}
156
157/// An error that arise when opening a new stream.
158#[derive(thiserror::Error, Debug)]
159pub enum StreamOpeningError {
160    /// Connection has been dropped.
161    #[error("Not connected")]
162    NotConnected,
163
164    /// The peer refused the stream, stopping it during initialization.
165    #[error("Opening stream refused")]
166    Refused,
167}
168
169/// Reason given by an application for closing the connection
170#[derive(Debug)]
171pub struct ApplicationClose {
172    code: VarInt,
173    reason: Box<[u8]>,
174}
175
176impl Display for ApplicationClose {
177    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178        if self.reason.is_empty() {
179            self.code.fmt(f)?;
180        } else {
181            f.write_str(&String::from_utf8_lossy(&self.reason))?;
182            f.write_str(" (code ")?;
183            self.code.fmt(f)?;
184            f.write_str(")")?;
185        }
186        Ok(())
187    }
188}
189
190/// Reason given by the transport for closing the connection.
191#[derive(Debug)]
192pub struct ConnectionClose(quinn::ConnectionClose);
193
194impl Display for ConnectionClose {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        self.0.fmt(f)
197    }
198}
199
200/// A struct representing an error in the HTTP3 layer.
201#[derive(Debug)]
202pub struct H3Error {
203    code: ErrorCode,
204}
205
206impl Display for H3Error {
207    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208        self.code.fmt(f)
209    }
210}
211
212impl From<quinn::ConnectionError> for ConnectionError {
213    fn from(error: quinn::ConnectionError) -> Self {
214        match error {
215            quinn::ConnectionError::VersionMismatch => ConnectionError::QuicProto(QuicProtoError {
216                code: None,
217                reason: "QUIC protocol version mismatched".to_string(),
218            }),
219            quinn::ConnectionError::TransportError(e) => {
220                ConnectionError::QuicProto(QuicProtoError {
221                    code: VarInt::try_from_u64(e.code.into()).ok(),
222                    reason: e.reason,
223                })
224            }
225            quinn::ConnectionError::ConnectionClosed(close) => {
226                ConnectionError::ConnectionClosed(ConnectionClose(close))
227            }
228            quinn::ConnectionError::ApplicationClosed(close) => {
229                ConnectionError::ApplicationClosed(ApplicationClose {
230                    code: varint_q2w(close.error_code),
231                    reason: close.reason.to_vec().into_boxed_slice(),
232                })
233            }
234            quinn::ConnectionError::Reset => ConnectionError::QuicProto(QuicProtoError {
235                code: None,
236                reason: "Connection has been reset".to_string(),
237            }),
238            quinn::ConnectionError::TimedOut => ConnectionError::TimedOut,
239            quinn::ConnectionError::LocallyClosed => ConnectionError::LocallyClosed,
240        }
241    }
242}
243
244/// A complete specification of an error over QUIC protocol.
245#[derive(Debug)]
246pub struct QuicProtoError {
247    code: Option<VarInt>,
248    reason: String,
249}
250
251impl Display for QuicProtoError {
252    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253        let code = self
254            .code
255            .map(|code| format!(" (code: {})", code))
256            .unwrap_or_default();
257
258        f.write_fmt(format_args!("{}{}", self.reason, code))
259    }
260}