use crate::http::{
self,
headers::{self, HeaderName, HeaderValues, ToHeaderValues},
Body, Error, Mime, StatusCode, Version,
};
use async_std::io::BufRead;
use futures_util::io::AsyncRead;
use serde::de::DeserializeOwned;
use std::fmt;
use std::io;
use std::ops::Index;
use std::pin::Pin;
use std::task::{Context, Poll};
pin_project_lite::pin_project! {
pub struct Response {
#[pin]
res: http_client::Response,
}
}
impl Response {
pub(crate) fn new(res: http_client::Response) -> Self {
Self { res }
}
pub fn status(&self) -> StatusCode {
self.res.status()
}
pub fn version(&self) -> Option<Version> {
self.res.version()
}
pub fn header(&self, name: impl Into<HeaderName>) -> Option<&HeaderValues> {
self.res.header(name)
}
pub fn header_mut(&mut self, name: impl Into<HeaderName>) -> Option<&mut HeaderValues> {
self.res.header_mut(name)
}
pub fn remove_header(&mut self, name: impl Into<HeaderName>) -> Option<HeaderValues> {
self.res.remove_header(name)
}
pub fn insert_header(&mut self, key: impl Into<HeaderName>, value: impl ToHeaderValues) {
self.res.insert_header(key, value);
}
pub fn append_header(&mut self, key: impl Into<HeaderName>, value: impl ToHeaderValues) {
self.res.append_header(key, value);
}
#[must_use]
pub fn iter(&self) -> headers::Iter<'_> {
self.res.iter()
}
#[must_use]
pub fn iter_mut(&mut self) -> headers::IterMut<'_> {
self.res.iter_mut()
}
#[must_use]
pub fn header_names(&self) -> headers::Names<'_> {
self.res.header_names()
}
#[must_use]
pub fn header_values(&self) -> headers::Values<'_> {
self.res.header_values()
}
#[must_use]
pub fn ext<T: Send + Sync + 'static>(&self) -> Option<&T> {
self.res.ext().get()
}
pub fn insert_ext<T: Send + Sync + 'static>(&mut self, val: T) {
self.res.ext_mut().insert(val);
}
pub fn content_type(&self) -> Option<Mime> {
self.res.content_type()
}
pub fn len(&self) -> Option<usize> {
self.res.len()
}
pub fn is_empty(&self) -> Option<bool> {
self.res.is_empty()
}
pub fn set_body(&mut self, body: impl Into<Body>) {
self.res.set_body(body);
}
pub fn take_body(&mut self) -> Body {
self.res.take_body()
}
pub fn swap_body(&mut self, body: &mut Body) {
self.res.swap_body(body)
}
pub async fn body_bytes(&mut self) -> crate::Result<Vec<u8>> {
self.res.body_bytes().await
}
pub async fn body_string(&mut self) -> crate::Result<String> {
let bytes = self.body_bytes().await?;
let mime = self.content_type();
let claimed_encoding = mime
.as_ref()
.and_then(|mime| mime.param("charset"))
.map(|name| name.to_string());
decode_body(bytes, claimed_encoding.as_deref())
}
pub async fn body_json<T: DeserializeOwned>(&mut self) -> crate::Result<T> {
let body_bytes = self.body_bytes().await?;
Ok(serde_json::from_slice(&body_bytes).map_err(crate::Error::from)?)
}
pub async fn body_form<T: serde::de::DeserializeOwned>(&mut self) -> crate::Result<T> {
self.res.body_form().await
}
}
impl From<http::Response> for Response {
fn from(response: http::Response) -> Self {
Self::new(response)
}
}
impl Into<http::Response> for Response {
fn into(self) -> http::Response {
self.res
}
}
impl AsRef<http::Response> for Response {
fn as_ref(&self) -> &http::Response {
&self.res
}
}
impl AsMut<http::Response> for Response {
fn as_mut(&mut self) -> &mut http::Response {
&mut self.res
}
}
impl AsyncRead for Response {
#[allow(missing_doc_code_examples)]
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<Result<usize, io::Error>> {
Pin::new(&mut self.res).poll_read(cx, buf)
}
}
impl BufRead for Response {
#[allow(missing_doc_code_examples)]
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&'_ [u8]>> {
let this = self.project();
this.res.poll_fill_buf(cx)
}
fn consume(mut self: Pin<&mut Self>, amt: usize) {
Pin::new(&mut self.res).consume(amt)
}
}
impl fmt::Debug for Response {
#[allow(missing_doc_code_examples)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Response")
.field("response", &self.res)
.finish()
}
}
impl Index<HeaderName> for Response {
type Output = HeaderValues;
#[inline]
fn index(&self, name: HeaderName) -> &HeaderValues {
&self.res[name]
}
}
impl Index<&str> for Response {
type Output = HeaderValues;
#[inline]
fn index(&self, name: &str) -> &HeaderValues {
&self.res[name]
}
}
#[derive(Clone)]
pub struct DecodeError {
pub encoding: String,
pub data: Vec<u8>,
}
impl fmt::Debug for DecodeError {
#[allow(missing_doc_code_examples)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DecodeError")
.field("encoding", &self.encoding)
.field("data", &format!("{} bytes", self.data.len()))
.finish()
}
}
impl fmt::Display for DecodeError {
#[allow(missing_doc_code_examples)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "could not decode body as {}", &self.encoding)
}
}
impl std::error::Error for DecodeError {}
#[allow(dead_code)]
fn is_utf8_encoding(encoding_label: &str) -> bool {
encoding_label.eq_ignore_ascii_case("utf-8")
|| encoding_label.eq_ignore_ascii_case("utf8")
|| encoding_label.eq_ignore_ascii_case("unicode-1-1-utf-8")
}
#[cfg(not(feature = "encoding"))]
fn decode_body(bytes: Vec<u8>, content_encoding: Option<&str>) -> Result<String, Error> {
if is_utf8_encoding(content_encoding.unwrap_or("utf-8")) {
Ok(String::from_utf8(bytes).map_err(|err| {
let err = DecodeError {
encoding: "utf-8".to_string(),
data: err.into_bytes(),
};
io::Error::new(io::ErrorKind::InvalidData, err)
})?)
} else {
let err = DecodeError {
encoding: "utf-8".to_string(),
data: bytes,
};
Err(io::Error::new(io::ErrorKind::InvalidData, err).into())
}
}
#[cfg(all(feature = "encoding", not(target_arch = "wasm32")))]
fn decode_body(bytes: Vec<u8>, content_encoding: Option<&str>) -> Result<String, Error> {
use encoding_rs::Encoding;
use std::borrow::Cow;
let content_encoding = content_encoding.unwrap_or("utf-8");
if let Some(encoding) = Encoding::for_label(content_encoding.as_bytes()) {
let (decoded, encoding_used, failed) = encoding.decode(&bytes);
if failed {
let err = DecodeError {
encoding: encoding_used.name().into(),
data: bytes,
};
Err(io::Error::new(io::ErrorKind::InvalidData, err).into())
} else {
Ok(match decoded {
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(bytes) },
Cow::Owned(string) => string,
})
}
} else {
let err = DecodeError {
encoding: content_encoding.to_string(),
data: bytes,
};
Err(io::Error::new(io::ErrorKind::InvalidData, err).into())
}
}
#[cfg(all(feature = "encoding", target_arch = "wasm32"))]
fn decode_body(mut bytes: Vec<u8>, content_encoding: Option<&str>) -> Result<String, Error> {
use web_sys::TextDecoder;
let content_encoding = content_encoding.unwrap_or("utf-8").to_ascii_lowercase();
if is_utf8_encoding(&content_encoding) {
return String::from_utf8(bytes)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err).into());
}
let decoder = TextDecoder::new_with_label(&content_encoding).unwrap();
Ok(decoder.decode_with_u8_array(&mut bytes).map_err(|_| {
let err = DecodeError {
encoding: content_encoding.to_string(),
data: bytes,
};
io::Error::new(io::ErrorKind::InvalidData, err)
})?)
}
#[cfg(test)]
mod decode_tests {
use super::decode_body;
#[test]
fn utf8() {
let input = "Rød grød med fløde";
assert_eq!(
decode_body(input.as_bytes().to_vec(), Some("utf-8")).unwrap(),
input,
"Parses utf-8"
);
}
#[test]
fn default_utf8() {
let input = "Rød grød med fløde";
assert_eq!(
decode_body(input.as_bytes().to_vec(), None).unwrap(),
input,
"Defaults to utf-8"
);
}
#[test]
fn euc_kr() {
let input = vec![
0xb3, 0xbb, 0x20, 0xc7, 0xb0, 0xc0, 0xb8, 0xb7, 0xce, 0x20, 0xb5, 0xb9, 0xbe, 0xc6,
0xbf, 0xc0, 0xb6, 0xf3, 0x2c, 0x20, 0xb3, 0xbb, 0x20, 0xbe, 0xc8, 0xbf, 0xa1, 0xbc,
0xad, 0x20, 0xc0, 0xe1, 0xb5, 0xe9, 0xb0, 0xc5, 0xb6, 0xf3,
];
let result = decode_body(input, Some("euc-kr"));
if cfg!(feature = "encoding") {
assert_eq!(result.unwrap(), "내 품으로 돌아오라, 내 안에서 잠들거라");
} else {
assert!(result.is_err(), "Only utf-8 is supported");
}
}
}