use std::time::Duration;
use reqwest::Client;
use crate::constants::USER_AGENT;
#[cfg(not(any(feature = "native-tls", feature = "rustls-tls")))]
compile_error!(
"At least one TLS backend feature must be enabled: `native-tls` (recommended) or `rustls-tls`. \
Add `native-tls` to your feature list to use the platform's native TLS, \
which avoids requiring cmake/NASM on Windows."
);
pub fn tls_backend_name() -> &'static str {
#[cfg(feature = "native-tls")]
{
"native-tls"
}
#[cfg(all(feature = "rustls-tls", not(feature = "native-tls")))]
{
"rustls-tls"
}
#[cfg(not(any(feature = "native-tls", feature = "rustls-tls")))]
{
"none"
}
}
#[derive(Debug, Clone)]
pub struct HttpClientConfig {
pub user_agent: String,
pub connect_timeout: Option<Duration>,
pub timeout: Option<Duration>,
}
impl Default for HttpClientConfig {
fn default() -> Self {
Self {
user_agent: USER_AGENT.to_string(),
connect_timeout: Some(Duration::from_secs(30)),
timeout: Some(Duration::from_secs(300)),
}
}
}
impl HttpClientConfig {
pub fn with_user_agent(user_agent: impl Into<String>) -> Self {
Self {
user_agent: user_agent.into(),
..Default::default()
}
}
pub fn connect_timeout(mut self, timeout: Duration) -> Self {
self.connect_timeout = Some(timeout);
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn build(&self) -> Client {
create_http_client_with_config(self)
}
}
pub fn create_http_client() -> Client {
create_http_client_with_config(&HttpClientConfig::default())
}
pub fn create_http_client_with_config(config: &HttpClientConfig) -> Client {
let mut builder = Client::builder()
.user_agent(&config.user_agent)
.pool_max_idle_per_host(10)
.pool_idle_timeout(std::time::Duration::from_secs(90));
#[cfg(feature = "native-tls")]
{
builder = builder.use_native_tls();
}
#[cfg(all(feature = "rustls-tls", not(feature = "native-tls")))]
{
builder = builder.use_rustls_tls();
}
if let Some(timeout) = config.connect_timeout {
builder = builder.connect_timeout(timeout);
}
if let Some(timeout) = config.timeout {
builder = builder.timeout(timeout);
}
builder.build().expect("Failed to create HTTP client")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = HttpClientConfig::default();
assert!(config.user_agent.contains("msvc-kit"));
assert_eq!(config.connect_timeout, Some(Duration::from_secs(30)));
assert_eq!(config.timeout, Some(Duration::from_secs(300)));
}
#[test]
fn test_custom_config() {
let config = HttpClientConfig::with_user_agent("custom-agent/1.0")
.connect_timeout(Duration::from_secs(10))
.timeout(Duration::from_secs(60));
assert_eq!(config.user_agent, "custom-agent/1.0");
assert_eq!(config.connect_timeout, Some(Duration::from_secs(10)));
assert_eq!(config.timeout, Some(Duration::from_secs(60)));
}
#[test]
fn test_create_client() {
let client = create_http_client();
drop(client);
}
#[test]
fn test_build_applies_config() {
let config = HttpClientConfig::with_user_agent("msvc-kit/test")
.connect_timeout(Duration::from_secs(5))
.timeout(Duration::from_secs(15));
let client = config.build();
assert_eq!(config.user_agent, "msvc-kit/test");
assert_eq!(config.connect_timeout, Some(Duration::from_secs(5)));
assert_eq!(config.timeout, Some(Duration::from_secs(15)));
let _request = client
.get("http://example.com")
.build()
.expect("request build should succeed");
}
#[test]
fn test_tls_backend_name() {
let backend = tls_backend_name();
#[cfg(feature = "native-tls")]
assert_eq!(backend, "native-tls");
#[cfg(all(feature = "rustls-tls", not(feature = "native-tls")))]
assert_eq!(backend, "rustls-tls");
assert!(!backend.is_empty());
}
#[test]
fn test_tls_backend_is_not_none() {
let backend = tls_backend_name();
assert_ne!(backend, "none", "A TLS backend must be enabled");
}
#[test]
fn test_create_client_with_tls() {
let client = create_http_client();
let _request = client
.get("https://example.com")
.build()
.expect("HTTPS request build should succeed with TLS backend");
}
#[test]
fn test_client_builder_with_tls_config() {
let config = HttpClientConfig::default();
let client = config.build();
let _request = client
.get("https://example.com")
.build()
.expect("HTTPS request build should succeed");
}
}