rust_web_server/blocklist/
mod.rs1#[cfg(test)]
25mod tests;
26
27use std::sync::{Mutex, OnceLock};
28
29use crate::application::Application;
30use crate::error::{AppError, IntoResponse};
31use crate::middleware::Middleware;
32use crate::request::Request;
33use crate::response::Response;
34use crate::server::ConnectionInfo;
35
36pub struct Blocklist {
38 denied: Mutex<Vec<String>>,
39}
40
41impl Blocklist {
42 fn new() -> Self {
43 Blocklist { denied: Mutex::new(Vec::new()) }
44 }
45
46 pub fn block(&self, ip: &str) {
48 let mut guard = self.denied.lock().unwrap();
49 if !guard.iter().any(|e| e == ip) {
50 guard.push(ip.to_string());
51 }
52 }
53
54 pub fn unblock(&self, ip: &str) {
56 self.denied.lock().unwrap().retain(|e| e != ip);
57 }
58
59 pub fn is_blocked(&self, ip: &str) -> bool {
61 self.denied.lock().unwrap().iter().any(|e| e == ip)
62 }
63
64 pub fn list(&self) -> Vec<String> {
66 self.denied.lock().unwrap().clone()
67 }
68
69 pub fn clear(&self) {
71 self.denied.lock().unwrap().clear();
72 }
73}
74
75static INSTANCE: OnceLock<Blocklist> = OnceLock::new();
76
77pub fn global() -> &'static Blocklist {
79 INSTANCE.get_or_init(Blocklist::new)
80}
81
82pub struct BlocklistLayer;
87
88impl Middleware for BlocklistLayer {
89 fn handle(
90 &self,
91 request: &Request,
92 connection: &ConnectionInfo,
93 next: &dyn Application,
94 ) -> Result<Response, String> {
95 if global().is_blocked(&connection.client.ip) {
96 return Ok(AppError::Forbidden.into_response());
97 }
98 next.execute(request, connection)
99 }
100}