1use crate::driver::utils::varint_q2w;
2use crate::driver::DriverError;
3use crate::VarInt;
4use std::borrow::Cow;
5use std::fmt::Display;
6use std::net::SocketAddr;
7use wtransport_proto::error::ErrorCode;
8
9#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
11pub enum ConnectionError {
12 #[error("connection aborted by peer: {0}")]
14 ConnectionClosed(ConnectionClose),
15
16 #[error("connection closed by peer: {0}")]
18 ApplicationClosed(ApplicationClose),
19
20 #[error("connection locally closed")]
22 LocallyClosed,
23
24 #[error("connection locally aborted: {0}")]
26 LocalH3Error(H3Error),
27
28 #[error("connection timed out")]
30 TimedOut,
31
32 #[error("QUIC protocol error: {0}")]
34 QuicProto(QuicProtoError),
35
36 #[error("CIDs exhausted")]
40 CidsExhausted,
41}
42
43impl ConnectionError {
44 pub(crate) fn with_driver_error(
45 driver_error: DriverError,
46 quic_connection: &quinn::Connection,
47 ) -> Self {
48 match driver_error {
49 DriverError::Proto(error_code) => Self::local_h3_error(error_code),
50 DriverError::ApplicationClosed(close) => Self::ApplicationClosed(close),
51 DriverError::NotConnected => Self::no_connect(quic_connection),
52 }
53 }
54
55 pub(crate) fn no_connect(quic_connection: &quinn::Connection) -> Self {
56 quic_connection
57 .close_reason()
58 .expect("QUIC connection is still alive on close-cast")
59 .into()
60 }
61
62 pub(crate) fn local_h3_error(error_code: ErrorCode) -> Self {
63 ConnectionError::LocalH3Error(H3Error { code: error_code })
64 }
65}
66
67#[derive(thiserror::Error, Debug)]
69pub enum ConnectingError {
70 #[error("invalid URL: {0}")]
72 InvalidUrl(String),
73
74 #[error("cannot resolve domain: {0}")]
76 DnsLookup(std::io::Error),
77
78 #[error("cannot resolve domain")]
80 DnsNotFound,
81
82 #[error(transparent)]
84 ConnectionError(ConnectionError),
85
86 #[error("server rejected WebTransport session request")]
88 SessionRejected,
89
90 #[error("additional header '{0}' is reserved")]
92 ReservedHeader(String),
93
94 #[error("endpoint stopping")]
98 EndpointStopping,
99
100 #[error("CIDs exhausted")]
104 CidsExhausted,
105
106 #[error("invalid server name: {0}")]
108 InvalidServerName(String),
109
110 #[error("invalid remote address: {0}")]
114 InvalidRemoteAddress(SocketAddr),
115}
116
117impl ConnectingError {
118 pub(crate) fn with_no_connection(quic_connection: &quinn::Connection) -> Self {
119 ConnectingError::ConnectionError(
120 quic_connection
121 .close_reason()
122 .expect("QUIC connection is still alive on close-cast")
123 .into(),
124 )
125 }
126
127 pub(crate) fn with_connect_error(error: quinn::ConnectError) -> Self {
128 match error {
129 quinn::ConnectError::EndpointStopping => ConnectingError::EndpointStopping,
130 quinn::ConnectError::CidsExhausted => ConnectingError::CidsExhausted,
131 quinn::ConnectError::InvalidServerName(name) => {
132 ConnectingError::InvalidServerName(name)
133 }
134 quinn::ConnectError::InvalidRemoteAddress(socket_addr) => {
135 ConnectingError::InvalidRemoteAddress(socket_addr)
136 }
137 quinn::ConnectError::NoDefaultClientConfig => {
138 unreachable!("quic client config is internally provided")
139 }
140 quinn::ConnectError::UnsupportedVersion => {
141 unreachable!("quic version is internally configured")
142 }
143 }
144 }
145}
146
147#[derive(thiserror::Error, Debug)]
149#[error("closed stream")]
150pub struct ClosedStream;
151
152#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
154pub enum StreamWriteError {
155 #[error("not connected")]
157 NotConnected,
158
159 #[error("stream closed")]
161 Closed,
162
163 #[error("stream stopped (code: {0})")]
165 Stopped(VarInt),
166
167 #[error("QUIC protocol error")]
169 QuicProto,
170}
171
172#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
174pub enum StreamReadError {
175 #[error("not connected")]
177 NotConnected,
178
179 #[error("stream reset (code: {0})")]
181 Reset(VarInt),
182
183 #[error("QUIC protocol error")]
185 QuicProto,
186}
187
188#[derive(thiserror::Error, Debug, Clone)]
190pub enum StreamReadExactError {
191 #[error("stream finished too early ({0} bytes read)")]
193 FinishedEarly(usize),
194
195 #[error(transparent)]
197 Read(StreamReadError),
198}
199
200#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
202pub enum SendDatagramError {
203 #[error("not connected")]
205 NotConnected,
206
207 #[error("peer does not support datagrams")]
209 UnsupportedByPeer,
210
211 #[error("datagram payload too large")]
213 TooLarge,
214}
215
216#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
218pub enum StreamOpeningError {
219 #[error("not connected")]
221 NotConnected,
222
223 #[error("opening stream refused")]
225 Refused,
226}
227
228#[derive(Debug, Clone, Eq, PartialEq)]
230pub struct ApplicationClose {
231 code: VarInt,
232 reason: Box<[u8]>,
233}
234
235impl Display for ApplicationClose {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 if self.reason.is_empty() {
238 self.code.fmt(f)?;
239 } else {
240 f.write_str(&String::from_utf8_lossy(&self.reason))?;
241 f.write_str(" (code ")?;
242 self.code.fmt(f)?;
243 f.write_str(")")?;
244 }
245 Ok(())
246 }
247}
248
249impl ApplicationClose {
250 pub(crate) fn new(code: VarInt, reason: Box<[u8]>) -> Self {
252 Self { code, reason }
253 }
254
255 pub fn code(&self) -> VarInt {
257 self.code
258 }
259
260 pub fn reason(&self) -> &[u8] {
262 &self.reason
263 }
264}
265
266#[derive(Debug, Clone, PartialEq, Eq)]
268pub struct ConnectionClose(quinn::ConnectionClose);
269
270impl Display for ConnectionClose {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 self.0.fmt(f)
273 }
274}
275
276#[derive(Debug, Clone, PartialEq, Eq)]
278pub struct H3Error {
279 code: ErrorCode,
280}
281
282impl Display for H3Error {
283 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284 self.code.fmt(f)
285 }
286}
287
288impl From<quinn::ConnectionError> for ConnectionError {
289 fn from(error: quinn::ConnectionError) -> Self {
290 match error {
291 quinn::ConnectionError::VersionMismatch => ConnectionError::QuicProto(QuicProtoError {
292 code: None,
293 reason: Cow::Borrowed("QUIC protocol version mismatched"),
294 }),
295 quinn::ConnectionError::TransportError(e) => {
296 ConnectionError::QuicProto(QuicProtoError {
297 code: VarInt::try_from_u64(e.code.into()).ok(),
298 reason: Cow::Owned(e.reason),
299 })
300 }
301 quinn::ConnectionError::ConnectionClosed(close) => {
302 ConnectionError::ConnectionClosed(ConnectionClose(close))
303 }
304 quinn::ConnectionError::ApplicationClosed(close) => {
305 ConnectionError::ApplicationClosed(ApplicationClose {
306 code: varint_q2w(close.error_code),
307 reason: close.reason.to_vec().into_boxed_slice(),
308 })
309 }
310 quinn::ConnectionError::Reset => ConnectionError::QuicProto(QuicProtoError {
311 code: None,
312 reason: Cow::Borrowed("connection has been reset"),
313 }),
314 quinn::ConnectionError::TimedOut => ConnectionError::TimedOut,
315 quinn::ConnectionError::LocallyClosed => ConnectionError::LocallyClosed,
316 quinn::ConnectionError::CidsExhausted => ConnectionError::CidsExhausted,
317 }
318 }
319}
320
321#[derive(Debug, Clone, Eq, PartialEq)]
323pub struct QuicProtoError {
324 code: Option<VarInt>,
325 reason: Cow<'static, str>,
326}
327
328impl Display for QuicProtoError {
329 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
330 let code = self
331 .code
332 .map(|code| format!(" (code: {})", code))
333 .unwrap_or_default();
334
335 f.write_fmt(format_args!("{}{}", self.reason, code))
336 }
337}
338
339#[derive(Debug, thiserror::Error, Clone, Eq, PartialEq)]
343#[error("cannot derive keying material as requested output length is too large")]
344pub struct ExportKeyingMaterialError;