1use core::fmt;
6use core::net::IpAddr;
7
8use crate::std::string::String;
9use crate::std::vec::Vec;
10
11use rama_core::error::BoxError;
12use rama_core::extensions::Extension;
13
14mod obfuscated;
15#[doc(inline)]
16use obfuscated::{ObfNode, ObfPort};
17
18mod node;
19#[doc(inline)]
20pub use node::NodeId;
21
22mod element;
23#[doc(inline)]
24pub use element::{ForwardedAuthority, ForwardedElement};
25
26mod proto;
27#[doc(inline)]
28pub use proto::ForwardedProtocol;
29
30mod version;
31#[doc(inline)]
32pub use version::ForwardedVersion;
33
34use crate::address::SocketAddress;
35
36#[derive(Debug, Clone, PartialEq, Eq, Extension)]
37#[extension(tags(net))]
38pub struct Forwarded {
49 first: ForwardedElement,
50 others: Vec<ForwardedElement>,
51}
52
53impl Forwarded {
54 #[must_use]
57 pub const fn new(element: ForwardedElement) -> Self {
58 Self {
59 first: element,
60 others: Vec::new(),
61 }
62 }
63
64 #[must_use]
70 pub fn client_host(&self) -> Option<&ForwardedAuthority> {
71 self.first.forwarded_host()
72 }
73
74 #[must_use]
80 pub fn client_socket_addr(&self) -> Option<SocketAddress> {
81 self.first
82 .forwarded_for()
83 .and_then(|node| match (node.ip(), node.port()) {
84 (Some(ip), Some(port)) => Some((ip, port).into()),
85 _ => None,
86 })
87 }
88
89 #[must_use]
92 pub fn client_port(&self) -> Option<u16> {
93 self.first.forwarded_for().and_then(|node| node.port())
94 }
95
96 #[must_use]
105 pub fn client_ip(&self) -> Option<IpAddr> {
106 self.first.forwarded_for().and_then(|node| node.ip())
107 }
108
109 #[must_use]
112 pub fn client_proto(&self) -> Option<ForwardedProtocol> {
113 self.first.forwarded_proto()
114 }
115
116 #[must_use]
119 pub fn client_version(&self) -> Option<ForwardedVersion> {
120 self.first.forwarded_version()
121 }
122
123 pub fn append(&mut self, element: ForwardedElement) -> &mut Self {
125 self.others.push(element);
126 self
127 }
128
129 pub fn extend(&mut self, elements: impl IntoIterator<Item = ForwardedElement>) -> &mut Self {
131 self.others.extend(elements);
132 self
133 }
134
135 pub fn iter(&self) -> impl Iterator<Item = &ForwardedElement> {
137 core::iter::once(&self.first).chain(self.others.iter())
138 }
139}
140
141impl IntoIterator for Forwarded {
142 type Item = ForwardedElement;
143 type IntoIter = core::iter::Chain<
144 core::iter::Once<ForwardedElement>,
145 crate::std::vec::IntoIter<ForwardedElement>,
146 >;
147
148 fn into_iter(self) -> Self::IntoIter {
149 let iter = self.others.into_iter();
150 core::iter::once(self.first).chain(iter)
151 }
152}
153
154impl From<ForwardedElement> for Forwarded {
155 #[inline]
156 fn from(value: ForwardedElement) -> Self {
157 Self::new(value)
158 }
159}
160
161impl fmt::Display for Forwarded {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 self.first.fmt(f)?;
164 for other in &self.others {
165 write!(f, ",{other}")?;
166 }
167 Ok(())
168 }
169}
170
171impl core::str::FromStr for Forwarded {
172 type Err = BoxError;
173
174 fn from_str(s: &str) -> Result<Self, Self::Err> {
175 let (first, others) = element::parse_one_plus_forwarded_elements(s.as_bytes())?;
176 Ok(Self { first, others })
177 }
178}
179
180impl TryFrom<String> for Forwarded {
181 type Error = BoxError;
182
183 fn try_from(s: String) -> Result<Self, Self::Error> {
184 let (first, others) = element::parse_one_plus_forwarded_elements(s.as_bytes())?;
185 Ok(Self { first, others })
186 }
187}
188
189impl TryFrom<&str> for Forwarded {
190 type Error = BoxError;
191
192 fn try_from(s: &str) -> Result<Self, Self::Error> {
193 let (first, others) = element::parse_one_plus_forwarded_elements(s.as_bytes())?;
194 Ok(Self { first, others })
195 }
196}
197
198impl TryFrom<Vec<u8>> for Forwarded {
199 type Error = BoxError;
200
201 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
202 let (first, others) = element::parse_one_plus_forwarded_elements(bytes.as_ref())?;
203 Ok(Self { first, others })
204 }
205}
206
207impl TryFrom<&[u8]> for Forwarded {
208 type Error = BoxError;
209
210 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
211 let (first, others) = element::parse_one_plus_forwarded_elements(bytes)?;
212 Ok(Self { first, others })
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use crate::address::HostWithOptPort;
220
221 #[test]
222 fn test_forwarded_parse_invalid() {
223 for s in [
224 "",
225 "foobar",
226 "127.0.0.1",
227 "⌨️",
228 "for=_foo;for=_bar",
229 ",",
230 "for=127.0.0.1,",
231 "for=127.0.0.1,foobar",
232 "for=127.0.0.1,127.0.0.1",
233 "for=127.0.0.1,⌨️",
234 "for=127.0.0.1,for=_foo;for=_bar",
235 "foobar,for=127.0.0.1",
236 "127.0.0.1,for=127.0.0.1",
237 "⌨️,for=127.0.0.1",
238 "for=_foo;for=_bar,for=127.0.0.1",
239 ] {
240 if let Ok(el) = Forwarded::try_from(s) {
241 panic!("unexpected parse success: input {s}: {el:?}");
242 }
243 }
244 }
245
246 #[test]
247 fn test_forwarded_parse_happy_spec() {
248 for (s, expected) in [
249 (
250 r##"for="_gazonk""##,
251 Forwarded {
252 first: ForwardedElement::new_forwarded_for(
253 NodeId::try_from("_gazonk").unwrap(),
254 ),
255 others: Vec::new(),
256 },
257 ),
258 (
259 r##"for=192.0.2.43, for=198.51.100.17"##,
260 Forwarded {
261 first: ForwardedElement::new_forwarded_for(
262 NodeId::try_from("192.0.2.43").unwrap(),
263 ),
264 others: vec![ForwardedElement::new_forwarded_for(
265 NodeId::try_from("198.51.100.17").unwrap(),
266 )],
267 },
268 ),
269 (
270 r##"for=192.0.2.43,for=198.51.100.17"##,
271 Forwarded {
272 first: ForwardedElement::new_forwarded_for(
273 NodeId::try_from("192.0.2.43").unwrap(),
274 ),
275 others: vec![ForwardedElement::new_forwarded_for(
276 NodeId::try_from("198.51.100.17").unwrap(),
277 )],
278 },
279 ),
280 (
281 r##"for=192.0.2.43,for=198.51.100.17,for=127.0.0.1"##,
282 Forwarded {
283 first: ForwardedElement::new_forwarded_for(
284 NodeId::try_from("192.0.2.43").unwrap(),
285 ),
286 others: vec![
287 ForwardedElement::new_forwarded_for(
288 NodeId::try_from("198.51.100.17").unwrap(),
289 ),
290 ForwardedElement::new_forwarded_for(NodeId::try_from("127.0.0.1").unwrap()),
291 ],
292 },
293 ),
294 (
295 r##"for=192.0.2.43,for=198.51.100.17,for=unknown"##,
296 Forwarded {
297 first: ForwardedElement::new_forwarded_for(
298 NodeId::try_from("192.0.2.43").unwrap(),
299 ),
300 others: vec![
301 ForwardedElement::new_forwarded_for(
302 NodeId::try_from("198.51.100.17").unwrap(),
303 ),
304 ForwardedElement::new_forwarded_for(NodeId::try_from("unknown").unwrap()),
305 ],
306 },
307 ),
308 (
309 r##"for=192.0.2.43,for="[2001:db8:cafe::17]",for=unknown"##,
310 Forwarded {
311 first: ForwardedElement::new_forwarded_for(
312 NodeId::try_from("192.0.2.43").unwrap(),
313 ),
314 others: vec![
315 ForwardedElement::new_forwarded_for(
316 NodeId::try_from("[2001:db8:cafe::17]").unwrap(),
317 ),
318 ForwardedElement::new_forwarded_for(NodeId::try_from("unknown").unwrap()),
319 ],
320 },
321 ),
322 (
323 r##"for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown"##,
324 Forwarded {
325 first: ForwardedElement::new_forwarded_for(
326 NodeId::try_from("192.0.2.43").unwrap(),
327 ),
328 others: vec![
329 ForwardedElement::new_forwarded_for(
330 NodeId::try_from("[2001:db8:cafe::17]").unwrap(),
331 ),
332 ForwardedElement::new_forwarded_for(NodeId::try_from("unknown").unwrap()),
333 ],
334 },
335 ),
336 (
337 r##"for=192.0.2.43, for="[2001:db8:cafe::17]:4000", for=unknown"##,
338 Forwarded {
339 first: ForwardedElement::new_forwarded_for(
340 NodeId::try_from("192.0.2.43").unwrap(),
341 ),
342 others: vec![
343 ForwardedElement::new_forwarded_for(
344 NodeId::try_from("[2001:db8:cafe::17]:4000").unwrap(),
345 ),
346 ForwardedElement::new_forwarded_for(NodeId::try_from("unknown").unwrap()),
347 ],
348 },
349 ),
350 (
351 r##"for=192.0.2.43,for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com"##,
352 Forwarded {
353 first: ForwardedElement::new_forwarded_for(
354 NodeId::try_from("192.0.2.43").unwrap(),
355 ),
356 others: vec![
357 ForwardedElement::try_from(
358 "for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com",
359 )
360 .unwrap(),
361 ],
362 },
363 ),
364 (
365 r##"for="192.0.2.43:4000",for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com"##,
366 Forwarded {
367 first: ForwardedElement::new_forwarded_for(
368 NodeId::try_from("192.0.2.43:4000").unwrap(),
369 ),
370 others: vec![
371 ForwardedElement::try_from(
372 "for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com",
373 )
374 .unwrap(),
375 ],
376 },
377 ),
378 ] {
379 let element = match Forwarded::try_from(s) {
380 Ok(el) => el,
381 Err(err) => panic!("failed to parse happy spec el '{s}': {err}"),
382 };
383 assert_eq!(element, expected, "input: {s}");
384 }
385 }
386
387 #[test]
388 fn test_forwarded_client_authority() {
389 for (s, expected) in [
390 (
391 r##"for=192.0.2.43,for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com"##,
392 None,
393 ),
394 (
395 r##"host=example.com,for=195.2.34.12"##,
396 Some(HostWithOptPort::example_domain()),
397 ),
398 (
399 r##"host="example.com:443",for=195.2.34.12"##,
400 Some(HostWithOptPort::example_domain_https()),
401 ),
402 ] {
403 let forwarded = Forwarded::try_from(s).unwrap();
404 assert_eq!(
405 forwarded
406 .iter()
407 .next()
408 .and_then(|el| el.forwarded_host())
409 .map(|authority| authority.0.clone()),
410 expected
411 );
412 }
413 }
414
415 #[test]
416 fn test_forwarded_client_protoy() {
417 for (s, expected) in [
418 (
419 r##"for=192.0.2.43,for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com"##,
420 None,
421 ),
422 (
423 r##"proto=http,for=195.2.34.12"##,
424 Some(ForwardedProtocol::HTTP),
425 ),
426 ] {
427 let forwarded = Forwarded::try_from(s).unwrap();
428 assert_eq!(
429 forwarded.iter().next().and_then(|el| el.forwarded_proto()),
430 expected
431 );
432 }
433 }
434}