diff --git a/ylong_http_client/src/async_impl/client.rs b/ylong_http_client/src/async_impl/client.rs index 6b4f6e472ff4b3018611300c2da8f1988166af51..7f43f8006291dd50a405da0b2546c2114b0134f2 100644 --- a/ylong_http_client/src/async_impl/client.rs +++ b/ylong_http_client/src/async_impl/client.rs @@ -171,8 +171,11 @@ impl Client { ) -> Result, HttpClientError> { let response = self .send_request_with_uri(request.uri().clone(), request) - .await?; - self.redirect_request(response, request).await + .await + .map_err(|e| e.add_request_info(request.part().clone()))?; + self.redirect_request(response, request) + .await + .map_err(|e| e.add_request_info(request.part().clone())) } async fn redirect_request( @@ -191,15 +194,21 @@ impl Client { &redirected_list, &response, request, - )?; + ) + .map_err(|e| e.add_redirect_info(redirected_list.clone()))?; - UriFormatter::new().format(&mut dst_uri)?; + UriFormatter::new() + .format(&mut dst_uri) + .map_err(|e| e.add_redirect_info(redirected_list.clone()))?; let _ = request .headers_mut() .insert("Host", dst_uri.authority().unwrap().to_string().as_bytes()); match trigger { TriggerKind::NextLink => { - response = self.send_request_with_uri(dst_uri.clone(), request).await?; + response = self + .send_request_with_uri(dst_uri.clone(), request) + .await + .map_err(|e| e.add_redirect_info(redirected_list.clone()))?; continue; } TriggerKind::Stop => { diff --git a/ylong_http_client/src/error.rs b/ylong_http_client/src/error.rs index e6d6a69db109723907276b8f830ca858ccedbddb..26466d2403fb3c6995443ca71efd065315e710ce 100644 --- a/ylong_http_client/src/error.rs +++ b/ylong_http_client/src/error.rs @@ -17,11 +17,16 @@ use core::fmt::{Debug, Display, Formatter}; use std::error::Error; +use ylong_http::request::uri::Uri; +use ylong_http::request::RequestPart; + /// The structure encapsulates errors that can be encountered when working with /// the HTTP client. pub struct HttpClientError { kind: ErrorKind, cause: Option>, + request: Option>, + redirect: Option>, } impl HttpClientError { @@ -38,6 +43,8 @@ impl HttpClientError { Self { kind: ErrorKind::UserAborted, cause: None, + request: None, + redirect: None, } } @@ -56,6 +63,8 @@ impl HttpClientError { Self { kind: ErrorKind::Other, cause: cause.map(|e| e.into()), + request: None, + redirect: None, } } @@ -80,6 +89,8 @@ impl HttpClientError { Self { kind, cause: cause.map(|e| e.into()), + request: None, + redirect: None, } } @@ -87,8 +98,20 @@ impl HttpClientError { Self { kind, cause: Some(CauseMessage::new(message).into()), + request: None, + redirect: None, } } + + pub(crate) fn add_request_info(mut self, request: RequestPart) -> Self { + self.request = Some(request.into()); + self + } + + pub(crate) fn add_redirect_info(mut self, redirect: Vec) -> Self { + self.redirect = Some(redirect); + self + } } impl Debug for HttpClientError { @@ -98,18 +121,19 @@ impl Debug for HttpClientError { if let Some(ref cause) = self.cause { builder.field("Cause", cause); } + if let Some(ref part) = self.request { + builder.field("Request", part); + } + if let Some(ref vec) = self.redirect { + builder.field("Redirection", vec); + } builder.finish() } } impl Display for HttpClientError { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - f.write_str(self.kind.as_str())?; - - if let Some(ref cause) = self.cause { - write!(f, ": {cause}")?; - } - Ok(()) + Debug::fmt(self, f) } } @@ -197,3 +221,43 @@ impl Display for CauseMessage { } impl Error for CauseMessage {} + +#[cfg(test)] +mod ut_error { + use ylong_http::request::uri::Uri; + use ylong_http::request::Request; + + use crate::{ErrorKind, HttpClientError}; + + /// UT test cases for `HttpClientError::add_redirect_info` and + /// `HttpClientError::add_request_info`. + /// + /// # Brief + /// 1. Creates a `HttpClientError` by calling + /// `HttpClientError::new_with_message`. + /// 2. Calls `add_redirect_info`, `add_request_info` on the HttpClientError + /// respectively. + /// 3. Checks if the result is as expected. + #[test] + fn ut_error_info() { + let request = Request::get("127.0.0.1:3000").body("".as_bytes()).unwrap(); + let redirections = vec![ + Uri::try_from("www.test1.com:80").unwrap(), + Uri::try_from("www.test2.com:80").unwrap(), + Uri::try_from("www.test3.com:80").unwrap(), + ]; + let error = + HttpClientError::new_with_message(ErrorKind::Redirect, "test redirect error !") + .add_redirect_info(redirections) + .add_request_info(request.part().clone()); + + assert_eq!( + format!("{error:?}"), + r#"HttpClientError { ErrorKind: Redirect, Cause: test redirect error !, Request: RequestPart { uri: Uri { scheme: None, authority: Some(Authority { host: Host("127.0.0.1"), port: Some(Port("3000")) }), path: None, query: None }, method: Method(Get), version: Version(Http11), headers: Headers { map: {} } }, Redirection: [Uri { scheme: None, authority: Some(Authority { host: Host("www.test1.com"), port: Some(Port("80")) }), path: None, query: None }, Uri { scheme: None, authority: Some(Authority { host: Host("www.test2.com"), port: Some(Port("80")) }), path: None, query: None }, Uri { scheme: None, authority: Some(Authority { host: Host("www.test3.com"), port: Some(Port("80")) }), path: None, query: None }] }"# + ); + assert_eq!( + format!("{error}"), + r#"HttpClientError { ErrorKind: Redirect, Cause: test redirect error !, Request: RequestPart { uri: Uri { scheme: None, authority: Some(Authority { host: Host("127.0.0.1"), port: Some(Port("3000")) }), path: None, query: None }, method: Method(Get), version: Version(Http11), headers: Headers { map: {} } }, Redirection: [Uri { scheme: None, authority: Some(Authority { host: Host("www.test1.com"), port: Some(Port("80")) }), path: None, query: None }, Uri { scheme: None, authority: Some(Authority { host: Host("www.test2.com"), port: Some(Port("80")) }), path: None, query: None }, Uri { scheme: None, authority: Some(Authority { host: Host("www.test3.com"), port: Some(Port("80")) }), path: None, query: None }] }"# + ); + } +}