rama_http_headers/forwarded/
x_forwarded_proto.rs1use crate::{Error, HeaderDecode, HeaderEncode, TypedHeader};
2use rama_core::telemetry::tracing;
3use rama_http_types::{HeaderName, HeaderValue, header};
4use rama_net::forwarded::{ForwardedElement, ForwardedProtocol};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct XForwardedProto(ForwardedProtocol);
29
30impl TypedHeader for XForwardedProto {
31 fn name() -> &'static HeaderName {
32 &header::X_FORWARDED_PROTO
33 }
34}
35
36impl HeaderDecode for XForwardedProto {
37 fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, Error> {
38 Ok(Self(
39 values
40 .next()
41 .and_then(|value| value.to_str().ok().and_then(|s| s.parse().ok()))
42 .ok_or_else(Error::invalid)?,
43 ))
44 }
45}
46
47impl HeaderEncode for XForwardedProto {
48 fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
49 let s = self.0.to_string();
50 match HeaderValue::try_from(s) {
51 Ok(value) => values.extend(::std::iter::once(value)),
52 Err(err) => {
53 tracing::debug!("failed to encode x-forwarded-proto as header value: {err}")
54 }
55 }
56 }
57}
58
59impl XForwardedProto {
60 #[must_use]
62 pub fn protocol(&self) -> &ForwardedProtocol {
63 &self.0
64 }
65
66 #[must_use]
68 pub fn into_protocol(self) -> ForwardedProtocol {
69 self.0
70 }
71}
72
73impl IntoIterator for XForwardedProto {
74 type Item = ForwardedElement;
75 type IntoIter = XForwardedProtoIterator;
76
77 fn into_iter(self) -> Self::IntoIter {
78 XForwardedProtoIterator(Some(self.0))
79 }
80}
81
82impl super::ForwardHeader for XForwardedProto {
83 fn try_from_forwarded<'a, I>(input: I) -> Option<Self>
84 where
85 I: IntoIterator<Item = &'a ForwardedElement>,
86 {
87 let proto = input.into_iter().next()?.forwarded_proto()?;
88 Some(Self(proto))
89 }
90}
91
92#[derive(Debug, Clone)]
93pub struct XForwardedProtoIterator(Option<ForwardedProtocol>);
95
96impl Iterator for XForwardedProtoIterator {
97 type Item = ForwardedElement;
98
99 fn next(&mut self) -> Option<Self::Item> {
100 self.0.take().map(ForwardedElement::new_forwarded_proto)
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 use rama_http_types::HeaderValue;
109
110 macro_rules! test_header {
111 ($name: ident, $input: expr, $expected: expr) => {
112 #[test]
113 fn $name() {
114 assert_eq!(
115 XForwardedProto::decode(
116 &mut $input
117 .into_iter()
118 .map(|s| HeaderValue::from_bytes(s.as_bytes()).unwrap())
119 .collect::<Vec<_>>()
120 .iter()
121 )
122 .ok(),
123 $expected,
124 );
125 }
126 };
127 }
128
129 test_header!(
131 test1,
132 vec!["https"],
133 Some(XForwardedProto(ForwardedProtocol::HTTPS))
134 );
135 test_header!(
136 test2,
137 vec!["https", "http"],
139 Some(XForwardedProto(ForwardedProtocol::HTTPS))
140 );
141 test_header!(
142 test3,
143 vec!["http"],
144 Some(XForwardedProto(ForwardedProtocol::HTTP))
145 );
146
147 #[test]
148 fn test_x_forwarded_proto_symmetric_encoder() {
149 for input in [ForwardedProtocol::HTTP, ForwardedProtocol::HTTPS] {
150 let input = XForwardedProto(input);
151 let mut values = Vec::new();
152 input.encode(&mut values);
153 let output = XForwardedProto::decode(&mut values.iter()).unwrap();
154 assert_eq!(input, output);
155 }
156 }
157}