1 //! Header Filters
2 //!
3 //! These filters are used to interact with the Request HTTP headers. Some
4 //! of them, like `exact` and `exact_ignore_case`, are just predicates,
5 //! they don't extract any values. The `header` filter allows parsing
6 //! a type from any header.
7 use std::convert::Infallible;
8 use std::str::FromStr;
9 
10 use futures::future;
11 use headers::{Header, HeaderMapExt};
12 use http::header::HeaderValue;
13 use http::HeaderMap;
14 
15 use crate::filter::{filter_fn, filter_fn_one, Filter, One};
16 use crate::reject::{self, Rejection};
17 
18 /// Create a `Filter` that tries to parse the specified header.
19 ///
20 /// This `Filter` will look for a header with supplied name, and try to
21 /// parse to a `T`, otherwise rejects the request.
22 ///
23 /// # Example
24 ///
25 /// ```
26 /// use std::net::SocketAddr;
27 ///
28 /// // Parse `content-length: 100` as a `u64`
29 /// let content_length = warp::header::<u64>("content-length");
30 ///
31 /// // Parse `host: 127.0.0.1:8080` as a `SocketAddr
32 /// let local_host = warp::header::<SocketAddr>("host");
33 ///
34 /// // Parse `foo: bar` into a `String`
35 /// let foo = warp::header::<String>("foo");
36 /// ```
header<T: FromStr + Send + 'static>( name: &'static str, ) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy37 pub fn header<T: FromStr + Send + 'static>(
38     name: &'static str,
39 ) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy {
40     filter_fn_one(move |route| {
41         tracing::trace!("header({:?})", name);
42         let route = route
43             .headers()
44             .get(name)
45             .ok_or_else(|| reject::missing_header(name))
46             .and_then(|value| value.to_str().map_err(|_| reject::invalid_header(name)))
47             .and_then(|s| T::from_str(s).map_err(|_| reject::invalid_header(name)));
48         future::ready(route)
49     })
50 }
51 
header2<T: Header + Send + 'static>( ) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy52 pub(crate) fn header2<T: Header + Send + 'static>(
53 ) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy {
54     filter_fn_one(move |route| {
55         tracing::trace!("header2({:?})", T::name());
56         let route = route
57             .headers()
58             .typed_get()
59             .ok_or_else(|| reject::invalid_header(T::name().as_str()));
60         future::ready(route)
61     })
62 }
63 
64 /// Create a `Filter` that tries to parse the specified header, if it exists.
65 ///
66 /// If the header does not exist, it yields `None`. Otherwise, it will try to
67 /// parse as a `T`, and if it fails, a invalid header rejection is return. If
68 /// successful, the filter yields `Some(T)`.
69 ///
70 /// # Example
71 ///
72 /// ```
73 /// // Grab the `authorization` header if it exists.
74 /// let opt_auth = warp::header::optional::<String>("authorization");
75 /// ```
optional<T>( name: &'static str, ) -> impl Filter<Extract = One<Option<T>>, Error = Rejection> + Copy where T: FromStr + Send + 'static,76 pub fn optional<T>(
77     name: &'static str,
78 ) -> impl Filter<Extract = One<Option<T>>, Error = Rejection> + Copy
79 where
80     T: FromStr + Send + 'static,
81 {
82     filter_fn_one(move |route| {
83         tracing::trace!("optional({:?})", name);
84         let result = route.headers().get(name).map(|value| {
85             value
86                 .to_str()
87                 .map_err(|_| reject::invalid_header(name))?
88                 .parse::<T>()
89                 .map_err(|_| reject::invalid_header(name))
90         });
91 
92         match result {
93             Some(Ok(t)) => future::ok(Some(t)),
94             Some(Err(e)) => future::err(e),
95             None => future::ok(None),
96         }
97     })
98 }
99 
optional2<T>() -> impl Filter<Extract = One<Option<T>>, Error = Infallible> + Copy where T: Header + Send + 'static,100 pub(crate) fn optional2<T>() -> impl Filter<Extract = One<Option<T>>, Error = Infallible> + Copy
101 where
102     T: Header + Send + 'static,
103 {
104     filter_fn_one(move |route| future::ready(Ok(route.headers().typed_get())))
105 }
106 
107 /* TODO
108 pub fn exact2<T>(header: T) -> impl FilterClone<Extract=(), Error=Rejection>
109 where
110     T: Header + PartialEq + Clone + Send,
111 {
112     filter_fn(move |route| {
113         tracing::trace!("exact2({:?})", T::NAME);
114         route.headers()
115             .typed_get::<T>()
116             .and_then(|val| if val == header {
117                 Some(())
118             } else {
119                 None
120             })
121             .ok_or_else(|| reject::bad_request())
122     })
123 }
124 */
125 
126 /// Create a `Filter` that requires a header to match the value exactly.
127 ///
128 /// This `Filter` will look for a header with supplied name and the exact
129 /// value, otherwise rejects the request.
130 ///
131 /// # Example
132 ///
133 /// ```
134 /// // Require `dnt: 1` header to be set.
135 /// let must_dnt = warp::header::exact("dnt", "1");
136 /// ```
exact( name: &'static str, value: &'static str, ) -> impl Filter<Extract = (), Error = Rejection> + Copy137 pub fn exact(
138     name: &'static str,
139     value: &'static str,
140 ) -> impl Filter<Extract = (), Error = Rejection> + Copy {
141     filter_fn(move |route| {
142         tracing::trace!("exact?({:?}, {:?})", name, value);
143         let route = route
144             .headers()
145             .get(name)
146             .ok_or_else(|| reject::missing_header(name))
147             .and_then(|val| {
148                 if val == value {
149                     Ok(())
150                 } else {
151                     Err(reject::invalid_header(name))
152                 }
153             });
154         future::ready(route)
155     })
156 }
157 
158 /// Create a `Filter` that requires a header to match the value exactly.
159 ///
160 /// This `Filter` will look for a header with supplied name and the exact
161 /// value, ignoring ASCII case, otherwise rejects the request.
162 ///
163 /// # Example
164 ///
165 /// ```
166 /// // Require `connection: keep-alive` header to be set.
167 /// let keep_alive = warp::header::exact_ignore_case("connection", "keep-alive");
168 /// ```
exact_ignore_case( name: &'static str, value: &'static str, ) -> impl Filter<Extract = (), Error = Rejection> + Copy169 pub fn exact_ignore_case(
170     name: &'static str,
171     value: &'static str,
172 ) -> impl Filter<Extract = (), Error = Rejection> + Copy {
173     filter_fn(move |route| {
174         tracing::trace!("exact_ignore_case({:?}, {:?})", name, value);
175         let route = route
176             .headers()
177             .get(name)
178             .ok_or_else(|| reject::missing_header(name))
179             .and_then(|val| {
180                 if val.as_bytes().eq_ignore_ascii_case(value.as_bytes()) {
181                     Ok(())
182                 } else {
183                     Err(reject::invalid_header(name))
184                 }
185             });
186         future::ready(route)
187     })
188 }
189 
190 /// Create a `Filter` that gets a `HeaderValue` for the name.
191 ///
192 /// # Example
193 ///
194 /// ```
195 /// use warp::{Filter, http::header::HeaderValue};
196 ///
197 /// let filter = warp::header::value("x-token")
198 ///     .map(|value: HeaderValue| {
199 ///         format!("header value bytes: {:?}", value)
200 ///     });
201 /// ```
value( name: &'static str, ) -> impl Filter<Extract = One<HeaderValue>, Error = Rejection> + Copy202 pub fn value(
203     name: &'static str,
204 ) -> impl Filter<Extract = One<HeaderValue>, Error = Rejection> + Copy {
205     filter_fn_one(move |route| {
206         tracing::trace!("value({:?})", name);
207         let route = route
208             .headers()
209             .get(name)
210             .cloned()
211             .ok_or_else(|| reject::missing_header(name));
212         future::ready(route)
213     })
214 }
215 
216 /// Create a `Filter` that returns a clone of the request's `HeaderMap`.
217 ///
218 /// # Example
219 ///
220 /// ```
221 /// use warp::{Filter, http::HeaderMap};
222 ///
223 /// let headers = warp::header::headers_cloned()
224 ///     .map(|headers: HeaderMap| {
225 ///         format!("header count: {}", headers.len())
226 ///     });
227 /// ```
headers_cloned() -> impl Filter<Extract = One<HeaderMap>, Error = Infallible> + Copy228 pub fn headers_cloned() -> impl Filter<Extract = One<HeaderMap>, Error = Infallible> + Copy {
229     filter_fn_one(|route| future::ok(route.headers().clone()))
230 }
231