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    ///Parses `Node` identifier
37    pub fn parse_node(mut node: &'a str) -> Self {
38        node = node.trim_matches('"');
39        if node.eq_ignore_ascii_case("unknown") {
40            return Self::Unknown;
41        }
42
43        if let Some(mut ipv6) = node.strip_prefix('[') {
44            if let Some(end_addr_idx) = ipv6.find(']') {
45                ipv6 = &ipv6[..end_addr_idx];
46                return Self::parse_name(ipv6);
47            } else {
48                return Self::Name(ipv6);
49            }
50        }
51
52        let mut node = node.rsplit(':');
53        let port_or_ip = node.next().unwrap();
54        let ip = if let Some(ip) = node.next() {
55            ip
56        } else {
57            port_or_ip
58        };
59
60        ForwardedNode::parse_name(ip)
61    }
62}
63
64impl fmt::Display for ForwardedNode<'_> {
65    #[inline(always)]
66    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match self {
68            Self::Ip(ip) => fmt::Display::fmt(&ip, fmt),
69            Self::Name(ip) => fmt.write_str(&ip),
70            Self::Unknown => fmt.write_str("-"),
71        }
72    }
73}
74
75#[derive(Copy, Clone, PartialEq, Eq, Debug)]
76///`Forwarded` entry value
77pub enum ForwardedValue<'a> {
78    ///Identifies node that passed request to the proxy
79    ///
80    ///This potentially can be the same as `For`
81    By(ForwardedNode<'a>),
82    ///Contains client's IP information
83    ///
84    ///This is normally what you're looking for when you need to identify client's original IP
85    For(ForwardedNode<'a>),
86    ///Original value of `Host` header
87    Host(&'a str),
88    ///String with protocol name
89    ///
90    ///<https://datatracker.ietf.org/doc/html/rfc7239#section-5.4>
91    Protocol(&'a str)
92}
93
94///Iterator of `Forwarded` entry's components
95pub struct ForwardedEntryIter<'a> {
96    components: core::str::Split<'a, char>,
97}
98
99impl<'a> ForwardedEntryIter<'a> {
100    ///Parses single entry within `Forwarded` header
101    ///
102    ///It performs no error checking, ignoring invalid values, as it assumes you parse valid `Forwarded` header
103    ///
104    ///Values within entry is separated by `;`
105    ///
106    ///This iterator returns [ForwardedValue](enum.ForwardedValue.html)
107    pub fn parse_entry(value: &'a str) -> Self {
108        Self {
109            components: value.split(ENTRY_SEP)
110        }
111    }
112}
113
114impl<'a> Iterator for ForwardedEntryIter<'a> {
115    type Item = ForwardedValue<'a>;
116
117    fn next(&mut self) -> Option<Self::Item> {
118        while let Some(value) = self.components.next() {
119            let mut pairs = value.splitn(2, PAIR_SEP);
120            let key = pairs.next().unwrap();
121            if key.eq_ignore_ascii_case("for") {
122                if let Some(node) = pairs.next() {
123                    return Some(ForwardedValue::For(ForwardedNode::parse_node(node)))
124                }
125            } else if key.eq_ignore_ascii_case("by") {
126                if let Some(node) = pairs.next() {
127                    return Some(ForwardedValue::By(ForwardedNode::parse_node(node)))
128                }
129            } else if key.eq_ignore_ascii_case("proto") {
130                if let Some(proto) = pairs.next() {
131                    return Some(ForwardedValue::Protocol(proto))
132                }
133            } else if key.eq_ignore_ascii_case("host") {
134                if let Some(host) = pairs.next() {
135                    return Some(ForwardedValue::Host(host))
136                }
137            }
138        }
139
140        None
141    }
142}
143
144///Iterator over entries components within `Forwarded` header
145pub struct ForwardedIter<'a, I> {
146    components: I,
147    _lifetime: marker::PhantomData<&'a I>,
148}
149
150impl<'a, I: Iterator<Item = &'a str> + 'a> Iterator for ForwardedIter<'a, I> {
151    type Item = ForwardedEntryIter<'a>;
152
153    #[inline]
154    fn next(&mut self) -> Option<Self::Item> {
155        self.components.next().map(ForwardedEntryIter::parse_entry)
156    }
157}
158
159///Iterator over `For` components within `Forwarded` header
160///
161///This is most likely what you need most of the time in order to determine client's actual IP, but
162///you can use [ForwardedIter](struct.ForwardedIter.html) when you need to iterate over all
163///components
164pub struct ForwardedForIter<'a, I> {
165    components: I,
166    _lifetime: marker::PhantomData<&'a I>,
167}
168
169impl<'a, I: Iterator<Item = &'a str> + 'a> Iterator for ForwardedForIter<'a, I> {
170    type Item = ForwardedNode<'a>;
171
172    #[inline]
173    fn next(&mut self) -> Option<Self::Item> {
174        while let Some(value) = self.components.next() {
175            let mut pairs = value.splitn(2, PAIR_SEP);
176            let key = pairs.next().unwrap();
177            if key.eq_ignore_ascii_case("for") {
178                if let Some(node) = pairs.next() {
179                    return Some(ForwardedNode::parse_node(node))
180                }
181            }
182        }
183
184        None
185    }
186}
187
188#[inline(always)]
189///Parses provided string as `Forwarded` header
190///
191///It performs no error checking, ignoring invalid values, as it assumes you parse valid `Forwarded` header
192///
193///Every proxy's entry is separated by `,`
194///
195///This iterator returns iterator over individual proxy's entries within `value`
196pub fn parse_forwarded<'a>(value: &'a str) -> ForwardedIter<'a, impl Iterator<Item = &'a str>> {
197    ForwardedIter {
198        components: value.split(FORWARDED_SEP),
199        _lifetime: marker::PhantomData,
200    }
201}
202
203#[inline(always)]
204///Variant of [parse_forwarded](fn.parse_forwarded.html) that reverses order of output
205pub fn parse_forwarded_rev<'a>(value: &'a str) -> ForwardedIter<'a, impl Iterator<Item = &'a str>> {
206    ForwardedIter {
207        components: value.rsplit(FORWARDED_SEP),
208        _lifetime: marker::PhantomData,
209    }
210}
211
212#[inline(always)]
213///Parses provided string as `Forwarded` header returning all `For` nodes in order
214pub fn parse_forwarded_for<'a>(value: &'a str) -> ForwardedForIter<'a, impl Iterator<Item = &'a str>> {
215    ForwardedForIter {
216        components: value.split([FORWARDED_SEP, ENTRY_SEP]),
217        _lifetime: marker::PhantomData,
218    }
219}
220
221#[inline(always)]
222///Parses provided string as `Forwarded` header returning all `For` nodes in reverse order
223pub fn parse_forwarded_for_rev<'a>(value: &'a str) -> ForwardedForIter<'a, impl Iterator<Item = &'a str>> {
224    ForwardedForIter {
225        components: value.rsplit([FORWARDED_SEP, ENTRY_SEP]),
226        _lifetime: marker::PhantomData,
227    }
228}