Skip to main content

reqwest_proxy_pool/
config.rs

1//! Configuration for the proxy pool.
2
3use crate::classifier::{DefaultResponseClassifier, ResponseClassifier};
4use std::fmt;
5use std::sync::Arc;
6use std::time::Duration;
7
8/// Strategy for selecting a proxy from the pool.
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub enum ProxySelectionStrategy {
11    /// Select the proxy with the fastest response time.
12    FastestResponse,
13    /// Select the proxy with the highest success rate.
14    MostReliable,
15    /// Select a random healthy proxy.
16    Random,
17    /// Select proxies in round-robin fashion.
18    RoundRobin,
19}
20
21/// Configuration for the proxy pool.
22#[derive(Clone)]
23pub struct ProxyPoolConfig {
24    /// Source URLs to fetch proxy lists from.
25    pub(crate) sources: Vec<String>,
26    /// Interval between health checks.
27    pub(crate) health_check_interval: Duration,
28    /// Timeout for health checks.
29    pub(crate) health_check_timeout: Duration,
30    /// Minimum number of available proxies.
31    pub(crate) min_available_proxies: usize,
32    /// URL used for health checks.
33    pub(crate) health_check_url: String,
34    /// Number of times to retry a request with different proxies.
35    pub(crate) retry_count: usize,
36    /// Strategy for selecting proxies.
37    pub(crate) selection_strategy: ProxySelectionStrategy,
38    /// Maximum requests per second per proxy.
39    pub(crate) max_requests_per_second: f64,
40    /// Response classifier for business-level proxy health feedback.
41    pub(crate) response_classifier: Arc<dyn ResponseClassifier>,
42    /// Accept invalid TLS certificates (needed for most free SOCKS5 proxies).
43    pub(crate) danger_accept_invalid_certs: bool,
44}
45
46impl fmt::Debug for ProxyPoolConfig {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.debug_struct("ProxyPoolConfig")
49            .field("sources", &self.sources)
50            .field("health_check_interval", &self.health_check_interval)
51            .field("health_check_timeout", &self.health_check_timeout)
52            .field("min_available_proxies", &self.min_available_proxies)
53            .field("health_check_url", &self.health_check_url)
54            .field("retry_count", &self.retry_count)
55            .field("selection_strategy", &self.selection_strategy)
56            .field("max_requests_per_second", &self.max_requests_per_second)
57            .field("response_classifier", &"<dyn ResponseClassifier>")
58            .field(
59                "danger_accept_invalid_certs",
60                &self.danger_accept_invalid_certs,
61            )
62            .finish()
63    }
64}
65
66impl ProxyPoolConfig {
67    /// Create a new configuration builder.
68    pub fn builder() -> ProxyPoolConfigBuilder {
69        ProxyPoolConfigBuilder::new()
70    }
71
72    /// Source URLs to fetch proxy lists from.
73    pub fn sources(&self) -> &[String] {
74        &self.sources
75    }
76
77    /// Interval between health checks.
78    pub fn health_check_interval(&self) -> Duration {
79        self.health_check_interval
80    }
81
82    /// Timeout for health checks.
83    pub fn health_check_timeout(&self) -> Duration {
84        self.health_check_timeout
85    }
86
87    /// Minimum number of available proxies.
88    pub fn min_available_proxies(&self) -> usize {
89        self.min_available_proxies
90    }
91
92    /// URL used for health checks.
93    pub fn health_check_url(&self) -> &str {
94        &self.health_check_url
95    }
96
97    /// Number of times to retry a request with different proxies.
98    pub fn retry_count(&self) -> usize {
99        self.retry_count
100    }
101
102    /// Strategy for selecting proxies.
103    pub fn selection_strategy(&self) -> ProxySelectionStrategy {
104        self.selection_strategy
105    }
106
107    /// Maximum requests per second per proxy.
108    pub fn max_requests_per_second(&self) -> f64 {
109        self.max_requests_per_second
110    }
111
112    /// Response classifier for business-level proxy health feedback.
113    pub fn response_classifier(&self) -> &Arc<dyn ResponseClassifier> {
114        &self.response_classifier
115    }
116
117    /// Whether invalid TLS certificates are accepted when connecting through proxies.
118    pub fn danger_accept_invalid_certs(&self) -> bool {
119        self.danger_accept_invalid_certs
120    }
121}
122
123/// Builder for `ProxyPoolConfig`.
124pub struct ProxyPoolConfigBuilder {
125    sources: Vec<String>,
126    health_check_interval: Option<Duration>,
127    health_check_timeout: Option<Duration>,
128    min_available_proxies: Option<usize>,
129    health_check_url: Option<String>,
130    retry_count: Option<usize>,
131    selection_strategy: Option<ProxySelectionStrategy>,
132    max_requests_per_second: Option<f64>,
133    response_classifier: Option<Arc<dyn ResponseClassifier>>,
134    danger_accept_invalid_certs: bool,
135}
136
137impl ProxyPoolConfigBuilder {
138    /// Create a new builder with default values.
139    pub fn new() -> Self {
140        Self {
141            sources: Vec::new(),
142            health_check_interval: None,
143            health_check_timeout: None,
144            min_available_proxies: None,
145            health_check_url: None,
146            retry_count: None,
147            selection_strategy: None,
148            max_requests_per_second: None,
149            response_classifier: None,
150            danger_accept_invalid_certs: false,
151        }
152    }
153
154    /// Set the source URLs to fetch proxy lists from.
155    pub fn sources(mut self, sources: Vec<impl Into<String>>) -> Self {
156        self.sources = sources.into_iter().map(Into::into).collect();
157        self
158    }
159
160    /// Set the interval between health checks.
161    pub fn health_check_interval(mut self, interval: Duration) -> Self {
162        self.health_check_interval = Some(interval);
163        self
164    }
165
166    /// Set the timeout for health checks.
167    pub fn health_check_timeout(mut self, timeout: Duration) -> Self {
168        self.health_check_timeout = Some(timeout);
169        self
170    }
171
172    /// Set the minimum number of available proxies.
173    pub fn min_available_proxies(mut self, count: usize) -> Self {
174        self.min_available_proxies = Some(count);
175        self
176    }
177
178    /// Set the URL used for health checks.
179    pub fn health_check_url(mut self, url: impl Into<String>) -> Self {
180        self.health_check_url = Some(url.into());
181        self
182    }
183
184    /// Set the number of times to retry a request with different proxies.
185    pub fn retry_count(mut self, count: usize) -> Self {
186        self.retry_count = Some(count);
187        self
188    }
189
190    /// Set the strategy for selecting proxies.
191    pub fn selection_strategy(mut self, strategy: ProxySelectionStrategy) -> Self {
192        self.selection_strategy = Some(strategy);
193        self
194    }
195
196    /// Set the maximum requests per second per proxy.
197    pub fn max_requests_per_second(mut self, rps: f64) -> Self {
198        self.max_requests_per_second = Some(rps);
199        self
200    }
201
202    /// Set a custom response classifier for business-level proxy health feedback.
203    ///
204    /// Use this to detect captchas, anti-bot pages, or other blocking responses
205    /// that pass HTTP-level checks but indicate the proxy is unusable.
206    pub fn response_classifier(mut self, classifier: impl ResponseClassifier) -> Self {
207        self.response_classifier = Some(Arc::new(classifier));
208        self
209    }
210
211    /// Accept invalid TLS certificates when connecting through proxies.
212    /// Required for most free SOCKS5 proxies that perform TLS interception.
213    /// Default: `false`.
214    pub fn danger_accept_invalid_certs(mut self, accept: bool) -> Self {
215        self.danger_accept_invalid_certs = accept;
216        self
217    }
218
219    /// Build the configuration.
220    pub fn build(self) -> ProxyPoolConfig {
221        ProxyPoolConfig {
222            sources: self.sources,
223            health_check_interval: self
224                .health_check_interval
225                .unwrap_or(Duration::from_secs(300)),
226            health_check_timeout: self.health_check_timeout.unwrap_or(Duration::from_secs(10)),
227            min_available_proxies: self.min_available_proxies.unwrap_or(3),
228            health_check_url: self
229                .health_check_url
230                .unwrap_or_else(|| "https://www.google.com".to_string()),
231            retry_count: self.retry_count.unwrap_or(3),
232            selection_strategy: self
233                .selection_strategy
234                .unwrap_or(ProxySelectionStrategy::FastestResponse),
235            max_requests_per_second: self.max_requests_per_second.unwrap_or(5.0),
236            response_classifier: self
237                .response_classifier
238                .unwrap_or_else(|| Arc::new(DefaultResponseClassifier)),
239            danger_accept_invalid_certs: self.danger_accept_invalid_certs,
240        }
241    }
242}
243
244impl Default for ProxyPoolConfigBuilder {
245    fn default() -> Self {
246        Self::new()
247    }
248}