rama_http_headers/forwarded/
x_forwarded_host.rs1use crate::{Error, HeaderDecode, HeaderEncode, TypedHeader};
2use rama_core::telemetry::tracing;
3use rama_http_types::header;
4use rama_http_types::{HeaderName, HeaderValue};
5use rama_net::address::Host;
6use rama_net::forwarded::{ForwardedAuthority, ForwardedElement};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct XForwardedHost(ForwardedAuthority);
31
32impl TypedHeader for XForwardedHost {
33 fn name() -> &'static HeaderName {
34 &header::X_FORWARDED_HOST
35 }
36}
37
38impl HeaderDecode for XForwardedHost {
39 fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, Error> {
40 Ok(Self(
41 values
42 .next()
43 .and_then(|value| value.to_str().ok().and_then(|s| s.parse().ok()))
44 .ok_or_else(Error::invalid)?,
45 ))
46 }
47}
48
49impl HeaderEncode for XForwardedHost {
50 fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
51 let s = self.0.to_string();
52 match HeaderValue::try_from(s) {
53 Ok(value) => values.extend(::std::iter::once(value)),
54 Err(err) => {
55 tracing::debug!("failed to encode x-forwarded-host as header value: {err}")
56 }
57 }
58 }
59}
60
61impl XForwardedHost {
62 #[inline]
63 #[must_use]
65 pub fn host(&self) -> &Host {
66 &self.0.0.host
67 }
68
69 #[inline]
70 #[must_use]
73 pub fn port(&self) -> Option<u16> {
74 self.0.0.port.as_u16()
75 }
76
77 #[must_use]
79 pub fn inner(&self) -> &ForwardedAuthority {
80 &self.0
81 }
82
83 #[must_use]
85 pub fn into_inner(self) -> ForwardedAuthority {
86 self.0
87 }
88}
89
90impl IntoIterator for XForwardedHost {
91 type Item = ForwardedElement;
92 type IntoIter = XForwardedHostIterator;
93
94 fn into_iter(self) -> Self::IntoIter {
95 XForwardedHostIterator(Some(self.0))
96 }
97}
98
99impl super::ForwardHeader for XForwardedHost {
100 fn try_from_forwarded<'a, I>(input: I) -> Option<Self>
101 where
102 I: IntoIterator<Item = &'a ForwardedElement>,
103 {
104 let el = input.into_iter().next()?;
105 let host = el.forwarded_host().cloned()?;
106 Some(Self(host))
107 }
108}
109
110#[derive(Debug, Clone)]
111pub struct XForwardedHostIterator(Option<ForwardedAuthority>);
113
114impl Iterator for XForwardedHostIterator {
115 type Item = ForwardedElement;
116
117 fn next(&mut self) -> Option<Self::Item> {
118 self.0.take().map(ForwardedElement::new_forwarded_host)
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 use rama_http_types::HeaderValue;
127
128 macro_rules! test_header {
129 ($name: ident, $input: expr, $expected: expr) => {
130 #[test]
131 fn $name() {
132 assert_eq!(
133 XForwardedHost::decode(
134 &mut $input
135 .into_iter()
136 .map(|s| HeaderValue::from_bytes(s.as_bytes()).unwrap())
137 .collect::<Vec<_>>()
138 .iter()
139 )
140 .ok(),
141 $expected,
142 );
143 }
144 };
145 }
146
147 test_header!(
149 test1,
150 vec!["id42.example-cdn.com"],
151 Some(XForwardedHost("id42.example-cdn.com".parse().unwrap()))
152 );
153 test_header!(
154 test2,
155 vec!["id42.example-cdn.com", "example.com"],
157 Some(XForwardedHost("id42.example-cdn.com".parse().unwrap()))
158 );
159 test_header!(
160 test3,
161 vec!["id42.example-cdn.com:443"],
162 Some(XForwardedHost("id42.example-cdn.com:443".parse().unwrap()))
163 );
164 test_header!(
165 test4,
166 vec!["203.0.113.195"],
167 Some(XForwardedHost("203.0.113.195".parse().unwrap()))
168 );
169 test_header!(
170 test5,
171 vec!["203.0.113.195:80"],
172 Some(XForwardedHost("203.0.113.195:80".parse().unwrap()))
173 );
174 test_header!(
175 test6,
176 vec!["2001:db8:85a3:8d3:1319:8a2e:370:7348"],
177 Some(XForwardedHost(
178 "2001:db8:85a3:8d3:1319:8a2e:370:7348".parse().unwrap()
179 ))
180 );
181 test_header!(
182 test7,
183 vec!["[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8080"],
184 Some(XForwardedHost(
185 "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8080"
186 .parse()
187 .unwrap()
188 ))
189 );
190
191 #[test]
192 fn test_x_forwarded_host_symmetry_encode() {
193 for input in [
194 XForwardedHost("id42.example-cdn.com".parse().unwrap()),
195 XForwardedHost("id42.example-cdn.com:443".parse().unwrap()),
196 XForwardedHost("127.0.0.1".parse().unwrap()),
197 ] {
198 let mut values = Vec::new();
199 input.encode(&mut values);
200 assert_eq!(XForwardedHost::decode(&mut values.iter()).unwrap(), input);
201 }
202 }
203}