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