Skip to main content

reqwest_proxy_pool/
proxy.rs

1//! Proxy representation and status.
2
3use governor::{
4    clock::DefaultClock,
5    middleware::NoOpMiddleware,
6    state::{InMemoryState, NotKeyed},
7    Quota, RateLimiter,
8};
9use std::num::NonZeroU32;
10use std::sync::Arc;
11use std::time::{Duration, Instant};
12
13/// Status of a proxy.
14#[derive(Debug, Clone, Copy, PartialEq)]
15pub enum ProxyStatus {
16    /// The proxy has not been tested yet.
17    Unknown,
18    /// The proxy is healthy and can be used.
19    Healthy,
20    /// The proxy is unhealthy and should not be used.
21    Unhealthy,
22}
23
24/// Representation of a proxy server.
25#[derive(Debug, Clone)]
26pub struct Proxy {
27    /// The URL of the proxy (e.g. "socks5://127.0.0.1:1080").
28    pub url: String,
29    /// The current status of the proxy.
30    pub status: ProxyStatus,
31    /// Number of successful requests made through this proxy.
32    pub success_count: usize,
33    /// Number of failed requests made through this proxy.
34    pub failure_count: usize,
35    /// Time when this proxy was last checked.
36    pub last_check: Instant,
37    /// Average response time in seconds, if available.
38    pub response_time: Option<f64>,
39    /// Rate limiter to enforce minimum interval between requests.
40    pub limiter: Arc<RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>>,
41}
42
43impl Proxy {
44    /// Create a new proxy with the given URL and rate limit.
45    pub fn new(url: String, min_request_interval_ms: u64) -> Self {
46        // Create a rate limiter for this proxy (1 request per interval).
47        let period = Duration::from_millis(min_request_interval_ms.max(1));
48        let quota = Quota::with_period(period)
49            .unwrap_or_else(|| Quota::per_second(NonZeroU32::new(1).unwrap()))
50            .allow_burst(NonZeroU32::new(1).unwrap());
51        let limiter = Arc::new(RateLimiter::direct(quota));
52
53        Self {
54            url,
55            status: ProxyStatus::Unknown,
56            success_count: 0,
57            failure_count: 0,
58            last_check: Instant::now(),
59            response_time: None,
60            limiter,
61        }
62    }
63
64    /// Convert the proxy URL to a reqwest::Proxy.
65    pub fn to_reqwest_proxy(&self) -> Result<reqwest::Proxy, reqwest::Error> {
66        reqwest::Proxy::all(&self.url)
67    }
68
69    /// Calculate the success rate of this proxy.
70    pub fn success_rate(&self) -> f64 {
71        let total = self.success_count + self.failure_count;
72        if total == 0 {
73            return 0.0;
74        }
75        self.success_count as f64 / total as f64
76    }
77}