1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
use async_trait::async_trait;
use bytes::Bytes;
use http::{Request, Response};
#[cfg(any(feature = "reqwest-blocking-client", feature = "reqwest-client"))]
use std::convert::TryInto;
use std::fmt::Debug;

type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;

/// HTTP client used by the exporter to send telemetry to Application Insights
///
/// This trait can be implemented for different async runtimes, which makes the exporter agnostic
/// to any runtime the user may choose.
#[async_trait]
pub trait HttpClient: Debug + Send + Sync {
    /// Send telemetry to Application Insights
    ///
    /// This may fail if it can't connect to the server or if the request cannot be completed due
    /// to redirects. In those cases the exporter will retry the request.
    async fn send(&self, request: Request<Vec<u8>>) -> Result<Response<Bytes>, BoxError>;
}

/// `HttpClient` implementation for `reqwest::Client`
///
/// Requires the **reqwest-client** feature.
#[cfg(feature = "reqwest-client")]
#[async_trait]
impl HttpClient for reqwest::Client {
    async fn send(&self, request: Request<Vec<u8>>) -> Result<Response<Bytes>, BoxError> {
        let res = self.execute(request.try_into()?).await?;
        Ok(Response::builder()
            .status(res.status())
            .body(res.bytes().await?)?)
    }
}

/// `HttpClient` implementation for `reqwest::blocking::Client`
///
/// Requires the **reqwest-blocking-client** feature.
#[cfg(feature = "reqwest-blocking-client")]
#[async_trait]
impl HttpClient for reqwest::blocking::Client {
    async fn send(&self, request: Request<Vec<u8>>) -> Result<Response<Bytes>, BoxError> {
        let res = self.execute(request.try_into()?)?;
        Ok(Response::builder()
            .status(res.status())
            .body(res.bytes()?)?)
    }
}

/// `HttpClient` implementation for `surf::Client`
///
/// Requires the **surf-client** feature.
#[cfg(feature = "surf")]
#[async_trait]
impl HttpClient for surf::Client {
    async fn send(&self, request: Request<Vec<u8>>) -> Result<Response<Bytes>, BoxError> {
        let (parts, body) = request.into_parts();
        let req = surf::post(parts.uri.to_string())
            .content_type("application/json")
            .body(body);
        let mut res = self.send(req).await?;
        Ok(Response::builder()
            .status(res.status() as u16)
            .body(res.body_bytes().await?.into())?)
    }
}