Coverage Report

Created: 2025-07-11 06:53

/src/h2/src/error.rs
Line
Count
Source (jump to first uncovered line)
1
use crate::codec::{SendError, UserError};
2
use crate::frame::StreamId;
3
use crate::proto::{self, Initiator};
4
5
use bytes::Bytes;
6
use std::{error, fmt, io};
7
8
pub use crate::frame::Reason;
9
10
/// Represents HTTP/2 operation errors.
11
///
12
/// `Error` covers error cases raised by protocol errors caused by the
13
/// peer, I/O (transport) errors, and errors caused by the user of the library.
14
///
15
/// If the error was caused by the remote peer, then it will contain a
16
/// [`Reason`] which can be obtained with the [`reason`] function.
17
///
18
/// [`Reason`]: struct.Reason.html
19
/// [`reason`]: #method.reason
20
#[derive(Debug)]
21
pub struct Error {
22
    kind: Kind,
23
}
24
25
#[derive(Debug)]
26
enum Kind {
27
    /// A RST_STREAM frame was received or sent.
28
    #[allow(dead_code)]
29
    Reset(StreamId, Reason, Initiator),
30
31
    /// A GO_AWAY frame was received or sent.
32
    GoAway(Bytes, Reason, Initiator),
33
34
    /// The user created an error from a bare Reason.
35
    Reason(Reason),
36
37
    /// An error resulting from an invalid action taken by the user of this
38
    /// library.
39
    User(UserError),
40
41
    /// An `io::Error` occurred while trying to read or write.
42
    Io(io::Error),
43
}
44
45
// ===== impl Error =====
46
47
impl Error {
48
    /// If the error was caused by the remote peer, the error reason.
49
    ///
50
    /// This is either an error received by the peer or caused by an invalid
51
    /// action taken by the peer (i.e. a protocol error).
52
0
    pub fn reason(&self) -> Option<Reason> {
53
0
        match self.kind {
54
0
            Kind::Reset(_, reason, _) | Kind::GoAway(_, reason, _) | Kind::Reason(reason) => {
55
0
                Some(reason)
56
            }
57
0
            _ => None,
58
        }
59
0
    }
60
61
    /// Returns true if the error is an io::Error
62
0
    pub fn is_io(&self) -> bool {
63
0
        matches!(self.kind, Kind::Io(..))
64
0
    }
65
66
    /// Returns the error if the error is an io::Error
67
0
    pub fn get_io(&self) -> Option<&io::Error> {
68
0
        match self.kind {
69
0
            Kind::Io(ref e) => Some(e),
70
0
            _ => None,
71
        }
72
0
    }
73
74
    /// Returns the error if the error is an io::Error
75
0
    pub fn into_io(self) -> Option<io::Error> {
76
0
        match self.kind {
77
0
            Kind::Io(e) => Some(e),
78
0
            _ => None,
79
        }
80
0
    }
81
82
27
    pub(crate) fn from_io(err: io::Error) -> Self {
83
27
        Error {
84
27
            kind: Kind::Io(err),
85
27
        }
86
27
    }
87
88
    /// Returns true if the error is from a `GOAWAY`.
89
0
    pub fn is_go_away(&self) -> bool {
90
0
        matches!(self.kind, Kind::GoAway(..))
91
0
    }
92
93
    /// Returns true if the error is from a `RST_STREAM`.
94
0
    pub fn is_reset(&self) -> bool {
95
0
        matches!(self.kind, Kind::Reset(..))
96
0
    }
97
98
    /// Returns true if the error was received in a frame from the remote.
99
    ///
100
    /// Such as from a received `RST_STREAM` or `GOAWAY` frame.
101
0
    pub fn is_remote(&self) -> bool {
102
0
        matches!(
103
0
            self.kind,
104
            Kind::GoAway(_, _, Initiator::Remote) | Kind::Reset(_, _, Initiator::Remote)
105
        )
106
0
    }
107
108
    /// Returns true if the error was created by `h2`.
109
    ///
110
    /// Such as noticing some protocol error and sending a GOAWAY or RST_STREAM.
111
0
    pub fn is_library(&self) -> bool {
112
0
        matches!(
113
0
            self.kind,
114
            Kind::GoAway(_, _, Initiator::Library) | Kind::Reset(_, _, Initiator::Library)
115
        )
116
0
    }
117
}
118
119
impl From<proto::Error> for Error {
120
39.6k
    fn from(src: proto::Error) -> Error {
121
        use crate::proto::Error::*;
122
123
        Error {
124
39.6k
            kind: match src {
125
143
                Reset(stream_id, reason, initiator) => Kind::Reset(stream_id, reason, initiator),
126
28.9k
                GoAway(debug_data, reason, initiator) => {
127
28.9k
                    Kind::GoAway(debug_data, reason, initiator)
128
                }
129
10.6k
                Io(kind, inner) => {
130
10.6k
                    Kind::Io(inner.map_or_else(|| kind.into(), |inner| io::Error::new(kind, inner)))
131
                }
132
            },
133
        }
134
39.6k
    }
135
}
136
137
impl From<Reason> for Error {
138
0
    fn from(src: Reason) -> Error {
139
0
        Error {
140
0
            kind: Kind::Reason(src),
141
0
        }
142
0
    }
143
}
144
145
impl From<SendError> for Error {
146
86
    fn from(src: SendError) -> Error {
147
86
        match src {
148
86
            SendError::User(e) => e.into(),
149
0
            SendError::Connection(e) => e.into(),
150
        }
151
86
    }
152
}
153
154
impl From<UserError> for Error {
155
115
    fn from(src: UserError) -> Error {
156
115
        Error {
157
115
            kind: Kind::User(src),
158
115
        }
159
115
    }
160
}
161
162
impl fmt::Display for Error {
163
0
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
164
0
        let debug_data = match self.kind {
165
0
            Kind::Reset(_, reason, Initiator::User) => {
166
0
                return write!(fmt, "stream error sent by user: {}", reason)
167
            }
168
0
            Kind::Reset(_, reason, Initiator::Library) => {
169
0
                return write!(fmt, "stream error detected: {}", reason)
170
            }
171
0
            Kind::Reset(_, reason, Initiator::Remote) => {
172
0
                return write!(fmt, "stream error received: {}", reason)
173
            }
174
0
            Kind::GoAway(ref debug_data, reason, Initiator::User) => {
175
0
                write!(fmt, "connection error sent by user: {}", reason)?;
176
0
                debug_data
177
            }
178
0
            Kind::GoAway(ref debug_data, reason, Initiator::Library) => {
179
0
                write!(fmt, "connection error detected: {}", reason)?;
180
0
                debug_data
181
            }
182
0
            Kind::GoAway(ref debug_data, reason, Initiator::Remote) => {
183
0
                write!(fmt, "connection error received: {}", reason)?;
184
0
                debug_data
185
            }
186
0
            Kind::Reason(reason) => return write!(fmt, "protocol error: {}", reason),
187
0
            Kind::User(ref e) => return write!(fmt, "user error: {}", e),
188
0
            Kind::Io(ref e) => return e.fmt(fmt),
189
        };
190
191
0
        if !debug_data.is_empty() {
192
0
            write!(fmt, " ({:?})", debug_data)?;
193
0
        }
194
195
0
        Ok(())
196
0
    }
197
}
198
199
impl error::Error for Error {}
200
201
#[cfg(test)]
202
mod tests {
203
    use super::Error;
204
    use crate::Reason;
205
206
    #[test]
207
    fn error_from_reason() {
208
        let err = Error::from(Reason::HTTP_1_1_REQUIRED);
209
        assert_eq!(err.reason(), Some(Reason::HTTP_1_1_REQUIRED));
210
    }
211
}