pipedash_core/infrastructure/
http_client.rs1use 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 let _ = rustls::crypto::ring::default_provider().install_default();
49
50 Client::builder()
51 .use_rustls_tls()
52 .pool_max_idle_per_host(pool_size)
53 .pool_idle_timeout(Duration::from_secs(90))
54 .tcp_keepalive(Duration::from_secs(60))
55 .timeout(Duration::from_secs(30))
56 .connect_timeout(Duration::from_secs(10))
57 .build()
58 .map_err(|e| DomainError::InternalError(format!("Failed to create HTTP client: {}", e)))
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn test_http_client_manager_creation() {
68 let manager = HttpClientManager::new().unwrap();
69 let client = manager.default_client();
70 assert!(Arc::strong_count(&client) >= 1);
71 }
72
73 #[test]
74 fn test_client_caching() {
75 let manager = HttpClientManager::new().unwrap();
76
77 let client1 = manager.client_for_url("https://api.github.com").unwrap();
78 let client2 = manager.client_for_url("https://api.github.com").unwrap();
79
80 assert!(Arc::ptr_eq(&client1, &client2));
81 }
82
83 #[test]
84 fn test_different_urls_different_clients() {
85 let manager = HttpClientManager::new().unwrap();
86
87 let client1 = manager.client_for_url("https://api.github.com").unwrap();
88 let client2 = manager.client_for_url("https://gitlab.com").unwrap();
89
90 assert!(!Arc::ptr_eq(&client1, &client2));
91 }
92
93 #[test]
94 fn test_default_client_separate_from_custom() {
95 let manager = HttpClientManager::new().unwrap();
96
97 let default_client = manager.default_client();
98 let custom_client = manager.client_for_url("https://api.github.com").unwrap();
99
100 assert!(!Arc::ptr_eq(&default_client, &custom_client));
101 }
102}