s2n_quic_core/stream/
error.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{application, connection, frame::ConnectionClose, transport};
5use core::{fmt, panic};
6
7/// Errors that a stream can encounter.
8#[derive(PartialEq, Eq, Debug, Copy, Clone)]
9#[non_exhaustive]
10pub enum StreamError {
11    /// The Stream ID which was referenced is invalid
12    ///
13    /// This could mean the ID is no longer tracked by the Connection.
14    #[non_exhaustive]
15    InvalidStream {
16        source: &'static panic::Location<'static>,
17    },
18    /// The Stream had been reset by the peer via a `RESET_STREAM` frame.
19    ///
20    /// Inside this frame the peer will deliver an error code, which will be
21    /// provided by the parameter.
22    #[non_exhaustive]
23    StreamReset {
24        error: application::Error,
25        source: &'static panic::Location<'static>,
26    },
27    /// A send attempt had been performed on a Stream after it was closed
28    #[non_exhaustive]
29    SendAfterFinish {
30        source: &'static panic::Location<'static>,
31    },
32    /// Attempting to write data would exceed the stream limit
33    ///
34    /// This is caused because the maximum possible amount
35    /// of data (2^62-1 bytes) had already been written to the
36    /// Stream.
37    #[non_exhaustive]
38    MaxStreamDataSizeExceeded {
39        source: &'static panic::Location<'static>,
40    },
41    /// The Stream was reset due to a Connection Error
42    #[non_exhaustive]
43    ConnectionError { error: connection::Error },
44    /// The stream is not readable
45    #[non_exhaustive]
46    NonReadable {
47        source: &'static panic::Location<'static>,
48    },
49    /// The stream is not writable
50    #[non_exhaustive]
51    NonWritable {
52        source: &'static panic::Location<'static>,
53    },
54    /// The stream is blocked on writing data
55    ///
56    /// This is caused by trying to send data before polling readiness
57    #[non_exhaustive]
58    SendingBlocked {
59        source: &'static panic::Location<'static>,
60    },
61    /// The stream was provided a non-empty placeholder buffer for receiving data.
62    ///
63    /// The application should ensure only empty buffers are provided to receive calls,
64    /// otherwise it can lead to data loss on the stream.
65    #[non_exhaustive]
66    NonEmptyOutput {
67        source: &'static panic::Location<'static>,
68    },
69}
70
71#[cfg(feature = "std")]
72impl std::error::Error for StreamError {}
73
74impl fmt::Display for StreamError {
75    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76        match self {
77            Self::InvalidStream { .. } => {
78                write!(f, "The Stream ID which was referenced is invalid")
79            }
80            Self::StreamReset { error, .. } => write!(
81                f,
82                "The Stream had been reset with the error {:?} by {}",
83                error,
84                crate::endpoint::Location::Remote,
85            ),
86            Self::SendAfterFinish { .. } => write!(
87                f,
88                "A send attempt had been performed on a Stream after it was closed"
89            ),
90            Self::MaxStreamDataSizeExceeded { .. } => {
91                write!(f, "Attempting to write data would exceed the stream limit")
92            }
93            Self::ConnectionError { error, .. } => error.fmt(f),
94            Self::NonReadable { .. } => write!(f, "The stream is not readable"),
95            Self::NonWritable { .. } => write!(f, "The stream is not writable"),
96            Self::SendingBlocked { .. } => write!(f, "The stream is blocked on writing data"),
97            Self::NonEmptyOutput { .. } => write!(
98                f,
99                "The stream was provided a non-empty placeholder buffer for receiving data."
100            ),
101        }
102    }
103}
104
105impl StreamError {
106    /// Returns the [`panic::Location`] for the error
107    pub fn source(&self) -> &'static panic::Location<'static> {
108        match self {
109            StreamError::InvalidStream { source } => source,
110            StreamError::StreamReset { source, .. } => source,
111            StreamError::SendAfterFinish { source } => source,
112            StreamError::MaxStreamDataSizeExceeded { source } => source,
113            StreamError::ConnectionError { error } => error.source(),
114            StreamError::NonReadable { source } => source,
115            StreamError::NonWritable { source } => source,
116            StreamError::SendingBlocked { source } => source,
117            StreamError::NonEmptyOutput { source } => source,
118        }
119    }
120
121    #[track_caller]
122    #[inline]
123    #[doc(hidden)]
124    pub fn invalid_stream() -> StreamError {
125        let source = panic::Location::caller();
126        StreamError::InvalidStream { source }
127    }
128
129    #[track_caller]
130    #[inline]
131    #[doc(hidden)]
132    pub fn stream_reset(error: application::Error) -> StreamError {
133        let source = panic::Location::caller();
134        StreamError::StreamReset { source, error }
135    }
136
137    #[track_caller]
138    #[inline]
139    #[doc(hidden)]
140    pub fn send_after_finish() -> StreamError {
141        let source = panic::Location::caller();
142        StreamError::SendAfterFinish { source }
143    }
144
145    #[track_caller]
146    #[inline]
147    #[doc(hidden)]
148    pub fn max_stream_data_size_exceeded() -> StreamError {
149        let source = panic::Location::caller();
150        StreamError::MaxStreamDataSizeExceeded { source }
151    }
152
153    #[track_caller]
154    #[inline]
155    #[doc(hidden)]
156    pub fn non_readable() -> StreamError {
157        let source = panic::Location::caller();
158        StreamError::NonReadable { source }
159    }
160
161    #[track_caller]
162    #[inline]
163    #[doc(hidden)]
164    pub fn non_writable() -> StreamError {
165        let source = panic::Location::caller();
166        StreamError::NonWritable { source }
167    }
168
169    #[track_caller]
170    #[inline]
171    #[doc(hidden)]
172    pub fn sending_blocked() -> StreamError {
173        let source = panic::Location::caller();
174        StreamError::SendingBlocked { source }
175    }
176
177    #[track_caller]
178    #[inline]
179    #[doc(hidden)]
180    pub fn non_empty_output() -> StreamError {
181        let source = panic::Location::caller();
182        StreamError::NonEmptyOutput { source }
183    }
184}
185
186impl application::error::TryInto for StreamError {
187    fn application_error(&self) -> Option<application::Error> {
188        if let StreamError::ConnectionError { error, .. } = self {
189            error.application_error()
190        } else {
191            None
192        }
193    }
194}
195
196impl From<connection::Error> for StreamError {
197    fn from(error: connection::Error) -> Self {
198        Self::ConnectionError { error }
199    }
200}
201
202impl From<transport::Error> for StreamError {
203    #[track_caller]
204    fn from(error: transport::Error) -> Self {
205        let error: connection::Error = error.into();
206        error.into()
207    }
208}
209
210impl From<ConnectionClose<'_>> for StreamError {
211    #[track_caller]
212    fn from(error: ConnectionClose) -> Self {
213        let error: connection::Error = error.into();
214        error.into()
215    }
216}
217
218#[cfg(feature = "std")]
219impl From<StreamError> for std::io::Error {
220    fn from(error: StreamError) -> Self {
221        let kind = error.into();
222        std::io::Error::new(kind, error)
223    }
224}
225
226#[cfg(feature = "std")]
227impl From<StreamError> for std::io::ErrorKind {
228    fn from(error: StreamError) -> Self {
229        use std::io::ErrorKind;
230        match error {
231            StreamError::InvalidStream { .. } => ErrorKind::NotFound,
232            StreamError::StreamReset { .. } => ErrorKind::ConnectionReset,
233            StreamError::SendAfterFinish { .. } => ErrorKind::BrokenPipe,
234            StreamError::MaxStreamDataSizeExceeded { .. } => ErrorKind::Other,
235            StreamError::ConnectionError { error, .. } => error.into(),
236            StreamError::NonReadable { .. } => ErrorKind::Other,
237            StreamError::NonWritable { .. } => ErrorKind::Other,
238            StreamError::SendingBlocked { .. } => ErrorKind::WouldBlock,
239            StreamError::NonEmptyOutput { .. } => ErrorKind::InvalidInput,
240        }
241    }
242}