1use std::{error, fmt};
2
3pub use crate::value::{InvalidHeaderValue, ToStrError};
4
5#[derive(Copy, Clone, PartialEq, Eq, Hash)]
6pub struct Error {
13 inner: ErrorKind,
14}
15
16#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[error("Invalid URI")]
18pub struct InvalidUri {
20 _priv: (),
21}
22
23#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[error("Invalid HTTP header name")]
25pub struct InvalidHeaderName {
27 _priv: (),
28}
29
30#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
31#[error("Invalid status code")]
32pub struct InvalidStatusCode {
37 _priv: (),
38}
39
40#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
41#[error("Invalid HTTP method")]
42pub struct InvalidMethod {
44 _priv: (),
45}
46
47#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
48#[error("Unsupported content encoding")]
49pub struct InvalidContentEncoding {
51 _priv: (),
52}
53
54impl InvalidContentEncoding {
55 pub(crate) const fn new() -> Self {
56 InvalidContentEncoding { _priv: () }
57 }
58}
59
60#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
61enum ErrorKind {
62 StatusCode(InvalidStatusCode),
63 Method(InvalidMethod),
64 Uri(InvalidUri),
65 UriParts(InvalidUri),
66 HeaderName(InvalidHeaderName),
67 HeaderValue(InvalidHeaderValue),
68 ContentEncoding(InvalidContentEncoding),
69 Http,
70}
71
72impl fmt::Debug for Error {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 f.debug_tuple("ntex_http::Error")
75 .field(&self.get_ref())
77 .finish()
78 }
79}
80
81impl fmt::Display for Error {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 fmt::Display::fmt(self.get_ref(), f)
84 }
85}
86
87impl Error {
88 pub fn is<T: error::Error + 'static>(&self) -> bool {
90 self.get_ref().is::<T>()
91 }
92
93 pub fn get_ref(&self) -> &(dyn error::Error + 'static) {
95 match self.inner {
96 ErrorKind::StatusCode(ref e) => e,
97 ErrorKind::Method(ref e) => e,
98 ErrorKind::Uri(ref e) | ErrorKind::UriParts(ref e) => e,
99 ErrorKind::HeaderName(ref e) => e,
100 ErrorKind::HeaderValue(ref e) => e,
101 ErrorKind::ContentEncoding(ref e) => e,
102 ErrorKind::Http => &DEFAULT_ERR,
103 }
104 }
105}
106
107#[derive(thiserror::Error, Copy, Clone, Debug)]
108#[error("{_0}")]
109struct ErrorMessage(&'static str);
110
111const DEFAULT_ERR: ErrorMessage = ErrorMessage("http error");
112
113impl error::Error for Error {
114 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
117 self.get_ref().source()
118 }
119}
120
121impl From<http::status::InvalidStatusCode> for Error {
122 fn from(_: http::status::InvalidStatusCode) -> Error {
123 Error {
124 inner: ErrorKind::StatusCode(InvalidStatusCode { _priv: () }),
125 }
126 }
127}
128
129impl From<http::method::InvalidMethod> for Error {
130 fn from(_: http::method::InvalidMethod) -> Error {
131 Error {
132 inner: ErrorKind::Method(InvalidMethod { _priv: () }),
133 }
134 }
135}
136
137impl From<http::uri::InvalidUri> for Error {
138 fn from(_: http::uri::InvalidUri) -> Error {
139 Error {
140 inner: ErrorKind::Uri(InvalidUri { _priv: () }),
141 }
142 }
143}
144
145impl From<http::uri::InvalidUriParts> for Error {
146 fn from(_: http::uri::InvalidUriParts) -> Error {
147 Error {
148 inner: ErrorKind::UriParts(InvalidUri { _priv: () }),
149 }
150 }
151}
152
153impl From<http::header::InvalidHeaderName> for Error {
154 fn from(_: http::header::InvalidHeaderName) -> Error {
155 Error {
156 inner: ErrorKind::HeaderName(InvalidHeaderName { _priv: () }),
157 }
158 }
159}
160
161impl From<InvalidHeaderValue> for Error {
162 fn from(err: InvalidHeaderValue) -> Error {
163 Error {
164 inner: ErrorKind::HeaderValue(err),
165 }
166 }
167}
168
169impl From<InvalidContentEncoding> for Error {
170 fn from(err: InvalidContentEncoding) -> Error {
171 Error {
172 inner: ErrorKind::ContentEncoding(err),
173 }
174 }
175}
176
177impl From<http::Error> for Error {
178 fn from(err: http::Error) -> Error {
179 let inner = if err.is::<http::status::InvalidStatusCode>() {
180 ErrorKind::StatusCode(InvalidStatusCode { _priv: () })
181 } else if err.is::<http::method::InvalidMethod>() {
182 ErrorKind::Method(InvalidMethod { _priv: () })
183 } else if err.is::<http::uri::InvalidUri>() {
184 ErrorKind::Uri(InvalidUri { _priv: () })
185 } else if err.is::<http::uri::InvalidUriParts>() {
186 ErrorKind::UriParts(InvalidUri { _priv: () })
187 } else if err.is::<http::header::InvalidHeaderName>() {
188 ErrorKind::HeaderName(InvalidHeaderName { _priv: () })
189 } else if err.is::<http::header::InvalidHeaderValue>() {
190 ErrorKind::HeaderValue(InvalidHeaderValue::default())
191 } else {
192 ErrorKind::Http
193 };
194 Error { inner }
195 }
196}
197
198impl From<std::convert::Infallible> for Error {
199 fn from(err: std::convert::Infallible) -> Error {
200 match err {}
201 }
202}
203
204impl From<http::status::InvalidStatusCode> for InvalidStatusCode {
205 fn from(_: http::status::InvalidStatusCode) -> InvalidStatusCode {
206 InvalidStatusCode { _priv: () }
207 }
208}
209
210impl From<http::method::InvalidMethod> for InvalidMethod {
211 fn from(_: http::method::InvalidMethod) -> InvalidMethod {
212 InvalidMethod { _priv: () }
213 }
214}
215
216impl From<http::uri::InvalidUri> for InvalidUri {
217 fn from(_: http::uri::InvalidUri) -> InvalidUri {
218 InvalidUri { _priv: () }
219 }
220}
221
222impl From<http::header::InvalidHeaderName> for InvalidHeaderName {
223 fn from(_: http::header::InvalidHeaderName) -> InvalidHeaderName {
224 InvalidHeaderName { _priv: () }
225 }
226}
227
228impl From<http::header::InvalidHeaderValue> for InvalidHeaderValue {
229 fn from(_: http::header::InvalidHeaderValue) -> InvalidHeaderValue {
230 InvalidHeaderValue::default()
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237 use std::error::Error as StdError;
238
239 #[test]
240 fn inner_http_error() {
241 let e = http::method::Method::from_bytes(b"").unwrap_err();
242 let err: Error = http::Error::from(e).into();
243 let ie = err.get_ref();
244 assert!(!ie.is::<http::Error>());
245 }
246
247 #[test]
248 fn inner_error_is_invalid_status_code() {
249 let e = http::status::StatusCode::from_u16(6666).unwrap_err();
250 let err: Error = e.into();
251 let ie = err.get_ref();
252 assert!(!ie.is::<InvalidHeaderValue>());
253 assert!(ie.is::<InvalidStatusCode>());
254 ie.downcast_ref::<InvalidStatusCode>().unwrap();
255
256 assert!(err.source().is_none());
257 assert!(!err.is::<InvalidHeaderValue>());
258 assert!(err.is::<InvalidStatusCode>());
259
260 let s = format!("{err:?}");
261 assert!(s.starts_with("ntex_http::Error"));
262 }
263
264 #[test]
265 fn inner_error_is_invalid_method() {
266 let e = http::method::Method::from_bytes(b"").unwrap_err();
267 let err: Error = e.into();
268 let ie = err.get_ref();
269 assert!(ie.is::<InvalidMethod>());
270 ie.downcast_ref::<InvalidMethod>().unwrap();
271
272 assert!(err.source().is_none());
273 assert!(err.is::<InvalidMethod>());
274
275 let s = format!("{err:?}");
276 assert!(s.starts_with("ntex_http::Error"));
277 }
278
279 #[test]
280 fn other_http_error() {
281 let e = http::HeaderMap::<http::HeaderValue>::new()
283 .try_reserve(usize::MAX)
284 .unwrap_err();
285 let err: Error = http::Error::from(e).into();
286 assert_eq!(err.to_string(), "http error");
287 assert!(err.source().is_none());
288 assert!(!err.is::<InvalidStatusCode>());
289 assert_eq!(
290 format!("{err:?}"),
291 "ntex_http::Error(ErrorMessage(\"http error\"))"
292 );
293 }
294}