Skip to main content

trillium_http/headers/
known_header_name.rs

1use super::{HeaderName, HeaderNameInner};
2use std::{
3    fmt::{self, Debug, Display, Formatter},
4    hash::Hash,
5    str::FromStr,
6};
7
8impl Display for KnownHeaderName {
9    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
10        f.write_str(self.as_ref())
11    }
12}
13
14impl From<KnownHeaderName> for HeaderName<'_> {
15    fn from(khn: KnownHeaderName) -> Self {
16        Self(HeaderNameInner::KnownHeader(khn))
17    }
18}
19
20impl PartialEq<HeaderName<'_>> for KnownHeaderName {
21    fn eq(&self, other: &HeaderName) -> bool {
22        matches!(&other.0, HeaderNameInner::KnownHeader(k) if self == k)
23    }
24}
25
26impl AsRef<str> for KnownHeaderName {
27    fn as_ref(&self) -> &str {
28        self.as_str()
29    }
30}
31
32macro_rules! known_headers {
33    (
34        $(
35            ($capitalized:literal, $variant:tt, $lower:literal)
36        ),+
37    ) => {
38
39        /// A short non-exhaustive enum of headers that trillium represents as an enum.
40        ///
41        /// Use a `KnownHeaderName` variant instead of a &'static str anywhere possible, as it
42        /// allows trillium to skip parsing the header entirely.
43        ///
44        /// Please do not rely on the u8 representation being stable between releases.
45        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
46        #[non_exhaustive]
47        #[repr(u8)]
48        pub enum KnownHeaderName {
49            $(
50                #[doc = concat!("The [", $capitalized, "](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/", $capitalized, ") header.")]
51                $variant,
52            )+
53        }
54
55        impl KnownHeaderName {
56            #[cfg(test)]
57            pub(crate) const VARIANTS: &[KnownHeaderName] = &[$(Self::$variant,)+];
58
59            /// Retrieve a static string representation of this header name
60            pub fn as_str(&self) -> &'static str {
61                match self {
62                    $( Self::$variant => $capitalized, )+
63                }
64            }
65
66            /// Retrieve a lowercase static string representation of this header name
67            pub(crate) fn as_lower_str(&self) -> &'static str {
68                match self {
69                    $( Self::$variant => $lower, )+
70                }
71            }
72
73            /// Look for a case sensitive exact byte match
74            pub(crate) fn lowercase_byte_match(bytes: &[u8]) -> Option<Self> {
75                let len = bytes.len();
76
77                $( if len == $lower.len() && bytes == $lower.as_bytes() { return Some(Self::$variant); } )+
78                None
79            }
80        }
81
82        impl FromStr for KnownHeaderName {
83            type Err = ();
84            fn from_str(s: &str) -> Result<Self, Self::Err> {
85                if !s.is_ascii() { return Err(()); }
86                let len = s.len();
87
88                $( if len == $capitalized.len() && s.eq_ignore_ascii_case($capitalized) { return Ok(Self::$variant); } )+
89                Err(())
90            }
91        }
92
93        #[cfg(test)]
94        mod known_header_name_tests {
95            use super::*;
96
97            #[test]
98            fn roundtrip_all_variants() {
99                $(
100                    let parsed: KnownHeaderName = $capitalized.parse()
101                        .unwrap_or_else(|_| panic!("failed to parse {:?}", $capitalized));
102                    assert_eq!(
103                        parsed,
104                        KnownHeaderName::$variant,
105                        "parse({:?}) returned wrong variant",
106                        $capitalized,
107                    );
108                    assert_eq!(
109                        parsed.as_str(),
110                        $capitalized,
111                        "as_str() mismatch for {:?}",
112                        stringify!($variant),
113                    );
114                )+
115            }
116
117            #[test]
118            fn roundtrip_all_lower_variants() {
119                $(
120                    let parsed: KnownHeaderName = $lower.parse()
121                        .unwrap_or_else(|_| panic!("failed to parse {:?}", $lower));
122                    assert_eq!(
123                        parsed,
124                        KnownHeaderName::$variant,
125                        "parse({:?}) returned wrong variant",
126                        $lower,
127                    );
128                    assert_eq!(
129                        parsed.as_lower_str(),
130                        $lower,
131                        "as_str() mismatch for {:?}",
132                        stringify!($variant),
133                    );
134                )+
135            }
136
137
138            #[test]
139            fn case_insensitive_roundtrip() {
140                $(
141                    let lower: KnownHeaderName = $capitalized.to_lowercase().parse()
142                        .unwrap_or_else(|_| panic!("failed to parse lowercase {:?}", $capitalized));
143                    assert_eq!(lower, KnownHeaderName::$variant);
144
145                    let upper: KnownHeaderName = $capitalized.to_uppercase().parse()
146                        .unwrap_or_else(|_| panic!("failed to parse uppercase {:?}", $capitalized));
147                    assert_eq!(upper, KnownHeaderName::$variant);
148                )+
149            }
150
151            #[test]
152            fn unknown_headers_return_err() {
153                assert!("X-Unknown-Custom-Header".parse::<KnownHeaderName>().is_err());
154                assert!("".parse::<KnownHeaderName>().is_err());
155                assert!("Hostt".parse::<KnownHeaderName>().is_err());
156                assert!("Hos".parse::<KnownHeaderName>().is_err());
157            }
158        }
159    }
160}
161
162// generated with
163//
164// console.log($$('main > article > div > dl > dt > a > code').map(code => {
165// let lowered = code.innerText.toLowerCase();
166// let enum_ = lowered.replace(/(?:-|^)([a-z])/g, (_, p1) => p1.toUpperCase());
167// return`("${code.innerText}", ${enum_}, "${lowered}")`
168// }).join(",\n"))
169//
170// on https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
171//
172//
173// per https://httpwg.org/specs/rfc9110.html#rfc.section.5.3,
174//
175// The order in which field lines with differing field names are received in a section is not
176// significant. However, it is good practice to send header fields that contain additional control
177// data first, such as Host on requests and Date on responses, so that implementations can decide
178// when not to handle a message as early as possible.
179known_headers! {
180    ("Host", Host, "host"),
181    ("Date", Date, "date"),
182
183    ("Accept", Accept, "accept"),
184    ("Accept-CH", AcceptCh, "accept-ch"),
185    ("Accept-CH-Lifetime", AcceptChLifetime, "accept-ch-lifetime"),
186    ("Accept-Charset", AcceptCharset, "accept-charset"),
187    ("Accept-Encoding", AcceptEncoding, "accept-encoding"),
188    ("Accept-Language", AcceptLanguage, "accept-language"),
189    ("Accept-Push-Policy", AcceptPushPolicy, "accept-push-policy"),
190    ("Accept-Ranges", AcceptRanges, "accept-ranges"),
191    ("Accept-Signature", AcceptSignature, "accept-signature"),
192    ("Access-Control-Allow-Credentials", AccessControlAllowCredentials, "access-control-allow-credentials"),
193    ("Access-Control-Allow-Headers", AccessControlAllowHeaders, "access-control-allow-headers"),
194    ("Access-Control-Allow-Methods", AccessControlAllowMethods, "access-control-allow-methods"),
195    ("Access-Control-Allow-Origin", AccessControlAllowOrigin, "access-control-allow-origin"),
196    ("Access-Control-Expose-Headers", AccessControlExposeHeaders, "access-control-expose-headers"),
197    ("Access-Control-Max-Age", AccessControlMaxAge, "access-control-max-age"),
198    ("Access-Control-Request-Headers", AccessControlRequestHeaders, "access-control-request-headers"),
199    ("Access-Control-Request-Method", AccessControlRequestMethod, "access-control-request-method"),
200    ("Age", Age, "age"),
201    ("Allow", Allow, "allow"),
202    ("Alt-Svc", AltSvc, "alt-svc"),
203    ("Alt-Used", AltUsed, "alt-used"),
204    ("Authentication-Info", AuthenticationInfo, "authentication-info"),
205    ("Authorization", Authorization, "authorization"),
206    ("Cache-Control", CacheControl, "cache-control"),
207    ("Clear-Site-Data", ClearSiteData, "clear-site-data"),
208    ("Connection", Connection, "connection"),
209    ("Content-DPR", ContentDpr, "content-dpr"),
210    ("Content-Digest", ContentDigest, "content-digest"),
211    ("Content-Disposition", ContentDisposition, "content-disposition"),
212    ("Content-Encoding", ContentEncoding, "content-encoding"),
213    ("Content-Language", ContentLanguage, "content-language"),
214    ("Content-Length", ContentLength, "content-length"),
215    ("Content-Location", ContentLocation, "content-location"),
216    ("Content-Range", ContentRange, "content-range"),
217    ("Content-Security-Policy", ContentSecurityPolicy, "content-security-policy"),
218    ("Content-Security-Policy-Report-Only", ContentSecurityPolicyReportOnly, "content-security-policy-report-only"),
219    ("Content-Type", ContentType, "content-type"),
220    ("Cookie", Cookie, "cookie"),
221    ("Cookie2", Cookie2, "cookie2"),
222    ("Cross-Origin-Embedder-Policy", CrossOriginEmbedderPolicy, "cross-origin-embedder-policy"),
223    ("Cross-Origin-Opener-Policy", CrossOriginOpenerPolicy, "cross-origin-opener-policy"),
224    ("Cross-Origin-Resource-Policy", CrossOriginResourcePolicy, "cross-origin-resource-policy"),
225    ("DNT", Dnt, "dnt"),
226    ("DPR", Dpr, "dpr"),
227    ("DPoP", Dpop, "dpop"),
228    ("Deprecation", Deprecation, "deprecation"),
229    ("Device-Memory", DeviceMemory, "device-memory"),
230    ("Digest", Digest, "digest"),
231    ("Downlink", Downlink, "downlink"),
232    ("ECT", Ect, "ect"),
233    ("ETag", Etag, "etag"),
234    ("Early-Data", EarlyData, "early-data"),
235    ("Expect", Expect, "expect"),
236    ("Expect-CT", ExpectCt, "expect-ct"),
237    ("Expires", Expires, "expires"),
238    ("Feature-Policy", FeaturePolicy, "feature-policy"),
239    ("Forwarded", Forwarded, "forwarded"),
240    ("From", From, "from"),
241    ("If-Match", IfMatch, "if-match"),
242    ("If-Modified-Since", IfModifiedSince, "if-modified-since"),
243    ("If-None-Match", IfNoneMatch, "if-none-match"),
244    ("If-Range", IfRange, "if-range"),
245    ("If-Unmodified-Since", IfUnmodifiedSince, "if-unmodified-since"),
246    ("Keep-Alive", KeepAlive, "keep-alive"),
247    ("Large-Allocation", LargeAllocation, "large-allocation"),
248    ("Last-Event-ID", LastEventId, "last-event-id"),
249    ("Last-Modified", LastModified, "last-modified"),
250    ("Link", Link, "link"),
251    ("Location", Location, "location"),
252    ("Max-Forwards", MaxForwards, "max-forwards"),
253    ("NEL", Nel, "nel"),
254    ("Origin", Origin, "origin"),
255    ("Origin-Isolation", OriginIsolation, "origin-isolation"),
256    ("Permissions-Policy", PermissionsPolicy, "permissions-policy"),
257    ("Ping-From", PingFrom, "ping-from"),
258    ("Ping-To", PingTo, "ping-to"),
259    ("Pragma", Pragma, "pragma"),
260    ("Priority", Priority, "priority"),
261    ("Proxy-Authenticate", ProxyAuthenticate, "proxy-authenticate"),
262    ("Proxy-Authentication-Info", ProxyAuthenticationInfo, "proxy-authentication-info"),
263    ("Proxy-Authorization", ProxyAuthorization, "proxy-authorization"),
264    ("Proxy-Connection", ProxyConnection, "proxy-connection"),
265    ("Proxy-Status", ProxyStatus, "proxy-status"),
266    ("Public-Key-Pins", PublicKeyPins, "public-key-pins"),
267    ("Public-Key-Pins-Report-Only", PublicKeyPinsReportOnly, "public-key-pins-report-only"),
268    ("Purpose", Purpose, "purpose"),
269    ("Push-Policy", PushPolicy, "push-policy"),
270    ("RTT", Rtt, "rtt"),
271    ("Range", Range, "range"),
272    ("RateLimit-Reset", RatelimitReset, "ratelimit-reset"),
273    ("Ratelimit-Limit", RatelimitLimit, "ratelimit-limit"),
274    ("Ratelimit-Remaining", RatelimitRemaining, "ratelimit-remaining"),
275    ("Referer", Referer, "referer"),
276    ("Referrer-Policy", ReferrerPolicy, "referrer-policy"),
277    ("Refresh", Refresh, "refresh"),
278    ("Refresh-Cache", RefreshCache, "refresh-cache"),
279    ("Report-To", ReportTo, "report-to"),
280    ("Repr-Digest", ReprDigest, "repr-digest"),
281    ("Retry-After", RetryAfter, "retry-after"),
282    ("Save-Data", SaveData, "save-data"),
283    ("Sec-CH-UA", SecChUa, "sec-ch-ua"),
284    ("Sec-CH-UA-Mobile", SecChUAMobile, "sec-ch-ua-mobile"),
285    ("Sec-CH-UA-Platform", SecChUAPlatform, "sec-ch-ua-platform"),
286    ("Sec-Fetch-Dest", SecFetchDest, "sec-fetch-dest"),
287    ("Sec-Fetch-Mode", SecFetchMode, "sec-fetch-mode"),
288    ("Sec-Fetch-Site", SecFetchSite, "sec-fetch-site"),
289    ("Sec-Fetch-User", SecFetchUser, "sec-fetch-user"),
290    ("Sec-GPC", SecGpc, "sec-gpc"),
291    ("Sec-WebSocket-Accept", SecWebsocketAccept, "sec-websocket-accept"),
292    ("Sec-WebSocket-Extensions", SecWebsocketExtensions, "sec-websocket-extensions"),
293    ("Sec-WebSocket-Key", SecWebsocketKey, "sec-websocket-key"),
294    ("Sec-WebSocket-Protocol", SecWebsocketProtocol, "sec-websocket-protocol"),
295    ("Sec-WebSocket-Version", SecWebsocketVersion, "sec-websocket-version"),
296    ("Server", Server, "server"),
297    ("Server-Timing", ServerTiming, "server-timing"),
298    ("Service-Worker-Allowed", ServiceWorkerAllowed, "service-worker-allowed"),
299    ("Set-Cookie", SetCookie, "set-cookie"),
300    ("Set-Cookie2", SetCookie2, "set-cookie2"),
301    ("Signature", Signature, "signature"),
302    ("Signed-Headers", SignedHeaders, "signed-headers"),
303    ("SourceMap", Sourcemap, "sourcemap"),
304    ("Strict-Transport-Security", StrictTransportSecurity, "strict-transport-security"),
305    ("TE", Te, "te"),
306    ("Timing-Allow-Origin", TimingAllowOrigin, "timing-allow-origin"),
307    ("Traceparent", Traceparent, "traceparent"),
308    ("Tracestate", Tracestate, "tracestate"),
309    ("Trailer", Trailer, "trailer"),
310    ("Transfer-Encoding", TransferEncoding, "transfer-encoding"),
311    ("Upgrade", Upgrade, "upgrade"),
312    ("Upgrade-Insecure-Requests", UpgradeInsecureRequests, "upgrade-insecure-requests"),
313    ("User-Agent", UserAgent, "user-agent"),
314    ("Vary", Vary, "vary"),
315    ("Via", Via, "via"),
316    ("Viewport-Width", ViewportWidth, "viewport-width"),
317    ("WWW-Authenticate", WwwAuthenticate, "www-authenticate"),
318    ("Want-Content-Digest", WantContentDigest, "want-content-digest"),
319    ("Want-Digest", WantDigest, "want-digest"),
320    ("Want-Repr-Digest", WantReprDigest, "want-repr-digest"),
321    ("Warning", Warning, "warning"),
322    ("Width", Width, "width"),
323    ("X-B3-Traceid", Xb3Traceid, "x-b3-traceid"),
324    ("X-Cache", Xcache, "x-cache"),
325    ("X-Content-Type-Options", XcontentTypeOptions, "x-content-type-options"),
326    ("X-Correlation-ID", XcorrelationId, "x-correlation-id"),
327    ("X-DNS-Prefetch-Control", XdnsPrefetchControl, "x-dns-prefetch-control"),
328    ("X-Download-Options", XdownloadOptions, "x-download-options"),
329    ("X-Firefox-Spdy", XfirefoxSpdy, "x-firefox-spdy"),
330    ("X-Forwarded-By", XforwardedBy, "x-forwarded-by"),
331    ("X-Forwarded-For", XforwardedFor, "x-forwarded-for"),
332    ("X-Forwarded-Host", XforwardedHost, "x-forwarded-host"),
333    ("X-Forwarded-Proto", XforwardedProto, "x-forwarded-proto"),
334    ("X-Forwarded-SSL", XforwardedSsl, "x-forwarded-ssl"),
335    ("X-Frame-Options", XframeOptions, "x-frame-options"),
336    ("X-Permitted-Cross-Domain-Policies", XpermittedCrossDomainPolicies, "x-permitted-cross-domain-policies"),
337    ("X-Pingback", Xpingback, "x-pingback"),
338    ("X-Powered-By", XpoweredBy, "x-powered-by"),
339    ("X-Real-IP", XrealIp, "x-real-ip"),
340    ("X-Request-Id", XrequestId, "x-request-id"),
341    ("X-Requested-With", XrequestedWith, "x-requested-with"),
342    ("X-Robots-Tag", XrobotsTag, "x-robots-tag"),
343    ("X-Runtime", Xruntime, "x-runtime"),
344    ("X-Served-By", XservedBy, "x-served-by"),
345    ("X-UA-Compatible", XuaCompatible, "x-ua-compatible"),
346    ("X-XSS-Protection", XxssProtection, "x-xss-protection")
347}