h3/error/
connection_error_creators.rs

1//! functions to create the error structs
2
3use std::task::Poll;
4
5use bytes::Buf;
6
7use crate::{
8    connection::ConnectionInner,
9    frame::FrameStreamError,
10    quic::{self, ConnectionErrorIncoming, StreamErrorIncoming},
11    shared_state::ConnectionState,
12};
13
14use super::{
15    codes::Code,
16    internal_error::{ErrorOrigin, InternalConnectionError},
17    ConnectionError, LocalError, StreamError,
18};
19
20/// This trait is implemented for all types which can close the connection
21impl<C, B> ConnectionInner<C, B>
22where
23    C: quic::Connection<B>,
24    B: Buf,
25{
26    /// Handle errors on the connection, closes the connection if needed
27    ///
28    /// This can be a [`ConnectionErrorIncoming`] or a [`InternalConnectionError`]
29    pub fn handle_connection_error<T: Into<ErrorOrigin>>(&mut self, error: T) -> ConnectionError {
30        if let Some(ref error) = self.handled_connection_error {
31            return error.clone();
32        }
33
34        let err = self.set_conn_error(error.into());
35        let err = self.close_if_needed(err);
36        // err might be a different error so match again
37        self.convert_to_connection_error(err)
38    }
39
40    /// Closes the connection if needed
41    /// Check self.handled_connection_error before calling this function
42    fn close_if_needed(&mut self, error: ErrorOrigin) -> ErrorOrigin {
43        match error {
44            ErrorOrigin::Internal(ref internal_error) => {
45                self.close_connection(internal_error.code, internal_error.message.clone())
46            }
47            ErrorOrigin::Quic(ConnectionErrorIncoming::InternalError(ref reason)) => {
48                self.close_connection(Code::H3_INTERNAL_ERROR, reason.clone())
49            }
50
51            // All other path do not need to close the connection
52            _ => (),
53        }
54        error
55    }
56
57    /// Converts a [`ErrorOrigin`] into a [`ConnectionError`] and sets self.handled_connection_error
58    ///
59    /// Check close the connection if needed before calling this function
60    fn convert_to_connection_error(&mut self, error: ErrorOrigin) -> ConnectionError {
61        let error = convert_to_connection_error(error);
62        self.handled_connection_error = Some(error.clone());
63        error
64    }
65
66    /// Polls for the connection error
67    ///
68    /// Returns an Result to allow using ? in the calling function
69    pub fn poll_connection_error(
70        &mut self,
71        cx: &mut std::task::Context<'_>,
72    ) -> Poll<Result<(), ConnectionError>> {
73        if let Some(ref error) = self.handled_connection_error {
74            return Poll::Ready(Err(error.clone()));
75        };
76
77        // Check if the connection is in error state
78        if let Some(err) = self.get_conn_error() {
79            let err = self.close_if_needed(err);
80            // err might be a different error so match again
81            return Poll::Ready(Err(self.convert_to_connection_error(err)));
82        }
83        self.waker().register(cx.waker());
84        Poll::Pending
85    }
86
87    /// Close the connection
88    pub fn close_connection(&mut self, code: Code, reason: String) -> () {
89        self.conn.close(code, reason.as_bytes())
90    }
91}
92
93/// Converts a [`ErrorOrigin`] into a [`ConnectionError`] and sets self.handled_connection_error
94fn convert_to_connection_error(error: ErrorOrigin) -> ConnectionError {
95    match error {
96        ErrorOrigin::Internal(internal_error) => ConnectionError::Local {
97            error: LocalError::Application {
98                code: internal_error.code,
99                reason: internal_error.message,
100            },
101        },
102        ErrorOrigin::Quic(ConnectionErrorIncoming::Timeout) => ConnectionError::Timeout,
103        ErrorOrigin::Quic(connection_error) => ConnectionError::Remote(connection_error),
104    }
105}
106
107/// This trait is implemented for all types which can close a stream
108pub trait CloseStream: ConnectionState {
109    /// Handles a connection error on a stream
110    fn handle_connection_error_on_stream(
111        &mut self,
112        internal_error: InternalConnectionError,
113    ) -> StreamError {
114        let err = self.set_conn_error_and_wake(internal_error);
115        StreamError::ConnectionError(convert_to_connection_error(err))
116    }
117
118    /// Handles a incoming stream error from the quic layer
119    fn handle_quic_stream_error(&self, error: StreamErrorIncoming) -> StreamError {
120        match error {
121            StreamErrorIncoming::ConnectionErrorIncoming { connection_error } => {
122                let err = self.set_conn_error_and_wake(connection_error);
123                StreamError::ConnectionError(convert_to_connection_error(err))
124            }
125            StreamErrorIncoming::StreamTerminated { error_code } => StreamError::RemoteTerminate {
126                code: Code::from(error_code),
127            },
128            StreamErrorIncoming::Unknown(custom_quic_impl_error) => {
129                StreamError::Undefined(custom_quic_impl_error)
130            }
131        }
132    }
133
134    /// Checks if the peer connection is closing an if it is allowed to send a request / server push
135    fn check_peer_connection_closing(&self) -> Option<StreamError> {
136        if self.is_closing() {
137            return Some(StreamError::RemoteClosing);
138        };
139        None
140    }
141}
142
143pub(crate) trait CloseRawQuicConnection<B: Buf>: quic::Connection<B> {
144    // Should only be used when there is no h3 connection created
145    fn handle_quic_error_raw(&mut self, error: ConnectionErrorIncoming) -> ConnectionError {
146        match error {
147            ConnectionErrorIncoming::Timeout => ConnectionError::Timeout,
148            ConnectionErrorIncoming::InternalError(reason) => {
149                let local_error = LocalError::Application {
150                    code: Code::H3_INTERNAL_ERROR,
151                    reason: reason.to_string(),
152                };
153                self.close(Code::H3_INTERNAL_ERROR, reason.as_bytes());
154                let conn_error = ConnectionError::Local { error: local_error };
155                conn_error
156            }
157            _ => ConnectionError::Remote(error),
158        }
159    }
160
161    // Should only be used when there is no h3 connection created
162    fn close_raw_connection_with_h3_error(
163        &mut self,
164        internal_error: InternalConnectionError,
165    ) -> ConnectionError {
166        let error = ConnectionError::Local {
167            error: internal_error.clone().into(),
168        };
169        self.close(internal_error.code, internal_error.message.as_bytes());
170        error
171    }
172}
173
174impl<T, B> CloseRawQuicConnection<B> for T
175where
176    T: quic::Connection<B>,
177    B: Buf,
178{
179}
180
181pub(crate) trait HandleFrameStreamErrorOnRequestStream {
182    fn handle_frame_stream_error_on_request_stream(
183        &mut self,
184        error: FrameStreamError,
185    ) -> StreamError;
186}
187
188impl<T> HandleFrameStreamErrorOnRequestStream for T
189where
190    T: CloseStream,
191{
192    fn handle_frame_stream_error_on_request_stream(
193        &mut self,
194        error: FrameStreamError,
195    ) -> StreamError {
196        match error {
197            FrameStreamError::Quic(error) => self.handle_quic_stream_error(error),
198            FrameStreamError::Proto(frame_error) => self.handle_connection_error_on_stream(
199                InternalConnectionError::got_frame_error(frame_error).into(),
200            ),
201            FrameStreamError::UnexpectedEnd => {
202                self.handle_connection_error_on_stream(InternalConnectionError::new(
203                    Code::H3_FRAME_ERROR,
204                    "received incomplete frame".to_string(),
205                ))
206            }
207        }
208    }
209}