trusted_proxies/
extract.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/// A trait to extract required information from a request in order to fetch trusted information
pub trait RequestInformation {
    /// Check if the host header is allowed
    ///
    /// Most implementations should return `true` if the HTTP version is less than HTTP/2
    fn is_host_header_allowed(&self) -> bool;

    /// Get the host header of the request
    fn host_header(&self) -> Option<&str>;

    /// Get the authority of the request
    fn authority(&self) -> Option<&str>;

    /// Get the `Forwarded` header values
    ///
    /// A double-ended iterator is returned to allow the implementation to optimize the iteration in
    /// case of multiple values
    fn forwarded(&self) -> impl DoubleEndedIterator<Item = &str>;

    /// Get the `X-Forwarded-For` header values
    fn x_forwarded_for(&self) -> impl DoubleEndedIterator<Item = &str>;

    /// Get the `X-Forwarded-Host` header values
    fn x_forwarded_host(&self) -> impl DoubleEndedIterator<Item = &str>;

    /// Get the `X-Forwarded-Proto` header values
    fn x_forwarded_proto(&self) -> impl DoubleEndedIterator<Item = &str>;

    /// Get the `X-Forwarded-By` header values
    fn x_forwarded_by(&self) -> impl DoubleEndedIterator<Item = &str>;

    /// Return the default host of the request when no trusted headers are found
    ///
    /// Default to host header if allowed or authority
    fn default_host(&self) -> Option<&str> {
        self.host_header()
            // skip host header if HTTP/2, we should use :authority instead
            .filter(|_| self.is_host_header_allowed())
            .or_else(|| self.authority())
    }

    /// Return the default scheme of the request when no trusted headers are found
    fn default_scheme(&self) -> Option<&str>;
}

#[cfg(feature = "http")]
mod http {
    use super::RequestInformation;

    impl<T> RequestInformation for http::Request<T> {
        fn is_host_header_allowed(&self) -> bool {
            self.version() < http::Version::HTTP_2
        }

        fn host_header(&self) -> Option<&str> {
            self.headers()
                .get("host")
                .and_then(|value| value.to_str().ok())
        }

        fn authority(&self) -> Option<&str> {
            self.uri().authority().map(|auth| auth.as_str())
        }

        fn forwarded(&self) -> impl DoubleEndedIterator<Item = &str> {
            self.headers()
                .get_all("forwarded")
                .iter()
                .filter_map(|value| value.to_str().ok())
        }

        fn x_forwarded_for(&self) -> impl DoubleEndedIterator<Item = &str> {
            self.headers()
                .get_all("x-forwarded-for")
                .iter()
                .filter_map(|value| value.to_str().ok())
        }

        fn x_forwarded_host(&self) -> impl DoubleEndedIterator<Item = &str> {
            self.headers()
                .get_all("x-forwarded-host")
                .iter()
                .filter_map(|value| value.to_str().ok())
        }

        fn x_forwarded_proto(&self) -> impl DoubleEndedIterator<Item = &str> {
            self.headers()
                .get_all("x-forwarded-proto")
                .iter()
                .filter_map(|value| value.to_str().ok())
        }

        fn x_forwarded_by(&self) -> impl DoubleEndedIterator<Item = &str> {
            self.headers()
                .get_all("x-forwarded-by")
                .iter()
                .filter_map(|value| value.to_str().ok())
        }

        fn default_scheme(&self) -> Option<&str> {
            self.uri().scheme_str()
        }
    }

    impl RequestInformation for http::request::Parts {
        fn is_host_header_allowed(&self) -> bool {
            self.version < http::Version::HTTP_2
        }

        fn host_header(&self) -> Option<&str> {
            self.headers
                .get("host")
                .and_then(|value| value.to_str().ok())
        }

        fn authority(&self) -> Option<&str> {
            self.uri.authority().map(|auth| auth.as_str())
        }

        fn forwarded(&self) -> impl DoubleEndedIterator<Item = &str> {
            self.headers
                .get_all("forwarded")
                .iter()
                .filter_map(|value| value.to_str().ok())
        }

        fn x_forwarded_for(&self) -> impl DoubleEndedIterator<Item = &str> {
            self.headers
                .get_all("x-forwarded-for")
                .iter()
                .filter_map(|value| value.to_str().ok())
        }

        fn x_forwarded_host(&self) -> impl DoubleEndedIterator<Item = &str> {
            self.headers
                .get_all("x-forwarded-host")
                .iter()
                .filter_map(|value| value.to_str().ok())
        }

        fn x_forwarded_proto(&self) -> impl DoubleEndedIterator<Item = &str> {
            self.headers
                .get_all("x-forwarded-proto")
                .iter()
                .filter_map(|value| value.to_str().ok())
        }

        fn x_forwarded_by(&self) -> impl DoubleEndedIterator<Item = &str> {
            self.headers
                .get_all("x-forwarded-by")
                .iter()
                .filter_map(|value| value.to_str().ok())
        }

        fn default_scheme(&self) -> Option<&str> {
            self.uri.scheme_str()
        }
    }
}