http_ip/
forwarded.rs

1//! `Forwarded` header module
2
3use core::{marker, fmt};
4use core::net::IpAddr;
5
6//Forwarded syntax
7//Syntax is: <entry 1>, <entry N>
8//Entry is: <key1>=<value1>;<keyN>=<valueN>
9const FORWARDED_SEP: char = ',';
10const ENTRY_SEP: char = ';';
11const PAIR_SEP: char = '=';
12
13#[derive(Clone, Copy, PartialEq, Eq, Debug)]
14///Parsed node of the Forwarded header
15///
16///See details <https://datatracker.ietf.org/doc/html/rfc7239#section-4>
17pub enum ForwardedNode<'a> {
18    ///Proxy specified real IP address
19    Ip(IpAddr),
20    ///Proxy decided to obscure
21    Name(&'a str),
22    ///Proxy indicates it cannot know IP
23    Unknown,
24}
25
26impl<'a> ForwardedNode<'a> {
27    #[inline(always)]
28    fn parse_name(name: &'a str) -> Self {
29        if let Ok(name) = name.parse() {
30            return Self::Ip(name);
31        } else {
32            return Self::Name(name)
33        }
34    }
35
36    #[inline(always)]
37    ///Returns `ip` value if node is valid IP address
38    pub const fn ip(&self) -> Option<IpAddr> {
39        match self {
40            Self::Ip(ip) => Some(*ip),
41            _ => None
42        }
43    }
44
45    #[inline]
46    ///Parses X-Forwarded-For's `Node` identifier
47    pub fn parse_x_node(mut node: &'a str) -> Self {
48        node = node.trim();
49        match node.parse() {
50            Ok(ip) => ForwardedNode::Ip(ip),
51            Err(_) => ForwardedNode::Name(node)
52        }
53    }
54
55    ///Parses `Node` identifier
56    pub fn parse_node(mut node: &'a str) -> Self {
57        node = node.trim_matches('"');
58        if node.eq_ignore_ascii_case("unknown") {
59            return Self::Unknown;
60        }
61
62        if let Some(mut ipv6) = node.strip_prefix('[') {
63            if let Some(end_addr_idx) = ipv6.find(']') {
64                ipv6 = &ipv6[..end_addr_idx];
65                return Self::parse_name(ipv6);
66            } else {
67                return Self::Name(ipv6);
68            }
69        }
70
71        let mut node = node.rsplit(':');
72        let port_or_ip = node.next().unwrap();
73        let ip = if let Some(ip) = node.next() {
74            ip
75        } else {
76            port_or_ip
77        };
78
79        ForwardedNode::parse_name(ip)
80    }
81}
82
83impl fmt::Display for ForwardedNode<'_> {
84    #[inline(always)]
85    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
86        match self {
87            Self::Ip(ip) => fmt::Display::fmt(&ip, fmt),
88            Self::Name(ip) => fmt.write_str(&ip),
89            Self::Unknown => fmt.write_str("-"),
90        }
91    }
92}
93
94#[derive(Copy, Clone, PartialEq, Eq, Debug)]
95///`Forwarded` entry value
96pub enum ForwardedValue<'a> {
97    ///Identifies node that passed request to the proxy
98    ///
99    ///This potentially can be the same as `For`
100    By(ForwardedNode<'a>),
101    ///Contains client's IP information
102    ///
103    ///This is normally what you're looking for when you need to identify client's original IP
104    For(ForwardedNode<'a>),
105    ///Original value of `Host` header
106    Host(&'a str),
107    ///String with protocol name
108    ///
109    ///<https://datatracker.ietf.org/doc/html/rfc7239#section-5.4>
110    Protocol(&'a str)
111}
112
113///Iterator of `Forwarded` entry's components
114pub struct ForwardedEntryIter<'a> {
115    components: core::str::Split<'a, char>,
116}
117
118impl<'a> ForwardedEntryIter<'a> {
119    ///Parses single entry within `Forwarded` header
120    ///
121    ///It performs no error checking, ignoring invalid values, as it assumes you parse valid `Forwarded` header
122    ///
123    ///Values within entry is separated by `;`
124    ///
125    ///This iterator returns [ForwardedValue](enum.ForwardedValue.html)
126    pub fn parse_entry(value: &'a str) -> Self {
127        Self {
128            components: value.split(ENTRY_SEP)
129        }
130    }
131}
132
133impl<'a> Iterator for ForwardedEntryIter<'a> {
134    type Item = ForwardedValue<'a>;
135
136    fn next(&mut self) -> Option<Self::Item> {
137        while let Some(value) = self.components.next() {
138            let mut pairs = value.splitn(2, PAIR_SEP);
139            let key = pairs.next().unwrap();
140            if key.eq_ignore_ascii_case("for") {
141                if let Some(node) = pairs.next() {
142                    return Some(ForwardedValue::For(ForwardedNode::parse_node(node)))
143                }
144            } else if key.eq_ignore_ascii_case("by") {
145                if let Some(node) = pairs.next() {
146                    return Some(ForwardedValue::By(ForwardedNode::parse_node(node)))
147                }
148            } else if key.eq_ignore_ascii_case("proto") {
149                if let Some(proto) = pairs.next() {
150                    return Some(ForwardedValue::Protocol(proto))
151                }
152            } else if key.eq_ignore_ascii_case("host") {
153                if let Some(host) = pairs.next() {
154                    return Some(ForwardedValue::Host(host))
155                }
156            }
157        }
158
159        None
160    }
161}
162
163///Iterator over entries components within `Forwarded` header
164pub struct ForwardedIter<'a, I> {
165    components: I,
166    _lifetime: marker::PhantomData<&'a I>,
167}
168
169impl<'a, I: Iterator<Item = &'a str> + 'a> Iterator for ForwardedIter<'a, I> {
170    type Item = ForwardedEntryIter<'a>;
171
172    #[inline]
173    fn next(&mut self) -> Option<Self::Item> {
174        self.components.next().map(ForwardedEntryIter::parse_entry)
175    }
176}
177
178///Iterator over `For` components within `Forwarded` header
179///
180///This is most likely what you need most of the time in order to determine client's actual IP, but
181///you can use [ForwardedIter](struct.ForwardedIter.html) when you need to iterate over all
182///components
183pub struct ForwardedForIter<'a, I> {
184    components: I,
185    _lifetime: marker::PhantomData<&'a I>,
186}
187
188impl<'a, I: Iterator<Item = &'a str> + 'a> Iterator for ForwardedForIter<'a, I> {
189    type Item = ForwardedNode<'a>;
190
191    #[inline]
192    fn next(&mut self) -> Option<Self::Item> {
193        while let Some(value) = self.components.next() {
194            let mut pairs = value.splitn(2, PAIR_SEP);
195            let key = pairs.next().unwrap();
196            if key.eq_ignore_ascii_case("for") {
197                if let Some(node) = pairs.next() {
198                    return Some(ForwardedNode::parse_node(node))
199                }
200            }
201        }
202
203        None
204    }
205}
206
207///Iterator over `X-Forwarded-For` header
208///
209///This header is not standard and iterator assumes it is simple list of IP addresses.
210pub struct XForwardedForIter<'a, I> {
211    components: I,
212    _lifetime: marker::PhantomData<&'a I>,
213}
214
215impl<'a, I: Iterator<Item = &'a str> + 'a> Iterator for XForwardedForIter<'a, I> {
216    type Item = ForwardedNode<'a>;
217
218    #[inline]
219    fn next(&mut self) -> Option<Self::Item> {
220        self.components.next().map(ForwardedNode::parse_x_node)
221    }
222}
223
224#[inline(always)]
225///Parses provided string as `Forwarded` header
226///
227///It performs no error checking, ignoring invalid values, as it assumes you parse valid `Forwarded` header
228///
229///Every proxy's entry is separated by `,`
230///
231///This iterator returns iterator over individual proxy's entries within `value`
232pub fn parse_forwarded<'a>(value: &'a str) -> ForwardedIter<'a, impl Iterator<Item = &'a str>> {
233    ForwardedIter {
234        components: value.split(FORWARDED_SEP),
235        _lifetime: marker::PhantomData,
236    }
237}
238
239#[inline(always)]
240///Variant of [parse_forwarded](fn.parse_forwarded.html) that reverses order of output
241pub fn parse_forwarded_rev<'a>(value: &'a str) -> ForwardedIter<'a, impl Iterator<Item = &'a str>> {
242    ForwardedIter {
243        components: value.rsplit(FORWARDED_SEP),
244        _lifetime: marker::PhantomData,
245    }
246}
247
248#[inline(always)]
249///Parses provided string as `Forwarded` header returning all `For` nodes in order
250pub fn parse_forwarded_for<'a>(value: &'a str) -> ForwardedForIter<'a, impl Iterator<Item = &'a str>> {
251    ForwardedForIter {
252        components: value.split([FORWARDED_SEP, ENTRY_SEP]),
253        _lifetime: marker::PhantomData,
254    }
255}
256
257#[inline(always)]
258///Parses provided string as `Forwarded` header returning all `For` nodes in reverse order
259pub fn parse_forwarded_for_rev<'a>(value: &'a str) -> ForwardedForIter<'a, impl Iterator<Item = &'a str>> {
260    ForwardedForIter {
261        components: value.rsplit([FORWARDED_SEP, ENTRY_SEP]),
262        _lifetime: marker::PhantomData,
263    }
264}
265
266#[inline(always)]
267///Parses provided string as `X-Forwarded-For` header returning all nodes in order
268pub fn parse_x_forwarded_for<'a>(value: &'a str) -> XForwardedForIter<'a, impl Iterator<Item = &'a str>> {
269    XForwardedForIter {
270        components: value.split(FORWARDED_SEP),
271        _lifetime: marker::PhantomData,
272    }
273}
274
275#[inline(always)]
276///Parses provided string as `X-Forwarded-For` header returning all nodes in reverse order
277pub fn parse_x_forwarded_for_rev<'a>(value: &'a str) -> XForwardedForIter<'a, impl Iterator<Item = &'a str>> {
278    XForwardedForIter {
279        components: value.rsplit(FORWARDED_SEP),
280        _lifetime: marker::PhantomData,
281    }
282}