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::str::FromStr;
8 
9 use headers::{Header, HeaderMapExt};
10 use http::HeaderMap;
11 
12 use filter::{filter_fn, filter_fn_one, Filter, One};
13 use never::Never;
14 use reject::{self, Rejection};
15 
16 /// Create a `Filter` that tries to parse the specified header.
17 ///
18 /// This `Filter` will look for a header with supplied name, and try to
19 /// parse to a `T`, otherwise rejects the request.
20 ///
21 /// # Example
22 ///
23 /// ```
24 /// use std::net::SocketAddr;
25 ///
26 /// // Parse `content-length: 100` as a `u64`
27 /// let content_length = warp::header::<u64>("content-length");
28 ///
29 /// // Parse `host: 127.0.0.1:8080` as a `SocketAddr
30 /// let local_host = warp::header::<SocketAddr>("host");
31 ///
32 /// // Parse `foo: bar` into a `String`
33 /// let foo = warp::header::<String>("foo");
34 /// ```
header<T: FromStr + Send>( name: &'static str, ) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy35 pub fn header<T: FromStr + Send>(
36     name: &'static str,
37 ) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy {
38     filter_fn_one(move |route| {
39         trace!("header({:?})", name);
40         route
41             .headers()
42             .get(name)
43             .ok_or_else(|| reject::missing_header(name))
44             .and_then(|value| {
45                 value
46                     .to_str()
47                     .map_err(|_| reject::invalid_header(name))
48             })
49             .and_then(|s| T::from_str(s).map_err(|_| reject::invalid_header(name)))
50     })
51 }
52 
header2<T: Header + Send>() -> impl Filter<Extract = One<T>, Error = Rejection> + Copy53 pub(crate) fn header2<T: Header + Send>() -> impl Filter<Extract = One<T>, Error = Rejection> + Copy
54 {
55     filter_fn_one(move |route| {
56         trace!("header2({:?})", T::name());
57         route
58             .headers()
59             .typed_get()
60             .ok_or_else(|| reject::invalid_header(T::name().as_str()))
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,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,
81 {
82     filter_fn_one(move |route| {
83         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)) => Ok(Some(t)),
94             Some(Err(e)) => Err(e),
95             None => Ok(None),
96         }
97     })
98 }
99 
optional2<T>() -> impl Filter<Extract = One<Option<T>>, Error = Never> + Copy where T: Header + Send,100 pub(crate) fn optional2<T>() -> impl Filter<Extract = One<Option<T>>, Error = Never> + Copy
101 where
102     T: Header + Send,
103 {
104     filter_fn_one(move |route| 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         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         trace!("exact?({:?}, {:?})", name, value);
143         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     })
155 }
156 
157 /// Create a `Filter` that requires a header to match the value exactly.
158 ///
159 /// This `Filter` will look for a header with supplied name and the exact
160 /// value, ignoring ASCII case, otherwise rejects the request.
161 ///
162 /// # Example
163 ///
164 /// ```
165 /// // Require `connection: keep-alive` header to be set.
166 /// let keep_alive = warp::header::exact("connection", "keep-alive");
167 /// ```
exact_ignore_case( name: &'static str, value: &'static str, ) -> impl Filter<Extract = (), Error = Rejection> + Copy168 pub fn exact_ignore_case(
169     name: &'static str,
170     value: &'static str,
171 ) -> impl Filter<Extract = (), Error = Rejection> + Copy {
172     filter_fn(move |route| {
173         trace!("exact_ignore_case({:?}, {:?})", name, value);
174         route
175             .headers()
176             .get(name)
177             .ok_or_else(|| reject::missing_header(name))
178             .and_then(|val| {
179                 if val.as_bytes().eq_ignore_ascii_case(value.as_bytes()) {
180                     Ok(())
181                 } else {
182                     Err(reject::invalid_header(name))
183                 }
184             })
185     })
186 }
187 
188 /// Create a `Filter` that returns a clone of the request's `HeaderMap`.
189 ///
190 /// # Example
191 ///
192 /// ```
193 /// use warp::{Filter, http::HeaderMap};
194 ///
195 /// let headers = warp::header::headers_cloned()
196 ///     .map(|headers: HeaderMap| {
197 ///         format!("header count: {}", headers.len())
198 ///     });
199 /// ```
headers_cloned() -> impl Filter<Extract = One<HeaderMap>, Error = Never> + Copy200 pub fn headers_cloned() -> impl Filter<Extract = One<HeaderMap>, Error = Never> + Copy {
201     filter_fn_one(|route| Ok(route.headers().clone()))
202 }
203