1use std::sync::Arc;
2
3use thiserror::Error;
4
5use crate::{ConnectError, SettingsError};
6
7#[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#[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#[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#[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#[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#[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#[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#[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#[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
228impl 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}