Skip to main content

pipedash_core/infrastructure/
http_client.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use dashmap::DashMap;
5use reqwest::Client;
6
7use crate::domain::{
8    DomainError,
9    DomainResult,
10};
11
12pub struct HttpClientManager {
13    default_client: Arc<Client>,
14    custom_clients: DashMap<String, Arc<Client>>,
15}
16
17impl HttpClientManager {
18    pub fn new() -> DomainResult<Self> {
19        let default_client = Self::create_optimized_client()?;
20        Ok(Self {
21            default_client: Arc::new(default_client),
22            custom_clients: DashMap::new(),
23        })
24    }
25
26    pub fn default_client(&self) -> Arc<Client> {
27        Arc::clone(&self.default_client)
28    }
29
30    pub fn client_for_url(&self, base_url: &str) -> DomainResult<Arc<Client>> {
31        if let Some(client) = self.custom_clients.get(base_url) {
32            return Ok(Arc::clone(client.value()));
33        }
34
35        let client = Self::create_optimized_client()?;
36        let client = Arc::new(client);
37        self.custom_clients
38            .insert(base_url.to_string(), Arc::clone(&client));
39        Ok(client)
40    }
41
42    fn create_optimized_client() -> DomainResult<Client> {
43        let pool_size = std::env::var("PIPEDASH_HTTP_POOL_SIZE")
44            .ok()
45            .and_then(|s| s.parse().ok())
46            .unwrap_or(10);
47
48        Client::builder()
49            .use_rustls_tls()
50            .pool_max_idle_per_host(pool_size)
51            .pool_idle_timeout(Duration::from_secs(90))
52            .tcp_keepalive(Duration::from_secs(60))
53            .timeout(Duration::from_secs(30))
54            .connect_timeout(Duration::from_secs(10))
55            .build()
56            .map_err(|e| DomainError::InternalError(format!("Failed to create HTTP client: {}", e)))
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_http_client_manager_creation() {
66        let manager = HttpClientManager::new().unwrap();
67        let client = manager.default_client();
68        assert!(Arc::strong_count(&client) >= 1);
69    }
70
71    #[test]
72    fn test_client_caching() {
73        let manager = HttpClientManager::new().unwrap();
74
75        let client1 = manager.client_for_url("https://api.github.com").unwrap();
76        let client2 = manager.client_for_url("https://api.github.com").unwrap();
77
78        assert!(Arc::ptr_eq(&client1, &client2));
79    }
80
81    #[test]
82    fn test_different_urls_different_clients() {
83        let manager = HttpClientManager::new().unwrap();
84
85        let client1 = manager.client_for_url("https://api.github.com").unwrap();
86        let client2 = manager.client_for_url("https://gitlab.com").unwrap();
87
88        assert!(!Arc::ptr_eq(&client1, &client2));
89    }
90
91    #[test]
92    fn test_default_client_separate_from_custom() {
93        let manager = HttpClientManager::new().unwrap();
94
95        let default_client = manager.default_client();
96        let custom_client = manager.client_for_url("https://api.github.com").unwrap();
97
98        assert!(!Arc::ptr_eq(&default_client, &custom_client));
99    }
100}