Skip to main content

s2n_quic_dc/stream/send/
error.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    event::IntoEvent,
6    stream::{packet_number, recv, shared::ShutdownKind},
7};
8use core::{fmt, panic::Location};
9use s2n_quic_core::{buffer, varint::VarInt};
10
11#[derive(Clone, Copy)]
12pub struct Error {
13    pub(crate) kind: Kind,
14    pub(crate) location: &'static Location<'static>,
15}
16
17impl fmt::Debug for Error {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        f.debug_struct("Error")
20            .field("kind", &self.kind)
21            .field("crate", &"s2n-quic-dc")
22            .field("file", &self.file())
23            .field("line", &self.location.line())
24            .finish()
25    }
26}
27
28impl fmt::Display for Error {
29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30        let Self { kind, location } = self;
31        let file = self.file();
32        let line = location.line();
33        write!(f, "[s2n-quic-dc::{file}:{line}]: {kind}")
34    }
35}
36
37impl core::error::Error for Error {}
38
39impl IntoEvent<Error> for Error {
40    fn into_event(self) -> Error {
41        self
42    }
43}
44
45impl Error {
46    #[track_caller]
47    #[inline]
48    pub fn new(kind: Kind) -> Self {
49        Self {
50            kind,
51            location: Location::caller(),
52        }
53    }
54
55    #[inline]
56    pub fn kind(&self) -> &Kind {
57        &self.kind
58    }
59
60    #[inline]
61    fn file(&self) -> &'static str {
62        self.location
63            .file()
64            .trim_start_matches(concat!(env!("CARGO_MANIFEST_DIR"), "/src/"))
65    }
66
67    pub(crate) fn for_recv(self) -> Option<recv::Error> {
68        let kind = self.kind.for_recv()?;
69        Some(recv::Error {
70            kind,
71            location: self.location,
72        })
73    }
74}
75
76impl From<Kind> for Error {
77    #[track_caller]
78    #[inline]
79    fn from(kind: Kind) -> Self {
80        Self::new(kind)
81    }
82}
83
84#[derive(Clone, Copy, Debug, thiserror::Error)]
85pub enum Kind {
86    #[error("payload provided is too large and exceeded the maximum offset")]
87    PayloadTooLarge,
88    #[error("the provided packet buffer is too small for the minimum packet size")]
89    PacketBufferTooSmall,
90    #[error("the number of packets able to be sent on the sender has been exceeded")]
91    PacketNumberExhaustion,
92    #[error("retransmission not possible")]
93    RetransmissionFailure,
94    #[error("stream has been finished")]
95    StreamFinished,
96    #[error("the final size of the stream has changed")]
97    FinalSizeChanged,
98    #[error("the sender idle timer expired")]
99    IdleTimeout,
100    #[error("the crypto key has been replayed and is invalid")]
101    KeyReplayPrevented,
102    #[error("the crypto key has been potentially replayed (gap: {gap:?}) and is invalid")]
103    KeyReplayMaybePrevented { gap: Option<u64> },
104    #[error("the stream is using an unknown path secret")]
105    UnknownPathSecret,
106    #[error("the stream was reset by the peer with code {code}")]
107    TransportError { code: VarInt },
108    #[error("the stream was closed with application code {error}")]
109    ApplicationError {
110        error: s2n_quic_core::application::Error,
111    },
112    #[error("an invalid frame was received: {decoder}")]
113    FrameError { decoder: s2n_codec::DecoderError },
114    #[error("the stream experienced an unrecoverable error")]
115    FatalError,
116}
117
118impl Kind {
119    #[inline]
120    #[track_caller]
121    pub(crate) fn err(self) -> Error {
122        Error::new(self)
123    }
124
125    pub(crate) fn for_recv(self) -> Option<recv::ErrorKind> {
126        use recv::ErrorKind as RecvKind;
127
128        match self {
129            Kind::PayloadTooLarge => None,
130            Kind::PacketBufferTooSmall => None,
131            Kind::PacketNumberExhaustion => None,
132            Kind::RetransmissionFailure => None,
133            Kind::StreamFinished => None,
134            Kind::FinalSizeChanged => None,
135            Kind::IdleTimeout => Some(RecvKind::IdleTimeout),
136            Kind::KeyReplayPrevented => Some(RecvKind::KeyReplayPrevented),
137            Kind::KeyReplayMaybePrevented { gap } => {
138                Some(RecvKind::KeyReplayMaybePrevented { gap })
139            }
140            Kind::UnknownPathSecret => Some(RecvKind::UnknownPathSecret),
141            Kind::TransportError { code } => Some(RecvKind::TransportError { code }),
142            // If the application error is 0, it indicates a graceful shutdown
143            Kind::ApplicationError { error } if *error == 0 => None,
144            Kind::ApplicationError { error } => Some(RecvKind::ApplicationError { error }),
145            Kind::FrameError { .. } => None,
146            Kind::FatalError => Some(RecvKind::TruncatedTransport),
147        }
148    }
149}
150
151impl From<Error> for std::io::Error {
152    #[inline]
153    #[track_caller]
154    fn from(error: Error) -> Self {
155        Self::new(error.kind.into(), error)
156    }
157}
158
159impl From<Kind> for std::io::ErrorKind {
160    #[inline]
161    fn from(kind: Kind) -> Self {
162        use std::io::ErrorKind;
163        match kind {
164            Kind::PayloadTooLarge => ErrorKind::BrokenPipe,
165            Kind::PacketBufferTooSmall => ErrorKind::InvalidInput,
166            Kind::PacketNumberExhaustion => ErrorKind::BrokenPipe,
167            Kind::RetransmissionFailure => ErrorKind::BrokenPipe,
168            Kind::StreamFinished => ErrorKind::UnexpectedEof,
169            Kind::FinalSizeChanged => ErrorKind::InvalidInput,
170            Kind::IdleTimeout => ErrorKind::TimedOut,
171            Kind::KeyReplayPrevented => ErrorKind::PermissionDenied,
172            Kind::KeyReplayMaybePrevented { .. } => ErrorKind::PermissionDenied,
173            Kind::UnknownPathSecret => ErrorKind::PermissionDenied,
174            Kind::ApplicationError { error } if *error == ShutdownKind::PRUNED_CODE as u64 => {
175                ErrorKind::ConnectionRefused
176            }
177            Kind::ApplicationError { .. } => ErrorKind::ConnectionReset,
178            Kind::TransportError { .. } => ErrorKind::ConnectionAborted,
179            Kind::FrameError { .. } => ErrorKind::InvalidData,
180            Kind::FatalError => ErrorKind::BrokenPipe,
181        }
182    }
183}
184
185impl From<packet_number::ExhaustionError> for Error {
186    #[inline]
187    #[track_caller]
188    fn from(_error: packet_number::ExhaustionError) -> Self {
189        Kind::PacketNumberExhaustion.err()
190    }
191}
192
193impl From<buffer::Error<core::convert::Infallible>> for Error {
194    #[inline]
195    #[track_caller]
196    fn from(error: buffer::Error<core::convert::Infallible>) -> Self {
197        match error {
198            buffer::Error::OutOfRange => Kind::PayloadTooLarge.err(),
199            buffer::Error::InvalidFin => Kind::FinalSizeChanged.err(),
200            buffer::Error::ReaderError(_) => unreachable!(),
201        }
202    }
203}