httpio 0.2.4

A transport-agnostic, async HTTP/1.1 client library for any runtime.
Documentation
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(); //ignoring chunk extensions
        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)
}