use std::error::Error;
use std::future::Future;
use std::pin::Pin;
use openidconnect::{AsyncHttpClient, HttpClientError, HttpRequest, HttpResponse, http};
#[derive(Clone, Debug)]
pub struct OidcHttpClient(reqwest::Client);
impl OidcHttpClient {
pub fn new() -> reqwest::Result<Self> {
reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none())
.build()
.map(Self)
}
}
impl<'c> AsyncHttpClient<'c> for OidcHttpClient {
type Error = HttpClientError<reqwest::Error>;
type Future =
Pin<Box<dyn Future<Output = Result<HttpResponse, Self::Error>> + Send + Sync + 'c>>;
fn call(&'c self, request: HttpRequest) -> Self::Future {
Box::pin(async move {
let request = request.try_into().map_err(Box::new)?;
let response = self.0.execute(request).await.map_err(Box::new)?;
let mut builder = http::Response::builder()
.status(response.status())
.version(response.version());
for (name, value) in response.headers() {
builder = builder.header(name, value);
}
let body = response.bytes().await.map_err(Box::new)?;
builder.body(body.to_vec()).map_err(HttpClientError::Http)
})
}
}
pub fn error_chain(error: &dyn Error) -> String {
let mut message = error.to_string();
let mut source = error.source();
while let Some(cause) = source {
message.push_str(": ");
message.push_str(&cause.to_string());
source = cause.source();
}
message
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, thiserror::Error)]
#[error("outer")]
struct Outer(#[source] Inner);
#[derive(Debug, thiserror::Error)]
#[error("inner")]
struct Inner;
#[test]
fn error_chain_includes_all_sources() {
assert_eq!(error_chain(&Outer(Inner)), "outer: inner");
}
#[test]
fn error_chain_without_source_is_display() {
assert_eq!(error_chain(&Inner), "inner");
}
}