Skip to main content

htsget_config/config/advanced/
mod.rs

1//! Configuration options that are advanced in the documentation.
2//!
3
4use crate::config::service_info::PackageInfo;
5use crate::error::Error::ParseError;
6use crate::error::{Error, Result};
7use crate::http::client::HttpClientConfig;
8use http_cache_reqwest::{CACacheManager, Cache, CacheMode, HttpCache, HttpCacheOptions};
9use reqwest::Client;
10use reqwest_middleware::ClientWithMiddleware;
11use serde::{Deserialize, Serialize};
12use std::env::temp_dir;
13use std::fs::File;
14use std::io::Read;
15use std::path::PathBuf;
16
17pub mod allow_guard;
18pub mod auth;
19pub mod cors;
20pub mod regex_location;
21#[cfg(feature = "url")]
22pub mod url;
23
24/// The prefix used for context header values.
25pub const CONTEXT_HEADER_PREFIX: &str = "Htsget-Context-";
26
27/// Determines which tracing formatting style to use.
28#[derive(Debug, Copy, Clone, Serialize, Deserialize, Default)]
29#[serde(deny_unknown_fields)]
30pub enum FormattingStyle {
31  #[default]
32  Full,
33  Compact,
34  Pretty,
35  Json,
36}
37
38/// A wrapper around a reqwest client to support creating from config fields.
39#[derive(Deserialize, Debug, Clone)]
40#[serde(deny_unknown_fields, from = "HttpClientConfig")]
41pub struct HttpClient {
42  config: Option<HttpClientConfig>,
43  client: Option<ClientWithMiddleware>,
44}
45
46impl HttpClient {
47  /// Create a new client.
48  pub fn new(client: ClientWithMiddleware) -> Self {
49    Self {
50      config: None,
51      client: Some(client),
52    }
53  }
54
55  /// Set the client from an incomplete builder.
56  pub fn new_with_config(config: HttpClientConfig) -> Self {
57    Self {
58      config: Some(config),
59      client: None,
60    }
61  }
62
63  /// Get the client builder by taking out the config value.
64  pub fn take_config(&mut self) -> Result<HttpClientConfig> {
65    self
66      .config
67      .take()
68      .ok_or_else(|| ParseError("client already built".to_string()))
69  }
70
71  /// Set the builder.
72  pub fn set_config(&mut self, config: HttpClientConfig) {
73    self.config = Some(config);
74  }
75
76  /// Get the inner client, building it if necessary.
77  pub fn as_inner_built(&mut self) -> Result<&ClientWithMiddleware> {
78    if let Some(ref client) = self.client {
79      return Ok(client);
80    }
81
82    let config = self.take_config()?;
83    let mut builder = Client::builder();
84
85    let (certs, identity, use_cache, user_agent) = config.into_inner();
86
87    if let Some(certs) = certs {
88      for cert in certs {
89        builder = builder.add_root_certificate(cert);
90      }
91    }
92    if let Some(identity) = identity {
93      builder = builder.identity(identity);
94    }
95    if let Some(user_agent) = user_agent {
96      builder = builder.user_agent(user_agent);
97    }
98
99    let inner_client = builder
100      .build()
101      .map_err(|err| ParseError(format!("building http client: {err}")))?;
102    let client = if use_cache {
103      let client_cache = temp_dir().join("htsget_rs_client_cache");
104      reqwest_middleware::ClientBuilder::new(inner_client)
105        .with(Cache(HttpCache {
106          mode: CacheMode::Default,
107          manager: CACacheManager::new(client_cache, false),
108          options: HttpCacheOptions::default(),
109        }))
110        .build()
111    } else {
112      reqwest_middleware::ClientBuilder::new(inner_client).build()
113    };
114
115    self.client = Some(client);
116    Ok(self.client.as_ref().expect("expected client"))
117  }
118
119  /// Set the user-agent information from the package info.
120  pub fn set_from_package_info(&mut self, info: &PackageInfo) -> Result<()> {
121    let builder = self.take_config()?;
122    self.set_config(builder.with_user_agent(info.id.to_string()));
123
124    Ok(())
125  }
126}
127
128impl From<HttpClientConfig> for HttpClient {
129  fn from(config: HttpClientConfig) -> Self {
130    Self::new_with_config(config)
131  }
132}
133
134/// A wrapper around byte data to support reading files in config.
135pub struct Bytes(Vec<u8>);
136
137impl Bytes {
138  /// Create a new data wrapper.
139  pub fn new(data: Vec<u8>) -> Self {
140    Self(data)
141  }
142
143  /// Get the bytes.
144  pub fn into_inner(self) -> Vec<u8> {
145    self.0
146  }
147}
148
149impl TryFrom<PathBuf> for Bytes {
150  type Error = Error;
151
152  fn try_from(path: PathBuf) -> Result<Self> {
153    let mut bytes = vec![];
154    File::open(path)?.read_to_end(&mut bytes)?;
155    Ok(Self(bytes))
156  }
157}