htsget-config 0.23.0

Used to configure htsget-rs by using a config file or reading environment variables.
Documentation
//! TLS configuration related to HTTP clients.
//!

use crate::config::advanced::callout::CachePolicy;
use crate::error::{Error, Result};
use crate::http::{HttpClientOptions, load_reqwest_certs, load_reqwest_identity};
use reqwest::{Certificate, Identity};
use serde::Deserialize;

/// A certificate and key pair used for TLS. Serialization is not implemented because there
/// is no way to convert back to a `PathBuf`.
#[derive(Deserialize, Debug, Clone)]
#[serde(try_from = "HttpClientOptions", deny_unknown_fields)]
pub struct HttpClientConfig {
  cert: Option<Vec<Certificate>>,
  identity: Option<Identity>,
  use_cache: bool,
  user_agent: Option<String>,
  cache: CachePolicy,
}

impl Default for HttpClientConfig {
  fn default() -> Self {
    Self {
      cert: None,
      identity: None,
      use_cache: true,
      user_agent: None,
      cache: CachePolicy::default(),
    }
  }
}

impl HttpClientConfig {
  /// Create a new TlsClientConfig.
  pub fn new(cert: Option<Vec<Certificate>>, identity: Option<Identity>, use_cache: bool) -> Self {
    Self {
      cert,
      identity,
      use_cache,
      cache: CachePolicy::default(),
      ..Default::default()
    }
  }

  /// Create a new TlsClientConfig with a cache policy.
  pub fn new_with_cache(
    cert: Option<Vec<Certificate>>,
    identity: Option<Identity>,
    use_cache: bool,
    cache: CachePolicy,
  ) -> Self {
    Self {
      cert,
      identity,
      use_cache,
      cache,
      ..Default::default()
    }
  }

  /// Get the inner client config.
  pub fn into_inner(
    self,
  ) -> (
    Option<Vec<Certificate>>,
    Option<Identity>,
    bool,
    Option<String>,
    CachePolicy,
  ) {
    (
      self.cert,
      self.identity,
      self.use_cache,
      self.user_agent,
      self.cache,
    )
  }

  /// Set the user agent string.
  pub fn with_user_agent(mut self, user_agent: String) -> Self {
    self.user_agent = Some(user_agent);
    self
  }
}

impl TryFrom<HttpClientOptions> for HttpClientConfig {
  type Error = Error;

  fn try_from(root_store_pair: HttpClientOptions) -> Result<Self> {
    let (key_pair, root_store, use_cache, cache) = root_store_pair.into_inner();

    let cert = root_store.map(load_reqwest_certs).transpose()?;
    let identity = key_pair.as_ref().map(load_reqwest_identity).transpose()?;

    Ok(Self::new_with_cache(cert, identity, use_cache, cache))
  }
}

#[cfg(test)]
pub(crate) mod tests {
  use crate::http::tests::with_test_certificates;
  use crate::http::{CertificateKeyPairPath, HttpClientOptions};
  use std::path::Path;

  use super::*;

  #[tokio::test]
  async fn test_tls_client_config() {
    with_test_certificates(|path, _, _| {
      let client_config = client_config_from_path(path);
      let (certs, identity, _, _, _) = client_config.into_inner();

      assert_eq!(certs.unwrap().len(), 1);
      assert!(identity.is_some());
    });
  }

  pub(crate) fn client_config_from_path(path: &Path) -> HttpClientConfig {
    HttpClientConfig::try_from(HttpClientOptions::new(
      Some(CertificateKeyPairPath::new(
        path.join("cert.pem"),
        path.join("key.pem"),
      )),
      Some(path.join("cert.pem")),
      true,
    ))
    .unwrap()
  }
}