use bytes::Bytes;
use http::header::{HeaderMap, CONTENT_ENCODING};
use http::StatusCode;
use http2::RecvStream;
use std::io::Read;
use crate::error::{Error, Result};
pub enum ResponseBody {
Http2(RecvStream),
Http3(Vec<u8>),
}
pub struct Response {
status: StatusCode,
headers: HeaderMap,
body: ResponseBody,
url: String,
}
impl Response {
pub fn new(status: StatusCode, headers: HeaderMap, body: ResponseBody, url: String) -> Self {
Self {
status,
headers,
body,
url,
}
}
pub fn status(&self) -> StatusCode {
self.status
}
pub fn headers(&self) -> &HeaderMap {
&self.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 mut data = Vec::new();
match self.body {
ResponseBody::Http2(mut body_stream) => {
while let Some(chunk) = body_stream.data().await {
let chunk = chunk.map_err(Error::Http2)?;
data.extend_from_slice(chunk.as_ref());
}
}
ResponseBody::Http3(body_data) => {
data = body_data;
}
}
let encoding = self
.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())))
}
}