htsget_config/http/
client.rs1use crate::config::advanced::Bytes;
5use crate::error::Error::IoError;
6use crate::error::{Error, Result};
7use crate::http::RootCertStorePair;
8use crate::http::load_certs;
9use reqwest::{Certificate, Identity};
10use serde::Deserialize;
11
12#[derive(Deserialize, Debug, Clone)]
15#[serde(try_from = "RootCertStorePair", deny_unknown_fields)]
16pub struct HttpClientConfig {
17 cert: Option<Vec<Certificate>>,
18 identity: Option<Identity>,
19 use_cache: bool,
20 user_agent: Option<String>,
21}
22
23impl Default for HttpClientConfig {
24 fn default() -> Self {
25 Self {
26 cert: None,
27 identity: None,
28 use_cache: true,
29 user_agent: None,
30 }
31 }
32}
33
34impl HttpClientConfig {
35 pub fn new(cert: Option<Vec<Certificate>>, identity: Option<Identity>, use_cache: bool) -> Self {
37 Self {
38 cert,
39 identity,
40 use_cache,
41 ..Default::default()
42 }
43 }
44
45 pub fn into_inner(
47 self,
48 ) -> (
49 Option<Vec<Certificate>>,
50 Option<Identity>,
51 bool,
52 Option<String>,
53 ) {
54 (self.cert, self.identity, self.use_cache, self.user_agent)
55 }
56
57 pub fn with_user_agent(mut self, user_agent: String) -> Self {
59 self.user_agent = Some(user_agent);
60 self
61 }
62}
63
64impl TryFrom<RootCertStorePair> for HttpClientConfig {
65 type Error = Error;
66
67 fn try_from(root_store_pair: RootCertStorePair) -> Result<Self> {
68 let (key_pair, root_store, use_cache) = root_store_pair.into_inner();
69
70 let cert = root_store
71 .clone()
72 .map(|cert_path| {
73 let certs = load_certs(cert_path)?;
74
75 certs
76 .into_iter()
77 .map(|cert| {
78 Certificate::from_der(&cert)
79 .map_err(|err| IoError(format!("failed to read certificate from pem: {err}")))
80 })
81 .collect::<Result<Vec<_>>>()
82 })
83 .transpose()?;
84
85 let identity = key_pair
86 .clone()
87 .map(|pair| {
88 let key = Bytes::try_from(pair.key)?.into_inner();
89 let certs = Bytes::try_from(pair.cert)?.into_inner();
90
91 Identity::from_pem(&[certs, key].concat())
92 .map_err(|err| IoError(format!("failed to load pkcs8 pem identity: {err}")))
93 })
94 .transpose()?;
95
96 Ok(Self::new(cert, identity, use_cache))
97 }
98}
99
100#[cfg(test)]
101pub(crate) mod tests {
102 use crate::http::tests::with_test_certificates;
103 use crate::http::{CertificateKeyPairPath, RootCertStorePair};
104 use std::path::Path;
105
106 use super::*;
107
108 #[tokio::test]
109 async fn test_tls_client_config() {
110 with_test_certificates(|path, _, _| {
111 let client_config = client_config_from_path(path);
112 let (certs, identity, _, _) = client_config.into_inner();
113
114 assert_eq!(certs.unwrap().len(), 1);
115 assert!(identity.is_some());
116 });
117 }
118
119 pub(crate) fn client_config_from_path(path: &Path) -> HttpClientConfig {
120 HttpClientConfig::try_from(RootCertStorePair::new(
121 Some(CertificateKeyPairPath::new(
122 path.join("cert.pem"),
123 path.join("key.pem"),
124 )),
125 Some(path.join("cert.pem")),
126 true,
127 ))
128 .unwrap()
129 }
130}