rush_sync_server/server/
middleware.rs1use actix_web::{
2 dev::{Service, ServiceRequest, ServiceResponse, Transform},
3 Error,
4};
5use futures_util::future::LocalBoxFuture;
6use std::{
7 future::{ready, Ready},
8 sync::Arc,
9 time::Instant,
10};
11
12pub struct LoggingMiddleware {
13 server_logger: Arc<crate::server::logging::ServerLogger>,
14}
15
16impl LoggingMiddleware {
17 pub fn new(server_logger: Arc<crate::server::logging::ServerLogger>) -> Self {
18 Self { server_logger }
19 }
20}
21
22impl<S, B> Transform<S, ServiceRequest> for LoggingMiddleware
23where
24 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
25 S::Future: 'static,
26 B: 'static,
27{
28 type Response = ServiceResponse<B>;
29 type Error = Error;
30 type InitError = ();
31 type Transform = LoggingMiddlewareService<S>;
32 type Future = Ready<std::result::Result<Self::Transform, Self::InitError>>;
33
34 fn new_transform(&self, service: S) -> Self::Future {
35 ready(Ok(LoggingMiddlewareService {
36 service,
37 server_logger: self.server_logger.clone(),
38 }))
39 }
40}
41
42pub struct LoggingMiddlewareService<S> {
43 service: S,
44 server_logger: Arc<crate::server::logging::ServerLogger>,
45}
46
47impl<S, B> Service<ServiceRequest> for LoggingMiddlewareService<S>
48where
49 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
50 S::Future: 'static,
51 B: 'static,
52{
53 type Response = ServiceResponse<B>;
54 type Error = Error;
55 type Future = LocalBoxFuture<'static, std::result::Result<Self::Response, Self::Error>>;
56
57 actix_web::dev::forward_ready!(service);
58
59 fn call(&self, req: ServiceRequest) -> Self::Future {
60 let start_time = Instant::now();
61 let server_logger = self.server_logger.clone();
62
63 let ip = {
65 let connection_info = req.connection_info();
66 connection_info
67 .realip_remote_addr()
68 .or_else(|| connection_info.peer_addr())
69 .unwrap_or("unknown")
70 .split(':')
71 .next()
72 .unwrap_or("unknown")
73 .to_string()
74 };
75
76 let path = req.path().to_string();
77 let method = req.method().to_string();
78 let query_string = req.query_string().to_string();
79
80 let suspicious = path.contains("..")
82 || path.contains("<script")
83 || path.contains("sql")
84 || path.len() > 1000;
85
86 if suspicious {
87 let logger_clone = server_logger.clone();
88 let ip_clone = ip.clone();
89 let path_clone = path.clone();
90 tokio::spawn(async move {
91 let _ = logger_clone
92 .log_security_alert(
93 &ip_clone,
94 "Suspicious Request",
95 &format!("Suspicious path: {}", path_clone),
96 )
97 .await;
98 });
99 }
100
101 let headers: std::collections::HashMap<String, String> = req
103 .headers()
104 .iter()
105 .filter_map(|(name, value)| {
106 let header_name = name.as_str().to_lowercase();
107 if !["authorization", "cookie", "x-api-key"].contains(&header_name.as_str()) {
108 value
109 .to_str()
110 .ok()
111 .map(|v| (name.as_str().to_string(), v.to_string()))
112 } else {
113 Some((name.as_str().to_string(), "[FILTERED]".to_string()))
114 }
115 })
116 .collect();
117
118 let fut = self.service.call(req);
119
120 Box::pin(async move {
121 let res = fut.await?;
122 let response_time = start_time.elapsed().as_millis() as u64;
123 let status = res.status().as_u16();
124 let bytes_sent = res
125 .response()
126 .headers()
127 .get("content-length")
128 .and_then(|h| h.to_str().ok())
129 .and_then(|s| s.parse().ok())
130 .unwrap_or(0);
131
132 let entry = crate::server::logging::ServerLogEntry {
133 timestamp: chrono::Local::now()
134 .format("%Y-%m-%d %H:%M:%S%.3f")
135 .to_string(),
136 timestamp_unix: std::time::SystemTime::now()
137 .duration_since(std::time::UNIX_EPOCH)
138 .unwrap_or_default()
139 .as_secs(),
140 event_type: crate::server::logging::LogEventType::Request,
141 ip_address: ip,
142 user_agent: headers.get("user-agent").cloned(),
143 method,
144 path,
145 status_code: Some(status),
146 response_time_ms: Some(response_time),
147 bytes_sent: Some(bytes_sent),
148 referer: headers.get("referer").cloned(),
149 query_string: if query_string.is_empty() {
150 None
151 } else {
152 Some(query_string)
153 },
154 headers,
155 session_id: None, };
157
158 if let Err(e) = server_logger.write_log_entry(entry).await {
159 log::error!("Failed to log request: {}", e);
160 }
161
162 Ok(res)
163 })
164 }
165}