Skip to main content

ntex_http/
error.rs

1use std::{error, fmt};
2
3pub use crate::value::{InvalidHeaderValue, ToStrError};
4
5#[derive(Copy, Clone, PartialEq, Eq, Hash)]
6/// A generic "error" for HTTP connections
7///
8/// This error type is less specific than the error returned from other
9/// functions in this crate, but all other errors can be converted to this
10/// error. Consumers of this crate can typically consume and work with this form
11/// of error for conversions with the `?` operator.
12pub struct Error {
13    inner: ErrorKind,
14}
15
16#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[error("Invalid URI")]
18/// An error resulting from a failed attempt to construct a URI.
19pub struct InvalidUri {
20    _priv: (),
21}
22
23#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[error("Invalid HTTP header name")]
25/// A possible error when converting a `HeaderName` from another type.
26pub struct InvalidHeaderName {
27    _priv: (),
28}
29
30#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
31#[error("Invalid status code")]
32/// A possible error value when converting a `StatusCode` from a `u16` or `&str`.
33///
34/// This error indicates that the supplied input was not a valid number, was less
35/// than 100, or was greater than 999.
36pub struct InvalidStatusCode {
37    _priv: (),
38}
39
40#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
41#[error("Invalid HTTP method")]
42/// A possible error value when converting `Method` from bytes.
43pub struct InvalidMethod {
44    _priv: (),
45}
46
47#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
48enum ErrorKind {
49    StatusCode(InvalidStatusCode),
50    Method(InvalidMethod),
51    Uri(InvalidUri),
52    UriParts(InvalidUri),
53    HeaderName(InvalidHeaderName),
54    HeaderValue(InvalidHeaderValue),
55    Http,
56}
57
58impl fmt::Debug for Error {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        f.debug_tuple("ntex_http::Error")
61            // Skip the noise of the ErrorKind enum
62            .field(&self.get_ref())
63            .finish()
64    }
65}
66
67impl fmt::Display for Error {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        fmt::Display::fmt(self.get_ref(), f)
70    }
71}
72
73impl Error {
74    /// Return true if the underlying error has the same type as T.
75    pub fn is<T: error::Error + 'static>(&self) -> bool {
76        self.get_ref().is::<T>()
77    }
78
79    /// Return a reference to the lower level, inner error.
80    pub fn get_ref(&self) -> &(dyn error::Error + 'static) {
81        match self.inner {
82            ErrorKind::StatusCode(ref e) => e,
83            ErrorKind::Method(ref e) => e,
84            ErrorKind::Uri(ref e) | ErrorKind::UriParts(ref e) => e,
85            ErrorKind::HeaderName(ref e) => e,
86            ErrorKind::HeaderValue(ref e) => e,
87            ErrorKind::Http => &DEFAULT_ERR,
88        }
89    }
90}
91
92#[derive(thiserror::Error, Copy, Clone, Debug)]
93#[error("{_0}")]
94struct ErrorMessage(&'static str);
95
96const DEFAULT_ERR: ErrorMessage = ErrorMessage("http error");
97
98impl error::Error for Error {
99    // Return any available cause from the inner error. Note the inner error is
100    // not itself the cause.
101    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
102        self.get_ref().source()
103    }
104}
105
106impl From<http::status::InvalidStatusCode> for Error {
107    fn from(_: http::status::InvalidStatusCode) -> Error {
108        Error {
109            inner: ErrorKind::StatusCode(InvalidStatusCode { _priv: () }),
110        }
111    }
112}
113
114impl From<http::method::InvalidMethod> for Error {
115    fn from(_: http::method::InvalidMethod) -> Error {
116        Error {
117            inner: ErrorKind::Method(InvalidMethod { _priv: () }),
118        }
119    }
120}
121
122impl From<http::uri::InvalidUri> for Error {
123    fn from(_: http::uri::InvalidUri) -> Error {
124        Error {
125            inner: ErrorKind::Uri(InvalidUri { _priv: () }),
126        }
127    }
128}
129
130impl From<http::uri::InvalidUriParts> for Error {
131    fn from(_: http::uri::InvalidUriParts) -> Error {
132        Error {
133            inner: ErrorKind::UriParts(InvalidUri { _priv: () }),
134        }
135    }
136}
137
138impl From<http::header::InvalidHeaderName> for Error {
139    fn from(_: http::header::InvalidHeaderName) -> Error {
140        Error {
141            inner: ErrorKind::HeaderName(InvalidHeaderName { _priv: () }),
142        }
143    }
144}
145
146impl From<InvalidHeaderValue> for Error {
147    fn from(err: InvalidHeaderValue) -> Error {
148        Error {
149            inner: ErrorKind::HeaderValue(err),
150        }
151    }
152}
153
154impl From<http::Error> for Error {
155    fn from(err: http::Error) -> Error {
156        let inner = if err.is::<http::status::InvalidStatusCode>() {
157            ErrorKind::StatusCode(InvalidStatusCode { _priv: () })
158        } else if err.is::<http::method::InvalidMethod>() {
159            ErrorKind::Method(InvalidMethod { _priv: () })
160        } else if err.is::<http::uri::InvalidUri>() {
161            ErrorKind::Uri(InvalidUri { _priv: () })
162        } else if err.is::<http::header::InvalidHeaderName>() {
163            ErrorKind::HeaderName(InvalidHeaderName { _priv: () })
164        } else if err.is::<http::header::InvalidHeaderValue>() {
165            ErrorKind::HeaderValue(InvalidHeaderValue::default())
166        } else {
167            ErrorKind::Http
168        };
169        Error { inner }
170    }
171}
172
173impl From<std::convert::Infallible> for Error {
174    fn from(err: std::convert::Infallible) -> Error {
175        match err {}
176    }
177}
178
179impl From<http::status::InvalidStatusCode> for InvalidStatusCode {
180    fn from(_: http::status::InvalidStatusCode) -> InvalidStatusCode {
181        InvalidStatusCode { _priv: () }
182    }
183}
184
185impl From<http::method::InvalidMethod> for InvalidMethod {
186    fn from(_: http::method::InvalidMethod) -> InvalidMethod {
187        InvalidMethod { _priv: () }
188    }
189}
190
191impl From<http::uri::InvalidUri> for InvalidUri {
192    fn from(_: http::uri::InvalidUri) -> InvalidUri {
193        InvalidUri { _priv: () }
194    }
195}
196
197impl From<http::header::InvalidHeaderName> for InvalidHeaderName {
198    fn from(_: http::header::InvalidHeaderName) -> InvalidHeaderName {
199        InvalidHeaderName { _priv: () }
200    }
201}
202
203impl From<http::header::InvalidHeaderValue> for InvalidHeaderValue {
204    fn from(_: http::header::InvalidHeaderValue) -> InvalidHeaderValue {
205        InvalidHeaderValue::default()
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212    use std::error::Error as StdError;
213
214    #[test]
215    fn inner_http_error() {
216        let e = http::method::Method::from_bytes(b"").unwrap_err();
217        let err: Error = http::Error::from(e).into();
218        let ie = err.get_ref();
219        assert!(!ie.is::<http::Error>());
220    }
221
222    #[test]
223    fn inner_error_is_invalid_status_code() {
224        let e = http::status::StatusCode::from_u16(6666).unwrap_err();
225        let err: Error = e.into();
226        let ie = err.get_ref();
227        assert!(!ie.is::<InvalidHeaderValue>());
228        assert!(ie.is::<InvalidStatusCode>());
229        ie.downcast_ref::<InvalidStatusCode>().unwrap();
230
231        assert!(err.source().is_none());
232        assert!(!err.is::<InvalidHeaderValue>());
233        assert!(err.is::<InvalidStatusCode>());
234
235        let s = format!("{err:?}");
236        assert!(s.starts_with("ntex_http::Error"));
237    }
238
239    #[test]
240    fn inner_error_is_invalid_method() {
241        let e = http::method::Method::from_bytes(b"").unwrap_err();
242        let err: Error = e.into();
243        let ie = err.get_ref();
244        assert!(ie.is::<InvalidMethod>());
245        ie.downcast_ref::<InvalidMethod>().unwrap();
246
247        assert!(err.source().is_none());
248        assert!(err.is::<InvalidMethod>());
249
250        let s = format!("{err:?}");
251        assert!(s.starts_with("ntex_http::Error"));
252    }
253}