rama_http_headers/forwarded/
x_forwarded_for.rs1use crate::{Error, HeaderDecode, HeaderEncode, TypedHeader, util};
2use rama_core::telemetry::tracing;
3use rama_http_types::header;
4use rama_http_types::{HeaderName, HeaderValue};
5use rama_net::forwarded::ForwardedElement;
6use std::iter::FromIterator;
7use std::net::IpAddr;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct XForwardedFor(Vec<IpAddr>);
29
30impl TypedHeader for XForwardedFor {
31 fn name() -> &'static HeaderName {
32 &header::X_FORWARDED_FOR
33 }
34}
35
36impl HeaderDecode for XForwardedFor {
37 fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, Error> {
38 util::csv::from_comma_delimited(values).map(XForwardedFor)
39 }
40}
41
42impl HeaderEncode for XForwardedFor {
43 fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
44 use std::fmt;
45 struct Format<F>(F);
46 impl<F> fmt::Display for Format<F>
47 where
48 F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
49 {
50 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51 (self.0)(f)
52 }
53 }
54 let s = format!(
55 "{}",
56 Format(|f: &mut fmt::Formatter<'_>| {
57 util::csv::fmt_comma_delimited(&mut *f, self.0.iter())
58 })
59 );
60
61 match HeaderValue::try_from(s) {
62 Ok(value) => values.extend(::std::iter::once(value)),
63 Err(err) => {
64 tracing::debug!("failed to encode x-forwarded-for as header value: {err}")
65 }
66 }
67 }
68}
69
70impl FromIterator<IpAddr> for XForwardedFor {
71 fn from_iter<T>(iter: T) -> Self
72 where
73 T: IntoIterator<Item = IpAddr>,
74 {
75 Self(iter.into_iter().collect())
76 }
77}
78
79impl super::ForwardHeader for XForwardedFor {
80 fn try_from_forwarded<'a, I>(input: I) -> Option<Self>
81 where
82 I: IntoIterator<Item = &'a ForwardedElement>,
83 {
84 let vec: Vec<_> = input
85 .into_iter()
86 .filter_map(|el| el.forwarded_for()?.ip())
87 .collect();
88 if vec.is_empty() {
89 None
90 } else {
91 Some(Self(vec))
92 }
93 }
94}
95
96impl XForwardedFor {
97 pub fn iter(&self) -> impl Iterator<Item = &IpAddr> {
99 self.0.iter()
100 }
101}
102
103impl IntoIterator for XForwardedFor {
104 type Item = ForwardedElement;
105 type IntoIter = XForwardedForIterator;
106
107 fn into_iter(self) -> Self::IntoIter {
108 XForwardedForIterator(self.0.into_iter())
109 }
110}
111
112#[derive(Debug, Clone)]
113pub struct XForwardedForIterator(std::vec::IntoIter<IpAddr>);
115
116impl Iterator for XForwardedForIterator {
117 type Item = ForwardedElement;
118
119 fn next(&mut self) -> Option<Self::Item> {
120 self.0.next().map(ForwardedElement::new_forwarded_for)
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 use rama_http_types::HeaderValue;
129
130 macro_rules! test_header {
131 ($name: ident, $input: expr, $expected: expr) => {
132 #[test]
133 fn $name() {
134 assert_eq!(
135 XForwardedFor::decode(
136 &mut $input
137 .into_iter()
138 .map(|s| HeaderValue::from_bytes(s.as_bytes()).unwrap())
139 .collect::<Vec<_>>()
140 .iter()
141 )
142 .ok(),
143 $expected,
144 );
145 }
146 };
147 }
148
149 test_header!(
151 test1,
152 vec!["2001:db8:85a3:8d3:1319:8a2e:370:7348"],
153 Some(XForwardedFor(vec![
154 "2001:db8:85a3:8d3:1319:8a2e:370:7348".parse().unwrap(),
155 ]))
156 );
157 test_header!(
158 test2,
159 vec!["203.0.113.195"],
160 Some(XForwardedFor(vec!["203.0.113.195".parse().unwrap(),]))
161 );
162 test_header!(
163 test3,
164 vec!["203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348"],
165 Some(XForwardedFor(vec![
166 "203.0.113.195".parse().unwrap(),
167 "2001:db8:85a3:8d3:1319:8a2e:370:7348".parse().unwrap()
168 ]))
169 );
170 test_header!(
171 test4,
172 vec!["203.0.113.195", "2001:db8:85a3:8d3:1319:8a2e:370:7348"],
173 Some(XForwardedFor(vec![
174 "203.0.113.195".parse().unwrap(),
175 "2001:db8:85a3:8d3:1319:8a2e:370:7348".parse().unwrap()
176 ]))
177 );
178 test_header!(
179 test5,
180 vec![
181 "203.0.113.195,2001:db8:85a3:8d3:1319:8a2e:370:7348",
182 "198.51.100.178"
183 ],
184 Some(XForwardedFor(vec![
185 "203.0.113.195".parse().unwrap(),
186 "2001:db8:85a3:8d3:1319:8a2e:370:7348".parse().unwrap(),
187 "198.51.100.178".parse().unwrap()
188 ]))
189 );
190 test_header!(
191 test6,
192 vec![
193 "203.0.113.195",
194 "2001:db8:85a3:8d3:1319:8a2e:370:7348",
195 "198.51.100.178",
196 ],
197 Some(XForwardedFor(vec![
198 "203.0.113.195".parse().unwrap(),
199 "2001:db8:85a3:8d3:1319:8a2e:370:7348".parse().unwrap(),
200 "198.51.100.178".parse().unwrap()
201 ]))
202 );
203 test_header!(
204 test7,
205 vec![
206 "203.0.113.195",
207 "2001:db8:85a3:8d3:1319:8a2e:370:7348,198.51.100.178",
208 ],
209 Some(XForwardedFor(vec![
210 "203.0.113.195".parse().unwrap(),
211 "2001:db8:85a3:8d3:1319:8a2e:370:7348".parse().unwrap(),
212 "198.51.100.178".parse().unwrap()
213 ]))
214 );
215
216 #[test]
217 fn test_x_forwarded_for_symmetric_encoder() {
218 for input in [
219 XForwardedFor(vec!["203.0.113.195".parse().unwrap()]),
220 XForwardedFor(vec![
221 "2001:db8:85a3:8d3:1319:8a2e:370:7348".parse().unwrap(),
222 "203.0.113.195".parse().unwrap(),
223 ]),
224 ] {
225 let mut values = Vec::new();
226 input.encode(&mut values);
227 assert_eq!(XForwardedFor::decode(&mut values.iter()).ok(), Some(input),);
228 }
229 }
230}