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)
}