Skip to main content

mkt_core/http/
client.rs

1//! Shared HTTP client builder.
2
3use std::time::Duration;
4
5use reqwest::Client;
6
7use crate::error::Result;
8
9/// Default request timeout in seconds.
10const DEFAULT_TIMEOUT_SECS: u64 = 30;
11
12/// Connection establishment (TCP + TLS) timeout, separate from the total
13/// request timeout so a hung handshake fails fast.
14const CONNECT_TIMEOUT_SECS: u64 = 10;
15
16/// Build a configured `reqwest::Client` with sensible defaults.
17///
18/// Features:
19/// - `rustls-tls` (no OpenSSL)
20/// - Gzip + Brotli decompression
21/// - Configurable timeout (default: 30s) plus a 10s connect timeout
22/// - TCP keepalive so pooled connections survive NAT idling
23/// - Custom User-Agent
24///
25/// # Errors
26///
27/// Returns an error if the HTTP client cannot be built (e.g. TLS init failure).
28pub fn build_http_client(timeout_secs: Option<u64>) -> Result<Client> {
29    let timeout = Duration::from_secs(timeout_secs.unwrap_or(DEFAULT_TIMEOUT_SECS));
30
31    let client = Client::builder()
32        .timeout(timeout)
33        .connect_timeout(Duration::from_secs(CONNECT_TIMEOUT_SECS))
34        .tcp_keepalive(Duration::from_secs(60))
35        .pool_idle_timeout(Duration::from_secs(90))
36        .user_agent(format!(
37            "mkt/{} (+https://mktcli.com)",
38            env!("CARGO_PKG_VERSION")
39        ))
40        .gzip(true)
41        .brotli(true)
42        .build()?;
43
44    Ok(client)
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn build_client_with_defaults() {
53        let client = build_http_client(None);
54        assert!(client.is_ok());
55    }
56
57    #[test]
58    fn build_client_with_custom_timeout() {
59        let client = build_http_client(Some(60));
60        assert!(client.is_ok());
61    }
62}