ntex_http/
error.rs

1use std::{error, fmt, result};
2
3pub use crate::value::{InvalidHeaderValue, ToStrError};
4
5#[derive(Clone)]
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/// A `Result` typedef to use with the `http::Error` type
48pub type Result<T> = result::Result<T, Error>;
49
50#[derive(Clone)]
51enum ErrorKind {
52    StatusCode(InvalidStatusCode),
53    Method(InvalidMethod),
54    Uri(InvalidUri),
55    UriParts(InvalidUri),
56    HeaderName(InvalidHeaderName),
57    HeaderValue(InvalidHeaderValue),
58    Http,
59}
60
61impl fmt::Debug for Error {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.debug_tuple("ntex_http::Error")
64            // Skip the noise of the ErrorKind enum
65            .field(&self.get_ref())
66            .finish()
67    }
68}
69
70impl fmt::Display for Error {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        fmt::Display::fmt(self.get_ref(), f)
73    }
74}
75
76impl Error {
77    /// Return true if the underlying error has the same type as T.
78    pub fn is<T: error::Error + 'static>(&self) -> bool {
79        self.get_ref().is::<T>()
80    }
81
82    /// Return a reference to the lower level, inner error.
83    pub fn get_ref(&self) -> &(dyn error::Error + 'static) {
84        use self::ErrorKind::*;
85
86        match self.inner {
87            StatusCode(ref e) => e,
88            Method(ref e) => e,
89            Uri(ref e) => e,
90            UriParts(ref e) => e,
91            HeaderName(ref e) => e,
92            HeaderValue(ref e) => e,
93            Http => &DEFAULT_ERR,
94        }
95    }
96}
97
98#[derive(thiserror::Error, Copy, Clone, Debug)]
99#[error("{_0}")]
100struct ErrorMessage(&'static str);
101
102const DEFAULT_ERR: ErrorMessage = ErrorMessage("http error");
103
104impl error::Error for Error {
105    // Return any available cause from the inner error. Note the inner error is
106    // not itself the cause.
107    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
108        self.get_ref().source()
109    }
110}
111
112impl From<http::status::InvalidStatusCode> for Error {
113    fn from(_: http::status::InvalidStatusCode) -> Error {
114        Error {
115            inner: ErrorKind::StatusCode(InvalidStatusCode { _priv: () }),
116        }
117    }
118}
119
120impl From<http::method::InvalidMethod> for Error {
121    fn from(_: http::method::InvalidMethod) -> Error {
122        Error {
123            inner: ErrorKind::Method(InvalidMethod { _priv: () }),
124        }
125    }
126}
127
128impl From<http::uri::InvalidUri> for Error {
129    fn from(_: http::uri::InvalidUri) -> Error {
130        Error {
131            inner: ErrorKind::Uri(InvalidUri { _priv: () }),
132        }
133    }
134}
135
136impl From<http::uri::InvalidUriParts> for Error {
137    fn from(_: http::uri::InvalidUriParts) -> Error {
138        Error {
139            inner: ErrorKind::UriParts(InvalidUri { _priv: () }),
140        }
141    }
142}
143
144impl From<http::header::InvalidHeaderName> for Error {
145    fn from(_: http::header::InvalidHeaderName) -> Error {
146        Error {
147            inner: ErrorKind::HeaderName(InvalidHeaderName { _priv: () }),
148        }
149    }
150}
151
152impl From<InvalidHeaderValue> for Error {
153    fn from(err: InvalidHeaderValue) -> Error {
154        Error {
155            inner: ErrorKind::HeaderValue(err),
156        }
157    }
158}
159
160impl From<http::Error> for Error {
161    fn from(err: http::Error) -> Error {
162        let inner = if err.is::<http::status::InvalidStatusCode>() {
163            ErrorKind::StatusCode(InvalidStatusCode { _priv: () })
164        } else if err.is::<http::method::InvalidMethod>() {
165            ErrorKind::Method(InvalidMethod { _priv: () })
166        } else if err.is::<http::uri::InvalidUri>() {
167            ErrorKind::Uri(InvalidUri { _priv: () })
168        } else if err.is::<http::header::InvalidHeaderName>() {
169            ErrorKind::HeaderName(InvalidHeaderName { _priv: () })
170        } else if err.is::<http::header::InvalidHeaderValue>() {
171            ErrorKind::HeaderValue(InvalidHeaderValue::default())
172        } else {
173            ErrorKind::Http
174        };
175        Error { inner }
176    }
177}
178
179impl From<std::convert::Infallible> for Error {
180    fn from(err: std::convert::Infallible) -> Error {
181        match err {}
182    }
183}
184
185impl From<http::status::InvalidStatusCode> for InvalidStatusCode {
186    fn from(_: http::status::InvalidStatusCode) -> InvalidStatusCode {
187        InvalidStatusCode { _priv: () }
188    }
189}
190
191impl From<http::method::InvalidMethod> for InvalidMethod {
192    fn from(_: http::method::InvalidMethod) -> InvalidMethod {
193        InvalidMethod { _priv: () }
194    }
195}
196
197impl From<http::uri::InvalidUri> for InvalidUri {
198    fn from(_: http::uri::InvalidUri) -> InvalidUri {
199        InvalidUri { _priv: () }
200    }
201}
202
203impl From<http::header::InvalidHeaderName> for InvalidHeaderName {
204    fn from(_: http::header::InvalidHeaderName) -> InvalidHeaderName {
205        InvalidHeaderName { _priv: () }
206    }
207}
208
209impl From<http::header::InvalidHeaderValue> for InvalidHeaderValue {
210    fn from(_: http::header::InvalidHeaderValue) -> InvalidHeaderValue {
211        InvalidHeaderValue::default()
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218    use std::error::Error as StdError;
219
220    #[test]
221    fn inner_http_error() {
222        let e = http::method::Method::from_bytes(b"").unwrap_err();
223        let err: Error = http::Error::from(e).into();
224        let ie = err.get_ref();
225        assert!(!ie.is::<http::Error>());
226    }
227
228    #[test]
229    fn inner_error_is_invalid_status_code() {
230        let e = http::status::StatusCode::from_u16(6666).unwrap_err();
231        let err: Error = e.into();
232        let ie = err.get_ref();
233        assert!(!ie.is::<InvalidHeaderValue>());
234        assert!(ie.is::<InvalidStatusCode>());
235        ie.downcast_ref::<InvalidStatusCode>().unwrap();
236
237        assert!(err.source().is_none());
238        assert!(!err.is::<InvalidHeaderValue>());
239        assert!(err.is::<InvalidStatusCode>());
240
241        let s = format!("{err:?}");
242        assert!(s.starts_with("ntex_http::Error"));
243    }
244
245    #[test]
246    fn inner_error_is_invalid_method() {
247        let e = http::method::Method::from_bytes(b"").unwrap_err();
248        let err: Error = e.into();
249        let ie = err.get_ref();
250        assert!(ie.is::<InvalidMethod>());
251        ie.downcast_ref::<InvalidMethod>().unwrap();
252
253        assert!(err.source().is_none());
254        assert!(err.is::<InvalidMethod>());
255
256        let s = format!("{err:?}");
257        assert!(s.starts_with("ntex_http::Error"));
258    }
259}