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