Skip to main content

ntex_files/header/
mod.rs

1#![allow(dead_code, clippy::useless_conversion)]
2
3mod charset;
4mod common;
5mod content_disposition;
6mod entity;
7pub(crate) mod error;
8mod http_date;
9pub(crate) mod method;
10pub(crate) mod parsing;
11mod raw;
12
13pub use charset::Charset;
14pub use common::*;
15pub use content_disposition::*;
16pub use entity::EntityTag;
17use http::HeaderValue;
18pub use http_date::HttpDate;
19pub use raw::{Raw, RawLike};
20
21use self::sealed::HeaderClone;
22
23/// A trait for any object that will represent a header field and value.
24///
25/// This trait represents the construction and identification of headers,
26/// and contains trait-object unsafe methods.
27pub trait Header: 'static + HeaderClone + Send + Sync {
28    /// Returns the name of the header field this belongs to.
29    ///
30    /// This will become an associated constant once available.
31    fn header_name() -> &'static str
32    where
33        Self: Sized;
34
35    /// Parse a header from a raw stream of bytes.
36    ///
37    /// It's possible that a request can include a header field more than once,
38    /// and in that case, the slice will have a length greater than 1. However,
39    /// it's not necessarily the case that a Header is *allowed* to have more
40    /// than one field value. If that's the case, you **should** return `None`
41    /// if `raw.len() > 1`.
42    fn parse_header<'a, T>(raw: &'a T) -> error::Result<Self>
43    where
44        T: RawLike<'a>,
45        Self: Sized;
46
47    /// Format a header to outgoing stream.
48    ///
49    /// Most headers should be formatted on one line, and so a common pattern
50    /// would be to implement `std::fmt::Display` for this type as well, and
51    /// then just call `f.fmt_line(self)`.
52    ///
53    /// ## Note
54    ///
55    /// This has the ability to format a header over multiple lines.
56    ///
57    /// The main example here is `Set-Cookie`, which requires that every
58    /// cookie being set be specified in a separate line. Almost every other
59    /// case should only format as 1 single line.
60    fn fmt_header(&self, f: &mut Formatter) -> std::fmt::Result;
61}
62
63mod sealed {
64    use super::Header;
65
66    #[doc(hidden)]
67    pub trait HeaderClone {
68        fn clone_box(&self) -> Box<dyn Header + Send + Sync>;
69    }
70
71    impl<T: Header + Clone> HeaderClone for T {
72        #[inline]
73        fn clone_box(&self) -> Box<dyn Header + Send + Sync> {
74            Box::new(self.clone())
75        }
76    }
77}
78
79/// A formatter used to serialize headers to an output stream.
80#[allow(missing_debug_implementations)]
81pub struct Formatter<'a, 'b: 'a>(Multi<'a, 'b>);
82
83#[allow(unused)]
84enum Multi<'a, 'b: 'a> {
85    Line(&'a str, &'a mut std::fmt::Formatter<'b>),
86    Join(bool, &'a mut std::fmt::Formatter<'b>),
87    Raw(&'a mut raw::Raw),
88}
89
90impl<'a, 'b> Formatter<'a, 'b> {
91    /// Format one 'line' of a header.
92    ///
93    /// This writes the header name plus the `Display` value as a single line.
94    ///
95    /// ## Note
96    ///
97    /// This has the ability to format a header over multiple lines.
98    ///
99    /// The main example here is `Set-Cookie`, which requires that every
100    /// cookie being set be specified in a separate line. Almost every other
101    /// case should only format as 1 single line.
102    pub fn fmt_line(&mut self, line: &dyn std::fmt::Display) -> std::fmt::Result {
103        use std::fmt::Write;
104        match self.0 {
105            Multi::Line(name, ref mut f) => {
106                f.write_str(name)?;
107                f.write_str(": ")?;
108                write!(NewlineReplacer(*f), "{}", line)?;
109                f.write_str("\r\n")
110            }
111            Multi::Join(ref mut first, ref mut f) => {
112                if !*first {
113                    f.write_str(", ")?;
114                } else {
115                    *first = false;
116                }
117                write!(NewlineReplacer(*f), "{}", line)
118            }
119            Multi::Raw(ref mut raw) => {
120                let mut s = String::new();
121                write!(NewlineReplacer(&mut s), "{}", line)?;
122                raw.push(s);
123                Ok(())
124            }
125        }
126    }
127
128    fn danger_fmt_line_without_newline_replacer<T: std::fmt::Display>(
129        &mut self,
130        line: &T,
131    ) -> std::fmt::Result {
132        use std::fmt::Write;
133        match self.0 {
134            Multi::Line(name, ref mut f) => {
135                f.write_str(name)?;
136                f.write_str(": ")?;
137                std::fmt::Display::fmt(line, f)?;
138                f.write_str("\r\n")
139            }
140            Multi::Join(ref mut first, ref mut f) => {
141                if !*first {
142                    f.write_str(", ")?;
143                } else {
144                    *first = false;
145                }
146                std::fmt::Display::fmt(line, f)
147            }
148            Multi::Raw(ref mut raw) => {
149                let mut s = String::new();
150                write!(s, "{}", line)?;
151                raw.push(s);
152                Ok(())
153            }
154        }
155    }
156}
157
158struct NewlineReplacer<'a, F: std::fmt::Write + 'a>(&'a mut F);
159
160impl<'a, F: std::fmt::Write + 'a> std::fmt::Write for NewlineReplacer<'a, F> {
161    #[inline]
162    fn write_str(&mut self, s: &str) -> std::fmt::Result {
163        let mut since = 0;
164        for (i, &byte) in s.as_bytes().iter().enumerate() {
165            if byte == b'\r' || byte == b'\n' {
166                self.0.write_str(&s[since..i])?;
167                self.0.write_str(" ")?;
168                since = i + 1;
169            }
170        }
171        if since < s.len() { self.0.write_str(&s[since..]) } else { Ok(()) }
172    }
173
174    #[inline]
175    fn write_fmt(&mut self, args: std::fmt::Arguments) -> std::fmt::Result {
176        std::fmt::write(self, args)
177    }
178}
179
180/// A trait for the "standard" headers that have an associated `HeaderName`
181/// constant in the _http_ crate.
182pub trait StandardHeader: Header + Sized {
183    /// The `HeaderName` from the _http_ crate for this header.
184    fn http_header_name() -> ::http::header::HeaderName;
185}
186
187impl<'a> RawLike<'a> for &'a HeaderValue {
188    type IntoIter = ::std::iter::Once<&'a [u8]>;
189
190    fn len(&'a self) -> usize {
191        1
192    }
193
194    fn one(&'a self) -> Option<&'a [u8]> {
195        Some(self.as_bytes())
196    }
197
198    fn iter(&'a self) -> Self::IntoIter {
199        ::std::iter::once(self.as_bytes())
200    }
201}
202
203#[doc(hidden)]
204#[macro_export]
205macro_rules! standard_header {
206    ($local:ident, $hname:ident) => {
207        impl $crate::header::StandardHeader for $local {
208            #[inline]
209            fn http_header_name() -> ::http::header::HeaderName {
210                ::http::header::$hname
211            }
212        }
213    };
214}
215
216#[doc(hidden)]
217#[macro_export]
218macro_rules! __deref__ {
219    ($from:ty => $to:ty) => {
220        impl ::std::ops::Deref for $from {
221            type Target = $to;
222
223            #[inline]
224            fn deref(&self) -> &$to {
225                &self.0
226            }
227        }
228
229        impl ::std::ops::DerefMut for $from {
230            #[inline]
231            fn deref_mut(&mut self) -> &mut $to {
232                &mut self.0
233            }
234        }
235    };
236}
237
238#[doc(hidden)]
239#[macro_export]
240macro_rules! __tm__ {
241    ($id:ident, $tm:ident{$($tf:item)*}) => {
242        #[allow(unused_imports)]
243        #[cfg(test)]
244        mod $tm{
245            use std::str;
246            use $crate::header::*;
247            use mime::*;
248            use $crate::method::Method;
249            use super::$id as HeaderField;
250            $($tf)*
251        }
252
253    }
254}
255
256/// Create a custom header type.
257#[macro_export]
258macro_rules! header {
259    // $a:meta: Attributes associated with the header item (usually docs)
260    // $id:ident: Identifier of the header
261    // $n:expr: Lowercase name of the header
262    // $nn:expr: Nice name of the header
263
264    // List header, zero or more items
265    ($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)*) => {
266        $(#[$a])*
267        #[derive(Clone, Debug, PartialEq)]
268        pub struct $id(pub Vec<$item>);
269        $crate::__deref__!($id => Vec<$item>);
270        impl $crate::header::Header for $id {
271            fn header_name() -> &'static str {
272                static NAME: &'static str = $n;
273                NAME
274            }
275            #[inline]
276            fn parse_header<'a, T>(raw: &'a T) -> $crate::Result<Self>
277            where T: $crate::header::RawLike<'a>
278            {
279                $crate::header::parsing::from_comma_delimited(raw).map($id)
280            }
281            #[inline]
282            fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
283                f.fmt_line(self)
284            }
285        }
286        impl ::std::fmt::Display for $id {
287            #[inline]
288            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
289                $crate::header::parsing::fmt_comma_delimited(f, &self.0[..])
290            }
291        }
292    };
293    // List header, one or more items
294    ($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)+) => {
295        $(#[$a])*
296        #[derive(Clone, Debug, PartialEq)]
297        pub struct $id(pub Vec<$item>);
298        $crate::__deref__!($id => Vec<$item>);
299        impl $crate::header::Header for $id {
300            #[inline]
301            fn header_name() -> &'static str {
302                static NAME: &'static str = $n;
303                NAME
304            }
305            #[inline]
306            fn parse_header<'a, T>(raw: &'a T) -> $crate::Result<Self>
307            where T: $crate::header::RawLike<'a>
308            {
309                $crate::header::parsing::from_comma_delimited(raw).map($id)
310            }
311            #[inline]
312            fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
313                f.fmt_line(self)
314            }
315        }
316        impl ::std::fmt::Display for $id {
317            #[inline]
318            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
319                $crate::header::parsing::fmt_comma_delimited(f, &self.0[..])
320            }
321        }
322    };
323    // Single value header
324    ($(#[$a:meta])*($id:ident, $n:expr) => [$value:ty]) => {
325        $(#[$a])*
326        #[derive(Clone, Debug, PartialEq)]
327        pub struct $id(pub $value);
328        $crate::__deref__!($id => $value);
329        impl $crate::header::Header for $id {
330            #[inline]
331            fn header_name() -> &'static str {
332                static NAME: &'static str = $n;
333                NAME
334            }
335            #[inline]
336            fn parse_header<'a, T>(raw: &'a T) -> $crate::header::error::Result<Self>
337            where T: $crate::header::RawLike<'a>
338            {
339                $crate::header::parsing::from_one_raw_str(raw).map($id)
340            }
341            #[inline]
342            fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
343                f.fmt_line(self)
344            }
345        }
346        impl ::std::fmt::Display for $id {
347            #[inline]
348            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
349                ::std::fmt::Display::fmt(&self.0, f)
350            }
351        }
352    };
353    // Single value header (internal)
354    ($(#[$a:meta])*($id:ident, $n:expr) => danger [$value:ty]) => {
355        $(#[$a])*
356        #[derive(Clone, Debug, PartialEq)]
357        pub struct $id(pub $value);
358        $crate::__deref__!($id => $value);
359        impl $crate::header::Header for $id {
360            #[inline]
361            fn header_name() -> &'static str {
362                static NAME: &'static str = $n;
363                NAME
364            }
365            #[inline]
366            fn parse_header<'a, T>(raw: &'a T) -> $crate::Result<Self>
367            where T: $crate::header::RawLike<'a>
368            {
369                $crate::header::parsing::from_one_raw_str(raw).map($id)
370            }
371            #[inline]
372            fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
373                f.danger_fmt_line_without_newline_replacer(self)
374            }
375        }
376        impl ::std::fmt::Display for $id {
377            #[inline]
378            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
379                ::std::fmt::Display::fmt(&self.0, f)
380            }
381        }
382    };
383    // Single value cow header
384    ($(#[$a:meta])*($id:ident, $n:expr) => Cow[$value:ty]) => {
385        $(#[$a])*
386        #[derive(Clone, Debug, PartialEq)]
387        pub struct $id(::std::borrow::Cow<'static,$value>);
388        impl $id {
389            /// Creates a new $id
390            pub fn new<I: Into<::std::borrow::Cow<'static,$value>>>(value: I) -> Self {
391                $id(value.into())
392            }
393        }
394        impl ::std::ops::Deref for $id {
395            type Target = $value;
396            #[inline]
397            fn deref(&self) -> &Self::Target {
398                &(self.0)
399            }
400        }
401        impl $crate::header::Header for $id {
402            #[inline]
403            fn header_name() -> &'static str {
404                static NAME: &'static str = $n;
405                NAME
406            }
407            #[inline]
408            fn parse_header<'a, T>(raw: &'a T) -> $crate::Result<Self>
409            where T: $crate::header::RawLike<'a>
410            {
411                $crate::header::parsing::from_one_raw_str::<_, <$value as ::std::borrow::ToOwned>::Owned>(raw).map($id::new)
412            }
413            #[inline]
414            fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
415                f.fmt_line(self)
416            }
417        }
418        impl ::std::fmt::Display for $id {
419            #[inline]
420            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
421                ::std::fmt::Display::fmt(&self.0, f)
422            }
423        }
424    };
425    // List header, one or more items with "*" option
426    ($(#[$a:meta])*($id:ident, $n:expr) => {Any / ($item:ty)+}) => {
427        $(#[$a])*
428        #[derive(Clone, Debug, PartialEq)]
429        pub enum $id {
430            /// Any value is a match
431            Any,
432            /// Only the listed items are a match
433            Items(Vec<$item>),
434        }
435        impl $crate::header::Header for $id {
436            #[inline]
437            fn header_name() -> &'static str {
438                static NAME: &'static str = $n;
439                NAME
440            }
441            #[inline]
442            fn parse_header<'a, T>(raw: &'a T) -> $crate::header::error::Result<Self>
443            where T: $crate::header::RawLike<'a>
444            {
445                // FIXME: Return None if no item is in $id::Only
446                if let Some(l) = raw.one() {
447                    if l == b"*" {
448                        return Ok($id::Any)
449                    }
450                }
451                $crate::header::parsing::from_comma_delimited(raw).map($id::Items)
452            }
453            #[inline]
454            fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
455                f.fmt_line(self)
456            }
457        }
458        impl ::std::fmt::Display for $id {
459            #[inline]
460            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
461                match *self {
462                    $id::Any => f.write_str("*"),
463                    $id::Items(ref fields) => $crate::header::parsing::fmt_comma_delimited(
464                        f, &fields[..])
465                }
466            }
467        }
468    };
469
470    // optional test module
471    ($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)* $tm:ident{$($tf:item)*}) => {
472        header! {
473            $(#[$a])*
474            ($id, $n) => ($item)*
475        }
476
477        $crate::__tm__! { $id, $tm { $($tf)* }}
478    };
479    ($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)+ $tm:ident{$($tf:item)*}) => {
480        header! {
481            $(#[$a])*
482            ($id, $n) => ($item)+
483        }
484
485        $crate::__tm__! { $id, $tm { $($tf)* }}
486    };
487    ($(#[$a:meta])*($id:ident, $n:expr) => [$item:ty] $tm:ident{$($tf:item)*}) => {
488        header! {
489            $(#[$a])*
490            ($id, $n) => [$item]
491        }
492
493        $crate::__tm__! { $id, $tm { $($tf)* }}
494    };
495    ($(#[$a:meta])*($id:ident, $n:expr) => danger [$item:ty] $tm:ident{$($tf:item)*}) => {
496        header! {
497            $(#[$a])*
498            ($id, $n) => danger [$item]
499        }
500
501        $crate::__tm__! { $id, $tm { $($tf)* }}
502    };
503    ($(#[$a:meta])*($id:ident, $n:expr) => Cow[$item:ty] $tm:ident{$($tf:item)*}) => {
504        header! {
505            $(#[$a])*
506            ($id, $n) => Cow[$item]
507        }
508
509        $crate::__tm__! { $id, $tm { $($tf)* }}
510    };
511    ($(#[$a:meta])*($id:ident, $n:expr) => {Any / ($item:ty)+} $tm:ident{$($tf:item)*}) => {
512        header! {
513            $(#[$a])*
514            ($id, $n) => {Any / ($item)+}
515        }
516
517        $crate::__tm__! { $id, $tm { $($tf)* }}
518    };
519}