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 HalfOpen,
24}
25
26#[derive(Debug, Clone)]
28pub struct Proxy {
29 pub url: String,
31 pub status: ProxyStatus,
33 pub success_count: usize,
35 pub failure_count: usize,
37 pub last_check: Instant,
39 pub response_time: Option<f64>,
41 pub cooldown_until: Option<Instant>,
43 pub limiter: Arc<RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>>,
45}
46
47impl Proxy {
48 pub fn new(url: String, min_request_interval_ms: u64) -> Self {
50 let period = Duration::from_millis(min_request_interval_ms.max(1));
52 let quota = Quota::with_period(period)
53 .unwrap_or_else(|| Quota::per_second(NonZeroU32::new(1).unwrap()))
54 .allow_burst(NonZeroU32::new(1).unwrap());
55 let limiter = Arc::new(RateLimiter::direct(quota));
56
57 Self {
58 url,
59 status: ProxyStatus::Unknown,
60 success_count: 0,
61 failure_count: 0,
62 last_check: Instant::now(),
63 response_time: None,
64 cooldown_until: None,
65 limiter,
66 }
67 }
68
69 pub fn to_reqwest_proxy(&self) -> Result<reqwest::Proxy, reqwest::Error> {
71 reqwest::Proxy::all(&self.url)
72 }
73
74 pub fn success_rate(&self) -> f64 {
76 let total = self.success_count + self.failure_count;
77 if total == 0 {
78 return 0.0;
79 }
80 self.success_count as f64 / total as f64
81 }
82}