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