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
71impl core::error::Error for StreamError {}
72
73impl fmt::Display for StreamError {
74    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75        match self {
76            Self::InvalidStream { .. } => {
77                write!(f, "The Stream ID which was referenced is invalid")
78            }
79            Self::StreamReset { error, .. } => write!(
80                f,
81                "The Stream had been reset with the error {:?} by {}",
82                error,
83                crate::endpoint::Location::Remote,
84            ),
85            Self::SendAfterFinish { .. } => write!(
86                f,
87                "A send attempt had been performed on a Stream after it was closed"
88            ),
89            Self::MaxStreamDataSizeExceeded { .. } => {
90                write!(f, "Attempting to write data would exceed the stream limit")
91            }
92            Self::ConnectionError { error, .. } => error.fmt(f),
93            Self::NonReadable { .. } => write!(f, "The stream is not readable"),
94            Self::NonWritable { .. } => write!(f, "The stream is not writable"),
95            Self::SendingBlocked { .. } => write!(f, "The stream is blocked on writing data"),
96            Self::NonEmptyOutput { .. } => write!(
97                f,
98                "The stream was provided a non-empty placeholder buffer for receiving data."
99            ),
100        }
101    }
102}
103
104impl StreamError {
105    /// Returns the [`panic::Location`] for the error
106    pub fn source(&self) -> &'static panic::Location<'static> {
107        match self {
108            StreamError::InvalidStream { source } => source,
109            StreamError::StreamReset { source, .. } => source,
110            StreamError::SendAfterFinish { source } => source,
111            StreamError::MaxStreamDataSizeExceeded { source } => source,
112            StreamError::ConnectionError { error } => error.source(),
113            StreamError::NonReadable { source } => source,
114            StreamError::NonWritable { source } => source,
115            StreamError::SendingBlocked { source } => source,
116            StreamError::NonEmptyOutput { source } => source,
117        }
118    }
119
120    #[track_caller]
121    #[inline]
122    #[doc(hidden)]
123    pub fn invalid_stream() -> StreamError {
124        let source = panic::Location::caller();
125        StreamError::InvalidStream { source }
126    }
127
128    #[track_caller]
129    #[inline]
130    #[doc(hidden)]
131    pub fn stream_reset(error: application::Error) -> StreamError {
132        let source = panic::Location::caller();
133        StreamError::StreamReset { source, error }
134    }
135
136    #[track_caller]
137    #[inline]
138    #[doc(hidden)]
139    pub fn send_after_finish() -> StreamError {
140        let source = panic::Location::caller();
141        StreamError::SendAfterFinish { source }
142    }
143
144    #[track_caller]
145    #[inline]
146    #[doc(hidden)]
147    pub fn max_stream_data_size_exceeded() -> StreamError {
148        let source = panic::Location::caller();
149        StreamError::MaxStreamDataSizeExceeded { source }
150    }
151
152    #[track_caller]
153    #[inline]
154    #[doc(hidden)]
155    pub fn non_readable() -> StreamError {
156        let source = panic::Location::caller();
157        StreamError::NonReadable { source }
158    }
159
160    #[track_caller]
161    #[inline]
162    #[doc(hidden)]
163    pub fn non_writable() -> StreamError {
164        let source = panic::Location::caller();
165        StreamError::NonWritable { source }
166    }
167
168    #[track_caller]
169    #[inline]
170    #[doc(hidden)]
171    pub fn sending_blocked() -> StreamError {
172        let source = panic::Location::caller();
173        StreamError::SendingBlocked { source }
174    }
175
176    #[track_caller]
177    #[inline]
178    #[doc(hidden)]
179    pub fn non_empty_output() -> StreamError {
180        let source = panic::Location::caller();
181        StreamError::NonEmptyOutput { source }
182    }
183}
184
185impl application::error::TryInto for StreamError {
186    fn application_error(&self) -> Option<application::Error> {
187        if let StreamError::ConnectionError { error, .. } = self {
188            error.application_error()
189        } else {
190            None
191        }
192    }
193}
194
195impl From<connection::Error> for StreamError {
196    fn from(error: connection::Error) -> Self {
197        Self::ConnectionError { error }
198    }
199}
200
201impl From<transport::Error> for StreamError {
202    #[track_caller]
203    fn from(error: transport::Error) -> Self {
204        let error: connection::Error = error.into();
205        error.into()
206    }
207}
208
209impl From<ConnectionClose<'_>> for StreamError {
210    #[track_caller]
211    fn from(error: ConnectionClose) -> Self {
212        let error: connection::Error = error.into();
213        error.into()
214    }
215}
216
217#[cfg(feature = "std")]
218impl From<StreamError> for std::io::Error {
219    fn from(error: StreamError) -> Self {
220        let kind = error.into();
221        std::io::Error::new(kind, error)
222    }
223}
224
225#[cfg(feature = "std")]
226impl From<StreamError> for std::io::ErrorKind {
227    fn from(error: StreamError) -> Self {
228        use std::io::ErrorKind;
229        match error {
230            StreamError::InvalidStream { .. } => ErrorKind::NotFound,
231            StreamError::StreamReset { .. } => ErrorKind::ConnectionReset,
232            StreamError::SendAfterFinish { .. } => ErrorKind::BrokenPipe,
233            StreamError::MaxStreamDataSizeExceeded { .. } => ErrorKind::Other,
234            StreamError::ConnectionError { error, .. } => error.into(),
235            StreamError::NonReadable { .. } => ErrorKind::Other,
236            StreamError::NonWritable { .. } => ErrorKind::Other,
237            StreamError::SendingBlocked { .. } => ErrorKind::WouldBlock,
238            StreamError::NonEmptyOutput { .. } => ErrorKind::InvalidInput,
239        }
240    }
241}