use futures_lite::{io::Cursor, AsyncRead};
use isahc::{config::RedirectPolicy, prelude::*, HttpClient, Request};
use once_cell::sync::Lazy;
use openidconnect::{HttpRequest, HttpResponse};
#[derive(Debug, thiserror::Error)]
pub(crate) enum Error {
#[error("Isahc request failed")]
Isahc(#[source] isahc::Error),
#[error("HTTP error")]
Http(#[source] http::Error),
#[error("I/O error")]
Io(#[source] std::io::Error),
}
static HTTP_CLIENT: Lazy<HttpClient> = Lazy::new(|| {
HttpClient::builder()
.redirect_policy(RedirectPolicy::None)
.build()
.expect("Unable to initialize Isahc client.")
});
pub(crate) async fn http_client(openid_request: HttpRequest) -> Result<HttpResponse, Error> {
let mut request_builder = Request::builder()
.method(openid_request.method)
.uri(openid_request.url.as_str());
for (name, value) in &openid_request.headers {
request_builder = request_builder.header(name.as_str(), value.as_bytes());
}
let request = request_builder
.body(openid_request.body)
.map_err(Error::Http)?;
let response = HTTP_CLIENT
.send_async(request)
.await
.map_err(Error::Isahc)?;
Ok(HttpResponse {
status_code: response.status(),
headers: response.headers().to_owned(),
body: to_bytes(response.into_body()).await?,
})
}
async fn to_bytes<R>(reader: R) -> Result<Vec<u8>, Error>
where
R: AsyncRead + Unpin,
{
let mut writer = Cursor::new(Vec::new());
futures_lite::io::copy(reader, &mut writer)
.await
.map_err(Error::Io)?;
Ok(writer.into_inner())
}