use std::convert::TryFrom;
use std::fmt;
use std::net::SocketAddr;
use std::{borrow::Cow, collections::HashMap};
use bytes::Bytes;
use encoding_rs::{Encoding, UTF_8};
use http::{HeaderMap, HeaderValue, StatusCode};
use mime::Mime;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use url::Url;
#[cfg(feature = "cookies")]
use crate::cookie;
use crate::Version;
use super::request::header_map_from_hashmap;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SerializableResponse {
pub body: Vec<u8>,
pub status: u16,
pub version: Version,
pub headers: HashMap<String, Vec<String>>,
pub url: Url,
pub redirect_chain: Vec<Url>,
}
impl TryFrom<SerializableResponse> for HttpResponse {
type Error = crate::Error;
fn try_from(res: SerializableResponse) -> Result<Self, Self::Error> {
Ok(HttpResponse {
body: res.body,
status: StatusCode::from_u16(res.status).unwrap(),
version: res.version,
headers: header_map_from_hashmap(res.headers),
url: res.url,
redirect_chain: res.redirect_chain,
})
}
}
pub struct HttpResponse {
pub body: Vec<u8>,
pub status: StatusCode,
pub version: Version,
pub headers: HeaderMap<HeaderValue>,
pub url: Url,
pub redirect_chain: Vec<Url>,
}
impl HttpResponse {
#[inline]
pub fn status(&self) -> StatusCode {
self.status
}
#[inline]
pub fn version(&self) -> Version {
self.version
}
#[inline]
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
#[inline]
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
pub fn content_length(&self) -> Option<u64> {
Some(self.body().len() as u64)
}
#[cfg(feature = "cookies")]
#[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
pub fn cookies(&self) -> impl Iterator<Item = cookie::Cookie> {
cookie::extract_response_cookies(self.headers()).filter_map(Result::ok)
}
#[inline]
pub fn url(&self) -> &Url {
&self.url
}
pub fn remote_addr(&self) -> Option<SocketAddr> {
None
}
pub fn text(self) -> crate::Result<String> {
self.text_with_charset("utf-8")
}
pub fn text_with_charset(self, default_encoding: &str) -> crate::Result<String> {
let content_type = self
.headers()
.get(crate::header::CONTENT_TYPE)
.and_then(|value| value.to_str().ok())
.and_then(|value| value.parse::<Mime>().ok());
let encoding_name = content_type
.as_ref()
.and_then(|mime| mime.get_param("charset").map(|charset| charset.as_str()))
.unwrap_or(default_encoding);
let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
let full = self.body();
let (text, _, _) = encoding.decode(&full);
if let Cow::Owned(s) = text {
return Ok(s);
}
unsafe {
Ok(String::from_utf8_unchecked(full.to_vec()))
}
}
pub fn json<T: DeserializeOwned>(self) -> crate::Result<T> {
let full = self.body();
serde_json::from_slice(&full).map_err(crate::error::decode)
}
pub fn bytes(self) -> crate::Result<Bytes> {
Bytes::try_from(self.body)
.map_err(|e| crate::Error::new(crate::error::Kind::Decode, Some(e)))
}
pub fn body(&self) -> Vec<u8> {
self.body.clone()
}
pub fn chunk(&mut self) -> crate::Result<Option<Bytes>> {
Ok(None)
}
pub fn error_for_status(self) -> crate::Result<Self> {
let status = self.status();
if status.is_client_error() || status.is_server_error() {
Err(crate::error::status_code(self.url, status))
} else {
Ok(self)
}
}
pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
let status = self.status();
if status.is_client_error() || status.is_server_error() {
Err(crate::error::status_code(self.url.clone(), status))
} else {
Ok(self)
}
}
#[cfg(feature = "blocking")]
pub(crate) fn body_mut(&mut self) -> &mut Decoder {
self.res.body_mut()
}
}
impl fmt::Debug for HttpResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Response")
.field("url", self.url())
.field("status", &self.status())
.field("headers", self.headers())
.finish()
}
}