use crate::enums::char_set::CharSet;
use crate::enums::header::content_type::ContentType;
use crate::enums::header::http_header::HttpHeader;
use crate::enums::http_error::{HttpError, HttpErrorKind};
use crate::enums::media_type::media_type::MediaType;
use crate::structures::header::header_list::HttpHeaderList;
use crate::utils::string_util::latin1_to_string;
use async_regex::CRLF;
use futures::{AsyncBufRead, AsyncBufReadExt, AsyncReadExt};
use std::borrow::Cow;
use std::io;
use std::str::FromStr;
pub(crate) fn bytes_to_string(bytes: &[u8], charset: CharSet) -> Result<Cow<'_, str>, HttpError> {
match charset {
CharSet::Utf8 => Ok(String::from_utf8_lossy(bytes)),
CharSet::Iso88591 => Ok(Cow::Owned(latin1_to_string(&bytes))),
CharSet::Unknown(_) => return Err(HttpErrorKind::UnsupportedCharset.into()),
}
}
pub async fn read_chunked<R: AsyncBufRead + Unpin>(
mut reader: R,
headers: &mut HttpHeaderList,
) -> Result<Vec<u8>, HttpError> {
let mut response = Vec::with_capacity(4096);
let mut line: String = String::new();
let mut cr_lf_buf = [0u8; 2];
loop {
reader.read_line(&mut line).await?;
let chunk_size = line.trim_end().split(";").next().unwrap(); let chunk_size = usize::from_str_radix(&chunk_size, 16).unwrap();
line.clear();
if chunk_size == 0 {
break;
}
response.reserve(chunk_size);
let start = response.len();
response.resize(start + chunk_size, 0);
let buf = reader.fill_buf().await?;
debug_assert_ne!(buf.len(), 0);
reader.read_exact(&mut response[start..]).await?;
reader.read_exact(&mut cr_lf_buf).await?;
debug_assert_eq!(cr_lf_buf, CRLF);
}
loop {
reader.read_line(&mut line).await?;
if line.len() == 2 && line.as_bytes() == CRLF {
break;
}
headers.add(HttpHeader::from_str(&line[..line.len() - 2])?);
line.clear();
}
Ok(response)
}
pub async fn read_content_with_known_length(
mut reader: impl AsyncBufRead + Unpin,
content_length: usize,
) -> io::Result<Vec<u8>> {
let mut buf = vec![0; content_length];
reader.read_exact(&mut buf).await?;
Ok(buf)
}
pub async fn read_content_with_length(
mut reader: impl AsyncBufRead + Unpin,
content_length: Option<usize>,
) -> io::Result<Vec<u8>> {
let buf = match content_length {
Some(size) => read_content_with_known_length(reader, size).await?,
None => {
let mut buf = Vec::new();
reader.read_to_end(&mut buf).await?;
buf
}
};
Ok(buf)
}
pub fn get_multipart_data(content_types: &[ContentType]) -> (bool, String, String) {
let mut is_multipart = false;
let mut subtype = String::new();
let mut boundary = String::new();
for content_type in content_types.iter() {
match content_type {
ContentType::MediaType(MediaType::Multipart(st)) => {
subtype = st.to_string();
is_multipart = true;
}
ContentType::Boundary(b) => boundary = b[1..b.len() - 1].to_string(),
_ => (),
}
}
(is_multipart, subtype, boundary)
}