qiniu_http_client/client/response/
error.rs

1use super::{
2    super::super::{EndpointParseError, RetriedStatsInfo, RetryDecision},
3    X_LOG_HEADER_NAME, X_REQ_ID_HEADER_NAME,
4};
5use anyhow::Error as AnyError;
6use assert_impl::assert_impl;
7use qiniu_http::{
8    Extensions, HeaderValue, Metrics, ResponseError as HttpResponseError, ResponseErrorKind as HttpResponseErrorKind,
9    ResponseParts as HttpResponseParts, StatusCode as HttpStatusCode,
10};
11use qiniu_upload_token::ToStringError;
12use serde_json::Error as JsonError;
13use std::{
14    error::Error as StdError,
15    fmt::{self, Debug, Display},
16    io::{Error as IoError, Read, Result as IOResult},
17    mem::take,
18    net::IpAddr,
19    num::NonZeroU16,
20};
21
22#[cfg(feature = "async")]
23use futures::{AsyncRead, AsyncReadExt};
24
25/// HTTP 响应错误类型
26#[derive(Debug, Copy, Clone, PartialEq, Eq)]
27#[non_exhaustive]
28pub enum ErrorKind {
29    /// HTTP 客户端错误
30    HttpError(HttpResponseErrorKind),
31
32    /// 响应状态码错误
33    StatusCodeError(HttpStatusCode),
34
35    /// 未预期的状态码(例如 0 - 199 或 300 - 399,理论上应该由 HttpCaller 自动处理)
36    UnexpectedStatusCode(HttpStatusCode),
37
38    /// 解析响应体错误
39    ParseResponseError,
40
41    /// 响应体提前结束
42    UnexpectedEof,
43
44    /// 疑似响应被劫持
45    MaliciousResponse,
46
47    /// 系统调用失败
48    SystemCallError,
49
50    /// 没有尝试
51    NoTry,
52}
53
54/// HTTP 响应错误
55#[derive(Debug)]
56pub struct Error {
57    kind: ErrorKind,
58    error: AnyError,
59    server_ip: Option<IpAddr>,
60    server_port: Option<NonZeroU16>,
61    metrics: Option<Metrics>,
62    x_headers: XHeaders,
63    response_body_sample: Vec<u8>,
64    retried: Option<RetriedStatsInfo>,
65    retry_decision: Option<RetryDecision>,
66    extensions: Extensions,
67}
68
69const RESPONSE_BODY_SAMPLE_LEN_LIMIT: u64 = 1024;
70
71impl Error {
72    /// 创建 HTTP 响应错误
73    #[inline]
74    pub fn new(kind: ErrorKind, err: impl Into<AnyError>) -> Self {
75        Error {
76            kind,
77            error: err.into(),
78            server_ip: Default::default(),
79            server_port: Default::default(),
80            metrics: Default::default(),
81            x_headers: Default::default(),
82            response_body_sample: Default::default(),
83            retried: Default::default(),
84            retry_decision: Default::default(),
85            extensions: Default::default(),
86        }
87    }
88
89    /// 创建 HTTP 响应错误
90    #[inline]
91    pub fn new_with_msg(kind: ErrorKind, msg: impl Display + Debug + Send + Sync + 'static) -> Self {
92        Error {
93            kind,
94            error: AnyError::msg(msg),
95            server_ip: Default::default(),
96            server_port: Default::default(),
97            metrics: Default::default(),
98            x_headers: Default::default(),
99            response_body_sample: Default::default(),
100            retried: Default::default(),
101            retry_decision: Default::default(),
102            extensions: Default::default(),
103        }
104    }
105
106    /// 设置重试信息
107    #[inline]
108    #[must_use]
109    pub fn retried(mut self, retried: &RetriedStatsInfo) -> Self {
110        self.retried = Some(retried.to_owned());
111        self
112    }
113
114    /// 设置重试决定
115    #[inline]
116    #[must_use]
117    pub fn set_retry_decision(mut self, retry_decision: RetryDecision) -> Self {
118        self.retry_decision = Some(retry_decision);
119        self
120    }
121
122    /// 设置 HTTP 响应信息
123    #[inline]
124    #[must_use]
125    pub fn response_parts(mut self, response_parts: &HttpResponseParts) -> Self {
126        self.server_ip = response_parts.server_ip();
127        self.server_port = response_parts.server_port();
128        self.metrics = extract_metrics_from_response_parts(response_parts);
129        self.x_headers = response_parts.into();
130        self
131    }
132
133    /// 直接设置响应体样本
134    #[inline]
135    pub fn set_response_body_sample(mut self, body: Vec<u8>) -> Self {
136        self.response_body_sample = body;
137        self
138    }
139
140    /// 设置响应体样本
141    ///
142    /// 该方法的异步版本为 [`Error::async_read_response_body_sample`]。
143    #[inline]
144    pub fn read_response_body_sample<R: Read>(mut self, body: R) -> IOResult<Self> {
145        body.take(RESPONSE_BODY_SAMPLE_LEN_LIMIT)
146            .read_to_end(&mut self.response_body_sample)?;
147        Ok(self)
148    }
149
150    /// 异步设置响应体样本
151    #[inline]
152    #[cfg(feature = "async")]
153    pub async fn async_read_response_body_sample<R: AsyncRead + Unpin>(mut self, body: R) -> IOResult<Self> {
154        body.take(RESPONSE_BODY_SAMPLE_LEN_LIMIT)
155            .read_to_end(&mut self.response_body_sample)
156            .await?;
157        Ok(self)
158    }
159
160    /// 获取 HTTP 响应错误类型
161    #[inline]
162    pub fn kind(&self) -> ErrorKind {
163        self.kind
164    }
165
166    /// 获取重试决定
167    #[inline]
168    pub fn retry_decision(&self) -> Option<RetryDecision> {
169        self.retry_decision
170    }
171
172    /// 获取响应体样本
173    #[inline]
174    pub fn response_body_sample(&self) -> &[u8] {
175        &self.response_body_sample
176    }
177
178    /// 获取服务器 IP 地址
179    #[inline]
180    pub fn server_ip(&self) -> Option<IpAddr> {
181        self.server_ip
182    }
183
184    /// 获取服务器端口号
185    #[inline]
186    pub fn server_port(&self) -> Option<NonZeroU16> {
187        self.server_port
188    }
189
190    /// 获取 HTTP 响应指标信息
191    #[inline]
192    pub fn metrics(&self) -> Option<&Metrics> {
193        self.metrics.as_ref()
194    }
195
196    /// 获取 HTTP 响应的 X-Log 信息
197    #[inline]
198    pub fn x_log(&self) -> Option<&HeaderValue> {
199        self.x_headers.x_log.as_ref()
200    }
201
202    /// 获取 HTTP 响应的 X-ReqId 信息
203    #[inline]
204    pub fn x_reqid(&self) -> Option<&HeaderValue> {
205        self.x_headers.x_reqid.as_ref()
206    }
207
208    /// 获取扩展信息
209    #[inline]
210    pub fn extensions(&self) -> &Extensions {
211        &self.extensions
212    }
213
214    /// 获取扩展信息的可变引用
215    #[inline]
216    pub fn extensions_mut(&mut self) -> &mut Extensions {
217        &mut self.extensions
218    }
219
220    pub(in super::super) fn from_http_response_error(
221        mut err: HttpResponseError,
222        x_headers: XHeaders,
223        kind: Option<ErrorKind>,
224    ) -> Self {
225        Self {
226            x_headers,
227            server_ip: err.server_ip(),
228            server_port: err.server_port(),
229            metrics: take(err.metrics_mut()),
230            kind: kind.unwrap_or_else(|| err.kind().into()),
231            error: err.into_inner(),
232            response_body_sample: Default::default(),
233            retried: Default::default(),
234            retry_decision: Default::default(),
235            extensions: Default::default(),
236        }
237    }
238
239    pub(crate) fn from_endpoint_parse_error(error: EndpointParseError, parts: &HttpResponseParts) -> Self {
240        Self::new(ErrorKind::ParseResponseError, error).response_parts(parts)
241    }
242
243    #[allow(dead_code)]
244    fn assert() {
245        assert_impl!(Send: Self);
246        assert_impl!(Sync: Self);
247    }
248}
249
250#[derive(Debug, Default)]
251pub(in super::super) struct XHeaders {
252    x_log: Option<HeaderValue>,
253    x_reqid: Option<HeaderValue>,
254}
255
256impl From<&HttpResponseParts> for XHeaders {
257    #[inline]
258    fn from(parts: &HttpResponseParts) -> Self {
259        Self {
260            x_log: extract_x_log_from_response_parts(parts),
261            x_reqid: extract_x_reqid_from_response_parts(parts),
262        }
263    }
264}
265
266fn extract_x_log_from_response_parts(parts: &HttpResponseParts) -> Option<HeaderValue> {
267    parts.header(X_LOG_HEADER_NAME).cloned()
268}
269
270fn extract_x_reqid_from_response_parts(parts: &HttpResponseParts) -> Option<HeaderValue> {
271    parts.header(X_REQ_ID_HEADER_NAME).cloned()
272}
273
274fn extract_metrics_from_response_parts(parts: &HttpResponseParts) -> Option<Metrics> {
275    parts.metrics().cloned()
276}
277
278impl Display for Error {
279    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
280        write!(f, "[{:?}]", self.kind)?;
281        if let Some(retried) = self.retried.as_ref() {
282            write!(f, "[{retried}]")?;
283        }
284        if let Some(x_reqid) = self.x_headers.x_reqid.as_ref() {
285            write!(f, "[{x_reqid:?}]")?;
286        }
287        if let Some(x_log) = self.x_headers.x_log.as_ref() {
288            write!(f, "[{x_log:?}]")?;
289        }
290        write!(f, " {}", self.error)?;
291        if !self.response_body_sample.is_empty() {
292            write!(f, " [{}]", String::from_utf8_lossy(&self.response_body_sample))?;
293        }
294        Ok(())
295    }
296}
297
298impl StdError for Error {
299    #[inline]
300    fn source(&self) -> Option<&(dyn StdError + 'static)> {
301        Some(self.error.as_ref())
302    }
303}
304
305impl From<HttpResponseError> for Error {
306    #[inline]
307    fn from(error: HttpResponseError) -> Self {
308        Self::from_http_response_error(error, Default::default(), None)
309    }
310}
311
312impl From<HttpResponseErrorKind> for ErrorKind {
313    #[inline]
314    fn from(kind: HttpResponseErrorKind) -> Self {
315        ErrorKind::HttpError(kind)
316    }
317}
318
319impl From<JsonError> for Error {
320    #[inline]
321    fn from(error: JsonError) -> Self {
322        Self::new(ErrorKind::ParseResponseError, error)
323    }
324}
325
326impl From<IoError> for Error {
327    #[inline]
328    fn from(error: IoError) -> Self {
329        Self::new(ErrorKind::HttpError(HttpResponseErrorKind::LocalIoError), error)
330    }
331}
332
333impl From<ToStringError> for Error {
334    #[inline]
335    fn from(error: ToStringError) -> Self {
336        match error {
337            ToStringError::CredentialGetError(err) => err.into(),
338            ToStringError::CallbackError(err) => Self::new(HttpResponseErrorKind::CallbackError.into(), err),
339            err => Self::new(HttpResponseErrorKind::UnknownError.into(), err),
340        }
341    }
342}