rikka 0.1.0

Async HTTP client for native & WASM.
Documentation
use crate::error::{Error, Result};
use crate::request::{Method, Redirect, Request};
use crate::response::Response;
use reqwest::{
    header::{COOKIE, HeaderName, HeaderValue},
    redirect::Policy,
};

pub(crate) async fn send(request: Request) -> Result<Response> {
    let policy = match request.redirect {
        Redirect::Follow => Policy::default(),
        Redirect::Manual => Policy::none(),
    };

    let client = reqwest::Client::builder()
        .redirect(policy)
        .build()
        .map_err(transport)?;

    let mut builder = client.request(method(request.method), &request.url);

    for (key, value) in &request.headers {
        builder = builder.header(
            HeaderName::from_bytes(key.as_bytes()).map_err(|e| Error::Header(e.to_string()))?,
            HeaderValue::from_str(value).map_err(|e| Error::Header(e.to_string()))?,
        );
    }

    if let Some(cookie) = request.cookie_header() {
        builder = builder.header(COOKIE, cookie);
    }

    if let Some(form) = &request.form {
        builder = builder.form(form);
    }

    let response = builder.send().await.map_err(transport)?;
    let status = response.status().as_u16();

    let headers = response
        .headers()
        .iter()
        .filter_map(|(name, value)| {
            value
                .to_str()
                .ok()
                .map(|value| (name.as_str().to_owned(), value.to_owned()))
        })
        .collect();
    let body = response.bytes().await.map_err(transport)?.to_vec();

    Ok(Response::new(status, headers, body))
}

fn method(method: Method) -> reqwest::Method {
    match method {
        Method::Get => reqwest::Method::GET,
        Method::Post => reqwest::Method::POST,
        Method::Put => reqwest::Method::PUT,
        Method::Patch => reqwest::Method::PATCH,
        Method::Delete => reqwest::Method::DELETE,
        Method::Head => reqwest::Method::HEAD,
        Method::Options => reqwest::Method::OPTIONS,
    }
}

fn transport(error: reqwest::Error) -> Error {
    let mut message = error.to_string();
    let mut source = std::error::Error::source(&error);

    while let Some(cause) = source {
        message.push_str(": ");
        message.push_str(&cause.to_string());
        source = cause.source();
    }

    Error::Transport(message)
}