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 => {
60 Duration::from_hours(1)
61 }
62 PenaltyReason::SuspiciousActivity => Duration::from_hours(2),
63 PenaltyReason::RepeatedViolation => Duration::from_hours(24),
64 }
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum PowPenaltyReason {
71 ConnSignatureFailure,
73 ConnDuplicatePending,
75 ConnRejected,
77 ConnPowCheckFailed,
79}
80
81impl PowPenaltyReason {
82 pub fn affects_network(&self) -> bool {
84 match self {
85 PowPenaltyReason::ConnRejected => false, PowPenaltyReason::ConnSignatureFailure
88 | PowPenaltyReason::ConnDuplicatePending
89 | PowPenaltyReason::ConnPowCheckFailed => true,
90 }
91 }
92}
93
94#[derive(Debug, Clone)]
96pub struct BanEntry {
97 pub key: AddressKey,
99 pub reason: PenaltyReason,
101 pub created_at: Instant,
103 pub expires_at: Option<Instant>,
105}
106
107impl BanEntry {
108 pub fn is_expired(&self) -> bool {
110 self.expires_at.is_some_and(|exp| Instant::now() >= exp)
111 }
112
113 pub fn remaining_duration(&self) -> Option<Duration> {
115 self.expires_at.map(|exp| {
116 let now = Instant::now();
117 if now >= exp { Duration::ZERO } else { exp - now }
118 })
119 }
120}
121
122#[derive(Debug, Clone, Default)]
124pub struct RateLimiterStats {
125 pub tracked_addresses: usize,
127 pub active_bans: usize,
129 pub total_requests_limited: u64,
131 pub total_bans_issued: u64,
133 pub pow_individual_entries: usize,
135 pub pow_network_entries: usize,
137}
138
139pub trait RateLimitApi: Send + Sync {
141 fn get_status(
143 &self,
144 addr: &IpAddr,
145 category: &str,
146 ) -> ClResult<Vec<(AddressKey, RateLimitStatus)>>;
147
148 fn penalize(&self, addr: &IpAddr, reason: PenaltyReason, amount: u32) -> ClResult<()>;
150
151 fn grant(&self, addr: &IpAddr, amount: u32) -> ClResult<()>;
153
154 fn reset(&self, addr: &IpAddr) -> ClResult<()>;
156
157 fn ban(&self, addr: &IpAddr, duration: Duration, reason: PenaltyReason) -> ClResult<()>;
159
160 fn unban(&self, addr: &IpAddr) -> ClResult<()>;
162
163 fn is_banned(&self, addr: &IpAddr) -> bool;
165
166 fn list_bans(&self) -> Vec<BanEntry>;
168
169 fn stats(&self) -> RateLimiterStats;
171
172 fn get_pow_requirement(&self, addr: &IpAddr) -> u32;
176
177 fn increment_pow_counter(&self, addr: &IpAddr, reason: PowPenaltyReason) -> ClResult<()>;
179
180 fn decrement_pow_counter(&self, addr: &IpAddr, amount: u32) -> ClResult<()>;
182
183 fn verify_pow(&self, addr: &IpAddr, token: &str) -> Result<(), PowError>;
185}
186
187