azure_identity/
oauth2_http_client.rs

1//! Implements the oauth2 crate http client interface using an `azure_core::HttpClient` instance.
2//! <https://docs.rs/oauth2/latest/oauth2/#importing-oauth2-selecting-an-http-client-interface>
3
4use azure_core::{
5    error::{Error, ErrorKind, ResultExt},
6    HttpClient, Request,
7};
8use std::{collections::HashMap, str::FromStr, sync::Arc};
9use tracing::warn;
10
11pub(crate) struct Oauth2HttpClient {
12    http_client: Arc<dyn HttpClient>,
13}
14
15impl Oauth2HttpClient {
16    /// Create a new `Oauth2HttpClient`
17    pub fn new(http_client: Arc<dyn HttpClient>) -> Self {
18        Self { http_client }
19    }
20
21    pub(crate) async fn request(
22        &self,
23        oauth2_request: oauth2::HttpRequest,
24    ) -> Result<oauth2::HttpResponse, azure_core::error::Error> {
25        let method = try_from_method(&oauth2_request.method)?;
26        let mut request = Request::new(oauth2_request.url, method);
27        for (name, value) in to_headers(&oauth2_request.headers) {
28            request.insert_header(name, value);
29        }
30        request.set_body(oauth2_request.body);
31        let response = self.http_client.execute_request(&request).await?;
32        let status_code = try_from_status(response.status())?;
33        let headers = try_from_headers(response.headers())?;
34        let body = response.into_body().collect().await?.to_vec();
35        Ok(oauth2::HttpResponse {
36            status_code,
37            headers,
38            body,
39        })
40    }
41}
42
43fn try_from_method(method: &oauth2::http::Method) -> azure_core::Result<azure_core::Method> {
44    match *method {
45        oauth2::http::Method::GET => Ok(azure_core::Method::Get),
46        oauth2::http::Method::POST => Ok(azure_core::Method::Post),
47        oauth2::http::Method::PUT => Ok(azure_core::Method::Put),
48        oauth2::http::Method::DELETE => Ok(azure_core::Method::Delete),
49        oauth2::http::Method::HEAD => Ok(azure_core::Method::Head),
50        oauth2::http::Method::OPTIONS => Ok(azure_core::Method::Options),
51        oauth2::http::Method::CONNECT => Ok(azure_core::Method::Connect),
52        oauth2::http::Method::PATCH => Ok(azure_core::Method::Patch),
53        oauth2::http::Method::TRACE => Ok(azure_core::Method::Trace),
54        _ => Err(Error::with_message(ErrorKind::DataConversion, || {
55            format!("unsupported oauth2::http::Method {method}")
56        })),
57    }
58}
59
60fn try_from_headers(
61    headers: &azure_core::headers::Headers,
62) -> azure_core::Result<oauth2::http::HeaderMap> {
63    let mut header_map = oauth2::http::HeaderMap::new();
64    for (name, value) in headers.iter() {
65        let name = name.as_str();
66        let header_name = oauth2::http::header::HeaderName::from_str(name)
67            .with_context(ErrorKind::DataConversion, || {
68                format!("unable to convert http header name '{name}'")
69            })?;
70        let value = value.as_str().to_owned();
71        header_map.append(
72            header_name,
73            oauth2::http::HeaderValue::from_str(&value)
74                .with_context(ErrorKind::DataConversion, || {
75                    format!("unable to convert http header value for '{name}'")
76                })?,
77        );
78    }
79    Ok(header_map)
80}
81
82fn try_from_status(status: azure_core::StatusCode) -> azure_core::Result<oauth2::http::StatusCode> {
83    oauth2::http::StatusCode::from_u16(status as u16).map_kind(ErrorKind::DataConversion)
84}
85
86fn to_headers(map: &oauth2::http::header::HeaderMap) -> azure_core::headers::Headers {
87    let map = map
88        .iter()
89        .filter_map(|(k, v)| {
90            let key = k.as_str();
91            if let Ok(value) = v.to_str() {
92                Some((
93                    azure_core::headers::HeaderName::from(key.to_owned()),
94                    azure_core::headers::HeaderValue::from(value.to_owned()),
95                ))
96            } else {
97                warn!("header value for `{key}` is not utf8");
98                None
99            }
100        })
101        .collect::<HashMap<_, _>>();
102    azure_core::headers::Headers::from(map)
103}