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")]
12 UnexpectedEnd,
13
14 #[error("connection error: {0}")]
16 Connection(#[from] quinn::ConnectionError),
17
18 #[error("failed to write: {0}")]
20 WriteError(#[from] quinn::WriteError),
21
22 #[error("failed to read: {0}")]
24 ReadError(#[from] quinn::ReadError),
25
26 #[error("failed to exchange h3 settings: {0}")]
28 SettingsError(#[from] SettingsError),
29
30 #[error("failed to exchange h3 connect: {0}")]
32 HttpError(#[from] ConnectError),
33
34 #[error("quic error: {0}")]
36 QuinnError(#[from] quinn::ConnectError),
37
38 #[error("invalid DNS name: {0}")]
40 InvalidDnsName(String),
41
42 #[error("invalid url: {0}")]
44 InvalidUrl(String),
45
46 #[error("DNS resolution timed out")]
48 DnsTimeout,
49
50 #[error("connection handshake timed out")]
52 HandshakeTimeout,
53
54 #[error("io error: {0}")]
56 Io(Arc<std::io::Error>),
57
58 #[error("TLS configuration has no QUIC-compatible initial cipher suite")]
60 InvalidCryptoConfiguration,
61
62 #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
63 #[error("rustls error: {0}")]
65 Rustls(#[from] rustls::Error),
66}
67
68#[derive(Clone, Error, Debug)]
70pub enum SessionError {
71 #[error("connection error: {0}")]
73 ConnectionError(quinn::ConnectionError),
74
75 #[error("webtransport error: {0}")]
77 WebTransportError(#[from] WebTransportError),
78
79 #[error("send datagram error: {0}")]
81 SendDatagramError(#[from] quinn::SendDatagramError),
82}
83
84impl From<quinn::ConnectionError> for SessionError {
85 fn from(e: quinn::ConnectionError) -> Self {
86 match &e {
87 quinn::ConnectionError::ApplicationClosed(close) => {
88 match webtrans_proto::error_from_http3(close.error_code.into_inner()) {
89 Some(code) => WebTransportError::Closed(
90 code,
91 String::from_utf8_lossy(&close.reason).into_owned(),
92 )
93 .into(),
94 None => SessionError::ConnectionError(e),
95 }
96 }
97 _ => SessionError::ConnectionError(e),
98 }
99 }
100}
101
102#[derive(Clone, Error, Debug)]
104pub enum WebTransportError {
105 #[error("closed: code={0} reason={1}")]
107 Closed(u32, String),
108
109 #[error("unknown session")]
111 UnknownSession,
112
113 #[error("read error: {0}")]
115 ReadError(#[from] quinn::ReadExactError),
116
117 #[error("write error: {0}")]
119 WriteError(#[from] quinn::WriteError),
120}
121
122#[derive(Clone, Error, Debug)]
124pub enum WriteError {
125 #[error("STOP_SENDING: {0}")]
127 Stopped(u32),
128
129 #[error("invalid STOP_SENDING: {0}")]
131 InvalidStopped(quinn::VarInt),
132
133 #[error("session error: {0}")]
135 SessionError(#[from] SessionError),
136
137 #[error("stream closed")]
139 ClosedStream,
140}
141
142impl From<quinn::WriteError> for WriteError {
143 fn from(e: quinn::WriteError) -> Self {
144 match e {
145 quinn::WriteError::Stopped(code) => {
146 match webtrans_proto::error_from_http3(code.into_inner()) {
147 Some(code) => WriteError::Stopped(code),
148 None => WriteError::InvalidStopped(code),
149 }
150 }
151 quinn::WriteError::ClosedStream => WriteError::ClosedStream,
152 quinn::WriteError::ConnectionLost(e) => WriteError::SessionError(e.into()),
153 quinn::WriteError::ZeroRttRejected => unreachable!("0-RTT not supported"),
154 }
155 }
156}
157
158#[derive(Clone, Error, Debug)]
160pub enum ReadError {
161 #[error("session error: {0}")]
163 SessionError(#[from] SessionError),
164
165 #[error("RESET_STREAM: {0}")]
167 Reset(u32),
168
169 #[error("invalid RESET_STREAM: {0}")]
171 InvalidReset(quinn::VarInt),
172
173 #[error("stream already closed")]
175 ClosedStream,
176
177 #[error("ordered read on unordered stream")]
179 IllegalOrderedRead,
180}
181
182impl From<quinn::ReadError> for ReadError {
183 fn from(value: quinn::ReadError) -> Self {
184 match value {
185 quinn::ReadError::Reset(code) => {
186 match webtrans_proto::error_from_http3(code.into_inner()) {
187 Some(code) => ReadError::Reset(code),
188 None => ReadError::InvalidReset(code),
189 }
190 }
191 quinn::ReadError::ConnectionLost(e) => ReadError::SessionError(e.into()),
192 quinn::ReadError::IllegalOrderedRead => ReadError::IllegalOrderedRead,
193 quinn::ReadError::ClosedStream => ReadError::ClosedStream,
194 quinn::ReadError::ZeroRttRejected => unreachable!("0-RTT not supported"),
195 }
196 }
197}
198
199#[derive(Clone, Error, Debug)]
201pub enum ReadExactError {
202 #[error("finished early")]
204 FinishedEarly(usize),
205
206 #[error("read error: {0}")]
208 ReadError(#[from] ReadError),
209}
210
211impl From<quinn::ReadExactError> for ReadExactError {
212 fn from(e: quinn::ReadExactError) -> Self {
213 match e {
214 quinn::ReadExactError::FinishedEarly(size) => ReadExactError::FinishedEarly(size),
215 quinn::ReadExactError::ReadError(e) => ReadExactError::ReadError(e.into()),
216 }
217 }
218}
219
220#[derive(Clone, Error, Debug)]
222pub enum ReadToEndError {
223 #[error("too long")]
225 TooLong,
226
227 #[error("read error: {0}")]
229 ReadError(#[from] ReadError),
230}
231
232impl From<quinn::ReadToEndError> for ReadToEndError {
233 fn from(e: quinn::ReadToEndError) -> Self {
234 match e {
235 quinn::ReadToEndError::TooLong => ReadToEndError::TooLong,
236 quinn::ReadToEndError::Read(e) => ReadToEndError::ReadError(e.into()),
237 }
238 }
239}
240
241#[derive(Clone, Error, Debug)]
243#[error("stream closed")]
244pub struct ClosedStream;
245
246impl From<quinn::ClosedStream> for ClosedStream {
247 fn from(_: quinn::ClosedStream) -> Self {
248 ClosedStream
249 }
250}
251
252#[derive(Error, Debug, Clone)]
254pub enum ServerError {
255 #[error("WebTransport request was already completed")]
257 RequestAlreadyCompleted,
258
259 #[error("unexpected end of stream")]
261 UnexpectedEnd,
262
263 #[error("connection error")]
265 Connection(#[from] quinn::ConnectionError),
266
267 #[error("connection handshake timed out")]
269 HandshakeTimeout,
270
271 #[error("failed to write")]
273 WriteError(#[from] quinn::WriteError),
274
275 #[error("failed to read")]
277 ReadError(#[from] quinn::ReadError),
278
279 #[error("failed to exchange h3 settings")]
281 SettingsError(#[from] SettingsError),
282
283 #[error("failed to exchange h3 connect")]
285 ConnectError(#[from] ConnectError),
286
287 #[error("io error: {0}")]
289 IoError(Arc<std::io::Error>),
290
291 #[error("TLS configuration has no QUIC-compatible initial cipher suite")]
293 InvalidCryptoConfiguration,
294
295 #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
296 #[error("rustls error: {0}")]
298 Rustls(#[from] rustls::Error),
299}
300
301impl webtrans_trait::Error for SessionError {
328 fn session_error(&self) -> Option<(u32, String)> {
329 if let SessionError::WebTransportError(WebTransportError::Closed(code, reason)) = self {
330 return Some((*code, reason.to_string()));
331 }
332
333 None
334 }
335}
336
337impl webtrans_trait::Error for WriteError {
338 fn session_error(&self) -> Option<(u32, String)> {
339 if let WriteError::SessionError(e) = self {
340 return e.session_error();
341 }
342
343 None
344 }
345
346 fn stream_error(&self) -> Option<u32> {
347 match self {
348 WriteError::Stopped(code) => Some(*code),
349 _ => None,
350 }
351 }
352}
353
354impl webtrans_trait::Error for ReadError {
355 fn session_error(&self) -> Option<(u32, String)> {
356 if let ReadError::SessionError(e) = self {
357 return e.session_error();
358 }
359
360 None
361 }
362
363 fn stream_error(&self) -> Option<u32> {
364 match self {
365 ReadError::Reset(code) => Some(*code),
366 _ => None,
367 }
368 }
369}