requiem_http/header/common/
mod.rs

1//! A Collection of Header implementations for common HTTP Headers.
2//!
3//! ## Mime
4//!
5//! Several header fields use MIME values for their contents. Keeping with the
6//! strongly-typed theme, the [mime](https://docs.rs/mime) crate
7//! is used, such as `ContentType(pub Mime)`.
8#![cfg_attr(rustfmt, rustfmt_skip)]
9
10pub use self::accept_charset::AcceptCharset;
11//pub use self::accept_encoding::AcceptEncoding;
12pub use self::accept_language::AcceptLanguage;
13pub use self::accept::Accept;
14pub use self::allow::Allow;
15pub use self::cache_control::{CacheControl, CacheDirective};
16pub use self::content_disposition::{ContentDisposition, DispositionType, DispositionParam};
17pub use self::content_language::ContentLanguage;
18pub use self::content_range::{ContentRange, ContentRangeSpec};
19pub use self::content_type::ContentType;
20pub use self::date::Date;
21pub use self::etag::ETag;
22pub use self::expires::Expires;
23pub use self::if_match::IfMatch;
24pub use self::if_modified_since::IfModifiedSince;
25pub use self::if_none_match::IfNoneMatch;
26pub use self::if_range::IfRange;
27pub use self::if_unmodified_since::IfUnmodifiedSince;
28pub use self::last_modified::LastModified;
29//pub use self::range::{Range, ByteRangeSpec};
30
31#[doc(hidden)]
32#[macro_export]
33macro_rules! __hyper__deref {
34    ($from:ty => $to:ty) => {
35        impl ::std::ops::Deref for $from {
36            type Target = $to;
37
38            #[inline]
39            fn deref(&self) -> &$to {
40                &self.0
41            }
42        }
43
44        impl ::std::ops::DerefMut for $from {
45            #[inline]
46            fn deref_mut(&mut self) -> &mut $to {
47                &mut self.0
48            }
49        }
50    }
51}
52
53#[doc(hidden)]
54#[macro_export]
55macro_rules! __hyper__tm {
56    ($id:ident, $tm:ident{$($tf:item)*}) => {
57        #[allow(unused_imports)]
58        #[cfg(test)]
59        mod $tm{
60            use std::str;
61            use http::Method;
62            use mime::*;
63            use $crate::header::*;
64            use super::$id as HeaderField;
65            $($tf)*
66        }
67
68    }
69}
70
71#[doc(hidden)]
72#[macro_export]
73macro_rules! test_header {
74    ($id:ident, $raw:expr) => {
75        #[test]
76        fn $id() {
77            use $crate::test;
78            use super::*;
79
80            let raw = $raw;
81            let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect();
82            let mut req = test::TestRequest::default();
83            for item in a {
84                req = req.header(HeaderField::name(), item).take();
85            }
86            let req = req.finish();
87            let value = HeaderField::parse(&req);
88            let result = format!("{}", value.unwrap());
89            let expected = String::from_utf8(raw[0].to_vec()).unwrap();
90            let result_cmp: Vec<String> = result
91                .to_ascii_lowercase()
92                .split(' ')
93                .map(|x| x.to_owned())
94                .collect();
95            let expected_cmp: Vec<String> = expected
96                .to_ascii_lowercase()
97                .split(' ')
98                .map(|x| x.to_owned())
99                .collect();
100            assert_eq!(result_cmp.concat(), expected_cmp.concat());
101        }
102    };
103    ($id:ident, $raw:expr, $typed:expr) => {
104        #[test]
105        fn $id() {
106            use $crate::test;
107
108            let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect();
109            let mut req = test::TestRequest::default();
110            for item in a {
111                req.header(HeaderField::name(), item);
112            }
113            let req = req.finish();
114            let val = HeaderField::parse(&req);
115            let typed: Option<HeaderField> = $typed;
116            // Test parsing
117            assert_eq!(val.ok(), typed);
118            // Test formatting
119            if typed.is_some() {
120                let raw = &($raw)[..];
121                let mut iter = raw.iter().map(|b|str::from_utf8(&b[..]).unwrap());
122                let mut joined = String::new();
123                joined.push_str(iter.next().unwrap());
124                for s in iter {
125                    joined.push_str(", ");
126                    joined.push_str(s);
127                }
128                assert_eq!(format!("{}", typed.unwrap()), joined);
129            }
130        }
131    }
132}
133
134#[macro_export]
135macro_rules! header {
136    // $a:meta: Attributes associated with the header item (usually docs)
137    // $id:ident: Identifier of the header
138    // $n:expr: Lowercase name of the header
139    // $nn:expr: Nice name of the header
140
141    // List header, zero or more items
142    ($(#[$a:meta])*($id:ident, $name:expr) => ($item:ty)*) => {
143        $(#[$a])*
144        #[derive(Clone, Debug, PartialEq)]
145        pub struct $id(pub Vec<$item>);
146        __hyper__deref!($id => Vec<$item>);
147        impl $crate::http::header::Header for $id {
148            #[inline]
149            fn name() -> $crate::http::header::HeaderName {
150                $name
151            }
152            #[inline]
153            fn parse<T>(msg: &T) -> Result<Self, $crate::error::ParseError>
154                where T: $crate::HttpMessage
155            {
156                $crate::http::header::from_comma_delimited(
157                    msg.headers().get_all(Self::name())).map($id)
158            }
159        }
160        impl std::fmt::Display for $id {
161            #[inline]
162            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> ::std::fmt::Result {
163                $crate::http::header::fmt_comma_delimited(f, &self.0[..])
164            }
165        }
166        impl $crate::http::header::IntoHeaderValue for $id {
167            type Error = $crate::http::header::InvalidHeaderValue;
168
169            fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
170                use std::fmt::Write;
171                let mut writer = $crate::http::header::Writer::new();
172                let _ = write!(&mut writer, "{}", self);
173                $crate::http::header::HeaderValue::from_maybe_shared(writer.take())
174            }
175        }
176    };
177    // List header, one or more items
178    ($(#[$a:meta])*($id:ident, $name:expr) => ($item:ty)+) => {
179        $(#[$a])*
180        #[derive(Clone, Debug, PartialEq)]
181        pub struct $id(pub Vec<$item>);
182        __hyper__deref!($id => Vec<$item>);
183        impl $crate::http::header::Header for $id {
184            #[inline]
185            fn name() -> $crate::http::header::HeaderName {
186                $name
187            }
188            #[inline]
189            fn parse<T>(msg: &T) -> Result<Self, $crate::error::ParseError>
190                where T: $crate::HttpMessage
191            {
192                $crate::http::header::from_comma_delimited(
193                    msg.headers().get_all(Self::name())).map($id)
194            }
195        }
196        impl std::fmt::Display for $id {
197            #[inline]
198            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199                $crate::http::header::fmt_comma_delimited(f, &self.0[..])
200            }
201        }
202        impl $crate::http::header::IntoHeaderValue for $id {
203            type Error = $crate::http::header::InvalidHeaderValue;
204
205            fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
206                use std::fmt::Write;
207                let mut writer = $crate::http::header::Writer::new();
208                let _ = write!(&mut writer, "{}", self);
209                $crate::http::header::HeaderValue::from_maybe_shared(writer.take())
210            }
211        }
212    };
213    // Single value header
214    ($(#[$a:meta])*($id:ident, $name:expr) => [$value:ty]) => {
215        $(#[$a])*
216        #[derive(Clone, Debug, PartialEq)]
217        pub struct $id(pub $value);
218        __hyper__deref!($id => $value);
219        impl $crate::http::header::Header for $id {
220            #[inline]
221            fn name() -> $crate::http::header::HeaderName {
222                $name
223            }
224            #[inline]
225            fn parse<T>(msg: &T) -> Result<Self, $crate::error::ParseError>
226                where T: $crate::HttpMessage
227            {
228                $crate::http::header::from_one_raw_str(
229                    msg.headers().get(Self::name())).map($id)
230            }
231        }
232        impl std::fmt::Display for $id {
233            #[inline]
234            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235                std::fmt::Display::fmt(&self.0, f)
236            }
237        }
238        impl $crate::http::header::IntoHeaderValue for $id {
239            type Error = $crate::http::header::InvalidHeaderValue;
240
241            fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
242                self.0.try_into()
243            }
244        }
245    };
246    // List header, one or more items with "*" option
247    ($(#[$a:meta])*($id:ident, $name:expr) => {Any / ($item:ty)+}) => {
248        $(#[$a])*
249        #[derive(Clone, Debug, PartialEq)]
250        pub enum $id {
251            /// Any value is a match
252            Any,
253            /// Only the listed items are a match
254            Items(Vec<$item>),
255        }
256        impl $crate::http::header::Header for $id {
257            #[inline]
258            fn name() -> $crate::http::header::HeaderName {
259                $name
260            }
261            #[inline]
262            fn parse<T>(msg: &T) -> Result<Self, $crate::error::ParseError>
263                where T: $crate::HttpMessage
264            {
265                let any = msg.headers().get(Self::name()).and_then(|hdr| {
266                    hdr.to_str().ok().and_then(|hdr| Some(hdr.trim() == "*"))});
267
268                if let Some(true) = any {
269                    Ok($id::Any)
270                } else {
271                    Ok($id::Items(
272                        $crate::http::header::from_comma_delimited(
273                            msg.headers().get_all(Self::name()))?))
274                }
275            }
276        }
277        impl std::fmt::Display for $id {
278            #[inline]
279            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280                match *self {
281                    $id::Any => f.write_str("*"),
282                    $id::Items(ref fields) => $crate::http::header::fmt_comma_delimited(
283                        f, &fields[..])
284                }
285            }
286        }
287        impl $crate::http::header::IntoHeaderValue for $id {
288            type Error = $crate::http::header::InvalidHeaderValue;
289
290            fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> {
291                use std::fmt::Write;
292                let mut writer = $crate::http::header::Writer::new();
293                let _ = write!(&mut writer, "{}", self);
294                $crate::http::header::HeaderValue::from_maybe_shared(writer.take())
295            }
296        }
297    };
298
299    // optional test module
300    ($(#[$a:meta])*($id:ident, $name:expr) => ($item:ty)* $tm:ident{$($tf:item)*}) => {
301        header! {
302            $(#[$a])*
303            ($id, $name) => ($item)*
304        }
305
306        __hyper__tm! { $id, $tm { $($tf)* }}
307    };
308    ($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)+ $tm:ident{$($tf:item)*}) => {
309        header! {
310            $(#[$a])*
311            ($id, $n) => ($item)+
312        }
313
314        __hyper__tm! { $id, $tm { $($tf)* }}
315    };
316    ($(#[$a:meta])*($id:ident, $name:expr) => [$item:ty] $tm:ident{$($tf:item)*}) => {
317        header! {
318            $(#[$a])* ($id, $name) => [$item]
319        }
320
321        __hyper__tm! { $id, $tm { $($tf)* }}
322    };
323    ($(#[$a:meta])*($id:ident, $name:expr) => {Any / ($item:ty)+} $tm:ident{$($tf:item)*}) => {
324        header! {
325            $(#[$a])*
326            ($id, $name) => {Any / ($item)+}
327        }
328
329        __hyper__tm! { $id, $tm { $($tf)* }}
330    };
331}
332
333
334mod accept_charset;
335//mod accept_encoding;
336mod accept_language;
337mod accept;
338mod allow;
339mod cache_control;
340mod content_disposition;
341mod content_language;
342mod content_range;
343mod content_type;
344mod date;
345mod etag;
346mod expires;
347mod if_match;
348mod if_modified_since;
349mod if_none_match;
350mod if_range;
351mod if_unmodified_since;
352mod last_modified;