use comma_separated::CommaSeparatedIterator;
use either::Either;
use rfc7239::{parse, Forwarded, NodeIdentifier, NodeName};
use std::borrow::Cow;
use std::iter::{empty, IntoIterator};
use std::net::{AddrParseError, IpAddr};
use std::str::FromStr;
pub fn extract_forwarded_header(
header_value: &str,
) -> impl DoubleEndedIterator<Item = IpAddr> + '_ {
if parse(header_value).all(|result| result.is_ok()) {
Either::Left(parse(header_value).filter_map(|forward| match forward {
Ok(Forwarded {
forwarded_for:
Some(NodeIdentifier {
name: NodeName::Ip(ip),
..
}),
..
}) => Some(ip),
_ => None,
}))
} else {
Either::Right(empty())
}
}
fn extract_x_forwarded_for_header_raw(
header_value: &str,
) -> impl DoubleEndedIterator<Item = Result<IpAddr, AddrParseError>> + '_ {
CommaSeparatedIterator::new(header_value)
.map(str::trim)
.map(|x| IpAddr::from_str(maybe_bracketed(&maybe_quoted(x))))
}
pub fn extract_x_forwarded_for_header(
header_value: &str,
) -> impl DoubleEndedIterator<Item = IpAddr> + '_ {
if extract_x_forwarded_for_header_raw(header_value).all(|result| result.is_ok()) {
Either::Left(extract_x_forwarded_for_header_raw(header_value).flatten())
} else {
Either::Right(empty())
}
}
pub fn extract_real_ip_header(header_value: &str) -> impl DoubleEndedIterator<Item = IpAddr> + '_ {
IpAddr::from_str(maybe_bracketed(&maybe_quoted(header_value))).into_iter()
}
enum EscapeState {
Normal,
Escaped,
}
fn maybe_quoted<'a>(x: &'a str) -> Cow<'a, str> {
let mut i = x.chars();
if i.next() == Some('"') {
let mut s = String::with_capacity(x.len());
let mut state = EscapeState::Normal;
for c in i {
state = match state {
EscapeState::Normal => match c {
'"' => break,
'\\' => EscapeState::Escaped,
_ => {
s.push(c);
EscapeState::Normal
}
},
EscapeState::Escaped => {
s.push(c);
EscapeState::Normal
}
};
}
s.into()
} else {
x.into()
}
}
fn maybe_bracketed(x: &str) -> &str {
if x.as_bytes().first() == Some(&b'[') && x.as_bytes().last() == Some(&b']') {
&x[1..x.len() - 1]
} else {
x
}
}