httpio 0.2.4

A transport-agnostic, async HTTP/1.1 client library for any runtime.
Documentation
use std::error::Error;
use std::net::ToSocketAddrs;

use async_foundation::net::tcp_stream::TcpStream;
use futures::executor::block_on;
use futures::io::BufReader;
use httpio::enums::char_set::CharSet;
use httpio::enums::header::http_header::HttpHeader;
use httpio::enums::http_body::HttpBody;
use httpio::enums::http_request_method::HttpRequestMethod;
use httpio::enums::http_version::HttpVersion;
use httpio::structures::connection::http_connection::HttpConnection;
use httpio::utils::http_header_field_name;
use httpio::utils::url::Url;
use log::enums::log_level::LogLevel;
use log::structs::logger::Logger;

use httpio::compression::set_compression_provider;

#[path = "../shared/mod.rs"]
mod shared;
use shared::Flate2Decoder;

const TARGET: &str = "http://example.com";
const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15";
const ACCEPT: &str = "text/html, application/json, */*;q=0.8";
const ACCEPT_LANGUAGE: &str =
    "en,es-ES;q=0.9,es;q=0.8,it;q=0.7,de;q=0.6,cs;q=0.5,ru;q=0.4,ja;q=0.3,uk;q=0.2";

fn main() -> Result<(), Box<dyn Error>> {
    set_compression_provider(Box::new(Flate2Decoder))
        .map_err(|e| format!("Failed to set compression provider: {}", e))?;

    block_on(main_async())
}

async fn main_async() -> Result<(), Box<dyn Error>> {
    let url: Url = TARGET.into();
    let host_name = url.domain.to_string();
    let addrs = url
        .to_socket_addrs()?
        .filter(|addr| addr.is_ipv4())
        .collect::<Vec<_>>();
    let (reader, writer) = TcpStream::connect(&addrs[0])?.split();
    let reader = BufReader::new(reader);
    let mut connection = HttpConnection::new(
        reader,
        writer,
        HttpVersion::Http11,
        &host_name,
        request_headers().into(),
        logger("{time} [{level}][HTTP] {message}".to_string()),
    )?;
    connection
        .send_request(
            HttpRequestMethod::Get,
            &url.path_query(),
            vec![].into(),
            HttpBody::None,
        )
        .await?;
    let message = connection.read_response().await?;
    let charset = message.headers.get_charset(CharSet::Iso88591);
    let _message: String = message.body.to_string(charset)?;
    Ok(())
}

fn request_headers() -> Vec<HttpHeader> {
    vec![
        HttpHeader::new(http_header_field_name::USER_AGENT, USER_AGENT),
        HttpHeader::new(http_header_field_name::ACCEPT, ACCEPT),
        HttpHeader::Generic {
            key: "Cache-Control".to_string(),
            value: "no-cache".to_string(),
        },
        HttpHeader::Generic {
            key: "Accept-Language".to_string(),
            value: ACCEPT_LANGUAGE.to_string(),
        },
    ]
}

fn logger(format: String) -> Logger {
    Logger::with_level(LogLevel::Debug)
        .stdout()
        .time_format("%H:%M:%S%.3f")
        .format(format)
}