bindcar/middleware.rs
1// Copyright (c) 2025 Erick Bourgeois, firestoned
2// SPDX-License-Identifier: MIT
3
4//! Middleware for metrics collection
5
6use axum::{extract::MatchedPath, extract::Request, middleware::Next, response::Response};
7use std::time::Instant;
8
9use crate::metrics;
10
11/// Label used for requests that do not match any route (404s, scans).
12const UNMATCHED_PATH_LABEL: &str = "<unmatched>";
13
14/// Middleware to track HTTP request metrics
15pub async fn track_metrics(req: Request, next: Next) -> Response {
16 let start = Instant::now();
17 let method = req.method().to_string();
18 // Use the matched ROUTE TEMPLATE (e.g. "/api/v1/zones/{zone_name}/records")
19 // as the metric label, never the raw request path. The raw path is
20 // attacker-controlled and high-cardinality: it embeds zone/record names
21 // (information disclosure via the public /metrics endpoint) and lets an
22 // attacker mint unbounded distinct label values to exhaust memory (A-4).
23 let path = req
24 .extensions()
25 .get::<MatchedPath>()
26 .map(|m| m.as_str().to_string())
27 .unwrap_or_else(|| UNMATCHED_PATH_LABEL.to_string());
28
29 // Process the request
30 let response = next.run(req).await;
31
32 // Record metrics
33 let duration = start.elapsed().as_secs_f64();
34 let status = response.status().as_u16();
35
36 metrics::record_http_request(&method, &path, status, duration);
37
38 response
39}