Skip to main content

aria2_core/http/
client_pool.rs

1// HTTP client pool for connection reuse across multiple downloads.
2//
3// Provides a singleton HTTP client that can be shared across multiple
4// DownloadCommand instances to reduce connection establishment overhead
5// and improve memory efficiency.
6
7use once_cell::sync::Lazy;
8use reqwest::Client;
9use std::sync::Arc;
10use std::time::Duration;
11
12/// Global HTTP client instance for connection reuse.
13static GLOBAL_CLIENT: Lazy<Arc<Client>> = Lazy::new(|| {
14    let client = Client::builder()
15        .connect_timeout(Duration::from_secs(
16            crate::constants::HTTP_DEFAULT_CONNECT_TIMEOUT_SECS,
17        ))
18        .timeout(Duration::from_secs(
19            crate::constants::HTTP_DEFAULT_OVERALL_TIMEOUT_SECS,
20        ))
21        .user_agent(crate::constants::USER_AGENT)
22        .redirect(reqwest::redirect::Policy::limited(
23            crate::constants::HTTP_DEFAULT_MAX_REDIRECTS,
24        ))
25        .pool_max_idle_per_host(crate::constants::HTTP_CLIENT_POOL_MAX_IDLE_PER_HOST)
26        .pool_idle_timeout(Some(Duration::from_secs(
27            crate::constants::HTTP_CLIENT_POOL_IDLE_TIMEOUT_SECS,
28        )))
29        .tcp_keepalive(Some(Duration::from_secs(
30            crate::constants::HTTP_DEFAULT_TCP_KEEPALIVE_SECS,
31        )))
32        .build()
33        .expect("Failed to create global HTTP client");
34
35    Arc::new(client)
36});
37
38/// Get the global shared HTTP client instance.
39///
40/// This client is shared across all downloads, enabling:
41/// - TCP connection reuse
42/// - Reduced memory footprint
43/// - Better performance for concurrent downloads
44pub fn get_global_client() -> Arc<Client> {
45    GLOBAL_CLIENT.clone()
46}
47
48/// Create a custom HTTP client with specific configuration.
49///
50/// Use this when you need client settings different from the global defaults.
51pub fn create_custom_client(
52    connect_timeout: Duration,
53    timeout: Duration,
54    pool_max_idle_per_host: usize,
55) -> Arc<Client> {
56    let client = Client::builder()
57        .connect_timeout(connect_timeout)
58        .timeout(timeout)
59        .user_agent(crate::constants::USER_AGENT)
60        .redirect(reqwest::redirect::Policy::limited(
61            crate::constants::HTTP_DEFAULT_MAX_REDIRECTS,
62        ))
63        .pool_max_idle_per_host(pool_max_idle_per_host)
64        .pool_idle_timeout(Some(Duration::from_secs(
65            crate::constants::HTTP_CLIENT_POOL_IDLE_TIMEOUT_SECS,
66        )))
67        .tcp_keepalive(Some(Duration::from_secs(
68            crate::constants::HTTP_DEFAULT_TCP_KEEPALIVE_SECS,
69        )))
70        .build()
71        .expect("Failed to create custom HTTP client");
72
73    Arc::new(client)
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_global_client_is_shared() {
82        let client1 = get_global_client();
83        let client2 = get_global_client();
84
85        // Both should point to the same client instance
86        assert!(Arc::ptr_eq(&client1, &client2));
87    }
88
89    #[test]
90    fn test_custom_client_is_different() {
91        let global = get_global_client();
92        let custom = create_custom_client(Duration::from_secs(10), Duration::from_secs(60), 8);
93
94        // Should be different instances
95        assert!(!Arc::ptr_eq(&global, &custom));
96    }
97}