reqwest_proxy_pool/
proxy.rs1use 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#[derive(Debug, Clone, Copy, PartialEq)]
15pub enum ProxyStatus {
16 Unknown,
18 Healthy,
20 Unhealthy,
22}
23
24#[derive(Debug, Clone)]
26pub struct Proxy {
27 pub url: String,
29 pub status: ProxyStatus,
31 pub success_count: usize,
33 pub failure_count: usize,
35 pub last_check: Instant,
37 pub response_time: Option<f64>,
39 pub limiter: Arc<RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>>,
41}
42
43impl Proxy {
44 pub fn new(url: String, min_request_interval_ms: u64) -> Self {
46 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 pub fn to_reqwest_proxy(&self) -> Result<reqwest::Proxy, reqwest::Error> {
66 reqwest::Proxy::all(&self.url)
67 }
68
69 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}