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 | PenaltyReason::TokenVerificationFailure => {
58 Duration::from_secs(3600)
59 }
60 PenaltyReason::SuspiciousActivity => Duration::from_secs(7200), PenaltyReason::RepeatedViolation => Duration::from_secs(86400), }
63 }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum PowPenaltyReason {
69 ConnSignatureFailure,
71 ConnDuplicatePending,
73 ConnRejected,
75 ConnPowCheckFailed,
77}
78
79impl PowPenaltyReason {
80 pub fn affects_network(&self) -> bool {
82 match self {
83 PowPenaltyReason::ConnRejected => false, PowPenaltyReason::ConnSignatureFailure
86 | PowPenaltyReason::ConnDuplicatePending
87 | PowPenaltyReason::ConnPowCheckFailed => true,
88 }
89 }
90}
91
92#[derive(Debug, Clone)]
94pub struct BanEntry {
95 pub key: AddressKey,
97 pub reason: PenaltyReason,
99 pub created_at: Instant,
101 pub expires_at: Option<Instant>,
103}
104
105impl BanEntry {
106 pub fn is_expired(&self) -> bool {
108 self.expires_at.is_some_and(|exp| Instant::now() >= exp)
109 }
110
111 pub fn remaining_duration(&self) -> Option<Duration> {
113 self.expires_at.map(|exp| {
114 let now = Instant::now();
115 if now >= exp {
116 Duration::ZERO
117 } else {
118 exp - now
119 }
120 })
121 }
122}
123
124#[derive(Debug, Clone, Default)]
126pub struct RateLimiterStats {
127 pub tracked_addresses: usize,
129 pub active_bans: usize,
131 pub total_requests_limited: u64,
133 pub total_bans_issued: u64,
135 pub pow_individual_entries: usize,
137 pub pow_network_entries: usize,
139}
140
141pub trait RateLimitApi: Send + Sync {
143 fn get_status(
145 &self,
146 addr: &IpAddr,
147 category: &str,
148 ) -> ClResult<Vec<(AddressKey, RateLimitStatus)>>;
149
150 fn penalize(&self, addr: &IpAddr, reason: PenaltyReason, amount: u32) -> ClResult<()>;
152
153 fn grant(&self, addr: &IpAddr, amount: u32) -> ClResult<()>;
155
156 fn reset(&self, addr: &IpAddr) -> ClResult<()>;
158
159 fn ban(&self, addr: &IpAddr, duration: Duration, reason: PenaltyReason) -> ClResult<()>;
161
162 fn unban(&self, addr: &IpAddr) -> ClResult<()>;
164
165 fn is_banned(&self, addr: &IpAddr) -> bool;
167
168 fn list_bans(&self) -> Vec<BanEntry>;
170
171 fn stats(&self) -> RateLimiterStats;
173
174 fn get_pow_requirement(&self, addr: &IpAddr) -> u32;
178
179 fn increment_pow_counter(&self, addr: &IpAddr, reason: PowPenaltyReason) -> ClResult<()>;
181
182 fn decrement_pow_counter(&self, addr: &IpAddr, amount: u32) -> ClResult<()>;
184
185 fn verify_pow(&self, addr: &IpAddr, token: &str) -> Result<(), PowError>;
187}
188
189