Skip to content

Commit bc137fe

Browse files
TuntiiCopilot
andauthored
Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 9b8fae9 commit bc137fe

1 file changed

Lines changed: 63 additions & 1 deletion

File tree

crates/rustapi-core/src/server.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,74 @@ async fn handle_request(
288288
if let Some(ref metrics) = dashboard_metrics {
289289
let duration_ms = start.elapsed().as_millis() as u64;
290290
let is_error = response.status().is_server_error();
291-
metrics.record_request(&path, duration_ms, exec_path, is_error);
291+
let metrics_path = normalize_metrics_path(&path);
292+
metrics.record_request(&metrics_path, duration_ms, exec_path, is_error);
292293
}
293294

294295
response
295296
}
296297

298+
fn normalize_metrics_path(path: &str) -> String {
299+
const MAX_SEGMENTS: usize = 16;
300+
const MAX_SEGMENT_LEN: usize = 64;
301+
302+
if path.is_empty() || path == "/" {
303+
return "/".to_string();
304+
}
305+
306+
let mut normalized = String::with_capacity(path.len().min(256));
307+
normalized.push('/');
308+
309+
let mut first = true;
310+
for (idx, segment) in path.split('/').filter(|s| !s.is_empty()).enumerate() {
311+
if idx >= MAX_SEGMENTS {
312+
if !first {
313+
normalized.push('/');
314+
}
315+
normalized.push_str("{truncated}");
316+
break;
317+
}
318+
319+
if !first {
320+
normalized.push('/');
321+
}
322+
first = false;
323+
324+
if is_dynamic_metrics_segment(segment) || segment.len() > MAX_SEGMENT_LEN {
325+
normalized.push_str("{param}");
326+
} else {
327+
normalized.push_str(segment);
328+
}
329+
}
330+
331+
if normalized.len() > 256 {
332+
normalized.truncate(256);
333+
}
334+
335+
normalized
336+
}
337+
338+
fn is_dynamic_metrics_segment(segment: &str) -> bool {
339+
if segment.is_empty() {
340+
return false;
341+
}
342+
343+
if segment.bytes().all(|b| b.is_ascii_digit()) {
344+
return true;
345+
}
346+
347+
// Heuristic for UUID/opaque identifier segments:
348+
// long hex-ish strings, optionally containing dashes.
349+
let hex_or_dash = segment
350+
.bytes()
351+
.all(|b| b.is_ascii_hexdigit() || b == b'-');
352+
if hex_or_dash && segment.len() >= 16 {
353+
return true;
354+
}
355+
356+
false
357+
}
358+
297359
/// Direct routing without middleware chain - maximum performance path
298360
#[inline]
299361
async fn route_request_direct(

0 commit comments

Comments
 (0)