#![cfg(test)]
use std::str::FromStr;
use async_trait::async_trait;
use http::header::CONNECTION;
use reqwest::{NoProxy, Proxy};
static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "-test/", env!("CARGO_PKG_VERSION"),);
pub struct Client {
pub mock_server: mockito::ServerGuard,
pub http_client: reqwest::Client,
}
impl Default for Client {
fn default() -> Self {
let mut b = reqwest::Client::builder();
if let Ok(proxy) = std::env::var("http_proxy") {
b = b.proxy(Proxy::http(proxy).unwrap().no_proxy(NoProxy::from_env()));
}
let http_client = b.user_agent(USER_AGENT).build().unwrap();
Self {
mock_server: mockito::Server::new(),
http_client,
}
}
}
impl Client {
pub fn merge_into_url(&self, path: &str) -> url::Url {
self.mock_server
.url()
.parse::<url::Url>()
.expect("failed to make http URL")
.join(path)
.expect("failed to join URL")
}
pub(crate) async fn new() -> Self {
Self {
http_client: reqwest::Client::new(),
mock_server: mockito::Server::new_async().await,
}
}
}
#[async_trait]
impl crate::http::Client for Client {
#[tracing::instrument(skip(self))]
async fn send_request(
&self,
request: http::Request<crate::http::Body>,
) -> Result<http::Response<crate::http::Body>, crate::Error> {
let method = reqwest::Method::from_str(request.method().as_ref()).unwrap();
let mut req = reqwest::Request::new(
method,
request
.uri()
.to_string()
.parse()
.map_err(crate::Error::Url)?,
);
let it = request
.headers()
.iter()
.filter(|(h, _)| h != &CONNECTION)
.map(move |(header_name, header_value)| {
(
reqwest::header::HeaderName::from_str(header_name.as_str()).unwrap(),
reqwest::header::HeaderValue::from_bytes(header_value.as_bytes()).unwrap(),
)
});
req.headers_mut().extend(it);
*req.body_mut() = Some(reqwest::Body::from(request.body().as_bytes().to_vec()));
let resp = self
.http_client
.execute(req)
.await
.map_err(crate::Error::from)?;
let status = resp.status().as_u16();
let mut whole_resp = http::Response::builder().status(status);
for (name, value) in resp.headers() {
whole_resp = whole_resp.header(name.as_str(), value.as_bytes());
}
let body = resp
.bytes()
.await
.map_err(crate::Error::from)?
.into_iter()
.collect::<Vec<_>>()
.into();
whole_resp.body(body).map_err(crate::Error::Http)
}
}