kode_bridge/
parser_cache.rs

1use parking_lot::Mutex;
2use std::collections::VecDeque;
3use std::sync::Arc;
4use tracing::debug;
5
6/// Type alias for complex HTTP parsing result
7pub type HttpParseResult = Result<(String, String, Vec<(String, String)>), httparse::Error>;
8
9/// Thread-safe HTTP parser cache to avoid repeated allocations
10pub struct HttpParserCache {
11    parsers: Arc<Mutex<VecDeque<ReusableParser>>>,
12    max_cache_size: usize,
13}
14
15/// Reusable HTTP parser - simplified version without lifetime issues
16#[derive(Default)]
17pub struct ReusableParser {
18    // We'll create headers buffer on each use to avoid lifetime issues
19    // This is still more efficient than creating parsers themselves repeatedly
20}
21
22impl ReusableParser {
23    /// Create a new reusable parser
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    /// Parse HTTP response
29    pub fn parse_response(
30        &mut self,
31        buffer: &[u8],
32    ) -> Result<(u16, Vec<(String, String)>), httparse::Error> {
33        // Use a local header buffer - still more efficient than recreating parser objects
34        let mut headers = [httparse::EMPTY_HEADER; 128];
35        let mut response = httparse::Response::new(&mut headers);
36
37        match response.parse(buffer)? {
38            httparse::Status::Complete(_) => {
39                let status_code = response.code.ok_or(httparse::Error::Status)?;
40
41                // Extract headers
42                let mut parsed_headers = Vec::with_capacity(response.headers.len());
43                for header in response.headers.iter() {
44                    parsed_headers.push((
45                        header.name.to_string(),
46                        String::from_utf8_lossy(header.value).to_string(),
47                    ));
48                }
49
50                Ok((status_code, parsed_headers))
51            }
52            httparse::Status::Partial => Err(httparse::Error::TooManyHeaders),
53        }
54    }
55
56    /// Parse HTTP request
57    pub fn parse_request(&mut self, buffer: &[u8]) -> HttpParseResult {
58        // Use a local header buffer
59        let mut headers = [httparse::EMPTY_HEADER; 128];
60        let mut request = httparse::Request::new(&mut headers);
61
62        match request.parse(buffer)? {
63            httparse::Status::Complete(_) => {
64                let method = request.method.ok_or(httparse::Error::Status)?;
65                let path = request.path.ok_or(httparse::Error::Status)?;
66
67                // Extract headers
68                let mut parsed_headers = Vec::with_capacity(request.headers.len());
69                for header in request.headers.iter() {
70                    parsed_headers.push((
71                        header.name.to_string(),
72                        String::from_utf8_lossy(header.value).to_string(),
73                    ));
74                }
75
76                Ok((method.to_string(), path.to_string(), parsed_headers))
77            }
78            httparse::Status::Partial => Err(httparse::Error::TooManyHeaders),
79        }
80    }
81
82    /// Reset parser state for reuse (no-op in simplified version)
83    pub fn reset(&mut self) {
84        // No state to reset in simplified version
85    }
86}
87
88impl HttpParserCache {
89    /// Create a new parser cache
90    pub fn new(max_cache_size: usize) -> Self {
91        Self {
92            parsers: Arc::new(Mutex::new(VecDeque::with_capacity(max_cache_size))),
93            max_cache_size,
94        }
95    }
96
97    /// Get a parser from cache or create new one
98    pub fn get(&self) -> CachedParser {
99        let mut parser = {
100            let mut parsers = self.parsers.lock();
101            parsers.pop_front().unwrap_or_else(|| {
102                debug!("Creating new HTTP parser");
103                ReusableParser::new()
104            })
105        };
106
107        parser.reset();
108
109        CachedParser {
110            parser: Some(parser),
111            cache: Arc::downgrade(&self.parsers),
112            max_cache_size: self.max_cache_size,
113        }
114    }
115
116    /// Get current cache size
117    pub fn size(&self) -> usize {
118        self.parsers.lock().len()
119    }
120
121    /// Pre-warm the cache
122    pub fn warm_up(&self, count: usize) {
123        let mut parsers = self.parsers.lock();
124        let current_size = parsers.len();
125        let to_create =
126            (count.saturating_sub(current_size)).min(self.max_cache_size - current_size);
127
128        for _ in 0..to_create {
129            parsers.push_back(ReusableParser::new());
130        }
131
132        debug!("Parser cache warmed up with {} parsers", to_create);
133    }
134}
135
136impl Default for HttpParserCache {
137    fn default() -> Self {
138        Self::new(8) // Default to 8 cached parsers
139    }
140}
141
142/// RAII wrapper that returns parser to cache on drop
143pub struct CachedParser {
144    parser: Option<ReusableParser>,
145    cache: std::sync::Weak<Mutex<VecDeque<ReusableParser>>>,
146    max_cache_size: usize,
147}
148
149impl CachedParser {
150    /// Parse HTTP response
151    pub fn parse_response(
152        &mut self,
153        buffer: &[u8],
154    ) -> Result<(u16, Vec<(String, String)>), httparse::Error> {
155        self.parser
156            .as_mut()
157            .expect("Parser not available")
158            .parse_response(buffer)
159    }
160
161    /// Parse HTTP request  
162    pub fn parse_request(&mut self, buffer: &[u8]) -> HttpParseResult {
163        self.parser
164            .as_mut()
165            .expect("Parser not available")
166            .parse_request(buffer)
167    }
168}
169
170impl Drop for CachedParser {
171    fn drop(&mut self) {
172        if let (Some(parser), Some(cache)) = (self.parser.take(), self.cache.upgrade()) {
173            let mut parsers = cache.lock();
174            if parsers.len() < self.max_cache_size {
175                parsers.push_back(parser);
176                debug!("Parser returned to cache");
177            }
178        }
179    }
180}
181
182/// Response caching system for frequently accessed data
183use std::collections::HashMap;
184use std::hash::{Hash, Hasher};
185use std::time::{Duration, Instant};
186
187/// Cache key for responses
188#[derive(Clone, Debug)]
189pub struct CacheKey {
190    method: String,
191    path: String,
192    headers_hash: u64,
193}
194
195impl CacheKey {
196    pub fn new(method: &str, path: &str, headers: &[(String, String)]) -> Self {
197        let mut hasher = std::collections::hash_map::DefaultHasher::new();
198
199        // Hash relevant headers (ignore cache-control, etc.)
200        let relevant_headers: Vec<_> = headers
201            .iter()
202            .filter(|(name, _)| {
203                let name_lower = name.to_lowercase();
204                !name_lower.starts_with("cache-")
205                    && name_lower != "date"
206                    && name_lower != "last-modified"
207                    && name_lower != "etag"
208            })
209            .collect();
210
211        relevant_headers.hash(&mut hasher);
212
213        Self {
214            method: method.to_string(),
215            path: path.to_string(),
216            headers_hash: hasher.finish(),
217        }
218    }
219}
220
221impl Hash for CacheKey {
222    fn hash<H: Hasher>(&self, state: &mut H) {
223        self.method.hash(state);
224        self.path.hash(state);
225        self.headers_hash.hash(state);
226    }
227}
228
229impl PartialEq for CacheKey {
230    fn eq(&self, other: &Self) -> bool {
231        self.method == other.method
232            && self.path == other.path
233            && self.headers_hash == other.headers_hash
234    }
235}
236
237impl Eq for CacheKey {}
238
239/// Cached response entry
240#[derive(Clone)]
241pub struct CachedResponse {
242    pub status_code: u16,
243    pub headers: Vec<(String, String)>,
244    pub body: bytes::Bytes,
245    pub cached_at: Instant,
246    pub expires_at: Option<Instant>,
247}
248
249/// Simple LRU response cache
250pub struct ResponseCache {
251    cache: Arc<Mutex<HashMap<CacheKey, CachedResponse>>>,
252    max_entries: usize,
253}
254
255impl ResponseCache {
256    pub fn new(max_entries: usize, _default_ttl: Duration) -> Self {
257        Self {
258            cache: Arc::new(Mutex::new(HashMap::with_capacity(max_entries))),
259            max_entries,
260        }
261    }
262
263    /// Get cached response if available and not expired
264    pub fn get(&self, key: &CacheKey) -> Option<CachedResponse> {
265        let mut cache = self.cache.lock();
266
267        if let Some(entry) = cache.get(key) {
268            // Check if expired
269            if let Some(expires_at) = entry.expires_at {
270                if Instant::now() > expires_at {
271                    cache.remove(key);
272                    return None;
273                }
274            }
275
276            Some(entry.clone())
277        } else {
278            None
279        }
280    }
281
282    /// Store response in cache
283    pub fn put(&self, key: CacheKey, response: CachedResponse) {
284        let mut cache = self.cache.lock();
285
286        // Simple eviction: remove oldest entries if over limit
287        if cache.len() >= self.max_entries {
288            // Find oldest entry to remove
289            let oldest_key = cache
290                .iter()
291                .min_by_key(|(_, v)| v.cached_at)
292                .map(|(k, _)| k.clone());
293
294            if let Some(key_to_remove) = oldest_key {
295                cache.remove(&key_to_remove);
296            }
297        }
298
299        cache.insert(key, response);
300    }
301
302    /// Clear expired entries
303    pub fn cleanup_expired(&self) {
304        let mut cache = self.cache.lock();
305        let now = Instant::now();
306
307        cache.retain(|_, entry| match entry.expires_at {
308            None => true,
309            Some(expires_at) => now <= expires_at,
310        });
311    }
312
313    /// Get cache statistics
314    pub fn stats(&self) -> CacheStats {
315        let cache = self.cache.lock();
316        CacheStats {
317            entries: cache.len(),
318            max_entries: self.max_entries,
319        }
320    }
321
322    /// Clear all cached entries
323    pub fn clear(&self) {
324        let mut cache = self.cache.lock();
325        cache.clear();
326    }
327}
328
329#[derive(Debug, Clone)]
330pub struct CacheStats {
331    pub entries: usize,
332    pub max_entries: usize,
333}
334
335// Global instances
336use std::sync::OnceLock;
337
338static GLOBAL_PARSER_CACHE: OnceLock<HttpParserCache> = OnceLock::new();
339static GLOBAL_RESPONSE_CACHE: OnceLock<ResponseCache> = OnceLock::new();
340
341/// Get global parser cache
342pub fn global_parser_cache() -> &'static HttpParserCache {
343    GLOBAL_PARSER_CACHE.get_or_init(|| {
344        let cache = HttpParserCache::new(8);
345        cache.warm_up(4);
346        cache
347    })
348}
349
350/// Get global response cache
351pub fn global_response_cache() -> &'static ResponseCache {
352    GLOBAL_RESPONSE_CACHE.get_or_init(|| {
353        ResponseCache::new(100, Duration::from_secs(300)) // 100 entries, 5 min TTL
354    })
355}
356
357#[cfg(test)]
358mod tests {
359    use super::*;
360
361    #[test]
362    fn test_parser_cache() {
363        let cache = HttpParserCache::new(2);
364
365        {
366            let mut parser1 = cache.get();
367            let http_response = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello";
368            let result = parser1.parse_response(http_response).unwrap();
369            assert_eq!(result.0, 200);
370            assert_eq!(result.1.len(), 1);
371            assert_eq!(result.1[0].0, "Content-Length");
372        }
373
374        // Parser should be returned to cache
375        assert_eq!(cache.size(), 1);
376
377        {
378            let mut parser2 = cache.get();
379            let http_request = b"GET /api HTTP/1.1\r\nHost: localhost\r\n\r\n";
380            let result = parser2.parse_request(http_request).unwrap();
381            assert_eq!(result.0, "GET");
382            assert_eq!(result.1, "/api");
383            assert_eq!(result.2.len(), 1);
384        }
385    }
386
387    #[test]
388    fn test_response_cache() {
389        let cache = ResponseCache::new(2, Duration::from_secs(1));
390
391        let key = CacheKey::new("GET", "/api", &[]);
392        let response = CachedResponse {
393            status_code: 200,
394            headers: vec![("Content-Type".to_string(), "application/json".to_string())],
395            body: bytes::Bytes::from("test"),
396            cached_at: Instant::now(),
397            expires_at: Some(Instant::now() + Duration::from_secs(1)),
398        };
399
400        cache.put(key.clone(), response.clone());
401
402        let cached = cache.get(&key).unwrap();
403        assert_eq!(cached.status_code, 200);
404        assert_eq!(cached.body, bytes::Bytes::from("test"));
405
406        // Test expiration
407        std::thread::sleep(Duration::from_millis(1100));
408        assert!(cache.get(&key).is_none());
409    }
410
411    #[test]
412    fn test_cache_key_equality() {
413        let headers1 = vec![
414            ("Content-Type".to_string(), "application/json".to_string()),
415            ("Authorization".to_string(), "Bearer token".to_string()),
416        ];
417
418        let headers2 = vec![
419            ("Content-Type".to_string(), "application/json".to_string()),
420            ("Authorization".to_string(), "Bearer token".to_string()),
421            (
422                "Date".to_string(),
423                "Wed, 21 Oct 2015 07:28:00 GMT".to_string(),
424            ),
425        ];
426
427        let key1 = CacheKey::new("GET", "/api", &headers1);
428        let key2 = CacheKey::new("GET", "/api", &headers2);
429
430        // Keys should be equal because Date header is ignored
431        assert_eq!(key1, key2);
432    }
433}