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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use std::{str::FromStr, sync::Arc};

use anyhow::{anyhow, Result};
use async_rustls::rustls::{OwnedTrustAnchor, RootCertStore};
use hyper::{Body, Uri};
use once_cell::sync::Lazy;

use super::{
    async_std_compat::{self, CompatConnector},
    request::RequestBuilder,
    USER_AGENT,
};

type HyperClient = Arc<hyper::Client<CompatConnector, hyper::Body>>;

#[derive(Clone)]
pub struct Client {
    pub(crate) hyper: HyperClient,
}

#[cfg_attr(feature = "__skip-http-client-cert-verification", allow(dead_code))]
static ROOT_CERT_STORE: Lazy<RootCertStore> = Lazy::new(|| {
    let mut store = RootCertStore::empty();
    store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {
        OwnedTrustAnchor::from_subject_spki_name_constraints(
            ta.subject,
            ta.spki,
            ta.name_constraints,
        )
    }));
    store
});

impl Default for Client {
    fn default() -> Self {
        let tls = async_rustls::rustls::ClientConfig::builder().with_safe_defaults();

        #[cfg(not(feature = "__skip-http-client-cert-verification"))]
        let tls = tls.with_root_certificates(ROOT_CERT_STORE.to_owned());

        #[cfg(feature = "__skip-http-client-cert-verification")]
        let tls =
            tls.with_custom_certificate_verifier(Arc::new(no_verifier::NoCertificateVerification));

        let tls = tls.with_no_client_auth();

        let https = async_std_compat::CompatConnector::new(tls);

        let client: hyper::Client<_, hyper::Body> = hyper::Client::builder()
            .executor(async_std_compat::CompatExecutor)
            .build(https);

        Client {
            hyper: Arc::new(client),
        }
    }
}

impl Client {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn get(&self, uri: impl AsRef<str>) -> Result<RequestBuilder> {
        let uri = Uri::from_str(uri.as_ref())?;
        let req = http::request::Builder::new().uri(uri);
        Ok(RequestBuilder::new(self.clone(), req))
    }

    pub async fn send<B: Into<hyper::Body>>(
        &self,
        req: http::Request<B>,
    ) -> Result<http::Response<Body>, anyhow::Error> {
        // convert http::Request into hyper::Request
        let (mut parts, body) = req.into_parts();
        let body: hyper::Body = body.into();
        if !parts.headers.contains_key(http::header::USER_AGENT) {
            let agent = http::header::HeaderValue::from_static(USER_AGENT);
            parts.headers.append(http::header::USER_AGENT, agent);
        }
        let req = hyper::Request::<hyper::Body>::from_parts(parts, body);
        self.hyper
            .request(req)
            .await
            .map_err(|err| anyhow!("request error: {err:?}"))
    }
}

#[cfg(feature = "__skip-http-client-cert-verification")]
mod no_verifier {
    use std::time::SystemTime;

    use async_rustls::rustls::{
        client::{ServerCertVerified, ServerCertVerifier},
        Certificate, Error, ServerName,
    };

    pub struct NoCertificateVerification;

    impl ServerCertVerifier for NoCertificateVerification {
        fn verify_server_cert(
            &self,
            _end_entity: &Certificate,
            _intermediates: &[Certificate],
            _server_name: &ServerName,
            _scts: &mut dyn Iterator<Item = &[u8]>,
            _ocsp_response: &[u8],
            _now: SystemTime,
        ) -> Result<ServerCertVerified, Error> {
            Ok(ServerCertVerified::assertion())
        }
    }
}