Skip to main content

mocopr_server/
middleware.rs

1//! Middleware for MCP servers
2
3use mocopr_core::prelude::*;
4use tracing::{error, info, warn};
5
6/// Middleware trait for processing requests
7#[async_trait::async_trait]
8pub trait Middleware: Send + Sync {
9    /// Process a request before it reaches the handler
10    async fn before_request(&self, request: &JsonRpcRequest) -> Result<()>;
11
12    /// Process a response before it's sent back
13    async fn after_response(
14        &self,
15        request: &JsonRpcRequest,
16        response: &JsonRpcResponse,
17    ) -> Result<()>;
18
19    /// Handle errors that occur during processing
20    async fn on_error(&self, request: &JsonRpcRequest, error: &Error) -> Result<()>;
21}
22
23/// Logging middleware
24pub struct LoggingMiddleware {
25    pub log_requests: bool,
26    pub log_responses: bool,
27    pub log_timing: bool,
28}
29
30impl LoggingMiddleware {
31    pub fn new() -> Self {
32        Self {
33            log_requests: true,
34            log_responses: false,
35            log_timing: true,
36        }
37    }
38
39    pub fn with_requests(mut self, enabled: bool) -> Self {
40        self.log_requests = enabled;
41        self
42    }
43
44    pub fn with_responses(mut self, enabled: bool) -> Self {
45        self.log_responses = enabled;
46        self
47    }
48
49    pub fn with_timing(mut self, enabled: bool) -> Self {
50        self.log_timing = enabled;
51        self
52    }
53}
54
55impl Default for LoggingMiddleware {
56    fn default() -> Self {
57        Self::new()
58    }
59}
60
61#[async_trait::async_trait]
62impl Middleware for LoggingMiddleware {
63    async fn before_request(&self, request: &JsonRpcRequest) -> Result<()> {
64        if self.log_requests {
65            info!("Request: {} (ID: {:?})", request.method, request.id);
66        }
67        Ok(())
68    }
69
70    async fn after_response(
71        &self,
72        request: &JsonRpcRequest,
73        response: &JsonRpcResponse,
74    ) -> Result<()> {
75        if self.log_responses {
76            if response.error.is_some() {
77                warn!(
78                    "Response error for {}: {:?}",
79                    request.method, response.error
80                );
81            } else {
82                info!(
83                    "Response success for {} (ID: {:?})",
84                    request.method, request.id
85                );
86            }
87        }
88        Ok(())
89    }
90
91    async fn on_error(&self, request: &JsonRpcRequest, error: &Error) -> Result<()> {
92        error!("Error processing {}: {}", request.method, error);
93        Ok(())
94    }
95}
96
97/// Rate limiting middleware
98pub struct RateLimitMiddleware {
99    rate_limiter: std::sync::Arc<tokio::sync::Mutex<mocopr_core::utils::RateLimiter>>,
100}
101
102impl RateLimitMiddleware {
103    pub fn new(max_requests: u32, window_duration: std::time::Duration) -> Self {
104        Self {
105            rate_limiter: std::sync::Arc::new(tokio::sync::Mutex::new(
106                mocopr_core::utils::RateLimiter::new(max_requests, window_duration),
107            )),
108        }
109    }
110}
111
112#[async_trait::async_trait]
113impl Middleware for RateLimitMiddleware {
114    async fn before_request(&self, _request: &JsonRpcRequest) -> Result<()> {
115        let mut limiter = self.rate_limiter.lock().await;
116        if !limiter.check_rate_limit() {
117            return Err(Error::Protocol(
118                mocopr_core::error::ProtocolError::RateLimitExceeded,
119            ));
120        }
121        Ok(())
122    }
123
124    async fn after_response(
125        &self,
126        _request: &JsonRpcRequest,
127        _response: &JsonRpcResponse,
128    ) -> Result<()> {
129        Ok(())
130    }
131
132    async fn on_error(&self, _request: &JsonRpcRequest, _error: &Error) -> Result<()> {
133        Ok(())
134    }
135}
136
137/// Authentication middleware
138pub struct AuthMiddleware {
139    api_keys: std::collections::HashSet<String>,
140}
141
142impl AuthMiddleware {
143    pub fn new() -> Self {
144        Self {
145            api_keys: std::collections::HashSet::new(),
146        }
147    }
148
149    pub fn with_api_key(mut self, key: String) -> Self {
150        self.api_keys.insert(key);
151        self
152    }
153
154    pub fn with_api_keys<I>(mut self, keys: I) -> Self
155    where
156        I: IntoIterator<Item = String>,
157    {
158        self.api_keys.extend(keys);
159        self
160    }
161}
162
163impl Default for AuthMiddleware {
164    fn default() -> Self {
165        Self::new()
166    }
167}
168
169#[async_trait::async_trait]
170impl Middleware for AuthMiddleware {
171    async fn before_request(&self, request: &JsonRpcRequest) -> Result<()> {
172        if self.api_keys.is_empty() {
173            return Ok(()); // No authentication required
174        }
175
176        // Check for API key in request params
177        if let Some(params) = &request.params
178            && let Some(auth) = params.get("auth")
179            && let Some(api_key) = auth.get("api_key")
180            && let Some(key_str) = api_key.as_str()
181            && self.api_keys.contains(key_str)
182        {
183            return Ok(());
184        }
185        Err(Error::Protocol(
186            mocopr_core::error::ProtocolError::PermissionDenied,
187        ))
188    }
189
190    async fn after_response(
191        &self,
192        _request: &JsonRpcRequest,
193        _response: &JsonRpcResponse,
194    ) -> Result<()> {
195        Ok(())
196    }
197
198    async fn on_error(&self, _request: &JsonRpcRequest, _error: &Error) -> Result<()> {
199        Ok(())
200    }
201}
202
203/// Metrics collection middleware with actual timing measurements
204pub struct MetricsMiddleware {
205    request_counts: std::sync::Arc<tokio::sync::RwLock<std::collections::HashMap<String, u64>>>,
206    response_times:
207        std::sync::Arc<tokio::sync::RwLock<std::collections::HashMap<String, Vec<u64>>>>,
208    // Track start times for in-flight requests
209    request_start_times:
210        std::sync::Arc<tokio::sync::RwLock<std::collections::HashMap<String, std::time::Instant>>>,
211}
212
213impl MetricsMiddleware {
214    pub fn new() -> Self {
215        Self {
216            request_counts: std::sync::Arc::new(tokio::sync::RwLock::new(
217                std::collections::HashMap::new(),
218            )),
219            response_times: std::sync::Arc::new(tokio::sync::RwLock::new(
220                std::collections::HashMap::new(),
221            )),
222            request_start_times: std::sync::Arc::new(tokio::sync::RwLock::new(
223                std::collections::HashMap::new(),
224            )),
225        }
226    }
227
228    pub async fn get_metrics(&self) -> MetricsSnapshot {
229        let counts = self.request_counts.read().await;
230        let times = self.response_times.read().await;
231
232        MetricsSnapshot {
233            request_counts: counts.clone(),
234            average_response_times: times
235                .iter()
236                .map(|(method, times)| {
237                    let avg = if times.is_empty() {
238                        0.0
239                    } else {
240                        times.iter().sum::<u64>() as f64 / times.len() as f64
241                    };
242                    (method.clone(), avg)
243                })
244                .collect(),
245        }
246    }
247}
248
249impl Default for MetricsMiddleware {
250    fn default() -> Self {
251        Self::new()
252    }
253}
254
255#[derive(Debug, Clone)]
256pub struct MetricsSnapshot {
257    pub request_counts: std::collections::HashMap<String, u64>,
258    pub average_response_times: std::collections::HashMap<String, f64>,
259}
260
261#[async_trait::async_trait]
262impl Middleware for MetricsMiddleware {
263    async fn before_request(&self, request: &JsonRpcRequest) -> Result<()> {
264        // Track request count
265        let mut counts = self.request_counts.write().await;
266        *counts.entry(request.method.clone()).or_insert(0) += 1;
267
268        // Record request start time - use request ID if available, otherwise method name
269        let request_key = request
270            .id
271            .as_ref()
272            .map(|id| match id {
273                RequestId::String(s) => s.clone(),
274                RequestId::Number(n) => n.to_string(),
275            })
276            .unwrap_or_else(|| request.method.clone());
277
278        let mut start_times = self.request_start_times.write().await;
279        start_times.insert(request_key, std::time::Instant::now());
280
281        Ok(())
282    }
283
284    async fn after_response(
285        &self,
286        request: &JsonRpcRequest,
287        _response: &JsonRpcResponse,
288    ) -> Result<()> {
289        // Calculate actual response time from before_request to after_response
290        let request_key = request
291            .id
292            .as_ref()
293            .map(|id| match id {
294                RequestId::String(s) => s.clone(),
295                RequestId::Number(n) => n.to_string(),
296            })
297            .unwrap_or_else(|| request.method.clone());
298
299        let mut start_times = self.request_start_times.write().await;
300        if let Some(start_time) = start_times.remove(&request_key) {
301            let elapsed = start_time.elapsed();
302            let elapsed_ms = elapsed.as_millis() as u64;
303
304            let mut times = self.response_times.write().await;
305            times
306                .entry(request.method.clone())
307                .or_insert_with(Vec::new)
308                .push(elapsed_ms);
309        } else {
310            // Fallback: log a warning if we couldn't find the start time
311            warn!(
312                "No start time found for request: {} (method: {})",
313                request_key, request.method
314            );
315        }
316
317        Ok(())
318    }
319
320    async fn on_error(&self, _request: &JsonRpcRequest, _error: &Error) -> Result<()> {
321        Ok(())
322    }
323}