rama_net/forwarded/element/
mod.rs1use super::{ForwardedProtocol, ForwardedVersion, NodeId};
2use crate::address::{Authority, Host};
3use rama_core::error::{ErrorContext, OpaqueError};
4use std::fmt;
5use std::net::SocketAddr;
6use std::{collections::HashMap, net::IpAddr};
7
8#[cfg(feature = "http")]
9use rama_http_types::HeaderValue;
10
11mod parser;
12#[doc(inline)]
13pub(crate) use parser::{parse_one_plus_forwarded_elements, parse_single_forwarded_element};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct ForwardedElement {
20 by_node: Option<NodeId>,
21 for_node: Option<NodeId>,
22 authority: Option<ForwardedAuthority>,
23 proto: Option<ForwardedProtocol>,
24 proto_version: Option<ForwardedVersion>,
25
26 extensions: Option<HashMap<String, ExtensionValue>>,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33struct ExtensionValue {
34 value: String,
35 quoted: bool,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct ForwardedAuthority {
41 host: Host,
42 port: Option<u16>,
43}
44
45impl ForwardedAuthority {
46 pub const fn new(host: Host, port: Option<u16>) -> Self {
48 Self { host, port }
49 }
50
51 pub fn host(&self) -> &Host {
53 &self.host
54 }
55
56 pub fn port(&self) -> Option<u16> {
58 self.port
59 }
60
61 pub fn into_parts(self) -> (Host, Option<u16>) {
63 (self.host, self.port)
64 }
65}
66
67impl From<Host> for ForwardedAuthority {
68 fn from(value: Host) -> Self {
69 Self {
70 host: value,
71 port: None,
72 }
73 }
74}
75
76impl From<SocketAddr> for ForwardedAuthority {
77 fn from(value: SocketAddr) -> Self {
78 Self {
79 host: value.ip().into(),
80 port: Some(value.port()),
81 }
82 }
83}
84
85impl From<Authority> for ForwardedAuthority {
86 fn from(value: Authority) -> Self {
87 let (host, port) = value.into_parts();
88 Self {
89 host,
90 port: Some(port),
91 }
92 }
93}
94
95impl ForwardedElement {
96 pub fn merge(&mut self, other: ForwardedElement) -> &mut Self {
98 if let Some(by_node) = other.by_node {
99 self.by_node = Some(by_node);
100 }
101 if let Some(for_node) = other.for_node {
102 self.for_node = Some(for_node);
103 }
104 if let Some(authority) = other.authority {
105 self.authority = Some(authority);
106 }
107 if let Some(proto) = other.proto {
108 self.proto = Some(proto);
109 }
110 if let Some(extensions) = other.extensions {
111 match &mut self.extensions {
112 Some(map) => {
113 map.extend(extensions);
114 }
115 None => {
116 self.extensions = Some(extensions);
117 }
118 }
119 }
120 self
121 }
122
123 pub fn authority(&self) -> Option<(Host, Option<u16>)> {
125 self.authority
126 .as_ref()
127 .map(|authority| (authority.host.clone(), authority.port))
128 }
129
130 pub fn forwarded_host(authority: impl Into<ForwardedAuthority>) -> Self {
133 Self {
134 by_node: None,
135 for_node: None,
136 authority: Some(authority.into()),
137 proto: None,
138 proto_version: None,
139 extensions: None,
140 }
141 }
142
143 pub fn set_forwarded_host(&mut self, authority: impl Into<ForwardedAuthority>) -> &mut Self {
146 self.authority = Some(authority.into());
147 self
148 }
149
150 pub fn ref_forwarded_host(&self) -> Option<&ForwardedAuthority> {
152 self.authority.as_ref()
153 }
154
155 pub fn forwarded_for(node_id: impl Into<NodeId>) -> Self {
159 Self {
160 by_node: None,
161 for_node: Some(node_id.into()),
162 authority: None,
163 proto: None,
164 proto_version: None,
165 extensions: None,
166 }
167 }
168
169 pub fn set_forwarded_for(&mut self, node_id: impl Into<NodeId>) -> &mut Self {
172 self.for_node = Some(node_id.into());
173 self
174 }
175
176 pub fn ref_forwarded_for(&self) -> Option<&NodeId> {
178 self.for_node.as_ref()
179 }
180
181 pub fn forwarded_by(node_id: impl Into<NodeId>) -> Self {
185 Self {
186 by_node: Some(node_id.into()),
187 for_node: None,
188 authority: None,
189 proto: None,
190 proto_version: None,
191 extensions: None,
192 }
193 }
194
195 pub fn set_forwarded_by(&mut self, node_id: impl Into<NodeId>) -> &mut Self {
198 self.by_node = Some(node_id.into());
199 self
200 }
201
202 pub fn ref_forwarded_by(&self) -> Option<&NodeId> {
204 self.by_node.as_ref()
205 }
206
207 pub fn forwarded_proto(protocol: ForwardedProtocol) -> Self {
210 Self {
211 by_node: None,
212 for_node: None,
213 authority: None,
214 proto: Some(protocol),
215 proto_version: None,
216 extensions: None,
217 }
218 }
219
220 pub fn set_forwarded_proto(&mut self, protocol: ForwardedProtocol) -> &mut Self {
222 self.proto = Some(protocol);
223 self
224 }
225
226 pub fn ref_forwarded_proto(&self) -> Option<ForwardedProtocol> {
228 self.proto.clone()
229 }
230
231 pub fn forwarded_version(version: ForwardedVersion) -> Self {
234 Self {
235 by_node: None,
236 for_node: None,
237 authority: None,
238 proto: None,
239 proto_version: Some(version),
240 extensions: None,
241 }
242 }
243
244 pub fn set_forwarded_version(&mut self, version: ForwardedVersion) -> &mut Self {
246 self.proto_version = Some(version);
247 self
248 }
249
250 pub fn ref_forwarded_version(&self) -> Option<ForwardedVersion> {
252 self.proto_version
253 }
254}
255
256impl fmt::Display for ForwardedAuthority {
257 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258 match self.port {
259 Some(port) => match &self.host {
260 Host::Address(IpAddr::V6(ip)) => write!(f, "[{ip}]:{port}"),
261 host => write!(f, "{host}:{port}"),
262 },
263 None => self.host.fmt(f),
264 }
265 }
266}
267
268impl fmt::Display for ForwardedElement {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 let mut separator = "";
271
272 if let Some(ref by_node) = self.by_node {
273 write!(f, "by=")?;
274 let quoted =
275 by_node.has_any_port() || by_node.ip().map(|ip| ip.is_ipv6()).unwrap_or_default();
276 if quoted {
277 write!(f, r##""{by_node}""##)?;
278 } else {
279 by_node.fmt(f)?;
280 }
281 separator = ";";
282 }
283
284 if let Some(ref for_node) = self.for_node {
285 write!(f, "{separator}for=")?;
286 let quoted =
287 for_node.has_any_port() || for_node.ip().map(|ip| ip.is_ipv6()).unwrap_or_default();
288 if quoted {
289 write!(f, r##""{for_node}""##)?;
290 } else {
291 for_node.fmt(f)?;
292 }
293 separator = ";";
294 }
295
296 if let Some(ref authority) = self.authority {
297 write!(f, "{separator}host=")?;
298 let quoted =
299 authority.port.is_some() || matches!(authority.host, Host::Address(IpAddr::V6(_)));
300 if quoted {
301 write!(f, r##""{authority}""##)?;
302 } else {
303 authority.fmt(f)?;
304 }
305 separator = ";";
306 }
307
308 if let Some(ref proto) = self.proto {
309 write!(f, "{separator}proto=")?;
310 proto.fmt(f)?;
311 }
312
313 Ok(())
314 }
315}
316
317impl std::str::FromStr for ForwardedElement {
318 type Err = OpaqueError;
319
320 fn from_str(s: &str) -> Result<Self, Self::Err> {
321 parse_single_forwarded_element(s.as_bytes())
322 }
323}
324
325impl TryFrom<String> for ForwardedElement {
326 type Error = OpaqueError;
327
328 fn try_from(s: String) -> Result<Self, Self::Error> {
329 parse_single_forwarded_element(s.as_bytes())
330 }
331}
332
333impl TryFrom<&str> for ForwardedElement {
334 type Error = OpaqueError;
335
336 fn try_from(s: &str) -> Result<Self, Self::Error> {
337 parse_single_forwarded_element(s.as_bytes())
338 }
339}
340
341#[cfg(feature = "http")]
342impl TryFrom<HeaderValue> for ForwardedElement {
343 type Error = OpaqueError;
344
345 fn try_from(header: HeaderValue) -> Result<Self, Self::Error> {
346 parse_single_forwarded_element(header.as_bytes())
347 }
348}
349
350#[cfg(feature = "http")]
351impl TryFrom<&HeaderValue> for ForwardedElement {
352 type Error = OpaqueError;
353
354 fn try_from(header: &HeaderValue) -> Result<Self, Self::Error> {
355 parse_single_forwarded_element(header.as_bytes())
356 }
357}
358
359impl TryFrom<Vec<u8>> for ForwardedElement {
360 type Error = OpaqueError;
361
362 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
363 parse_single_forwarded_element(bytes.as_ref())
364 }
365}
366
367impl TryFrom<&[u8]> for ForwardedElement {
368 type Error = OpaqueError;
369
370 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
371 parse_single_forwarded_element(bytes)
372 }
373}
374
375impl std::str::FromStr for ForwardedAuthority {
376 type Err = OpaqueError;
377
378 fn from_str(s: &str) -> Result<Self, Self::Err> {
379 if let Ok(host) = Host::try_from(s) {
380 return Ok(ForwardedAuthority { host, port: None });
383 }
384
385 let (s, port) = try_to_split_num_port_from_str(s);
386 let host = Host::try_from(s).context("parse forwarded host")?;
387
388 match host {
389 Host::Address(IpAddr::V6(_)) if port.is_some() && !s.starts_with('[') => Err(
390 OpaqueError::from_display("missing brackets for host IPv6 address with port"),
391 ),
392 _ => Ok(ForwardedAuthority { host, port }),
393 }
394 }
395}
396
397fn try_to_split_num_port_from_str(s: &str) -> (&str, Option<u16>) {
398 if let Some(colon) = s.as_bytes().iter().rposition(|c| *c == b':') {
399 match s[colon + 1..].parse() {
400 Ok(port) => (&s[..colon], Some(port)),
401 Err(_) => (s, None),
402 }
403 } else {
404 (s, None)
405 }
406}
407
408#[cfg(test)]
409mod tests {
410 use super::*;
411
412 #[test]
413 fn test_forwarded_element_parse_invalid() {
414 for s in [
415 "",
416 "foobar",
417 "127.0.0.1",
418 "⌨️",
419 "for=_foo;for=_bar",
420 "for=foo,proto=http",
421 ] {
422 if let Ok(el) = ForwardedElement::try_from(s) {
423 panic!("unexpected parse success: input {s}: {el:?}");
424 }
425 }
426 }
427
428 #[test]
429 fn test_forwarded_element_parse_happy_spec() {
430 for (s, expected) in [
431 (
432 r##"for="_gazonk""##,
433 ForwardedElement::forwarded_for(NodeId::try_from("_gazonk").unwrap()),
434 ),
435 (
436 r##"For="[2001:db8:cafe::17]:4711""##,
437 ForwardedElement::forwarded_for(
438 NodeId::try_from("[2001:db8:cafe::17]:4711").unwrap(),
439 ),
440 ),
441 (
442 r##"For="[2001:db8:cafe::17]:4711";proto=http"##,
443 ForwardedElement {
444 by_node: None,
445 for_node: Some(NodeId::try_from("[2001:db8:cafe::17]:4711").unwrap()),
446 authority: None,
447 proto: Some(ForwardedProtocol::HTTP),
448 proto_version: None,
449 extensions: None,
450 },
451 ),
452 (
453 r##"For="[2001:db8:cafe::17]:4711";proto=http;foo=bar"##,
454 ForwardedElement {
455 by_node: None,
456 for_node: Some(NodeId::try_from("[2001:db8:cafe::17]:4711").unwrap()),
457 authority: None,
458 proto: Some(ForwardedProtocol::HTTP),
459 proto_version: None,
460 extensions: Some(
461 [(
462 "foo".to_owned(),
463 ExtensionValue {
464 value: "bar".to_owned(),
465 quoted: false,
466 },
467 )]
468 .into(),
469 ),
470 },
471 ),
472 (
473 r##"for=192.0.2.60;proto=http;by=203.0.113.43"##,
474 ForwardedElement {
475 by_node: Some(NodeId::try_from("203.0.113.43").unwrap()),
476 for_node: Some(NodeId::try_from("192.0.2.60").unwrap()),
477 authority: None,
478 proto: Some(ForwardedProtocol::HTTP),
479 proto_version: None,
480 extensions: None,
481 },
482 ),
483 ] {
484 let element = match ForwardedElement::try_from(s) {
485 Ok(el) => el,
486 Err(err) => panic!("failed to parse happy spec el '{s}': {err}"),
487 };
488 assert_eq!(element, expected, "input: {}", s);
489 }
490 }
491}