cloudillo_core/rate_limit/
api.rs1use std::net::IpAddr;
9use std::time::{Duration, Instant};
10
11use super::error::PowError;
12use super::extractors::AddressKey;
13use crate::prelude::*;
14
15#[derive(Debug, Clone)]
17pub struct RateLimitStatus {
18 pub is_limited: bool,
20 pub remaining: Option<u32>,
22 pub reset_at: Option<Instant>,
24 pub quota: u32,
26 pub is_banned: bool,
28 pub ban_expires_at: Option<Instant>,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum PenaltyReason {
35 AuthFailure,
37 TokenVerificationFailure,
39 SuspiciousActivity,
41 RepeatedViolation,
43}
44
45impl PenaltyReason {
46 pub fn failures_to_ban(&self) -> u32 {
48 match self {
49 PenaltyReason::AuthFailure => 5,
50 PenaltyReason::TokenVerificationFailure => 3,
51 PenaltyReason::SuspiciousActivity => 2,
52 PenaltyReason::RepeatedViolation => 1,
53 }
54 }
55
56 pub fn ban_duration(&self) -> Duration {
58 match self {
59 PenaltyReason::AuthFailure | PenaltyReason::TokenVerificationFailure => {
61 Duration::from_secs(3600)
62 }
63 PenaltyReason::SuspiciousActivity => Duration::from_secs(7200), PenaltyReason::RepeatedViolation => Duration::from_secs(86400), }
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum PowPenaltyReason {
72 ConnSignatureFailure,
74 ConnDuplicatePending,
76 ConnRejected,
78 ConnPowCheckFailed,
80}
81
82impl PowPenaltyReason {
83 pub fn affects_network(&self) -> bool {
85 match self {
86 PowPenaltyReason::ConnRejected => false, PowPenaltyReason::ConnSignatureFailure
89 | PowPenaltyReason::ConnDuplicatePending
90 | PowPenaltyReason::ConnPowCheckFailed => true,
91 }
92 }
93}
94
95#[derive(Debug, Clone)]
97pub struct BanEntry {
98 pub key: AddressKey,
100 pub reason: PenaltyReason,
102 pub created_at: Instant,
104 pub expires_at: Option<Instant>,
106}
107
108impl BanEntry {
109 pub fn is_expired(&self) -> bool {
111 self.expires_at.is_some_and(|exp| Instant::now() >= exp)
112 }
113
114 pub fn remaining_duration(&self) -> Option<Duration> {
116 self.expires_at.map(|exp| {
117 let now = Instant::now();
118 if now >= exp {
119 Duration::ZERO
120 } else {
121 exp - now
122 }
123 })
124 }
125}
126
127#[derive(Debug, Clone, Default)]
129pub struct RateLimiterStats {
130 pub tracked_addresses: usize,
132 pub active_bans: usize,
134 pub total_requests_limited: u64,
136 pub total_bans_issued: u64,
138 pub pow_individual_entries: usize,
140 pub pow_network_entries: usize,
142}
143
144pub trait RateLimitApi: Send + Sync {
146 fn get_status(
148 &self,
149 addr: &IpAddr,
150 category: &str,
151 ) -> ClResult<Vec<(AddressKey, RateLimitStatus)>>;
152
153 fn penalize(&self, addr: &IpAddr, reason: PenaltyReason, amount: u32) -> ClResult<()>;
155
156 fn grant(&self, addr: &IpAddr, amount: u32) -> ClResult<()>;
158
159 fn reset(&self, addr: &IpAddr) -> ClResult<()>;
161
162 fn ban(&self, addr: &IpAddr, duration: Duration, reason: PenaltyReason) -> ClResult<()>;
164
165 fn unban(&self, addr: &IpAddr) -> ClResult<()>;
167
168 fn is_banned(&self, addr: &IpAddr) -> bool;
170
171 fn list_bans(&self) -> Vec<BanEntry>;
173
174 fn stats(&self) -> RateLimiterStats;
176
177 fn get_pow_requirement(&self, addr: &IpAddr) -> u32;
181
182 fn increment_pow_counter(&self, addr: &IpAddr, reason: PowPenaltyReason) -> ClResult<()>;
184
185 fn decrement_pow_counter(&self, addr: &IpAddr, amount: u32) -> ClResult<()>;
187
188 fn verify_pow(&self, addr: &IpAddr, token: &str) -> Result<(), PowError>;
190}
191
192