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