cloudillo_core/rate_limit/
api.rs1use std::net::IpAddr;
6use std::time::{Duration, Instant};
7
8use super::error::PowError;
9use super::extractors::AddressKey;
10use crate::prelude::*;
11
12#[derive(Debug, Clone)]
14pub struct RateLimitStatus {
15 pub is_limited: bool,
17 pub remaining: Option<u32>,
19 pub reset_at: Option<Instant>,
21 pub quota: u32,
23 pub is_banned: bool,
25 pub ban_expires_at: Option<Instant>,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum PenaltyReason {
32 AuthFailure,
34 TokenVerificationFailure,
36 SuspiciousActivity,
38 RepeatedViolation,
40}
41
42impl PenaltyReason {
43 pub fn failures_to_ban(&self) -> u32 {
45 match self {
46 PenaltyReason::AuthFailure => 5,
47 PenaltyReason::TokenVerificationFailure => 3,
48 PenaltyReason::SuspiciousActivity => 2,
49 PenaltyReason::RepeatedViolation => 1,
50 }
51 }
52
53 pub fn ban_duration(&self) -> Duration {
55 match self {
56 PenaltyReason::AuthFailure => Duration::from_secs(3600), PenaltyReason::TokenVerificationFailure => Duration::from_secs(3600), PenaltyReason::SuspiciousActivity => Duration::from_secs(7200), PenaltyReason::RepeatedViolation => Duration::from_secs(86400), }
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum PowPenaltyReason {
67 ConnSignatureFailure,
69 ConnDuplicatePending,
71 ConnRejected,
73 ConnPowCheckFailed,
75}
76
77impl PowPenaltyReason {
78 pub fn affects_network(&self) -> bool {
80 match self {
81 PowPenaltyReason::ConnSignatureFailure => true,
82 PowPenaltyReason::ConnDuplicatePending => true,
83 PowPenaltyReason::ConnRejected => false, PowPenaltyReason::ConnPowCheckFailed => true, }
86 }
87}
88
89#[derive(Debug, Clone)]
91pub struct BanEntry {
92 pub key: AddressKey,
94 pub reason: PenaltyReason,
96 pub created_at: Instant,
98 pub expires_at: Option<Instant>,
100}
101
102impl BanEntry {
103 pub fn is_expired(&self) -> bool {
105 self.expires_at.is_some_and(|exp| Instant::now() >= exp)
106 }
107
108 pub fn remaining_duration(&self) -> Option<Duration> {
110 self.expires_at.map(|exp| {
111 let now = Instant::now();
112 if now >= exp {
113 Duration::ZERO
114 } else {
115 exp - now
116 }
117 })
118 }
119}
120
121#[derive(Debug, Clone, Default)]
123pub struct RateLimiterStats {
124 pub tracked_addresses: usize,
126 pub active_bans: usize,
128 pub total_requests_limited: u64,
130 pub total_bans_issued: u64,
132 pub pow_individual_entries: usize,
134 pub pow_network_entries: usize,
136}
137
138pub trait RateLimitApi: Send + Sync {
140 fn get_status(
142 &self,
143 addr: &IpAddr,
144 category: &str,
145 ) -> ClResult<Vec<(AddressKey, RateLimitStatus)>>;
146
147 fn penalize(&self, addr: &IpAddr, reason: PenaltyReason, amount: u32) -> ClResult<()>;
149
150 fn grant(&self, addr: &IpAddr, amount: u32) -> ClResult<()>;
152
153 fn reset(&self, addr: &IpAddr) -> ClResult<()>;
155
156 fn ban(&self, addr: &IpAddr, duration: Duration, reason: PenaltyReason) -> ClResult<()>;
158
159 fn unban(&self, addr: &IpAddr) -> ClResult<()>;
161
162 fn is_banned(&self, addr: &IpAddr) -> bool;
164
165 fn list_bans(&self) -> Vec<BanEntry>;
167
168 fn stats(&self) -> RateLimiterStats;
170
171 fn get_pow_requirement(&self, addr: &IpAddr) -> u32;
175
176 fn increment_pow_counter(&self, addr: &IpAddr, reason: PowPenaltyReason) -> ClResult<()>;
178
179 fn decrement_pow_counter(&self, addr: &IpAddr, amount: u32) -> ClResult<()>;
181
182 fn verify_pow(&self, addr: &IpAddr, token: &str) -> Result<(), PowError>;
184}
185
186