1use crate::core::tls::ClientConfig;
4use reqwest::Certificate;
5use std::{
6 fs::File,
7 io::Read,
8 path::{Path, PathBuf},
9 str::FromStr,
10};
11
12pub 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#[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 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 #[inline]
141 pub fn build(&self) -> anyhow::Result<reqwest::Client> {
142 self.new_client()
143 }
144}