pub mod headers;
use crate::headers::{
extract_forwarded_header, extract_real_ip_header, extract_x_forwarded_for_header,
};
use either::Either;
use http::HeaderMap;
pub use ipnet::IpNet;
use std::iter::{empty, once};
use std::net::IpAddr;
pub fn real_ip(headers: &HeaderMap, remote: IpAddr, trusted_proxies: &[IpNet]) -> Option<IpAddr> {
let mut hops = get_forwarded_for(headers).chain(once(remote));
let first = hops.next();
let hops = first.iter().copied().chain(hops);
'outer: for hop in hops.rev() {
for proxy in trusted_proxies {
dbg!(proxy);
if proxy.contains(&hop) {
continue 'outer;
}
}
return Some(hop);
}
first
}
pub fn get_forwarded_for(headers: &HeaderMap) -> impl DoubleEndedIterator<Item = IpAddr> + '_ {
if let Some(header) = headers.get("forwarded") {
let header = header.to_str().unwrap_or_default();
return Either::Left(Either::Left(extract_forwarded_header(header)));
}
if let Some(header) = headers.get("x-forwarded-for") {
let header = header.to_str().unwrap_or_default();
return Either::Left(Either::Right(extract_x_forwarded_for_header(header)));
}
if let Some(header) = headers.get("x-real-ip") {
let header = header.to_str().unwrap_or_default();
return Either::Right(Either::Left(extract_real_ip_header(header)));
}
Either::Right(Either::Right(empty()))
}
#[allow(dead_code)]
#[doc = include_str!("../README.md")]
fn test_readme_examples() {}
#[test]
fn test_malformed() {
use http::header::HeaderValue;
let mut headers = HeaderMap::new();
headers.insert(
"forwarded",
HeaderValue::from_static(
"by=203.0.111.42;for=1.2.3.4:8888,for=5.6.7.8;proto=https;nonsense",
),
);
let trusted_proxies = [
IpAddr::from([2, 3, 4, 5]).into(),
IpAddr::from([5, 6, 7, 8]).into(),
];
let remote = IpAddr::from([2, 3, 4, 5]);
assert!(get_forwarded_for(&headers).next().is_none());
assert_eq!(
Some(IpAddr::from([2, 3, 4, 5])),
real_ip(&headers, remote, &trusted_proxies)
);
}