use bytes::Bytes;
use http::header::{HeaderMap, CONTENT_ENCODING};
use http::{Response as HttpResponse, StatusCode};
use http2::RecvStream;
use std::io::Read;
use crate::error::{Error, Result};
pub struct Response {
inner: HttpResponse<RecvStream>,
url: String,
}
impl Response {
pub fn new(inner: HttpResponse<RecvStream>, url: String) -> Self {
Self { inner, url }
}
pub fn status(&self) -> StatusCode {
self.inner.status()
}
pub fn headers(&self) -> &HeaderMap {
self.inner.headers()
}
pub fn url(&self) -> &str {
&self.url
}
pub(crate) fn set_url(&mut self, url: String) {
self.url = url;
}
pub async fn bytes(self) -> Result<Bytes> {
let (parts, mut body_stream) = self.inner.into_parts();
let mut data = Vec::new();
while let Some(chunk) = body_stream.data().await {
let chunk = chunk.map_err(Error::Http2)?;
data.extend_from_slice(chunk.as_ref());
}
let encoding = parts
.headers
.get(CONTENT_ENCODING)
.and_then(|v| v.to_str().ok())
.unwrap_or("");
if encoding.contains("br") {
let mut decoder = brotli_decompressor::Decompressor::new(&data[..], 4096);
let mut decoded = Vec::new();
decoder
.read_to_end(&mut decoded)
.map_err(|e| Error::Connect(std::io::Error::other(e.to_string())))?;
Ok(Bytes::from(decoded))
} else if encoding.contains("zstd") {
let decoded = zstd::decode_all(&data[..])
.map_err(|e| Error::Connect(std::io::Error::other(e.to_string())))?;
Ok(Bytes::from(decoded))
} else if encoding.contains("gzip") {
let mut decoder = flate2::read::GzDecoder::new(&data[..]);
let mut decoded = Vec::new();
decoder
.read_to_end(&mut decoded)
.map_err(|e| Error::Connect(std::io::Error::other(e.to_string())))?;
Ok(Bytes::from(decoded))
} else {
Ok(Bytes::from(data))
}
}
pub async fn text(self) -> Result<String> {
let bytes = self.bytes().await?;
String::from_utf8(bytes.to_vec())
.map_err(|e| Error::Connect(std::io::Error::other(e.to_string())))
}
pub async fn json<T: serde::de::DeserializeOwned>(self) -> Result<T> {
let bytes = self.bytes().await?;
serde_json::from_slice(&bytes)
.map_err(|e| Error::Connect(std::io::Error::other(e.to_string())))
}
}