use crate::compression::{brotli_decode, gzip_decode, zlib_decode, zstd_decode};
use crate::enums::char_set::CharSet;
use crate::enums::content_encoding::ContentEncoding;
use crate::enums::http_error::HttpError;
use crate::enums::transfer_encoding::TransferEncoding;
use crate::structures::header::header_list::HttpHeaderList;
use crate::structures::http_multipart_body::HttpMultipartBody;
use crate::utils::parse_util;
use futures::AsyncBufRead;
use std::borrow::Cow;
use super::http_error::HttpErrorKind;
#[derive(Clone)]
pub enum HttpBody {
None,
Content(Vec<u8>),
Multipart(HttpMultipartBody),
}
impl HttpBody {
pub fn as_bytes(&self) -> Cow<'_, [u8]> {
match self {
HttpBody::Content(content) => Cow::Borrowed(content),
HttpBody::Multipart(multipart) => Cow::Owned(multipart.as_bytes()),
HttpBody::None => Cow::Owned(vec![]),
}
}
pub fn get_multipart(&self) -> Option<&HttpMultipartBody> {
match self {
HttpBody::Content(_) => None,
HttpBody::Multipart(multipart) => Some(multipart),
HttpBody::None => None,
}
}
pub fn get_content(&self) -> &[u8] {
if let HttpBody::Content(content) = self {
return content;
}
return &[];
}
pub async fn read<R: AsyncBufRead + Unpin>(
mut reader: R,
headers: &mut HttpHeaderList,
) -> Result<HttpBody, HttpError> {
let mut data: Vec<u8> = vec![];
let transfer_encodings = headers.get_transfer_encodings().to_vec();
for encoding in transfer_encodings.into_iter().rev() {
data = if data.is_empty() {
Self::read_transferred_content(&mut reader, encoding, headers, None).await?
} else {
Self::read_transferred_content(
&mut (&data as &[u8]),
encoding,
headers,
Some(data.len() * 3),
)
.await?
}
}
match headers.get_content_length() {
Some(content_length) => {
data = if data.is_empty() {
parse_util::read_content_with_known_length(&mut reader, content_length).await?
} else {
parse_util::read_content_with_known_length(
&mut (&data as &[u8]),
content_length,
)
.await?
};
}
_ => (),
}
let content_encodings = headers.get_content_encoding();
for encoding in content_encodings.iter().rev().copied() {
data = if data.is_empty() {
Self::read_compressed_content(&mut reader, encoding, None).await?
} else {
Self::read_compressed_content(&mut (&data as &[u8]), encoding, Some(data.len() * 3))
.await?
}
}
let (is_multipart, subtype, boundary) = headers.get_multipart_data();
if is_multipart {
let multipart_body = if data.is_empty() {
HttpMultipartBody::read(reader, &boundary, &subtype).await?
} else {
HttpMultipartBody::read(&mut (&data as &[u8]), &boundary, &subtype).await?
};
return Ok(HttpBody::Multipart(multipart_body));
} else {
Ok(HttpBody::Content(data))
}
}
async fn read_transferred_content(
mut reader: impl AsyncBufRead + Unpin,
encoding: TransferEncoding,
headers: &mut HttpHeaderList,
size_hint: Option<usize>,
) -> Result<Vec<u8>, HttpError> {
let data = match encoding {
TransferEncoding::Chunked => parse_util::read_chunked(&mut reader, headers).await?,
TransferEncoding::Deflate => zlib_decode(&mut reader, size_hint).await?,
TransferEncoding::Gzip => {
let (_, result) = gzip_decode(reader).await?;
result
}
_ => return Err(HttpErrorKind::Unimplemented.into()),
};
Ok(data)
}
async fn read_compressed_content<R: AsyncBufRead + Unpin>(
mut reader: R,
encoding: ContentEncoding,
size_hint: Option<usize>,
) -> Result<Vec<u8>, HttpError> {
let data = match encoding {
ContentEncoding::Deflate => zlib_decode(&mut reader, size_hint).await?,
ContentEncoding::Gzip => {
let (_, result) = gzip_decode(reader).await?;
result
}
ContentEncoding::Br => brotli_decode(&mut reader).await?,
ContentEncoding::ZStandard => zstd_decode(&mut reader).await?,
_ => return Err(HttpErrorKind::Unimplemented.into()),
};
Ok(data)
}
pub fn to_string(&self, charset: CharSet) -> Result<String, HttpError> {
let bytes = self.as_bytes();
let str = parse_util::bytes_to_string(&bytes, charset)?;
Ok(str.to_string())
}
}