rush_sync_server/server/
middleware.rs

1use 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        // Request-Info extrahieren (vollständig wie original)
64        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        // Security check
81        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        // Vollständige Header-Extraktion wie original
102        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, // Vereinfacht entfernt wegen Extension-Komplexität
156            };
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}