1use super::{decode::decode_body, new_headers};
2use http_types::{
3 self, Mime, StatusCode, Version,
4 headers::{self, HeaderName, HeaderValues, ToHeaderValues},
5};
6
7use http_types::{Headers, headers::CONTENT_TYPE};
8use serde::de::DeserializeOwned;
9
10use std::fmt;
11use std::ops::Index;
12
13#[derive(Clone, serde::Serialize, serde::Deserialize)]
15pub struct Response<Body> {
16 version: Option<http_types::Version>,
17 status: http_types::StatusCode,
18 #[serde(with = "header_serde")]
19 headers: Headers,
20 body: Option<Body>,
21}
22
23impl<Body> Response<Body> {
24 pub(crate) async fn new(mut res: super::ResponseAsync) -> crate::Result<Response<Vec<u8>>> {
26 let body = res.body_bytes().await?;
27 let status = res.status();
28
29 if status.is_client_error() || status.is_server_error() {
30 return Err(crate::HttpError::Http {
31 code: status,
32 message: status.to_string(),
33 body: Some(body),
34 });
35 }
36
37 let headers: &Headers = res.as_ref();
38 let headers = headers.clone();
39
40 Ok(Response {
41 status: res.status(),
42 headers,
43 version: res.version(),
44 body: Some(body),
45 })
46 }
47
48 #[allow(clippy::missing_const_for_fn)]
57 pub fn status(&self) -> StatusCode {
58 self.status
59 }
60
61 #[allow(clippy::missing_const_for_fn)]
71 pub fn version(&self) -> Option<Version> {
72 self.version
73 }
74
75 pub fn header(&self, name: impl Into<HeaderName>) -> Option<&HeaderValues> {
86 self.headers.get(name)
87 }
88
89 pub fn header_mut(&mut self, name: impl Into<HeaderName>) -> Option<&mut HeaderValues> {
91 self.headers.get_mut(name)
92 }
93
94 pub fn remove_header(&mut self, name: impl Into<HeaderName>) -> Option<HeaderValues> {
96 self.headers.remove(name)
97 }
98
99 pub fn insert_header(&mut self, key: impl Into<HeaderName>, value: impl ToHeaderValues) {
101 self.headers.insert(key, value);
102 }
103
104 pub fn append_header(&mut self, key: impl Into<HeaderName>, value: impl ToHeaderValues) {
106 self.headers.append(key, value);
107 }
108
109 #[must_use]
111 pub fn iter(&self) -> headers::Iter<'_> {
112 self.headers.iter()
113 }
114
115 #[must_use]
118 pub fn iter_mut(&mut self) -> headers::IterMut<'_> {
119 self.headers.iter_mut()
120 }
121
122 #[must_use]
124 pub fn header_names(&self) -> headers::Names<'_> {
125 self.headers.names()
126 }
127
128 #[must_use]
130 pub fn header_values(&self) -> headers::Values<'_> {
131 self.headers.values()
132 }
133
134 pub fn content_type(&self) -> Option<Mime> {
154 self.header(CONTENT_TYPE)?.last().as_str().parse().ok()
155 }
156
157 #[allow(clippy::missing_const_for_fn)]
158 pub fn body(&self) -> Option<&Body> {
159 self.body.as_ref()
160 }
161
162 #[allow(clippy::missing_const_for_fn)]
163 pub fn take_body(&mut self) -> Option<Body> {
164 self.body.take()
165 }
166
167 pub fn with_body<NewBody>(self, body: NewBody) -> Response<NewBody> {
168 Response {
169 body: Some(body),
170 headers: self.headers,
171 status: self.status,
172 version: self.version,
173 }
174 }
175}
176
177impl<'a, Body> IntoIterator for &'a Response<Body> {
178 type Item = (&'a headers::HeaderName, &'a headers::HeaderValues);
179 type IntoIter = headers::Iter<'a>;
180 fn into_iter(self) -> Self::IntoIter {
181 self.iter()
182 }
183}
184
185impl<'a, Body> IntoIterator for &'a mut Response<Body> {
186 type Item = (&'a headers::HeaderName, &'a mut headers::HeaderValues);
187 type IntoIter = headers::IterMut<'a>;
188 fn into_iter(self) -> Self::IntoIter {
189 self.iter_mut()
190 }
191}
192
193impl Response<Vec<u8>> {
194 pub(crate) fn new_with_status(status: http_types::StatusCode) -> Self {
195 let headers = new_headers();
196
197 Self {
198 status,
199 headers,
200 version: None,
201 body: None,
202 }
203 }
204
205 pub fn body_bytes(&mut self) -> crate::Result<Vec<u8>> {
227 self.body.take().ok_or_else(|| crate::HttpError::Http {
228 code: self.status(),
229 message: "Body had no bytes".to_string(),
230 body: None,
231 })
232 }
233
234 pub fn body_string(&mut self) -> crate::Result<String> {
268 let bytes = self.body_bytes()?;
269
270 let mime = self.content_type();
271 let claimed_encoding = mime
272 .as_ref()
273 .and_then(|mime| mime.param("charset"))
274 .map(std::string::ToString::to_string);
275 Ok(decode_body(bytes, claimed_encoding.as_deref())?)
276 }
277
278 pub fn body_json<T: DeserializeOwned>(&mut self) -> crate::Result<T> {
307 let body_bytes = self.body_bytes()?;
308 serde_json::from_slice(&body_bytes).map_err(crate::HttpError::from)
309 }
310}
311
312impl<Body> AsRef<http_types::Headers> for Response<Body> {
313 fn as_ref(&self) -> &http_types::Headers {
314 &self.headers
315 }
316}
317
318impl<Body> AsMut<http_types::Headers> for Response<Body> {
319 fn as_mut(&mut self) -> &mut http_types::Headers {
320 &mut self.headers
321 }
322}
323
324impl<Body> fmt::Debug for Response<Body> {
325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326 f.debug_struct("Response")
327 .field("version", &self.version)
328 .field("status", &self.status)
329 .field("headers", &self.headers)
330 .finish_non_exhaustive()
331 }
332}
333
334impl<Body> Index<HeaderName> for Response<Body> {
335 type Output = HeaderValues;
336
337 #[inline]
343 fn index(&self, name: HeaderName) -> &HeaderValues {
344 &self.headers[name]
345 }
346}
347
348impl<Body> Index<&str> for Response<Body> {
349 type Output = HeaderValues;
350
351 #[inline]
357 fn index(&self, name: &str) -> &HeaderValues {
358 &self.headers[name]
359 }
360}
361
362impl<Body> PartialEq for Response<Body>
363where
364 Body: PartialEq,
365{
366 fn eq(&self, other: &Self) -> bool {
367 self.version == other.version
368 && self.status == other.status
369 && self.headers.iter().zip(other.headers.iter()).all(
370 |((lhs_name, lhs_values), (rhs_name, rhs_values))| {
371 lhs_name == rhs_name
372 && lhs_values
373 .iter()
374 .zip(rhs_values.iter())
375 .all(|(lhs, rhs)| lhs == rhs)
376 },
377 )
378 && self.body == other.body
379 }
380}
381
382impl<Body> Eq for Response<Body> where Body: Eq {}
383
384#[cfg(feature = "http-compat")]
385impl<Body> TryInto<http::Response<Body>> for Response<Body> {
386 type Error = ();
387
388 fn try_into(self) -> Result<http::Response<Body>, Self::Error> {
389 let mut response = http::Response::new(self.body.ok_or(())?);
390
391 if let Some(version) = self.version {
392 let version = match version {
393 Version::Http0_9 => Some(http::Version::HTTP_09),
394 Version::Http1_0 => Some(http::Version::HTTP_10),
395 Version::Http1_1 => Some(http::Version::HTTP_11),
396 Version::Http2_0 => Some(http::Version::HTTP_2),
397 Version::Http3_0 => Some(http::Version::HTTP_3),
398 _ => None,
399 };
400
401 if let Some(version) = version {
402 *response.version_mut() = version;
403 }
404 }
405
406 let mut headers = self.headers;
407 headers_to_hyperium_headers(&mut headers, response.headers_mut());
408
409 Ok(response)
410 }
411}
412
413#[cfg(feature = "http-compat")]
414fn headers_to_hyperium_headers(headers: &mut Headers, hyperium_headers: &mut http::HeaderMap) {
415 for (name, values) in headers {
416 let name = format!("{name}").into_bytes();
417 let name = http::header::HeaderName::from_bytes(&name).unwrap();
418
419 for value in values.iter() {
420 let value = format!("{value}").into_bytes();
421 let value = http::header::HeaderValue::from_bytes(&value).unwrap();
422 hyperium_headers.append(&name, value);
423 }
424 }
425}
426
427mod header_serde {
428 use crate::{http::Headers, response::new_headers};
429 use http_types::headers::{HeaderName, HeaderValue};
430 use serde::{Deserializer, Serializer, de::Error};
431
432 pub fn serialize<S>(headers: &Headers, serializer: S) -> Result<S::Ok, S::Error>
433 where
434 S: Serializer,
435 {
436 serializer.collect_map(headers.iter().map(|(name, values)| {
437 (
438 name.as_str(),
439 values.iter().map(HeaderValue::as_str).collect::<Vec<_>>(),
440 )
441 }))
442 }
443
444 pub fn deserialize<'de, D>(deserializer: D) -> Result<Headers, D::Error>
445 where
446 D: Deserializer<'de>,
447 {
448 let strs = <Vec<(String, Vec<String>)> as serde::Deserialize>::deserialize(deserializer)?;
449
450 let mut headers = new_headers();
451
452 for (name, values) in strs {
453 let name = HeaderName::from_string(name).map_err(D::Error::custom)?;
454 for value in values {
455 headers.append(&name, value);
456 }
457 }
458
459 Ok(headers)
460 }
461}