1use comma_separated::CommaSeparatedIterator;
2use either::Either;
3use rfc7239::{parse, Forwarded, NodeIdentifier, NodeName};
4use std::borrow::Cow;
5use std::iter::{empty, IntoIterator};
6use std::net::{AddrParseError, IpAddr};
7use std::str::FromStr;
8
9pub fn extract_forwarded_header(
24 header_value: &str,
25) -> impl DoubleEndedIterator<Item = IpAddr> + '_ {
26 if parse(header_value).all(|result| result.is_ok()) {
27 Either::Left(parse(header_value).filter_map(|forward| match forward {
28 Ok(Forwarded {
29 forwarded_for:
30 Some(NodeIdentifier {
31 name: NodeName::Ip(ip),
32 ..
33 }),
34 ..
35 }) => Some(ip),
36 _ => None,
37 }))
38 } else {
39 Either::Right(empty())
40 }
41}
42
43fn extract_x_forwarded_for_header_raw(
44 header_value: &str,
45) -> impl DoubleEndedIterator<Item = Result<IpAddr, AddrParseError>> + '_ {
46 CommaSeparatedIterator::new(header_value)
47 .map(str::trim)
48 .map(|x| IpAddr::from_str(maybe_bracketed(&maybe_quoted(x))))
49}
50
51pub fn extract_x_forwarded_for_header(
64 header_value: &str,
65) -> impl DoubleEndedIterator<Item = IpAddr> + '_ {
66 if extract_x_forwarded_for_header_raw(header_value).all(|result| result.is_ok()) {
67 Either::Left(extract_x_forwarded_for_header_raw(header_value).flatten())
68 } else {
69 Either::Right(empty())
70 }
71}
72
73pub fn extract_real_ip_header(header_value: &str) -> impl DoubleEndedIterator<Item = IpAddr> + '_ {
86 IpAddr::from_str(maybe_bracketed(&maybe_quoted(header_value))).into_iter()
87}
88
89enum EscapeState {
90 Normal,
91 Escaped,
92}
93
94fn maybe_quoted<'a>(x: &'a str) -> Cow<'a, str> {
95 let mut i = x.chars();
96 if i.next() == Some('"') {
97 let mut s = String::with_capacity(x.len());
98 let mut state = EscapeState::Normal;
99 for c in i {
100 state = match state {
101 EscapeState::Normal => match c {
102 '"' => break,
103 '\\' => EscapeState::Escaped,
104 _ => {
105 s.push(c);
106 EscapeState::Normal
107 }
108 },
109 EscapeState::Escaped => {
110 s.push(c);
111 EscapeState::Normal
112 }
113 };
114 }
115 s.into()
116 } else {
117 x.into()
118 }
119}
120
121fn maybe_bracketed(x: &str) -> &str {
122 if x.as_bytes().first() == Some(&b'[') && x.as_bytes().last() == Some(&b']') {
123 &x[1..x.len() - 1]
124 } else {
125 x
126 }
127}