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 //! Definition of `HttpClientErrors` which includes errors that may occur in
15 //! this crate.
16 
17 use core::fmt::{Debug, Display, Formatter};
18 use std::sync::Once;
19 use std::{error, io};
20 
21 /// The structure encapsulates errors that can be encountered when working with
22 /// the HTTP client.
23 ///
24 /// # Examples
25 ///
26 /// ```
27 /// use ylong_http_client::HttpClientError;
28 ///
29 /// let error = HttpClientError::user_aborted();
30 /// ```
31 pub struct HttpClientError {
32     kind: ErrorKind,
33     cause: Cause,
34 }
35 
36 impl HttpClientError {
37     /// Creates a `UserAborted` error.
38     ///
39     /// # Examples
40     ///
41     /// ```
42     /// use ylong_http_client::HttpClientError;
43     ///
44     /// let user_aborted = HttpClientError::user_aborted();
45     /// ```
user_aborted() -> Self46     pub fn user_aborted() -> Self {
47         Self {
48             kind: ErrorKind::UserAborted,
49             cause: Cause::NoReason,
50         }
51     }
52 
53     /// Creates an `Other` error.
54     ///
55     /// # Examples
56     ///
57     /// ```
58     /// use ylong_http_client::HttpClientError;
59     ///
60     /// fn error(error: std::io::Error) {
61     ///     let other = HttpClientError::other(error);
62     /// }
63     /// ```
other<T>(cause: T) -> Self where T: Into<Box<dyn error::Error + Send + Sync>>,64     pub fn other<T>(cause: T) -> Self
65     where
66         T: Into<Box<dyn error::Error + Send + Sync>>,
67     {
68         Self {
69             kind: ErrorKind::Other,
70             cause: Cause::Other(cause.into()),
71         }
72     }
73 
74     /// Gets the `ErrorKind` of this `HttpClientError`.
75     ///
76     /// # Examples
77     ///
78     /// ```
79     /// use ylong_http_client::{ErrorKind, HttpClientError};
80     ///
81     /// let user_aborted = HttpClientError::user_aborted();
82     /// assert_eq!(user_aborted.error_kind(), ErrorKind::UserAborted);
83     /// ```
error_kind(&self) -> ErrorKind84     pub fn error_kind(&self) -> ErrorKind {
85         self.kind
86     }
87 
88     /// Gets the `io::Error` if this `HttpClientError` comes from an
89     /// `io::Error`.
90     ///
91     /// Returns `None` if the `HttpClientError` doesn't come from an
92     /// `io::Error`.
93     ///
94     /// # Examples
95     ///
96     /// ```
97     /// use ylong_http_client::HttpClientError;
98     ///
99     /// let error = HttpClientError::user_aborted().io_error();
100     /// ```
io_error(&self) -> Option<&io::Error>101     pub fn io_error(&self) -> Option<&io::Error> {
102         match self.cause {
103             Cause::Io(ref io) => Some(io),
104             _ => None,
105         }
106     }
107 
108     /// Check whether the cause of the error is dns error
109     ///
110     /// # Examples
111     ///
112     /// ```
113     /// use ylong_http_client::HttpClientError;
114     ///
115     /// assert!(!HttpClientError::user_aborted().is_dns_error())
116     /// ```
is_dns_error(&self) -> bool117     pub fn is_dns_error(&self) -> bool {
118         matches!(self.cause, Cause::Dns(_))
119     }
120 
121     /// Check whether the cause of the error is tls connection error
122     ///
123     /// # Examples
124     ///
125     /// ```
126     /// use ylong_http_client::HttpClientError;
127     ///
128     /// assert!(!HttpClientError::user_aborted().is_tls_error())
129     /// ```
130     #[cfg(feature = "__c_openssl")]
is_tls_error(&self) -> bool131     pub fn is_tls_error(&self) -> bool {
132         matches!(self.cause, Cause::Tls(_))
133     }
134 }
135 
136 impl HttpClientError {
from_error<T>(kind: ErrorKind, err: T) -> Self where T: Into<Box<dyn error::Error + Send + Sync>>,137     pub(crate) fn from_error<T>(kind: ErrorKind, err: T) -> Self
138     where
139         T: Into<Box<dyn error::Error + Send + Sync>>,
140     {
141         Self {
142             kind,
143             cause: Cause::Other(err.into()),
144         }
145     }
146 
147     #[cfg(feature = "__c_openssl")]
from_tls_error<T>(kind: ErrorKind, err: T) -> Self where T: Into<Box<dyn error::Error + Send + Sync>>,148     pub(crate) fn from_tls_error<T>(kind: ErrorKind, err: T) -> Self
149     where
150         T: Into<Box<dyn error::Error + Send + Sync>>,
151     {
152         Self {
153             kind,
154             cause: Cause::Tls(err.into()),
155         }
156     }
157 
from_str(kind: ErrorKind, msg: &'static str) -> Self158     pub(crate) fn from_str(kind: ErrorKind, msg: &'static str) -> Self {
159         Self {
160             kind,
161             cause: Cause::Msg(msg),
162         }
163     }
164 
from_io_error(kind: ErrorKind, err: io::Error) -> Self165     pub(crate) fn from_io_error(kind: ErrorKind, err: io::Error) -> Self {
166         Self {
167             kind,
168             cause: Cause::Io(err),
169         }
170     }
171 
from_dns_error(kind: ErrorKind, err: io::Error) -> Self172     pub(crate) fn from_dns_error(kind: ErrorKind, err: io::Error) -> Self {
173         Self {
174             kind,
175             cause: Cause::Dns(err),
176         }
177     }
178 }
179 
180 impl Debug for HttpClientError {
fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result181     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
182         let mut builder = f.debug_struct("HttpClientError");
183         builder.field("ErrorKind", &self.kind);
184         builder.field("Cause", &self.cause);
185         builder.finish()
186     }
187 }
188 
189 impl Display for HttpClientError {
fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result190     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
191         f.write_str(self.kind.as_str())?;
192         write!(f, ": {}", self.cause)?;
193         Ok(())
194     }
195 }
196 
197 impl error::Error for HttpClientError {
source(&self) -> Option<&(dyn error::Error + 'static)>198     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
199         static mut USER_ABORTED: Option<Box<dyn error::Error>> = None;
200         static ONCE: Once = Once::new();
201 
202         ONCE.call_once(|| {
203             unsafe { USER_ABORTED = Some(Box::new(HttpClientError::user_aborted())) };
204         });
205 
206         if self.kind == ErrorKind::UserAborted {
207             return unsafe { USER_ABORTED.as_ref().map(|e| e.as_ref()) };
208         }
209         None
210     }
211 }
212 
213 /// Error kinds which can indicate the type of `HttpClientError`.
214 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
215 pub enum ErrorKind {
216     /// Errors for decoding response body.
217     BodyDecode,
218 
219     /// Errors for transferring request body or response body.
220     BodyTransfer,
221 
222     /// Errors for using various builder.
223     Build,
224 
225     /// Errors for connecting to a server.
226     Connect,
227 
228     /// Errors for upgrading a connection.
229     ConnectionUpgrade,
230 
231     /// Other error kinds.
232     Other,
233 
234     /// Errors for following redirect.
235     Redirect,
236 
237     /// Errors for sending a request.
238     Request,
239 
240     /// Errors for reaching a timeout.
241     Timeout,
242 
243     /// User raised errors.
244     UserAborted,
245 }
246 
247 impl ErrorKind {
248     /// Gets the string info of this `ErrorKind`.
249     ///
250     /// # Examples
251     ///
252     /// ```
253     /// # use ylong_http_client::ErrorKind;
254     ///
255     /// assert_eq!(ErrorKind::UserAborted.as_str(), "User Aborted Error");
256     /// ```
as_str(&self) -> &'static str257     pub fn as_str(&self) -> &'static str {
258         match self {
259             Self::BodyDecode => "Body Decode Error",
260             Self::BodyTransfer => "Body Transfer Error",
261             Self::Build => "Build Error",
262             Self::Connect => "Connect Error",
263             Self::ConnectionUpgrade => "Connection Upgrade Error",
264             Self::Other => "Other Error",
265             Self::Redirect => "Redirect Error",
266             Self::Request => "Request Error",
267             Self::Timeout => "Timeout Error",
268             Self::UserAborted => "User Aborted Error",
269         }
270     }
271 }
272 
273 pub(crate) enum Cause {
274     NoReason,
275     Dns(io::Error),
276     #[cfg(feature = "__c_openssl")]
277     Tls(Box<dyn error::Error + Send + Sync>),
278     Io(io::Error),
279     Msg(&'static str),
280     Other(Box<dyn error::Error + Send + Sync>),
281 }
282 
283 impl Debug for Cause {
fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result284     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
285         match self {
286             Self::NoReason => write!(f, "No reason"),
287             Self::Dns(err) => Debug::fmt(err, f),
288             #[cfg(feature = "__c_openssl")]
289             Self::Tls(err) => Debug::fmt(err, f),
290             Self::Io(err) => Debug::fmt(err, f),
291             Self::Msg(msg) => write!(f, "{}", msg),
292             Self::Other(err) => Debug::fmt(err, f),
293         }
294     }
295 }
296 
297 impl Display for Cause {
fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result298     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
299         match self {
300             Self::NoReason => write!(f, "No reason"),
301             Self::Dns(err) => Display::fmt(err, f),
302             #[cfg(feature = "__c_openssl")]
303             Self::Tls(err) => Display::fmt(err, f),
304             Self::Io(err) => Display::fmt(err, f),
305             Self::Msg(msg) => write!(f, "{}", msg),
306             Self::Other(err) => Display::fmt(err, f),
307         }
308     }
309 }
310 
311 macro_rules! err_from_other {
312     ($kind: ident, $err: expr) => {{
313         use crate::error::{ErrorKind, HttpClientError};
314 
315         Err(HttpClientError::from_error(ErrorKind::$kind, $err))
316     }};
317 }
318 
319 macro_rules! err_from_io {
320     ($kind: ident, $err: expr) => {{
321         use crate::error::{ErrorKind, HttpClientError};
322 
323         Err(HttpClientError::from_io_error(ErrorKind::$kind, $err))
324     }};
325 }
326 
327 macro_rules! err_from_msg {
328     ($kind: ident, $msg: literal) => {{
329         use crate::error::{ErrorKind, HttpClientError};
330 
331         Err(HttpClientError::from_str(ErrorKind::$kind, $msg))
332     }};
333 }
334 
335 #[cfg(test)]
336 mod ut_util_error {
337     use std::io;
338 
339     use crate::{ErrorKind, HttpClientError};
340 
341     /// UT test cases for `ErrorKind::as_str`.
342     ///
343     /// # Brief
344     /// 1. Transfer ErrorKind to str a by calling `ErrorKind::as_str`.
345     /// 2. Checks if the results are correct.
346     #[test]
ut_err_as_str()347     fn ut_err_as_str() {
348         assert_eq!(ErrorKind::BodyDecode.as_str(), "Body Decode Error");
349         assert_eq!(ErrorKind::BodyTransfer.as_str(), "Body Transfer Error");
350         assert_eq!(ErrorKind::Build.as_str(), "Build Error");
351         assert_eq!(ErrorKind::Connect.as_str(), "Connect Error");
352         assert_eq!(
353             ErrorKind::ConnectionUpgrade.as_str(),
354             "Connection Upgrade Error"
355         );
356         assert_eq!(ErrorKind::Other.as_str(), "Other Error");
357         assert_eq!(ErrorKind::Redirect.as_str(), "Redirect Error");
358         assert_eq!(ErrorKind::Request.as_str(), "Request Error");
359         assert_eq!(ErrorKind::Timeout.as_str(), "Timeout Error");
360         assert_eq!(ErrorKind::UserAborted.as_str(), "User Aborted Error");
361     }
362 
363     /// UT test cases for `HttpClientError` error kind function.
364     ///
365     /// # Brief
366     /// 1. Calls `HttpClientError::user_aborted`, `HttpClientError::other`.
367     /// 2. Checks if the results are correct.
368     #[test]
ut_err_kind()369     fn ut_err_kind() {
370         let user_aborted = HttpClientError::user_aborted();
371         assert_eq!(user_aborted.error_kind(), ErrorKind::UserAborted);
372         let other = HttpClientError::other(user_aborted);
373         assert_eq!(other.error_kind(), ErrorKind::Other);
374     }
375 
376     /// UT test cases for `HttpClientError::from_io_error` function.
377     ///
378     /// # Brief
379     /// 1. Calls `HttpClientError::from_io_error`.
380     /// 2. Checks if the results are correct.
381     #[test]
ut_err_from_io_error()382     fn ut_err_from_io_error() {
383         let error_build =
384             HttpClientError::from_io_error(ErrorKind::Build, io::Error::from(io::ErrorKind::Other));
385         assert_eq!(error_build.error_kind(), ErrorKind::Build);
386     }
387 
388     /// UT test cases for `HttpClientError::from_error` function.
389     ///
390     /// # Brief
391     /// 1. Calls `HttpClientError::from_error`.
392     /// 2. Checks if the results are correct.
393     #[test]
ut_err_from_error()394     fn ut_err_from_error() {
395         let error_build = HttpClientError::from_error(
396             ErrorKind::Build,
397             HttpClientError::from_str(ErrorKind::Request, "test error"),
398         );
399         assert_eq!(error_build.error_kind(), ErrorKind::Build);
400     }
401 
402     /// UT test cases for `HttpClientError::from_str` function.
403     ///
404     /// # Brief
405     /// 1. Calls `HttpClientError::from_str`.
406     /// 2. Checks if the results are correct.
407     #[test]
ut_err_from_str()408     fn ut_err_from_str() {
409         let error_request = HttpClientError::from_str(ErrorKind::Request, "error");
410         assert_eq!(error_request.error_kind(), ErrorKind::Request);
411         let error_timeout = HttpClientError::from_str(ErrorKind::Timeout, "error");
412         assert_eq!(
413             format!("{:?}", error_timeout),
414             "HttpClientError { ErrorKind: Timeout, Cause: error }"
415         );
416         assert_eq!(format!("{error_timeout}"), "Timeout Error: error");
417     }
418 
419     /// UT test cases for `HttpClientError::io_error` function.
420     ///
421     /// # Brief
422     /// 1. Calls `HttpClientError::io_error`.
423     /// 2. Checks if the results are correct.
424     #[test]
ut_client_err_io_error()425     fn ut_client_err_io_error() {
426         let error = HttpClientError::from_io_error(
427             ErrorKind::Request,
428             io::Error::from(io::ErrorKind::BrokenPipe),
429         );
430         assert!(error.io_error().is_some());
431         let error = HttpClientError::from_str(ErrorKind::Request, "error");
432         assert!(error.io_error().is_none());
433     }
434 
435     /// UT test cases for `Debug` of `HttpClientError`.
436     ///
437     /// # Brief
438     /// 1. Calls `HttpClientError::fmt`.
439     /// 2. Checks if the results are correct.
440     #[test]
ut_client_err_debug_fmt()441     fn ut_client_err_debug_fmt() {
442         let error = HttpClientError::from_io_error(
443             ErrorKind::Request,
444             io::Error::from(io::ErrorKind::BrokenPipe),
445         );
446         assert_eq!(
447             format!("{:?}", error),
448             "HttpClientError { ErrorKind: Request, Cause: Kind(BrokenPipe) }"
449         );
450 
451         let error = HttpClientError::user_aborted();
452         assert_eq!(
453             format!("{:?}", error),
454             "HttpClientError { ErrorKind: UserAborted, Cause: No reason }"
455         );
456 
457         let error = HttpClientError::other(io::Error::from(io::ErrorKind::BrokenPipe));
458         assert_eq!(
459             format!("{:?}", error),
460             "HttpClientError { ErrorKind: Other, Cause: Kind(BrokenPipe) }"
461         );
462     }
463 
464     /// UT test cases for `Display` of `HttpClientError`.
465     ///
466     /// # Brief
467     /// 1. Calls `HttpClientError::fmt`.
468     /// 2. Checks if the results are correct.
469     #[test]
ut_client_err_display_fmt()470     fn ut_client_err_display_fmt() {
471         let error = HttpClientError::from_io_error(
472             ErrorKind::Request,
473             io::Error::from(io::ErrorKind::BrokenPipe),
474         );
475         assert_eq!(format!("{}", error), "Request Error: broken pipe");
476 
477         let error = HttpClientError::user_aborted();
478         assert_eq!(format!("{}", error), "User Aborted Error: No reason");
479 
480         let error = HttpClientError::other(io::Error::from(io::ErrorKind::BrokenPipe));
481         assert_eq!(format!("{}", error), "Other Error: broken pipe");
482     }
483 }
484