1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 //! [`Error Codes`] in [`HTTP/2`].
15 //!
16 //! [`Error Codes`]: https://httpwg.org/specs/rfc9113.html#ErrorCodes
17 //! [`HTTP/2`]: https://httpwg.org/specs/rfc9113.html
18 //!
19 //! # introduction
20 //! Error codes are 32-bit fields that are used in `RST_STREAM` and `GOAWAY`
21 //! frames to convey the reasons for the stream or connection error.
22 //!
23 //! Error codes share a common code space. Some error codes apply only to either
24 //! streams or the entire connection and have no defined semantics in the other
25 //! context.
26 
27 use std::convert::{Infallible, TryFrom};
28 
29 use crate::error::{ErrorKind, HttpError};
30 
31 /// The http2 error handle implementation
32 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
33 pub enum H2Error {
34     /// [`Stream Error`] Handling.
35     ///
36     /// [`Stream Error`]: https://www.rfc-editor.org/rfc/rfc9113.html#name-stream-error-handling
37     StreamError(u32, ErrorCode),
38 
39     /// [`Connection Error`] Handling.
40     ///
41     /// [`Connection Error`]: https://www.rfc-editor.org/rfc/rfc9113.html#name-connection-error-handling
42     ConnectionError(ErrorCode),
43 }
44 
45 /// [`Error Codes`] implementation.
46 ///
47 /// [`Error Codes`]: https://httpwg.org/specs/rfc9113.html#ErrorCodes
48 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
49 pub enum ErrorCode {
50     /// The associated condition is not a result of an error. For example,
51     /// a `GOAWAY` might include this code to indicate graceful shutdown of a
52     /// connection.
53     NoError = 0x00,
54 
55     /// The endpoint detected an unspecific protocol error. This error is for
56     /// use when a more specific error code is not available.
57     ProtocolError = 0x01,
58 
59     /// The endpoint encountered an unexpected internal error.
60     IntervalError = 0x02,
61 
62     /// The endpoint detected that its peer violated the flow-control protocol.
63     FlowControlError = 0x03,
64 
65     /// The endpoint sent a `SETTINGS` frame but did not receive a response in
66     /// a timely manner.
67     SettingsTimeout = 0x04,
68 
69     /// The endpoint received a frame after a stream was half-closed.
70     StreamClosed = 0x05,
71 
72     /// The endpoint received a frame with an invalid size.
73     FrameSizeError = 0x06,
74 
75     /// The endpoint refused the stream prior to performing any application
76     /// processing.
77     RefusedStream = 0x07,
78 
79     /// The endpoint uses this error code to indicate that the stream is no
80     /// longer needed.
81     Cancel = 0x08,
82 
83     /// The endpoint is unable to maintain the field section compression context
84     /// for the connection.
85     CompressionError = 0x09,
86 
87     /// The connection established in response to a `CONNECT` request was reset
88     /// or abnormally closed.
89     ConnectError = 0x0a,
90 
91     /// The endpoint detected that its peer is exhibiting a behavior that might
92     /// be generating excessive load.
93     EnhanceYourCalm = 0x0b,
94 
95     /// The underlying transport has properties that do not meet minimum
96     /// security requirements.
97     InadequateSecurity = 0x0c,
98 
99     /// The endpoint requires that HTTP/1.1 be used instead of HTTP/2.
100     Http1_1Required = 0x0d,
101 }
102 
103 impl ErrorCode {
104     /// Gets the error code of the `ErrorCode` enum.
into_code(self) -> u32105     pub fn into_code(self) -> u32 {
106         self as u32
107     }
108 }
109 
110 impl TryFrom<u32> for ErrorCode {
111     type Error = H2Error;
try_from(value: u32) -> Result<Self, Self::Error>112     fn try_from(value: u32) -> Result<Self, Self::Error> {
113         let err = match value {
114             0x00 => ErrorCode::NoError,
115             0x01 => ErrorCode::ProtocolError,
116             0x02 => ErrorCode::IntervalError,
117             0x03 => ErrorCode::FlowControlError,
118             0x04 => ErrorCode::SettingsTimeout,
119             0x05 => ErrorCode::StreamClosed,
120             0x06 => ErrorCode::FrameSizeError,
121             0x07 => ErrorCode::RefusedStream,
122             0x08 => ErrorCode::Cancel,
123             0x09 => ErrorCode::CompressionError,
124             0x0a => ErrorCode::ConnectError,
125             0x0b => ErrorCode::EnhanceYourCalm,
126             0x0c => ErrorCode::InadequateSecurity,
127             0x0d => ErrorCode::Http1_1Required,
128             _ => return Err(H2Error::ConnectionError(ErrorCode::ProtocolError)),
129         };
130         Ok(err)
131     }
132 }
133 
134 #[cfg(test)]
135 mod ut_h2_error {
136     use std::convert::TryInto;
137 
138     use super::*;
139 
140     /// Unit test cases for `ErrorCode::try_from`.
141     ///
142     /// # Brief
143     /// 1. Iterates over a range of valid u32 values that represent
144     ///    `ErrorCode`s.
145     /// 2. Attempts to convert each u32 value into an `ErrorCode` using
146     ///    `try_into`.
147     /// 3. Checks that the conversion is successful for each valid `ErrorCode`.
148     /// 4. Also attempts to convert an invalid u32 value into an `ErrorCode`.
149     /// 5. Checks that the conversion fails for the invalid value.
150     #[test]
ut_test_error_code_try_from()151     fn ut_test_error_code_try_from() {
152         // Test conversion from u32 to ErrorCode for valid error codes
153         for i in 0x00..=0x0d {
154             let error_code: Result<ErrorCode, _> = i.try_into();
155             assert!(error_code.is_ok());
156         }
157 
158         // Test conversion from u32 to ErrorCode for invalid error codes
159         let invalid_error_code: Result<ErrorCode, _> = 0x0e.try_into();
160         assert!(invalid_error_code.is_err());
161     }
162 }
163