Skip to main content

futu_core/
http_client.rs

1//! Shared reqwest builder that preserves the repository's curated webpki trust policy.
2
3use std::sync::Once;
4
5use rustls::{ClientConfig, RootCertStore};
6
7static RUSTLS_PROVIDER_INIT: Once = Once::new();
8
9/// Install the process-level ring provider used by direct Rustls clients.
10///
11/// Installing is idempotent. If another in-repo TLS path already selected a
12/// provider, Rustls keeps that process-level choice while this module still
13/// supplies an explicit webpki root store to every reqwest client.
14pub fn install_default_rustls_crypto_provider() {
15    RUSTLS_PROVIDER_INIT.call_once(|| {
16        if rustls::crypto::ring::default_provider()
17            .install_default()
18            .is_err()
19        {
20            tracing::debug!(
21                "rustls crypto provider already installed; keeping existing process-level provider"
22            );
23        }
24    });
25}
26
27/// Start a reqwest 0.13 builder with Mozilla webpki roots and no platform or
28/// user Keychain trust.
29pub fn webpki_builder() -> reqwest::ClientBuilder {
30    reqwest::Client::builder().tls_backend_preconfigured(webpki_client_config())
31}
32
33fn webpki_client_config() -> ClientConfig {
34    install_default_rustls_crypto_provider();
35    let roots = RootCertStore {
36        roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
37    };
38    let mut config = ClientConfig::builder()
39        .with_root_certificates(roots)
40        .with_no_client_auth();
41    // reqwest 0.13 does not populate ALPN on a preconfigured Rustls backend.
42    // Preserve its normal "all versions" preference explicitly.
43    config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
44    config
45}
46
47#[cfg(test)]
48#[path = "http_client/tests.rs"]
49mod tests;