Skip to main content

ntex_http/
header.rs

1//! Various http headers
2pub use crate::HeaderMap;
3#[doc(hidden)]
4pub use crate::map::{AsName, Either, GetAll, Iter, Value};
5pub use crate::value::{HeaderValue, InvalidHeaderValue, ToStrError};
6
7pub use http::header::{
8    ACCEPT, ACCEPT_CHARSET, ACCEPT_ENCODING, ACCEPT_LANGUAGE, ACCEPT_RANGES,
9    ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS,
10    ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_EXPOSE_HEADERS, ACCESS_CONTROL_MAX_AGE,
11    ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, AGE, ALLOW, ALT_SVC,
12    AUTHORIZATION, CACHE_CONTROL, CONNECTION, CONTENT_DISPOSITION, CONTENT_ENCODING,
13    CONTENT_LANGUAGE, CONTENT_LENGTH, CONTENT_LOCATION, CONTENT_RANGE, CONTENT_SECURITY_POLICY,
14    CONTENT_SECURITY_POLICY_REPORT_ONLY, CONTENT_TYPE, COOKIE, DATE, DNT, ETAG, EXPECT, EXPIRES,
15    FORWARDED, FROM, HOST, IF_MATCH, IF_MODIFIED_SINCE, IF_NONE_MATCH, IF_RANGE,
16    IF_UNMODIFIED_SINCE, LAST_MODIFIED, LINK, LOCATION, MAX_FORWARDS, ORIGIN, PRAGMA,
17    PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, PUBLIC_KEY_PINS, PUBLIC_KEY_PINS_REPORT_ONLY, RANGE,
18    REFERER, REFERRER_POLICY, REFRESH, RETRY_AFTER, SEC_WEBSOCKET_ACCEPT, SEC_WEBSOCKET_EXTENSIONS,
19    SEC_WEBSOCKET_KEY, SEC_WEBSOCKET_PROTOCOL, SEC_WEBSOCKET_VERSION, SERVER, SET_COOKIE,
20    STRICT_TRANSPORT_SECURITY, TE, TRAILER, TRANSFER_ENCODING, UPGRADE, UPGRADE_INSECURE_REQUESTS,
21    USER_AGENT, VARY, VIA, WARNING, WWW_AUTHENTICATE, X_CONTENT_TYPE_OPTIONS,
22    X_DNS_PREFETCH_CONTROL, X_FRAME_OPTIONS, X_XSS_PROTECTION,
23};
24pub use http::header::{HeaderName, InvalidHeaderName};
25
26pub use crate::error::InvalidContentEncoding;
27
28/// Represents supported types of content encodings
29#[derive(Copy, Clone, PartialEq, Eq, Debug)]
30pub enum ContentEncoding {
31    /// Automatically select encoding based on encoding negotiation
32    Auto,
33    /// A format using the Brotli algorithm
34    Br,
35    /// A format using the zlib structure with deflate algorithm
36    Deflate,
37    /// Gzip algorithm
38    Gzip,
39    /// A format using the Zstandard algorithm
40    Zstd,
41    /// Indicates the identity function (i.e. no compression, nor modification)
42    Identity,
43}
44
45impl ContentEncoding {
46    #[inline]
47    /// Is the content compressed?
48    pub fn is_compressed(self) -> bool {
49        !matches!(self, ContentEncoding::Identity | ContentEncoding::Auto)
50    }
51
52    #[inline]
53    /// Convert content encoding to string
54    pub fn as_str(self) -> &'static str {
55        match self {
56            ContentEncoding::Br => "br",
57            ContentEncoding::Gzip => "gzip",
58            ContentEncoding::Deflate => "deflate",
59            ContentEncoding::Zstd => "zstd",
60            ContentEncoding::Identity | ContentEncoding::Auto => "identity",
61        }
62    }
63
64    #[inline]
65    /// default quality value
66    pub fn quality(self) -> f64 {
67        match self {
68            ContentEncoding::Br => 1.1,
69            ContentEncoding::Zstd => 1.05,
70            ContentEncoding::Gzip => 1.0,
71            ContentEncoding::Deflate => 0.9,
72            ContentEncoding::Identity | ContentEncoding::Auto => 0.1,
73        }
74    }
75}
76
77impl std::str::FromStr for ContentEncoding {
78    type Err = InvalidContentEncoding;
79
80    /// Parse a content-coding name.
81    ///
82    /// Accepts `br`, `gzip` (or `x-gzip`), `deflate`, `zstd` and `identity`,
83    /// ignoring ASCII case and surrounding whitespace. Unknown or empty names
84    /// return an error, use `.unwrap_or(ContentEncoding::Identity)` to fall
85    /// back to no compression.
86    fn from_str(s: &str) -> Result<ContentEncoding, InvalidContentEncoding> {
87        let s = s.trim();
88
89        if s.eq_ignore_ascii_case("br") {
90            Ok(ContentEncoding::Br)
91        } else if s.eq_ignore_ascii_case("gzip") || s.eq_ignore_ascii_case("x-gzip") {
92            Ok(ContentEncoding::Gzip)
93        } else if s.eq_ignore_ascii_case("deflate") {
94            Ok(ContentEncoding::Deflate)
95        } else if s.eq_ignore_ascii_case("zstd") {
96            Ok(ContentEncoding::Zstd)
97        } else if s.eq_ignore_ascii_case("identity") {
98            Ok(ContentEncoding::Identity)
99        } else {
100            Err(InvalidContentEncoding::new())
101        }
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    /// STATEMENT: `ContentEncoding` reports compression, names and default quality per variant.
110    ///
111    /// HOW:
112    /// - Assert `is_compressed()` for compressed variants and not for `Identity`/`Auto`.
113    /// - Assert `as_str()`, `Debug` output and the relative `quality()` ordering.
114    #[test]
115    fn encoding() {
116        assert!(ContentEncoding::Br.is_compressed());
117        assert!(!ContentEncoding::Identity.is_compressed());
118        assert!(!ContentEncoding::Auto.is_compressed());
119        assert_eq!(format!("{:?}", ContentEncoding::Identity), "Identity");
120
121        assert!(ContentEncoding::Zstd.is_compressed());
122        assert_eq!(ContentEncoding::Zstd.as_str(), "zstd");
123        assert!(ContentEncoding::Br.quality() > ContentEncoding::Zstd.quality());
124        assert!(ContentEncoding::Zstd.quality() > ContentEncoding::Gzip.quality());
125    }
126
127    /// STATEMENT: Parsing accepts only supported coding names and rejects anything else.
128    ///
129    /// HOW:
130    /// - Parse every supported name, the `x-gzip` alias and explicit `identity`,
131    ///   with mixed case and surrounding whitespace, and assert the variant.
132    /// - Assert typos, unsupported codings, `auto`, empty and blank input return
133    ///   `InvalidContentEncoding`, which converts into the generic `Error`.
134    #[test]
135    fn parse() {
136        for (s, enc) in [
137            ("br", ContentEncoding::Br),
138            ("gzip", ContentEncoding::Gzip),
139            ("X-Gzip", ContentEncoding::Gzip),
140            ("Deflate", ContentEncoding::Deflate),
141            (" ZStd ", ContentEncoding::Zstd),
142            ("identity", ContentEncoding::Identity),
143            ("IDENTITY", ContentEncoding::Identity),
144        ] {
145            assert_eq!(s.parse::<ContentEncoding>(), Ok(enc), "{s:?}");
146        }
147
148        for s in ["gizp", "x-zstd", "compress", "auto", "gzip, br", "", "  "] {
149            let err = s.parse::<ContentEncoding>().unwrap_err();
150            assert_eq!(err.to_string(), "Unsupported content encoding");
151            assert!(
152                crate::Error::from(err).is::<InvalidContentEncoding>(),
153                "{s:?}"
154            );
155        }
156    }
157}