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 match quic_connection.close_reason() {
57 Some(reason) => reason.into(),
58 None => {
59 ConnectionError::LocallyClosed
63 }
64 }
65 }
66
67 pub(crate) fn local_h3_error(error_code: ErrorCode) -> Self {
68 ConnectionError::LocalH3Error(H3Error { code: error_code })
69 }
70}
71
72#[derive(thiserror::Error, Debug)]
74pub enum ConnectingError {
75 #[error("invalid URL: {0}")]
77 InvalidUrl(String),
78
79 #[error("cannot resolve domain: {0}")]
81 DnsLookup(std::io::Error),
82
83 #[error("cannot resolve domain")]
85 DnsNotFound,
86
87 #[error(transparent)]
89 ConnectionError(ConnectionError),
90
91 #[error("server rejected WebTransport session request")]
93 SessionRejected,
94
95 #[error("additional header '{0}' is reserved")]
97 ReservedHeader(String),
98
99 #[error("endpoint stopping")]
103 EndpointStopping,
104
105 #[error("CIDs exhausted")]
109 CidsExhausted,
110
111 #[error("invalid server name: {0}")]
113 InvalidServerName(String),
114
115 #[error("invalid remote address: {0}")]
119 InvalidRemoteAddress(SocketAddr),
120}
121
122impl ConnectingError {
123 pub(crate) fn with_no_connection(quic_connection: &quinn::Connection) -> Self {
124 ConnectingError::ConnectionError(match quic_connection.close_reason() {
125 Some(reason) => reason.into(),
126 None => {
127 ConnectionError::LocallyClosed
131 }
132 })
133 }
134
135 pub(crate) fn with_connect_error(error: quinn::ConnectError) -> Self {
136 match error {
137 quinn::ConnectError::EndpointStopping => ConnectingError::EndpointStopping,
138 quinn::ConnectError::CidsExhausted => ConnectingError::CidsExhausted,
139 quinn::ConnectError::InvalidServerName(name) => {
140 ConnectingError::InvalidServerName(name)
141 }
142 quinn::ConnectError::InvalidRemoteAddress(socket_addr) => {
143 ConnectingError::InvalidRemoteAddress(socket_addr)
144 }
145 quinn::ConnectError::NoDefaultClientConfig => {
146 unreachable!("quic client config is internally provided")
147 }
148 quinn::ConnectError::UnsupportedVersion => {
149 unreachable!("quic version is internally configured")
150 }
151 }
152 }
153}
154
155#[derive(thiserror::Error, Debug)]
157#[error("closed stream")]
158pub struct ClosedStream;
159
160#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
162pub enum StreamWriteError {
163 #[error("not connected")]
165 NotConnected,
166
167 #[error("stream closed")]
169 Closed,
170
171 #[error("stream stopped (code: {0})")]
173 Stopped(VarInt),
174
175 #[error("QUIC protocol error")]
177 QuicProto,
178}
179
180#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
182pub enum StreamReadError {
183 #[error("not connected")]
185 NotConnected,
186
187 #[error("stream reset (code: {0})")]
189 Reset(VarInt),
190
191 #[error("QUIC protocol error")]
193 QuicProto,
194}
195
196#[derive(thiserror::Error, Debug, Clone)]
198pub enum StreamReadExactError {
199 #[error("stream finished too early ({0} bytes read)")]
201 FinishedEarly(usize),
202
203 #[error(transparent)]
205 Read(StreamReadError),
206}
207
208#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
210pub enum SendDatagramError {
211 #[error("not connected")]
213 NotConnected,
214
215 #[error("peer does not support datagrams")]
217 UnsupportedByPeer,
218
219 #[error("datagram payload too large")]
221 TooLarge,
222}
223
224#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
226pub enum StreamOpeningError {
227 #[error("not connected")]
229 NotConnected,
230
231 #[error("opening stream refused")]
233 Refused,
234}
235
236#[derive(Debug, Clone, Eq, PartialEq)]
238pub struct ApplicationClose {
239 code: VarInt,
240 reason: Box<[u8]>,
241}
242
243impl Display for ApplicationClose {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 if self.reason.is_empty() {
246 self.code.fmt(f)?;
247 } else {
248 f.write_str(&String::from_utf8_lossy(&self.reason))?;
249 f.write_str(" (code ")?;
250 self.code.fmt(f)?;
251 f.write_str(")")?;
252 }
253 Ok(())
254 }
255}
256
257impl ApplicationClose {
258 pub(crate) fn new(code: VarInt, reason: Box<[u8]>) -> Self {
260 Self { code, reason }
261 }
262
263 pub fn code(&self) -> VarInt {
265 self.code
266 }
267
268 pub fn reason(&self) -> &[u8] {
270 &self.reason
271 }
272}
273
274#[derive(Debug, Clone, PartialEq, Eq)]
276pub struct ConnectionClose(quinn::ConnectionClose);
277
278impl Display for ConnectionClose {
279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280 self.0.fmt(f)
281 }
282}
283
284#[derive(Debug, Clone, PartialEq, Eq)]
286pub struct H3Error {
287 code: ErrorCode,
288}
289
290impl Display for H3Error {
291 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292 self.code.fmt(f)
293 }
294}
295
296impl From<quinn::ConnectionError> for ConnectionError {
297 fn from(error: quinn::ConnectionError) -> Self {
298 match error {
299 quinn::ConnectionError::VersionMismatch => ConnectionError::QuicProto(QuicProtoError {
300 code: None,
301 reason: Cow::Borrowed("QUIC protocol version mismatched"),
302 }),
303 quinn::ConnectionError::TransportError(e) => {
304 ConnectionError::QuicProto(QuicProtoError {
305 code: VarInt::try_from_u64(e.code.into()).ok(),
306 reason: Cow::Owned(e.reason),
307 })
308 }
309 quinn::ConnectionError::ConnectionClosed(close) => {
310 ConnectionError::ConnectionClosed(ConnectionClose(close))
311 }
312 quinn::ConnectionError::ApplicationClosed(close) => {
313 ConnectionError::ApplicationClosed(ApplicationClose {
314 code: varint_q2w(close.error_code),
315 reason: close.reason.to_vec().into_boxed_slice(),
316 })
317 }
318 quinn::ConnectionError::Reset => ConnectionError::QuicProto(QuicProtoError {
319 code: None,
320 reason: Cow::Borrowed("connection has been reset"),
321 }),
322 quinn::ConnectionError::TimedOut => ConnectionError::TimedOut,
323 quinn::ConnectionError::LocallyClosed => ConnectionError::LocallyClosed,
324 quinn::ConnectionError::CidsExhausted => ConnectionError::CidsExhausted,
325 }
326 }
327}
328
329#[derive(Debug, Clone, Eq, PartialEq)]
331pub struct QuicProtoError {
332 code: Option<VarInt>,
333 reason: Cow<'static, str>,
334}
335
336impl Display for QuicProtoError {
337 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
338 let code = self
339 .code
340 .map(|code| format!(" (code: {code})"))
341 .unwrap_or_default();
342
343 f.write_fmt(format_args!("{}{}", self.reason, code))
344 }
345}
346
347#[derive(Debug, thiserror::Error, Clone, Eq, PartialEq)]
351#[error("cannot derive keying material as requested output length is too large")]
352pub struct ExportKeyingMaterialError;