qiniu_http_client/client/response/
mod.rs

1use assert_impl::assert_impl;
2use qiniu_credential::HeaderValue;
3use qiniu_http::{
4    Response as HttpResponse, ResponseErrorKind as HttpResponseErrorKind, ResponseParts as HttpResponseParts,
5};
6use serde::{de::DeserializeOwned, Deserialize};
7use std::{
8    io::copy as io_copy,
9    mem::take,
10    ops::{Deref, DerefMut},
11};
12use tap::TapFallible;
13
14mod error;
15pub(super) use error::XHeaders;
16pub use error::{Error as ResponseError, ErrorKind as ResponseErrorKind};
17
18/// API 响应结果
19pub type ApiResult<T> = Result<T, ResponseError>;
20
21use qiniu_http::SyncResponseBody;
22
23#[cfg(feature = "async")]
24use futures::io::copy as async_io_copy;
25
26#[cfg(feature = "async")]
27use qiniu_http::AsyncResponseBody;
28
29const X_REQ_ID_HEADER_NAME: &str = "x-reqid";
30const X_LOG_HEADER_NAME: &str = "x-log";
31
32/// HTTP 响应
33#[derive(Default, Debug)]
34pub struct Response<B>(HttpResponse<B>);
35
36impl<B> Response<B> {
37    /// 转换为 HTTP 响应体
38    #[inline]
39    pub fn into_body(self) -> B {
40        self.0.into_body()
41    }
42
43    /// 转换为 HTTP 响应信息和响应体
44    #[inline]
45    pub fn into_parts_and_body(self) -> (HttpResponseParts, B) {
46        self.0.into_parts_and_body()
47    }
48
49    /// 根据 HTTP 响应信息和响应体创建 HTTP 响应
50    #[inline]
51    pub fn from_parts_and_body(parts: HttpResponseParts, body: B) -> Self {
52        Self(HttpResponse::from_parts_and_body(parts, body))
53    }
54
55    /// 获取 HTTP 响应的 X-ReqId 信息
56    #[inline]
57    pub fn x_reqid(&self) -> Option<&HeaderValue> {
58        self.header(X_REQ_ID_HEADER_NAME)
59    }
60
61    /// 获取 HTTP 响应的 X-Log 信息
62    #[inline]
63    pub fn x_log(&self) -> Option<&HeaderValue> {
64        self.header(X_LOG_HEADER_NAME)
65    }
66}
67
68impl<B> From<HttpResponse<B>> for Response<B> {
69    #[inline]
70    fn from(response: HttpResponse<B>) -> Self {
71        Self(response)
72    }
73}
74
75impl<B> From<Response<B>> for HttpResponse<B> {
76    #[inline]
77    fn from(response: Response<B>) -> Self {
78        response.0
79    }
80}
81
82impl<B> Deref for Response<B> {
83    type Target = HttpResponse<B>;
84
85    #[inline]
86    fn deref(&self) -> &Self::Target {
87        &self.0
88    }
89}
90
91impl<B> DerefMut for Response<B> {
92    #[inline]
93    fn deref_mut(&mut self) -> &mut Self::Target {
94        &mut self.0
95    }
96}
97
98impl<B: Sync + Send> Response<B> {
99    #[allow(dead_code)]
100    fn assert() {
101        assert_impl!(Send: Self);
102        assert_impl!(Sync: Self);
103    }
104}
105
106impl Response<SyncResponseBody> {
107    /// 解析 JSON 响应体
108    pub fn parse_json<T: DeserializeOwned>(self) -> ApiResult<Response<T>> {
109        let x_headers = XHeaders::from(self.parts());
110        let mut got_body = Vec::new();
111        let json_response = self
112            .fulfill()?
113            .try_map_body(|mut body| parse_json_from_slice(&body).tap_err(|_| got_body = take(&mut body)))
114            .map_err(|err| {
115                ResponseError::from_http_response_error(
116                    err.into_response_error(HttpResponseErrorKind::ReceiveError),
117                    x_headers,
118                    Some(ResponseErrorKind::ParseResponseError),
119                )
120                .set_response_body_sample(got_body)
121            })?;
122        Ok(Response::from(json_response))
123    }
124
125    pub(super) fn fulfill(self) -> ApiResult<HttpResponse<Vec<u8>>> {
126        let x_headers = XHeaders::from(self.parts());
127        self.0
128            .try_map_body(|mut body| {
129                let mut buf = Vec::new();
130                io_copy(&mut body, &mut buf).map(|_| buf)
131            })
132            .map_err(|err| {
133                ResponseError::from_http_response_error(
134                    err.into_response_error(HttpResponseErrorKind::ReceiveError),
135                    x_headers,
136                    None,
137                )
138            })
139    }
140}
141
142#[cfg(feature = "async")]
143impl Response<AsyncResponseBody> {
144    /// 异步解析 JSON 响应体
145    pub async fn parse_json<T: DeserializeOwned>(self) -> ApiResult<Response<T>> {
146        let x_headers = XHeaders::from(self.parts());
147        let mut got_body = Vec::new();
148        let json_response = self
149            .fulfill()
150            .await?
151            .try_map_body(|mut body| parse_json_from_slice(&body).tap_err(|_| got_body = take(&mut body)))
152            .map_err(|err| {
153                ResponseError::from_http_response_error(
154                    err.into_response_error(HttpResponseErrorKind::ReceiveError),
155                    x_headers,
156                    Some(ResponseErrorKind::ParseResponseError),
157                )
158                .set_response_body_sample(got_body)
159            })?;
160        Ok(Response::from(json_response))
161    }
162
163    pub(super) async fn fulfill(self) -> ApiResult<HttpResponse<Vec<u8>>> {
164        let x_headers = XHeaders::from(self.parts());
165        self.0
166            .try_async_map_body(|mut body| async move {
167                let mut buf = Vec::new();
168                async_io_copy(&mut body, &mut buf).await.map(|_| buf)
169            })
170            .await
171            .map_err(|err| {
172                ResponseError::from_http_response_error(
173                    err.into_response_error(HttpResponseErrorKind::ReceiveError),
174                    x_headers,
175                    None,
176                )
177            })
178    }
179}
180
181fn parse_json_from_slice<'a, T: Deserialize<'a>>(v: &'a [u8]) -> serde_json::Result<T> {
182    // Sometimes the API are supposed to response with JSON but actually Empty!
183    if v.is_empty() {
184        serde_json::from_slice(b"{}".as_slice())
185    } else {
186        serde_json::from_slice(v)
187    }
188}
189
190/// 阻塞 HTTP 响应
191pub type SyncResponse = Response<SyncResponseBody>;
192
193/// 异步 HTTP 响应
194#[cfg(feature = "async")]
195#[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
196pub type AsyncResponse = Response<AsyncResponseBody>;