Skip to main content

web_transport_noq/
error.rs

1use std::sync::Arc;
2
3use thiserror::Error;
4
5use crate::{ConnectError, SettingsError};
6
7/// An error returned when connecting to a WebTransport endpoint.
8#[derive(Error, Debug, Clone)]
9pub enum ClientError {
10    #[error("unexpected end of stream")]
11    UnexpectedEnd,
12
13    #[error("connection error: {0}")]
14    Connection(#[from] noq::ConnectionError),
15
16    #[error("failed to write: {0}")]
17    WriteError(#[from] noq::WriteError),
18
19    #[error("failed to read: {0}")]
20    ReadError(#[from] noq::ReadError),
21
22    #[error("failed to exchange h3 settings: {0}")]
23    SettingsError(#[from] SettingsError),
24
25    #[error("failed to exchange h3 connect: {0}")]
26    HttpError(#[from] ConnectError),
27
28    #[error("quic error: {0}")]
29    NoqError(#[from] noq::ConnectError),
30
31    #[error("invalid DNS name: {0}")]
32    InvalidDnsName(String),
33
34    #[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
35    #[error("rustls error: {0}")]
36    Rustls(#[from] rustls::Error),
37}
38
39/// An errors returned by [`crate::Session`], split based on if they are underlying QUIC errors or WebTransport errors.
40#[derive(Clone, Error, Debug)]
41pub enum SessionError {
42    #[error("connection error: {0}")]
43    ConnectionError(noq::ConnectionError),
44
45    #[error("webtransport error: {0}")]
46    WebTransportError(#[from] WebTransportError),
47
48    #[error("send datagram error: {0}")]
49    SendDatagramError(#[from] noq::SendDatagramError),
50}
51
52impl From<noq::ConnectionError> for SessionError {
53    fn from(e: noq::ConnectionError) -> Self {
54        match &e {
55            noq::ConnectionError::ApplicationClosed(close) => {
56                match web_transport_proto::error_from_http3(close.error_code.into_inner()) {
57                    Some(code) => WebTransportError::Closed(
58                        code,
59                        String::from_utf8_lossy(&close.reason).into_owned(),
60                    )
61                    .into(),
62                    None => SessionError::ConnectionError(e),
63                }
64            }
65            _ => SessionError::ConnectionError(e),
66        }
67    }
68}
69
70/// An error that can occur when reading/writing the WebTransport stream header.
71#[derive(Clone, Error, Debug)]
72pub enum WebTransportError {
73    #[error("closed: code={0} reason={1}")]
74    Closed(u32, String),
75
76    #[error("unknown session")]
77    UnknownSession,
78
79    #[error("read error: {0}")]
80    ReadError(#[from] noq::ReadExactError),
81
82    #[error("write error: {0}")]
83    WriteError(#[from] noq::WriteError),
84}
85
86/// An error when writing to [`crate::SendStream`]. Similar to [`noq::WriteError`].
87#[derive(Clone, Error, Debug)]
88pub enum WriteError {
89    #[error("STOP_SENDING: {0}")]
90    Stopped(u32),
91
92    #[error("invalid STOP_SENDING: {0}")]
93    InvalidStopped(noq::VarInt),
94
95    #[error("session error: {0}")]
96    SessionError(#[from] SessionError),
97
98    #[error("stream closed")]
99    ClosedStream,
100}
101
102impl From<noq::WriteError> for WriteError {
103    fn from(e: noq::WriteError) -> Self {
104        match e {
105            noq::WriteError::Stopped(code) => {
106                match web_transport_proto::error_from_http3(code.into_inner()) {
107                    Some(code) => WriteError::Stopped(code),
108                    None => WriteError::InvalidStopped(code),
109                }
110            }
111            noq::WriteError::ClosedStream => WriteError::ClosedStream,
112            noq::WriteError::ConnectionLost(e) => WriteError::SessionError(e.into()),
113            noq::WriteError::ZeroRttRejected => unreachable!("0-RTT not supported"),
114        }
115    }
116}
117
118/// An error when reading from [`crate::RecvStream`]. Similar to [`noq::ReadError`].
119#[derive(Clone, Error, Debug)]
120pub enum ReadError {
121    #[error("session error: {0}")]
122    SessionError(#[from] SessionError),
123
124    #[error("RESET_STREAM: {0}")]
125    Reset(u32),
126
127    #[error("invalid RESET_STREAM: {0}")]
128    InvalidReset(noq::VarInt),
129
130    #[error("stream already closed")]
131    ClosedStream,
132}
133
134impl From<noq::ReadError> for ReadError {
135    fn from(value: noq::ReadError) -> Self {
136        match value {
137            noq::ReadError::Reset(code) => {
138                match web_transport_proto::error_from_http3(code.into_inner()) {
139                    Some(code) => ReadError::Reset(code),
140                    None => ReadError::InvalidReset(code),
141                }
142            }
143            noq::ReadError::ConnectionLost(e) => ReadError::SessionError(e.into()),
144            noq::ReadError::ClosedStream => ReadError::ClosedStream,
145            noq::ReadError::ZeroRttRejected => unreachable!("0-RTT not supported"),
146        }
147    }
148}
149
150/// An error returned by [`crate::RecvStream::read_exact`]. Similar to [`noq::ReadExactError`].
151#[derive(Clone, Error, Debug)]
152pub enum ReadExactError {
153    #[error("finished early")]
154    FinishedEarly(usize),
155
156    #[error("read error: {0}")]
157    ReadError(#[from] ReadError),
158}
159
160impl From<noq::ReadExactError> for ReadExactError {
161    fn from(e: noq::ReadExactError) -> Self {
162        match e {
163            noq::ReadExactError::FinishedEarly(size) => ReadExactError::FinishedEarly(size),
164            noq::ReadExactError::ReadError(e) => ReadExactError::ReadError(e.into()),
165        }
166    }
167}
168
169/// An error returned by [`crate::RecvStream::read_to_end`]. Similar to [`noq::ReadToEndError`].
170#[derive(Clone, Error, Debug)]
171pub enum ReadToEndError {
172    #[error("too long")]
173    TooLong,
174
175    #[error("read error: {0}")]
176    ReadError(#[from] ReadError),
177}
178
179impl From<noq::ReadToEndError> for ReadToEndError {
180    fn from(e: noq::ReadToEndError) -> Self {
181        match e {
182            noq::ReadToEndError::TooLong => ReadToEndError::TooLong,
183            noq::ReadToEndError::Read(e) => ReadToEndError::ReadError(e.into()),
184        }
185    }
186}
187
188/// An error indicating the stream was already closed.
189#[derive(Clone, Error, Debug)]
190#[error("stream closed")]
191pub struct ClosedStream;
192
193impl From<noq::ClosedStream> for ClosedStream {
194    fn from(_: noq::ClosedStream) -> Self {
195        ClosedStream
196    }
197}
198
199/// An error returned when receiving a new WebTransport session.
200#[derive(Error, Debug, Clone)]
201pub enum ServerError {
202    #[error("unexpected end of stream")]
203    UnexpectedEnd,
204
205    #[error("connection error")]
206    Connection(#[from] noq::ConnectionError),
207
208    #[error("failed to write")]
209    WriteError(#[from] noq::WriteError),
210
211    #[error("failed to read")]
212    ReadError(#[from] noq::ReadError),
213
214    #[error("failed to exchange h3 settings")]
215    SettingsError(#[from] SettingsError),
216
217    #[error("failed to exchange h3 connect")]
218    ConnectError(#[from] ConnectError),
219
220    #[error("io error: {0}")]
221    IoError(Arc<std::io::Error>),
222
223    #[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
224    #[error("rustls error: {0}")]
225    Rustls(#[from] rustls::Error),
226}
227
228// #[derive(Clone, Error, Debug)]
229// pub enum SendDatagramError {
230//     #[error("Unsupported peer")]
231//     UnsupportedPeer,
232
233//     #[error("Datagram support Disabled by peer")]
234//     DatagramSupportDisabled,
235
236//     #[error("Datagram Too large")]
237//     TooLarge,
238
239//     #[error("Session errorr: {0}")]
240//     SessionError(#[from] SessionError),
241// }
242
243// impl From<noq::SendDatagramError> for SendDatagramError {
244//     fn from(value: noq::SendDatagramError) -> Self {
245//          match value {
246//              noq::SendDatagramError::UnsupportedByPeer => SendDatagramError::UnsupportedPeer,
247//              noq::SendDatagramError::Disabled => SendDatagramError::DatagramSupportDisabled,
248//              noq::SendDatagramError::TooLarge => SendDatagramError::TooLarge,
249//              noq::SendDatagramError::ConnectionLost(e) => SendDatagramError::SessionError(e.into()),
250//          }
251//     }
252// }
253
254impl web_transport_trait::Error for SessionError {
255    fn session_error(&self) -> Option<(u32, String)> {
256        if let SessionError::WebTransportError(WebTransportError::Closed(code, reason)) = self {
257            return Some((*code, reason.to_string()));
258        }
259
260        None
261    }
262}
263
264impl web_transport_trait::Error for WriteError {
265    fn session_error(&self) -> Option<(u32, String)> {
266        if let WriteError::SessionError(e) = self {
267            return e.session_error();
268        }
269
270        None
271    }
272
273    fn stream_error(&self) -> Option<u32> {
274        match self {
275            WriteError::Stopped(code) => Some(*code),
276            _ => None,
277        }
278    }
279}
280
281impl web_transport_trait::Error for ReadError {
282    fn session_error(&self) -> Option<(u32, String)> {
283        if let ReadError::SessionError(e) = self {
284            return e.session_error();
285        }
286
287        None
288    }
289
290    fn stream_error(&self) -> Option<u32> {
291        match self {
292            ReadError::Reset(code) => Some(*code),
293            _ => None,
294        }
295    }
296}