cratestack_axum/headers/
forwarded.rs1use axum::http::HeaderMap;
2
3pub fn parse_client_ip(headers: &HeaderMap) -> Option<String> {
8 if let Some(forwarded) = headers.get("forwarded").and_then(|v| v.to_str().ok()) {
9 for segment in forwarded.split(',').map(str::trim) {
10 for kv in segment.split(';').map(str::trim) {
11 if let Some(rest) = kv.strip_prefix("for=") {
12 let cleaned = rest.trim_matches('"');
13 let cleaned = cleaned
14 .strip_prefix('[')
15 .and_then(|s| s.strip_suffix(']'))
16 .unwrap_or(cleaned);
17 if !cleaned.is_empty() {
18 return Some(cleaned.to_owned());
19 }
20 }
21 }
22 }
23 }
24 headers
25 .get("x-forwarded-for")
26 .and_then(|v| v.to_str().ok())
27 .and_then(|raw| raw.split(',').next())
28 .map(|s| s.trim().to_owned())
29 .filter(|s| !s.is_empty())
30}