drogue_bazaar/
reqwest.rs

1//! Support for using `reqwest`.
2
3use crate::core::tls::ClientConfig;
4use reqwest::Certificate;
5use std::{
6    fs::File,
7    io::Read,
8    path::{Path, PathBuf},
9    str::FromStr,
10};
11
12/// Convert the name to an HTTP method.
13///
14/// If the name is empty, [`None`] is returned. If the method is invalid, and error will be returned.
15pub fn to_method(name: &str) -> Result<Option<reqwest::Method>, String> {
16    if name.is_empty() {
17        Ok(None)
18    } else {
19        match reqwest::Method::from_str(name) {
20            Ok(m) => Ok(Some(m)),
21            Err(_) => Err(format!("Invalid HTTP method: {}", name)),
22        }
23    }
24}
25
26fn add_cert<P: AsRef<Path>>(
27    mut client: reqwest::ClientBuilder,
28    cert: P,
29) -> anyhow::Result<reqwest::ClientBuilder> {
30    let cert = cert.as_ref();
31    log::info!("Adding root certificate: {:?}", cert);
32    let mut file = File::open(cert)?;
33    let mut buf = Vec::new();
34    file.read_to_end(&mut buf)?;
35
36    let pems = pem::parse_many(buf)?;
37    let pems = pems
38        .into_iter()
39        .map(|pem| Certificate::from_pem(&pem::encode(&pem).into_bytes()).map_err(|err| err.into()))
40        .collect::<anyhow::Result<Vec<_>>>()?;
41
42    log::info!("Found {} certificates", pems.len());
43
44    for pem in pems {
45        log::info!("Adding root certificate: {:?}", pem);
46        client = client.add_root_certificate(pem);
47    }
48
49    Ok(client)
50}
51
52fn make_insecure(client: reqwest::ClientBuilder) -> reqwest::ClientBuilder {
53    log::warn!("Disabling TLS verification for client. Do not use this in production!");
54    client
55        .danger_accept_invalid_certs(true)
56        .danger_accept_invalid_hostnames(true)
57}
58
59/// Allows us to create clients.
60///
61/// `reqwest` already has a `ClientBuilder`, however it is unable to be cloned. Also it is not
62/// possible to get a `ClientBuilder` from an existing `Client`. So we need to re-create all builders
63/// and clients.
64#[derive(Clone, Debug, Default, PartialEq, Eq)]
65pub struct ClientFactory {
66    insecure: bool,
67    ca_certs: Vec<PathBuf>,
68}
69
70impl From<ClientConfig> for ClientFactory {
71    fn from(config: ClientConfig) -> Self {
72        let mut factory = Self {
73            insecure: false,
74            ca_certs: vec![],
75        };
76
77        if config.tls_insecure {
78            factory = factory.make_insecure();
79        }
80
81        factory = factory.add_ca_certs(config.certificates());
82
83        factory
84    }
85}
86
87impl ClientFactory {
88    /// Create a new client factory from a default [`ClientConfig`].
89    pub fn new() -> Self {
90        ClientConfig::default().into()
91    }
92
93    fn dedup(&mut self) {
94        self.ca_certs.sort_unstable();
95        self.ca_certs.dedup();
96    }
97
98    pub fn make_insecure(mut self) -> Self {
99        self.insecure = true;
100        self.dedup();
101        self
102    }
103
104    pub fn add_ca_cert<P: Into<PathBuf>>(mut self, path: P) -> Self {
105        self.ca_certs.push(path.into());
106        self.dedup();
107        self
108    }
109
110    pub fn add_ca_certs<I, P>(mut self, paths: I) -> Self
111    where
112        I: IntoIterator<Item = P>,
113        P: Into<PathBuf>,
114    {
115        for path in paths {
116            self.ca_certs.push(path.into());
117        }
118        self
119    }
120
121    pub fn new_builder(&self) -> anyhow::Result<reqwest::ClientBuilder> {
122        let mut builder = reqwest::ClientBuilder::new();
123
124        for ca in &self.ca_certs {
125            builder = add_cert(builder, &ca)?;
126        }
127
128        if self.insecure {
129            builder = make_insecure(builder);
130        }
131
132        Ok(builder)
133    }
134
135    pub fn new_client(&self) -> anyhow::Result<reqwest::Client> {
136        Ok(self.new_builder()?.build()?)
137    }
138
139    /// Alias for `new_client`
140    #[inline]
141    pub fn build(&self) -> anyhow::Result<reqwest::Client> {
142        self.new_client()
143    }
144}