1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use crate::client::config::ClientConfig;
use crate::DatabaseConfiguration;
use reqwest::{Certificate, Client};
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use reqwest_retry::policies::ExponentialBackoff;
use reqwest_retry::RetryTransientMiddleware;
use std::fs::File;
use std::io::Read;

pub fn make_url(db_config: &DatabaseConfiguration, path: &str) -> String {
    db_config.endpoints[0].clone() + "/_db/" + &db_config.database + path
}

pub fn build_client(
    config: &ClientConfig,
) -> Result<reqwest_middleware::ClientWithMiddleware, String> {
    let mut client_builder = reqwest::Client::builder();
    client_builder = if config.use_tls {
        client_builder = client_builder
            .min_tls_version(reqwest::tls::Version::TLS_1_2)
            .https_only(true);

        if let Some(cert_path) = &config.tls_cert {
            let cert = get_cert(cert_path)?;
            client_builder.add_root_certificate(cert).use_rustls_tls()
        } else {
            client_builder
                .danger_accept_invalid_certs(true)
                .use_rustls_tls()
        }
    } else {
        client_builder.danger_accept_invalid_certs(true)
    };
    let client = client_builder
        .build()
        .map_err(|err| format!("Error message from request builder: {:?}", err))?;
    let client = client_with_retries(&config.n_retries, client);
    Ok(client)
}

fn get_cert(cert_path: &String) -> Result<Certificate, String> {
    if cert_path.is_empty() {
        return Err(
            "Error message from reading TLS certificate: Certificate path is empty".to_string(),
        );
    }
    let mut cert_buf = vec![];
    let mut file = File::open(cert_path)
        .map_err(|err| format!("Error message from reading TLS certificate: {:?}", err))?;
    file.read_to_end(&mut cert_buf)
        .map_err(|err| format!("Error message from reading TLS certificate: {:?}", err))?;
    let cert = reqwest::Certificate::from_pem(&cert_buf)
        .map_err(|err| format!("Error message from request builder: {:?}", err))?;
    Ok(cert)
}

fn client_with_retries(n_retries: &u32, client: Client) -> ClientWithMiddleware {
    let retry_policy = ExponentialBackoff::builder()
        .retry_bounds(
            std::time::Duration::from_millis(30),
            std::time::Duration::from_millis(3000),
        )
        .build_with_max_retries(*n_retries);
    let retry_middleware = RetryTransientMiddleware::new_with_policy(retry_policy);
    ClientBuilder::new(client).with(retry_middleware).build()
}