1 | use crate::byte_str::ByteStr; |
2 | use bytes::{Bytes, BytesMut}; |
3 | |
4 | use std::borrow::Borrow; |
5 | use std::convert::TryFrom; |
6 | use std::error::Error; |
7 | use std::fmt; |
8 | use std::hash::{Hash, Hasher}; |
9 | use std::mem::MaybeUninit; |
10 | use std::str::FromStr; |
11 | |
12 | /// Represents an HTTP header field name |
13 | /// |
14 | /// Header field names identify the header. Header sets may include multiple |
15 | /// headers with the same name. The HTTP specification defines a number of |
16 | /// standard headers, but HTTP messages may include non-standard header names as |
17 | /// well as long as they adhere to the specification. |
18 | /// |
19 | /// `HeaderName` is used as the [`HeaderMap`] key. Constants are available for |
20 | /// all standard header names in the [`header`] module. |
21 | /// |
22 | /// # Representation |
23 | /// |
24 | /// `HeaderName` represents standard header names using an `enum`, as such they |
25 | /// will not require an allocation for storage. All custom header names are |
26 | /// lower cased upon conversion to a `HeaderName` value. This avoids the |
27 | /// overhead of dynamically doing lower case conversion during the hash code |
28 | /// computation and the comparison operation. |
29 | /// |
30 | /// [`HeaderMap`]: struct.HeaderMap.html |
31 | /// [`header`]: index.html |
32 | #[derive (Clone, Eq, PartialEq, Hash)] |
33 | pub struct HeaderName { |
34 | inner: Repr<Custom>, |
35 | } |
36 | |
37 | // Almost a full `HeaderName` |
38 | #[derive (Debug, Hash)] |
39 | pub struct HdrName<'a> { |
40 | inner: Repr<MaybeLower<'a>>, |
41 | } |
42 | |
43 | #[derive (Debug, Clone, Eq, PartialEq, Hash)] |
44 | enum Repr<T> { |
45 | Standard(StandardHeader), |
46 | Custom(T), |
47 | } |
48 | |
49 | // Used to hijack the Hash impl |
50 | #[derive (Debug, Clone, Eq, PartialEq)] |
51 | struct Custom(ByteStr); |
52 | |
53 | #[derive (Debug, Clone)] |
54 | // Invariant: If lower then buf is valid UTF-8. |
55 | struct MaybeLower<'a> { |
56 | buf: &'a [u8], |
57 | lower: bool, |
58 | } |
59 | |
60 | /// A possible error when converting a `HeaderName` from another type. |
61 | pub struct InvalidHeaderName { |
62 | _priv: (), |
63 | } |
64 | |
65 | macro_rules! standard_headers { |
66 | ( |
67 | $( |
68 | $(#[$docs:meta])* |
69 | ($konst:ident, $upcase:ident, $name_bytes:literal); |
70 | )+ |
71 | ) => { |
72 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] |
73 | enum StandardHeader { |
74 | $( |
75 | $konst, |
76 | )+ |
77 | } |
78 | |
79 | $( |
80 | $(#[$docs])* |
81 | pub const $upcase: HeaderName = HeaderName { |
82 | inner: Repr::Standard(StandardHeader::$konst), |
83 | }; |
84 | )+ |
85 | |
86 | impl StandardHeader { |
87 | #[inline] |
88 | fn as_str(&self) -> &'static str { |
89 | match *self { |
90 | // Safety: test_parse_standard_headers ensures these &[u8]s are &str-safe. |
91 | $( |
92 | StandardHeader::$konst => unsafe { std::str::from_utf8_unchecked( $name_bytes ) }, |
93 | )+ |
94 | } |
95 | } |
96 | |
97 | const fn from_bytes(name_bytes: &[u8]) -> Option<StandardHeader> { |
98 | match name_bytes { |
99 | $( |
100 | $name_bytes => Some(StandardHeader::$konst), |
101 | )+ |
102 | _ => None, |
103 | } |
104 | } |
105 | } |
106 | |
107 | #[cfg(test)] |
108 | const TEST_HEADERS: &'static [(StandardHeader, &'static [u8])] = &[ |
109 | $( |
110 | (StandardHeader::$konst, $name_bytes), |
111 | )+ |
112 | ]; |
113 | |
114 | #[test] |
115 | fn test_parse_standard_headers() { |
116 | for &(std, name_bytes) in TEST_HEADERS { |
117 | // Test lower case |
118 | assert_eq!(HeaderName::from_bytes(name_bytes).unwrap(), HeaderName::from(std)); |
119 | |
120 | // Test upper case |
121 | let upper = std::str::from_utf8(name_bytes).expect("byte string constants are all utf-8" ).to_uppercase(); |
122 | assert_eq!(HeaderName::from_bytes(upper.as_bytes()).unwrap(), HeaderName::from(std)); |
123 | } |
124 | } |
125 | |
126 | #[test] |
127 | fn test_standard_headers_into_bytes() { |
128 | for &(std, name_bytes) in TEST_HEADERS { |
129 | let name = std::str::from_utf8(name_bytes).unwrap(); |
130 | let std = HeaderName::from(std); |
131 | // Test lower case |
132 | let bytes: Bytes = |
133 | HeaderName::from_bytes(name_bytes).unwrap().inner.into(); |
134 | assert_eq!(bytes, name); |
135 | assert_eq!(HeaderName::from_bytes(name_bytes).unwrap(), std); |
136 | |
137 | // Test upper case |
138 | let upper = name.to_uppercase(); |
139 | let bytes: Bytes = |
140 | HeaderName::from_bytes(upper.as_bytes()).unwrap().inner.into(); |
141 | assert_eq!(bytes, name_bytes); |
142 | assert_eq!(HeaderName::from_bytes(upper.as_bytes()).unwrap(), |
143 | std); |
144 | } |
145 | |
146 | } |
147 | } |
148 | } |
149 | |
150 | // Generate constants for all standard HTTP headers. This includes a static hash |
151 | // code for the "fast hash" path. The hash code for static headers *do not* have |
152 | // to match the text representation of those headers. This is because header |
153 | // strings are always converted to the static values (when they match) before |
154 | // being hashed. This means that it is impossible to compare the static hash |
155 | // code of CONTENT_LENGTH with "content-length". |
156 | standard_headers! { |
157 | /// Advertises which content types the client is able to understand. |
158 | /// |
159 | /// The Accept request HTTP header advertises which content types, expressed |
160 | /// as MIME types, the client is able to understand. Using content |
161 | /// negotiation, the server then selects one of the proposals, uses it and |
162 | /// informs the client of its choice with the Content-Type response header. |
163 | /// Browsers set adequate values for this header depending of the context |
164 | /// where the request is done: when fetching a CSS stylesheet a different |
165 | /// value is set for the request than when fetching an image, video or a |
166 | /// script. |
167 | (Accept, ACCEPT, b"accept" ); |
168 | |
169 | /// Advertises which character set the client is able to understand. |
170 | /// |
171 | /// The Accept-Charset request HTTP header advertises which character set |
172 | /// the client is able to understand. Using content negotiation, the server |
173 | /// then selects one of the proposals, uses it and informs the client of its |
174 | /// choice within the Content-Type response header. Browsers usually don't |
175 | /// set this header as the default value for each content type is usually |
176 | /// correct and transmitting it would allow easier fingerprinting. |
177 | /// |
178 | /// If the server cannot serve any matching character set, it can |
179 | /// theoretically send back a 406 (Not Acceptable) error code. But, for a |
180 | /// better user experience, this is rarely done and the more common way is |
181 | /// to ignore the Accept-Charset header in this case. |
182 | (AcceptCharset, ACCEPT_CHARSET, b"accept-charset" ); |
183 | |
184 | /// Advertises which content encoding the client is able to understand. |
185 | /// |
186 | /// The Accept-Encoding request HTTP header advertises which content |
187 | /// encoding, usually a compression algorithm, the client is able to |
188 | /// understand. Using content negotiation, the server selects one of the |
189 | /// proposals, uses it and informs the client of its choice with the |
190 | /// Content-Encoding response header. |
191 | /// |
192 | /// Even if both the client and the server supports the same compression |
193 | /// algorithms, the server may choose not to compress the body of a |
194 | /// response, if the identity value is also acceptable. Two common cases |
195 | /// lead to this: |
196 | /// |
197 | /// * The data to be sent is already compressed and a second compression |
198 | /// won't lead to smaller data to be transmitted. This may the case with |
199 | /// some image formats; |
200 | /// |
201 | /// * The server is overloaded and cannot afford the computational overhead |
202 | /// induced by the compression requirement. Typically, Microsoft recommends |
203 | /// not to compress if a server use more than 80 % of its computational |
204 | /// power. |
205 | /// |
206 | /// As long as the identity value, meaning no compression, is not explicitly |
207 | /// forbidden, by an identity;q=0 or a *;q=0 without another explicitly set |
208 | /// value for identity, the server must never send back a 406 Not Acceptable |
209 | /// error. |
210 | (AcceptEncoding, ACCEPT_ENCODING, b"accept-encoding" ); |
211 | |
212 | /// Advertises which languages the client is able to understand. |
213 | /// |
214 | /// The Accept-Language request HTTP header advertises which languages the |
215 | /// client is able to understand, and which locale variant is preferred. |
216 | /// Using content negotiation, the server then selects one of the proposals, |
217 | /// uses it and informs the client of its choice with the Content-Language |
218 | /// response header. Browsers set adequate values for this header according |
219 | /// their user interface language and even if a user can change it, this |
220 | /// happens rarely (and is frown upon as it leads to fingerprinting). |
221 | /// |
222 | /// This header is a hint to be used when the server has no way of |
223 | /// determining the language via another way, like a specific URL, that is |
224 | /// controlled by an explicit user decision. It is recommended that the |
225 | /// server never overrides an explicit decision. The content of the |
226 | /// Accept-Language is often out of the control of the user (like when |
227 | /// traveling and using an Internet Cafe in a different country); the user |
228 | /// may also want to visit a page in another language than the locale of |
229 | /// their user interface. |
230 | /// |
231 | /// If the server cannot serve any matching language, it can theoretically |
232 | /// send back a 406 (Not Acceptable) error code. But, for a better user |
233 | /// experience, this is rarely done and more common way is to ignore the |
234 | /// Accept-Language header in this case. |
235 | (AcceptLanguage, ACCEPT_LANGUAGE, b"accept-language" ); |
236 | |
237 | /// Marker used by the server to advertise partial request support. |
238 | /// |
239 | /// The Accept-Ranges response HTTP header is a marker used by the server to |
240 | /// advertise its support of partial requests. The value of this field |
241 | /// indicates the unit that can be used to define a range. |
242 | /// |
243 | /// In presence of an Accept-Ranges header, the browser may try to resume an |
244 | /// interrupted download, rather than to start it from the start again. |
245 | (AcceptRanges, ACCEPT_RANGES, b"accept-ranges" ); |
246 | |
247 | /// Preflight response indicating if the response to the request can be |
248 | /// exposed to the page. |
249 | /// |
250 | /// The Access-Control-Allow-Credentials response header indicates whether |
251 | /// or not the response to the request can be exposed to the page. It can be |
252 | /// exposed when the true value is returned; it can't in other cases. |
253 | /// |
254 | /// Credentials are cookies, authorization headers or TLS client |
255 | /// certificates. |
256 | /// |
257 | /// When used as part of a response to a preflight request, this indicates |
258 | /// whether or not the actual request can be made using credentials. Note |
259 | /// that simple GET requests are not preflighted, and so if a request is |
260 | /// made for a resource with credentials, if this header is not returned |
261 | /// with the resource, the response is ignored by the browser and not |
262 | /// returned to web content. |
263 | /// |
264 | /// The Access-Control-Allow-Credentials header works in conjunction with |
265 | /// the XMLHttpRequest.withCredentials property or with the credentials |
266 | /// option in the Request() constructor of the Fetch API. Credentials must |
267 | /// be set on both sides (the Access-Control-Allow-Credentials header and in |
268 | /// the XHR or Fetch request) in order for the CORS request with credentials |
269 | /// to succeed. |
270 | (AccessControlAllowCredentials, ACCESS_CONTROL_ALLOW_CREDENTIALS, b"access-control-allow-credentials" ); |
271 | |
272 | /// Preflight response indicating permitted HTTP headers. |
273 | /// |
274 | /// The Access-Control-Allow-Headers response header is used in response to |
275 | /// a preflight request to indicate which HTTP headers will be available via |
276 | /// Access-Control-Expose-Headers when making the actual request. |
277 | /// |
278 | /// The simple headers, Accept, Accept-Language, Content-Language, |
279 | /// Content-Type (but only with a MIME type of its parsed value (ignoring |
280 | /// parameters) of either application/x-www-form-urlencoded, |
281 | /// multipart/form-data, or text/plain), are always available and don't need |
282 | /// to be listed by this header. |
283 | /// |
284 | /// This header is required if the request has an |
285 | /// Access-Control-Request-Headers header. |
286 | (AccessControlAllowHeaders, ACCESS_CONTROL_ALLOW_HEADERS, b"access-control-allow-headers" ); |
287 | |
288 | /// Preflight header response indicating permitted access methods. |
289 | /// |
290 | /// The Access-Control-Allow-Methods response header specifies the method or |
291 | /// methods allowed when accessing the resource in response to a preflight |
292 | /// request. |
293 | (AccessControlAllowMethods, ACCESS_CONTROL_ALLOW_METHODS, b"access-control-allow-methods" ); |
294 | |
295 | /// Indicates whether the response can be shared with resources with the |
296 | /// given origin. |
297 | (AccessControlAllowOrigin, ACCESS_CONTROL_ALLOW_ORIGIN, b"access-control-allow-origin" ); |
298 | |
299 | /// Indicates which headers can be exposed as part of the response by |
300 | /// listing their names. |
301 | (AccessControlExposeHeaders, ACCESS_CONTROL_EXPOSE_HEADERS, b"access-control-expose-headers" ); |
302 | |
303 | /// Indicates how long the results of a preflight request can be cached. |
304 | (AccessControlMaxAge, ACCESS_CONTROL_MAX_AGE, b"access-control-max-age" ); |
305 | |
306 | /// Informs the server which HTTP headers will be used when an actual |
307 | /// request is made. |
308 | (AccessControlRequestHeaders, ACCESS_CONTROL_REQUEST_HEADERS, b"access-control-request-headers" ); |
309 | |
310 | /// Informs the server know which HTTP method will be used when the actual |
311 | /// request is made. |
312 | (AccessControlRequestMethod, ACCESS_CONTROL_REQUEST_METHOD, b"access-control-request-method" ); |
313 | |
314 | /// Indicates the time in seconds the object has been in a proxy cache. |
315 | /// |
316 | /// The Age header is usually close to zero. If it is Age: 0, it was |
317 | /// probably just fetched from the origin server; otherwise It is usually |
318 | /// calculated as a difference between the proxy's current date and the Date |
319 | /// general header included in the HTTP response. |
320 | (Age, AGE, b"age" ); |
321 | |
322 | /// Lists the set of methods support by a resource. |
323 | /// |
324 | /// This header must be sent if the server responds with a 405 Method Not |
325 | /// Allowed status code to indicate which request methods can be used. An |
326 | /// empty Allow header indicates that the resource allows no request |
327 | /// methods, which might occur temporarily for a given resource, for |
328 | /// example. |
329 | (Allow, ALLOW, b"allow" ); |
330 | |
331 | /// Advertises the availability of alternate services to clients. |
332 | (AltSvc, ALT_SVC, b"alt-svc" ); |
333 | |
334 | /// Contains the credentials to authenticate a user agent with a server. |
335 | /// |
336 | /// Usually this header is included after the server has responded with a |
337 | /// 401 Unauthorized status and the WWW-Authenticate header. |
338 | (Authorization, AUTHORIZATION, b"authorization" ); |
339 | |
340 | /// Specifies directives for caching mechanisms in both requests and |
341 | /// responses. |
342 | /// |
343 | /// Caching directives are unidirectional, meaning that a given directive in |
344 | /// a request is not implying that the same directive is to be given in the |
345 | /// response. |
346 | (CacheControl, CACHE_CONTROL, b"cache-control" ); |
347 | |
348 | /// Indicates how caches have handled a response and its corresponding request. |
349 | /// |
350 | /// See [RFC 9211](https://www.rfc-editor.org/rfc/rfc9211.html). |
351 | (CacheStatus, CACHE_STATUS, b"cache-status" ); |
352 | |
353 | /// Specifies directives that allow origin servers to control the behavior of CDN caches |
354 | /// interposed between them and clients separately from other caches that might handle the |
355 | /// response. |
356 | /// |
357 | /// See [RFC 9213](https://www.rfc-editor.org/rfc/rfc9213.html). |
358 | (CdnCacheControl, CDN_CACHE_CONTROL, b"cdn-cache-control" ); |
359 | |
360 | /// Controls whether or not the network connection stays open after the |
361 | /// current transaction finishes. |
362 | /// |
363 | /// If the value sent is keep-alive, the connection is persistent and not |
364 | /// closed, allowing for subsequent requests to the same server to be done. |
365 | /// |
366 | /// Except for the standard hop-by-hop headers (Keep-Alive, |
367 | /// Transfer-Encoding, TE, Connection, Trailer, Upgrade, Proxy-Authorization |
368 | /// and Proxy-Authenticate), any hop-by-hop headers used by the message must |
369 | /// be listed in the Connection header, so that the first proxy knows he has |
370 | /// to consume them and not to forward them further. Standard hop-by-hop |
371 | /// headers can be listed too (it is often the case of Keep-Alive, but this |
372 | /// is not mandatory. |
373 | (Connection, CONNECTION, b"connection" ); |
374 | |
375 | /// Indicates if the content is expected to be displayed inline. |
376 | /// |
377 | /// In a regular HTTP response, the Content-Disposition response header is a |
378 | /// header indicating if the content is expected to be displayed inline in |
379 | /// the browser, that is, as a Web page or as part of a Web page, or as an |
380 | /// attachment, that is downloaded and saved locally. |
381 | /// |
382 | /// In a multipart/form-data body, the HTTP Content-Disposition general |
383 | /// header is a header that can be used on the subpart of a multipart body |
384 | /// to give information about the field it applies to. The subpart is |
385 | /// delimited by the boundary defined in the Content-Type header. Used on |
386 | /// the body itself, Content-Disposition has no effect. |
387 | /// |
388 | /// The Content-Disposition header is defined in the larger context of MIME |
389 | /// messages for e-mail, but only a subset of the possible parameters apply |
390 | /// to HTTP forms and POST requests. Only the value form-data, as well as |
391 | /// the optional directive name and filename, can be used in the HTTP |
392 | /// context. |
393 | (ContentDisposition, CONTENT_DISPOSITION, b"content-disposition" ); |
394 | |
395 | /// Used to compress the media-type. |
396 | /// |
397 | /// When present, its value indicates what additional content encoding has |
398 | /// been applied to the entity-body. It lets the client know, how to decode |
399 | /// in order to obtain the media-type referenced by the Content-Type header. |
400 | /// |
401 | /// It is recommended to compress data as much as possible and therefore to |
402 | /// use this field, but some types of resources, like jpeg images, are |
403 | /// already compressed. Sometimes using additional compression doesn't |
404 | /// reduce payload size and can even make the payload longer. |
405 | (ContentEncoding, CONTENT_ENCODING, b"content-encoding" ); |
406 | |
407 | /// Used to describe the languages intended for the audience. |
408 | /// |
409 | /// This header allows a user to differentiate according to the users' own |
410 | /// preferred language. For example, if "Content-Language: de-DE" is set, it |
411 | /// says that the document is intended for German language speakers |
412 | /// (however, it doesn't indicate the document is written in German. For |
413 | /// example, it might be written in English as part of a language course for |
414 | /// German speakers). |
415 | /// |
416 | /// If no Content-Language is specified, the default is that the content is |
417 | /// intended for all language audiences. Multiple language tags are also |
418 | /// possible, as well as applying the Content-Language header to various |
419 | /// media types and not only to textual documents. |
420 | (ContentLanguage, CONTENT_LANGUAGE, b"content-language" ); |
421 | |
422 | /// Indicates the size of the entity-body. |
423 | /// |
424 | /// The header value must be a decimal indicating the number of octets sent |
425 | /// to the recipient. |
426 | (ContentLength, CONTENT_LENGTH, b"content-length" ); |
427 | |
428 | /// Indicates an alternate location for the returned data. |
429 | /// |
430 | /// The principal use case is to indicate the URL of the resource |
431 | /// transmitted as the result of content negotiation. |
432 | /// |
433 | /// Location and Content-Location are different: Location indicates the |
434 | /// target of a redirection (or the URL of a newly created document), while |
435 | /// Content-Location indicates the direct URL to use to access the resource, |
436 | /// without the need of further content negotiation. Location is a header |
437 | /// associated with the response, while Content-Location is associated with |
438 | /// the entity returned. |
439 | (ContentLocation, CONTENT_LOCATION, b"content-location" ); |
440 | |
441 | /// Indicates where in a full body message a partial message belongs. |
442 | (ContentRange, CONTENT_RANGE, b"content-range" ); |
443 | |
444 | /// Allows controlling resources the user agent is allowed to load for a |
445 | /// given page. |
446 | /// |
447 | /// With a few exceptions, policies mostly involve specifying server origins |
448 | /// and script endpoints. This helps guard against cross-site scripting |
449 | /// attacks (XSS). |
450 | (ContentSecurityPolicy, CONTENT_SECURITY_POLICY, b"content-security-policy" ); |
451 | |
452 | /// Allows experimenting with policies by monitoring their effects. |
453 | /// |
454 | /// The HTTP Content-Security-Policy-Report-Only response header allows web |
455 | /// developers to experiment with policies by monitoring (but not enforcing) |
456 | /// their effects. These violation reports consist of JSON documents sent |
457 | /// via an HTTP POST request to the specified URI. |
458 | (ContentSecurityPolicyReportOnly, CONTENT_SECURITY_POLICY_REPORT_ONLY, b"content-security-policy-report-only" ); |
459 | |
460 | /// Used to indicate the media type of the resource. |
461 | /// |
462 | /// In responses, a Content-Type header tells the client what the content |
463 | /// type of the returned content actually is. Browsers will do MIME sniffing |
464 | /// in some cases and will not necessarily follow the value of this header; |
465 | /// to prevent this behavior, the header X-Content-Type-Options can be set |
466 | /// to nosniff. |
467 | /// |
468 | /// In requests, (such as POST or PUT), the client tells the server what |
469 | /// type of data is actually sent. |
470 | (ContentType, CONTENT_TYPE, b"content-type" ); |
471 | |
472 | /// Contains stored HTTP cookies previously sent by the server with the |
473 | /// Set-Cookie header. |
474 | /// |
475 | /// The Cookie header might be omitted entirely, if the privacy setting of |
476 | /// the browser are set to block them, for example. |
477 | (Cookie, COOKIE, b"cookie" ); |
478 | |
479 | /// Indicates the client's tracking preference. |
480 | /// |
481 | /// This header lets users indicate whether they would prefer privacy rather |
482 | /// than personalized content. |
483 | (Dnt, DNT, b"dnt" ); |
484 | |
485 | /// Contains the date and time at which the message was originated. |
486 | (Date, DATE, b"date" ); |
487 | |
488 | /// Identifier for a specific version of a resource. |
489 | /// |
490 | /// This header allows caches to be more efficient, and saves bandwidth, as |
491 | /// a web server does not need to send a full response if the content has |
492 | /// not changed. On the other side, if the content has changed, etags are |
493 | /// useful to help prevent simultaneous updates of a resource from |
494 | /// overwriting each other ("mid-air collisions"). |
495 | /// |
496 | /// If the resource at a given URL changes, a new Etag value must be |
497 | /// generated. Etags are therefore similar to fingerprints and might also be |
498 | /// used for tracking purposes by some servers. A comparison of them allows |
499 | /// to quickly determine whether two representations of a resource are the |
500 | /// same, but they might also be set to persist indefinitely by a tracking |
501 | /// server. |
502 | (Etag, ETAG, b"etag" ); |
503 | |
504 | /// Indicates expectations that need to be fulfilled by the server in order |
505 | /// to properly handle the request. |
506 | /// |
507 | /// The only expectation defined in the specification is Expect: |
508 | /// 100-continue, to which the server shall respond with: |
509 | /// |
510 | /// * 100 if the information contained in the header is sufficient to cause |
511 | /// an immediate success, |
512 | /// |
513 | /// * 417 (Expectation Failed) if it cannot meet the expectation; or any |
514 | /// other 4xx status otherwise. |
515 | /// |
516 | /// For example, the server may reject a request if its Content-Length is |
517 | /// too large. |
518 | /// |
519 | /// No common browsers send the Expect header, but some other clients such |
520 | /// as cURL do so by default. |
521 | (Expect, EXPECT, b"expect" ); |
522 | |
523 | /// Contains the date/time after which the response is considered stale. |
524 | /// |
525 | /// Invalid dates, like the value 0, represent a date in the past and mean |
526 | /// that the resource is already expired. |
527 | /// |
528 | /// If there is a Cache-Control header with the "max-age" or "s-max-age" |
529 | /// directive in the response, the Expires header is ignored. |
530 | (Expires, EXPIRES, b"expires" ); |
531 | |
532 | /// Contains information from the client-facing side of proxy servers that |
533 | /// is altered or lost when a proxy is involved in the path of the request. |
534 | /// |
535 | /// The alternative and de-facto standard versions of this header are the |
536 | /// X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Proto headers. |
537 | /// |
538 | /// This header is used for debugging, statistics, and generating |
539 | /// location-dependent content and by design it exposes privacy sensitive |
540 | /// information, such as the IP address of the client. Therefore the user's |
541 | /// privacy must be kept in mind when deploying this header. |
542 | (Forwarded, FORWARDED, b"forwarded" ); |
543 | |
544 | /// Contains an Internet email address for a human user who controls the |
545 | /// requesting user agent. |
546 | /// |
547 | /// If you are running a robotic user agent (e.g. a crawler), the From |
548 | /// header should be sent, so you can be contacted if problems occur on |
549 | /// servers, such as if the robot is sending excessive, unwanted, or invalid |
550 | /// requests. |
551 | (From, FROM, b"from" ); |
552 | |
553 | /// Specifies the domain name of the server and (optionally) the TCP port |
554 | /// number on which the server is listening. |
555 | /// |
556 | /// If no port is given, the default port for the service requested (e.g., |
557 | /// "80" for an HTTP URL) is implied. |
558 | /// |
559 | /// A Host header field must be sent in all HTTP/1.1 request messages. A 400 |
560 | /// (Bad Request) status code will be sent to any HTTP/1.1 request message |
561 | /// that lacks a Host header field or contains more than one. |
562 | (Host, HOST, b"host" ); |
563 | |
564 | /// Makes a request conditional based on the E-Tag. |
565 | /// |
566 | /// For GET and HEAD methods, the server will send back the requested |
567 | /// resource only if it matches one of the listed ETags. For PUT and other |
568 | /// non-safe methods, it will only upload the resource in this case. |
569 | /// |
570 | /// The comparison with the stored ETag uses the strong comparison |
571 | /// algorithm, meaning two files are considered identical byte to byte only. |
572 | /// This is weakened when the W/ prefix is used in front of the ETag. |
573 | /// |
574 | /// There are two common use cases: |
575 | /// |
576 | /// * For GET and HEAD methods, used in combination with an Range header, it |
577 | /// can guarantee that the new ranges requested comes from the same resource |
578 | /// than the previous one. If it doesn't match, then a 416 (Range Not |
579 | /// Satisfiable) response is returned. |
580 | /// |
581 | /// * For other methods, and in particular for PUT, If-Match can be used to |
582 | /// prevent the lost update problem. It can check if the modification of a |
583 | /// resource that the user wants to upload will not override another change |
584 | /// that has been done since the original resource was fetched. If the |
585 | /// request cannot be fulfilled, the 412 (Precondition Failed) response is |
586 | /// returned. |
587 | (IfMatch, IF_MATCH, b"if-match" ); |
588 | |
589 | /// Makes a request conditional based on the modification date. |
590 | /// |
591 | /// The If-Modified-Since request HTTP header makes the request conditional: |
592 | /// the server will send back the requested resource, with a 200 status, |
593 | /// only if it has been last modified after the given date. If the request |
594 | /// has not been modified since, the response will be a 304 without any |
595 | /// body; the Last-Modified header will contain the date of last |
596 | /// modification. Unlike If-Unmodified-Since, If-Modified-Since can only be |
597 | /// used with a GET or HEAD. |
598 | /// |
599 | /// When used in combination with If-None-Match, it is ignored, unless the |
600 | /// server doesn't support If-None-Match. |
601 | /// |
602 | /// The most common use case is to update a cached entity that has no |
603 | /// associated ETag. |
604 | (IfModifiedSince, IF_MODIFIED_SINCE, b"if-modified-since" ); |
605 | |
606 | /// Makes a request conditional based on the E-Tag. |
607 | /// |
608 | /// The If-None-Match HTTP request header makes the request conditional. For |
609 | /// GET and HEAD methods, the server will send back the requested resource, |
610 | /// with a 200 status, only if it doesn't have an ETag matching the given |
611 | /// ones. For other methods, the request will be processed only if the |
612 | /// eventually existing resource's ETag doesn't match any of the values |
613 | /// listed. |
614 | /// |
615 | /// When the condition fails for GET and HEAD methods, then the server must |
616 | /// return HTTP status code 304 (Not Modified). For methods that apply |
617 | /// server-side changes, the status code 412 (Precondition Failed) is used. |
618 | /// Note that the server generating a 304 response MUST generate any of the |
619 | /// following header fields that would have been sent in a 200 (OK) response |
620 | /// to the same request: Cache-Control, Content-Location, Date, ETag, |
621 | /// Expires, and Vary. |
622 | /// |
623 | /// The comparison with the stored ETag uses the weak comparison algorithm, |
624 | /// meaning two files are considered identical not only if they are |
625 | /// identical byte to byte, but if the content is equivalent. For example, |
626 | /// two pages that would differ only by the date of generation in the footer |
627 | /// would be considered as identical. |
628 | /// |
629 | /// When used in combination with If-Modified-Since, it has precedence (if |
630 | /// the server supports it). |
631 | /// |
632 | /// There are two common use cases: |
633 | /// |
634 | /// * For `GET` and `HEAD` methods, to update a cached entity that has an associated ETag. |
635 | /// * For other methods, and in particular for `PUT`, `If-None-Match` used with |
636 | /// the `*` value can be used to save a file not known to exist, |
637 | /// guaranteeing that another upload didn't happen before, losing the data |
638 | /// of the previous put; this problems is the variation of the lost update |
639 | /// problem. |
640 | (IfNoneMatch, IF_NONE_MATCH, b"if-none-match" ); |
641 | |
642 | /// Makes a request conditional based on range. |
643 | /// |
644 | /// The If-Range HTTP request header makes a range request conditional: if |
645 | /// the condition is fulfilled, the range request will be issued and the |
646 | /// server sends back a 206 Partial Content answer with the appropriate |
647 | /// body. If the condition is not fulfilled, the full resource is sent back, |
648 | /// with a 200 OK status. |
649 | /// |
650 | /// This header can be used either with a Last-Modified validator, or with |
651 | /// an ETag, but not with both. |
652 | /// |
653 | /// The most common use case is to resume a download, to guarantee that the |
654 | /// stored resource has not been modified since the last fragment has been |
655 | /// received. |
656 | (IfRange, IF_RANGE, b"if-range" ); |
657 | |
658 | /// Makes the request conditional based on the last modification date. |
659 | /// |
660 | /// The If-Unmodified-Since request HTTP header makes the request |
661 | /// conditional: the server will send back the requested resource, or accept |
662 | /// it in the case of a POST or another non-safe method, only if it has not |
663 | /// been last modified after the given date. If the request has been |
664 | /// modified after the given date, the response will be a 412 (Precondition |
665 | /// Failed) error. |
666 | /// |
667 | /// There are two common use cases: |
668 | /// |
669 | /// * In conjunction non-safe methods, like POST, it can be used to |
670 | /// implement an optimistic concurrency control, like done by some wikis: |
671 | /// editions are rejected if the stored document has been modified since the |
672 | /// original has been retrieved. |
673 | /// |
674 | /// * In conjunction with a range request with a If-Range header, it can be |
675 | /// used to ensure that the new fragment requested comes from an unmodified |
676 | /// document. |
677 | (IfUnmodifiedSince, IF_UNMODIFIED_SINCE, b"if-unmodified-since" ); |
678 | |
679 | /// The Last-Modified header contains the date and time when the origin believes |
680 | /// the resource was last modified. |
681 | /// |
682 | /// The value is a valid Date/Time string defined in [RFC9910](https://datatracker.ietf.org/doc/html/rfc9110#section-5.6.7) |
683 | (LastModified, LAST_MODIFIED, b"last-modified" ); |
684 | |
685 | /// Allows the server to point an interested client to another resource |
686 | /// containing metadata about the requested resource. |
687 | (Link, LINK, b"link" ); |
688 | |
689 | /// Indicates the URL to redirect a page to. |
690 | /// |
691 | /// The Location response header indicates the URL to redirect a page to. It |
692 | /// only provides a meaning when served with a 3xx status response. |
693 | /// |
694 | /// The HTTP method used to make the new request to fetch the page pointed |
695 | /// to by Location depends of the original method and of the kind of |
696 | /// redirection: |
697 | /// |
698 | /// * If 303 (See Also) responses always lead to the use of a GET method, |
699 | /// 307 (Temporary Redirect) and 308 (Permanent Redirect) don't change the |
700 | /// method used in the original request; |
701 | /// |
702 | /// * 301 (Permanent Redirect) and 302 (Found) doesn't change the method |
703 | /// most of the time, though older user-agents may (so you basically don't |
704 | /// know). |
705 | /// |
706 | /// All responses with one of these status codes send a Location header. |
707 | /// |
708 | /// Beside redirect response, messages with 201 (Created) status also |
709 | /// include the Location header. It indicates the URL to the newly created |
710 | /// resource. |
711 | /// |
712 | /// Location and Content-Location are different: Location indicates the |
713 | /// target of a redirection (or the URL of a newly created resource), while |
714 | /// Content-Location indicates the direct URL to use to access the resource |
715 | /// when content negotiation happened, without the need of further content |
716 | /// negotiation. Location is a header associated with the response, while |
717 | /// Content-Location is associated with the entity returned. |
718 | (Location, LOCATION, b"location" ); |
719 | |
720 | /// Indicates the max number of intermediaries the request should be sent |
721 | /// through. |
722 | (MaxForwards, MAX_FORWARDS, b"max-forwards" ); |
723 | |
724 | /// Indicates where a fetch originates from. |
725 | /// |
726 | /// It doesn't include any path information, but only the server name. It is |
727 | /// sent with CORS requests, as well as with POST requests. It is similar to |
728 | /// the Referer header, but, unlike this header, it doesn't disclose the |
729 | /// whole path. |
730 | (Origin, ORIGIN, b"origin" ); |
731 | |
732 | /// HTTP/1.0 header usually used for backwards compatibility. |
733 | /// |
734 | /// The Pragma HTTP/1.0 general header is an implementation-specific header |
735 | /// that may have various effects along the request-response chain. It is |
736 | /// used for backwards compatibility with HTTP/1.0 caches where the |
737 | /// Cache-Control HTTP/1.1 header is not yet present. |
738 | (Pragma, PRAGMA, b"pragma" ); |
739 | |
740 | /// Defines the authentication method that should be used to gain access to |
741 | /// a proxy. |
742 | /// |
743 | /// Unlike `www-authenticate`, the `proxy-authenticate` header field applies |
744 | /// only to the next outbound client on the response chain. This is because |
745 | /// only the client that chose a given proxy is likely to have the |
746 | /// credentials necessary for authentication. However, when multiple proxies |
747 | /// are used within the same administrative domain, such as office and |
748 | /// regional caching proxies within a large corporate network, it is common |
749 | /// for credentials to be generated by the user agent and passed through the |
750 | /// hierarchy until consumed. Hence, in such a configuration, it will appear |
751 | /// as if Proxy-Authenticate is being forwarded because each proxy will send |
752 | /// the same challenge set. |
753 | /// |
754 | /// The `proxy-authenticate` header is sent along with a `407 Proxy |
755 | /// Authentication Required`. |
756 | (ProxyAuthenticate, PROXY_AUTHENTICATE, b"proxy-authenticate" ); |
757 | |
758 | /// Contains the credentials to authenticate a user agent to a proxy server. |
759 | /// |
760 | /// This header is usually included after the server has responded with a |
761 | /// 407 Proxy Authentication Required status and the Proxy-Authenticate |
762 | /// header. |
763 | (ProxyAuthorization, PROXY_AUTHORIZATION, b"proxy-authorization" ); |
764 | |
765 | /// Associates a specific cryptographic public key with a certain server. |
766 | /// |
767 | /// This decreases the risk of MITM attacks with forged certificates. If one |
768 | /// or several keys are pinned and none of them are used by the server, the |
769 | /// browser will not accept the response as legitimate, and will not display |
770 | /// it. |
771 | (PublicKeyPins, PUBLIC_KEY_PINS, b"public-key-pins" ); |
772 | |
773 | /// Sends reports of pinning violation to the report-uri specified in the |
774 | /// header. |
775 | /// |
776 | /// Unlike `Public-Key-Pins`, this header still allows browsers to connect |
777 | /// to the server if the pinning is violated. |
778 | (PublicKeyPinsReportOnly, PUBLIC_KEY_PINS_REPORT_ONLY, b"public-key-pins-report-only" ); |
779 | |
780 | /// Indicates the part of a document that the server should return. |
781 | /// |
782 | /// Several parts can be requested with one Range header at once, and the |
783 | /// server may send back these ranges in a multipart document. If the server |
784 | /// sends back ranges, it uses the 206 Partial Content for the response. If |
785 | /// the ranges are invalid, the server returns the 416 Range Not Satisfiable |
786 | /// error. The server can also ignore the Range header and return the whole |
787 | /// document with a 200 status code. |
788 | (Range, RANGE, b"range" ); |
789 | |
790 | /// Contains the address of the previous web page from which a link to the |
791 | /// currently requested page was followed. |
792 | /// |
793 | /// The Referer header allows servers to identify where people are visiting |
794 | /// them from and may use that data for analytics, logging, or optimized |
795 | /// caching, for example. |
796 | (Referer, REFERER, b"referer" ); |
797 | |
798 | /// Governs which referrer information should be included with requests |
799 | /// made. |
800 | (ReferrerPolicy, REFERRER_POLICY, b"referrer-policy" ); |
801 | |
802 | /// Informs the web browser that the current page or frame should be |
803 | /// refreshed. |
804 | (Refresh, REFRESH, b"refresh" ); |
805 | |
806 | /// The Retry-After response HTTP header indicates how long the user agent |
807 | /// should wait before making a follow-up request. There are two main cases |
808 | /// this header is used: |
809 | /// |
810 | /// * When sent with a 503 (Service Unavailable) response, it indicates how |
811 | /// long the service is expected to be unavailable. |
812 | /// |
813 | /// * When sent with a redirect response, such as 301 (Moved Permanently), |
814 | /// it indicates the minimum time that the user agent is asked to wait |
815 | /// before issuing the redirected request. |
816 | (RetryAfter, RETRY_AFTER, b"retry-after" ); |
817 | |
818 | /// The |Sec-WebSocket-Accept| header field is used in the WebSocket |
819 | /// opening handshake. It is sent from the server to the client to |
820 | /// confirm that the server is willing to initiate the WebSocket |
821 | /// connection. |
822 | (SecWebSocketAccept, SEC_WEBSOCKET_ACCEPT, b"sec-websocket-accept" ); |
823 | |
824 | /// The |Sec-WebSocket-Extensions| header field is used in the WebSocket |
825 | /// opening handshake. It is initially sent from the client to the |
826 | /// server, and then subsequently sent from the server to the client, to |
827 | /// agree on a set of protocol-level extensions to use for the duration |
828 | /// of the connection. |
829 | (SecWebSocketExtensions, SEC_WEBSOCKET_EXTENSIONS, b"sec-websocket-extensions" ); |
830 | |
831 | /// The |Sec-WebSocket-Key| header field is used in the WebSocket opening |
832 | /// handshake. It is sent from the client to the server to provide part |
833 | /// of the information used by the server to prove that it received a |
834 | /// valid WebSocket opening handshake. This helps ensure that the server |
835 | /// does not accept connections from non-WebSocket clients (e.g., HTTP |
836 | /// clients) that are being abused to send data to unsuspecting WebSocket |
837 | /// servers. |
838 | (SecWebSocketKey, SEC_WEBSOCKET_KEY, b"sec-websocket-key" ); |
839 | |
840 | /// The |Sec-WebSocket-Protocol| header field is used in the WebSocket |
841 | /// opening handshake. It is sent from the client to the server and back |
842 | /// from the server to the client to confirm the subprotocol of the |
843 | /// connection. This enables scripts to both select a subprotocol and be |
844 | /// sure that the server agreed to serve that subprotocol. |
845 | (SecWebSocketProtocol, SEC_WEBSOCKET_PROTOCOL, b"sec-websocket-protocol" ); |
846 | |
847 | /// The |Sec-WebSocket-Version| header field is used in the WebSocket |
848 | /// opening handshake. It is sent from the client to the server to |
849 | /// indicate the protocol version of the connection. This enables |
850 | /// servers to correctly interpret the opening handshake and subsequent |
851 | /// data being sent from the data, and close the connection if the server |
852 | /// cannot interpret that data in a safe manner. |
853 | (SecWebSocketVersion, SEC_WEBSOCKET_VERSION, b"sec-websocket-version" ); |
854 | |
855 | /// Contains information about the software used by the origin server to |
856 | /// handle the request. |
857 | /// |
858 | /// Overly long and detailed Server values should be avoided as they |
859 | /// potentially reveal internal implementation details that might make it |
860 | /// (slightly) easier for attackers to find and exploit known security |
861 | /// holes. |
862 | (Server, SERVER, b"server" ); |
863 | |
864 | /// Used to send cookies from the server to the user agent. |
865 | (SetCookie, SET_COOKIE, b"set-cookie" ); |
866 | |
867 | /// Tells the client to communicate with HTTPS instead of using HTTP. |
868 | (StrictTransportSecurity, STRICT_TRANSPORT_SECURITY, b"strict-transport-security" ); |
869 | |
870 | /// Informs the server of transfer encodings willing to be accepted as part |
871 | /// of the response. |
872 | /// |
873 | /// See also the Transfer-Encoding response header for more details on |
874 | /// transfer encodings. Note that chunked is always acceptable for HTTP/1.1 |
875 | /// recipients and you that don't have to specify "chunked" using the TE |
876 | /// header. However, it is useful for setting if the client is accepting |
877 | /// trailer fields in a chunked transfer coding using the "trailers" value. |
878 | (Te, TE, b"te" ); |
879 | |
880 | /// Allows the sender to include additional fields at the end of chunked |
881 | /// messages. |
882 | (Trailer, TRAILER, b"trailer" ); |
883 | |
884 | /// Specifies the form of encoding used to safely transfer the entity to the |
885 | /// client. |
886 | /// |
887 | /// `transfer-encoding` is a hop-by-hop header, that is applying to a |
888 | /// message between two nodes, not to a resource itself. Each segment of a |
889 | /// multi-node connection can use different `transfer-encoding` values. If |
890 | /// you want to compress data over the whole connection, use the end-to-end |
891 | /// header `content-encoding` header instead. |
892 | /// |
893 | /// When present on a response to a `HEAD` request that has no body, it |
894 | /// indicates the value that would have applied to the corresponding `GET` |
895 | /// message. |
896 | (TransferEncoding, TRANSFER_ENCODING, b"transfer-encoding" ); |
897 | |
898 | /// Contains a string that allows identifying the requesting client's |
899 | /// software. |
900 | (UserAgent, USER_AGENT, b"user-agent" ); |
901 | |
902 | /// Used as part of the exchange to upgrade the protocol. |
903 | (Upgrade, UPGRADE, b"upgrade" ); |
904 | |
905 | /// Sends a signal to the server expressing the client’s preference for an |
906 | /// encrypted and authenticated response. |
907 | (UpgradeInsecureRequests, UPGRADE_INSECURE_REQUESTS, b"upgrade-insecure-requests" ); |
908 | |
909 | /// Determines how to match future requests with cached responses. |
910 | /// |
911 | /// The `vary` HTTP response header determines how to match future request |
912 | /// headers to decide whether a cached response can be used rather than |
913 | /// requesting a fresh one from the origin server. It is used by the server |
914 | /// to indicate which headers it used when selecting a representation of a |
915 | /// resource in a content negotiation algorithm. |
916 | /// |
917 | /// The `vary` header should be set on a 304 Not Modified response exactly |
918 | /// like it would have been set on an equivalent 200 OK response. |
919 | (Vary, VARY, b"vary" ); |
920 | |
921 | /// Added by proxies to track routing. |
922 | /// |
923 | /// The `via` general header is added by proxies, both forward and reverse |
924 | /// proxies, and can appear in the request headers and the response headers. |
925 | /// It is used for tracking message forwards, avoiding request loops, and |
926 | /// identifying the protocol capabilities of senders along the |
927 | /// request/response chain. |
928 | (Via, VIA, b"via" ); |
929 | |
930 | /// General HTTP header contains information about possible problems with |
931 | /// the status of the message. |
932 | /// |
933 | /// More than one `warning` header may appear in a response. Warning header |
934 | /// fields can in general be applied to any message, however some warn-codes |
935 | /// are specific to caches and can only be applied to response messages. |
936 | (Warning, WARNING, b"warning" ); |
937 | |
938 | /// Defines the authentication method that should be used to gain access to |
939 | /// a resource. |
940 | (WwwAuthenticate, WWW_AUTHENTICATE, b"www-authenticate" ); |
941 | |
942 | /// Marker used by the server to indicate that the MIME types advertised in |
943 | /// the `content-type` headers should not be changed and be followed. |
944 | /// |
945 | /// This allows to opt-out of MIME type sniffing, or, in other words, it is |
946 | /// a way to say that the webmasters knew what they were doing. |
947 | /// |
948 | /// This header was introduced by Microsoft in IE 8 as a way for webmasters |
949 | /// to block content sniffing that was happening and could transform |
950 | /// non-executable MIME types into executable MIME types. Since then, other |
951 | /// browsers have introduced it, even if their MIME sniffing algorithms were |
952 | /// less aggressive. |
953 | /// |
954 | /// Site security testers usually expect this header to be set. |
955 | (XContentTypeOptions, X_CONTENT_TYPE_OPTIONS, b"x-content-type-options" ); |
956 | |
957 | /// Controls DNS prefetching. |
958 | /// |
959 | /// The `x-dns-prefetch-control` HTTP response header controls DNS |
960 | /// prefetching, a feature by which browsers proactively perform domain name |
961 | /// resolution on both links that the user may choose to follow as well as |
962 | /// URLs for items referenced by the document, including images, CSS, |
963 | /// JavaScript, and so forth. |
964 | /// |
965 | /// This prefetching is performed in the background, so that the DNS is |
966 | /// likely to have been resolved by the time the referenced items are |
967 | /// needed. This reduces latency when the user clicks a link. |
968 | (XDnsPrefetchControl, X_DNS_PREFETCH_CONTROL, b"x-dns-prefetch-control" ); |
969 | |
970 | /// Indicates whether or not a browser should be allowed to render a page in |
971 | /// a frame. |
972 | /// |
973 | /// Sites can use this to avoid clickjacking attacks, by ensuring that their |
974 | /// content is not embedded into other sites. |
975 | /// |
976 | /// The added security is only provided if the user accessing the document |
977 | /// is using a browser supporting `x-frame-options`. |
978 | (XFrameOptions, X_FRAME_OPTIONS, b"x-frame-options" ); |
979 | |
980 | /// Stop pages from loading when an XSS attack is detected. |
981 | /// |
982 | /// The HTTP X-XSS-Protection response header is a feature of Internet |
983 | /// Explorer, Chrome and Safari that stops pages from loading when they |
984 | /// detect reflected cross-site scripting (XSS) attacks. Although these |
985 | /// protections are largely unnecessary in modern browsers when sites |
986 | /// implement a strong Content-Security-Policy that disables the use of |
987 | /// inline JavaScript ('unsafe-inline'), they can still provide protections |
988 | /// for users of older web browsers that don't yet support CSP. |
989 | (XXssProtection, X_XSS_PROTECTION, b"x-xss-protection" ); |
990 | } |
991 | |
992 | /// Valid header name characters |
993 | /// |
994 | /// ```not_rust |
995 | /// field-name = token |
996 | /// separators = "(" | ")" | "<" | ">" | "@" |
997 | /// | "," | ";" | ":" | "\" | <"> |
998 | /// | "/" | "[" | "]" | "?" | "=" |
999 | /// | "{" | "}" | SP | HT |
1000 | /// token = 1*tchar |
1001 | /// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" |
1002 | /// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" |
1003 | /// / DIGIT / ALPHA |
1004 | /// ; any VCHAR, except delimiters |
1005 | /// ``` |
1006 | // HEADER_CHARS maps every byte that is 128 or larger to 0 so everything that is |
1007 | // mapped by HEADER_CHARS, maps to a valid single-byte UTF-8 codepoint. |
1008 | #[rustfmt::skip] |
1009 | const HEADER_CHARS: [u8; 256] = [ |
1010 | // 0 1 2 3 4 5 6 7 8 9 |
1011 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x |
1012 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x |
1013 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x |
1014 | 0, 0, 0, b'!' , 0, b'#' , b'$' , b'%' , b'&' , b' \'' , // 3x |
1015 | 0, 0, b'*' , b'+' , 0, b'-' , b'.' , 0, b'0' , b'1' , // 4x |
1016 | b'2' , b'3' , b'4' , b'5' , b'6' , b'7' , b'8' , b'9' , 0, 0, // 5x |
1017 | 0, 0, 0, 0, 0, b'a' , b'b' , b'c' , b'd' , b'e' , // 6x |
1018 | b'f' , b'g' , b'h' , b'i' , b'j' , b'k' , b'l' , b'm' , b'n' , b'o' , // 7x |
1019 | b'p' , b'q' , b'r' , b's' , b't' , b'u' , b'v' , b'w' , b'x' , b'y' , // 8x |
1020 | b'z' , 0, 0, 0, b'^' , b'_' , b'`' , b'a' , b'b' , b'c' , // 9x |
1021 | b'd' , b'e' , b'f' , b'g' , b'h' , b'i' , b'j' , b'k' , b'l' , b'm' , // 10x |
1022 | b'n' , b'o' , b'p' , b'q' , b'r' , b's' , b't' , b'u' , b'v' , b'w' , // 11x |
1023 | b'x' , b'y' , b'z' , 0, b'|' , 0, b'~' , 0, 0, 0, // 12x |
1024 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 13x |
1025 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x |
1026 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15x |
1027 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16x |
1028 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17x |
1029 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18x |
1030 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19x |
1031 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20x |
1032 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21x |
1033 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22x |
1034 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23x |
1035 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24x |
1036 | 0, 0, 0, 0, 0, 0 // 25x |
1037 | ]; |
1038 | |
1039 | /// Valid header name characters for HTTP/2.0 and HTTP/3.0 |
1040 | // HEADER_CHARS_H2 maps every byte that is 128 or larger to 0 so everything that is |
1041 | // mapped by HEADER_CHARS_H2, maps to a valid single-byte UTF-8 codepoint. |
1042 | #[rustfmt::skip] |
1043 | const HEADER_CHARS_H2: [u8; 256] = [ |
1044 | // 0 1 2 3 4 5 6 7 8 9 |
1045 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x |
1046 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x |
1047 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x |
1048 | 0, 0, 0, b'!' , b'"' , b'#' , b'$' , b'%' , b'&' , b' \'' , // 3x |
1049 | 0, 0, b'*' , b'+' , 0, b'-' , b'.' , 0, b'0' , b'1' , // 4x |
1050 | b'2' , b'3' , b'4' , b'5' , b'6' , b'7' , b'8' , b'9' , 0, 0, // 5x |
1051 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x |
1052 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7x |
1053 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x |
1054 | 0, 0, 0, 0, b'^' , b'_' , b'`' , b'a' , b'b' , b'c' , // 9x |
1055 | b'd' , b'e' , b'f' , b'g' , b'h' , b'i' , b'j' , b'k' , b'l' , b'm' , // 10x |
1056 | b'n' , b'o' , b'p' , b'q' , b'r' , b's' , b't' , b'u' , b'v' , b'w' , // 11x |
1057 | b'x' , b'y' , b'z' , 0, b'|' , 0, b'~' , 0, 0, 0, // 12x |
1058 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 13x |
1059 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x |
1060 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15x |
1061 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16x |
1062 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17x |
1063 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18x |
1064 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19x |
1065 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20x |
1066 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21x |
1067 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22x |
1068 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23x |
1069 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24x |
1070 | 0, 0, 0, 0, 0, 0 // 25x |
1071 | ]; |
1072 | |
1073 | fn parse_hdr<'a>( |
1074 | data: &'a [u8], |
1075 | b: &'a mut [MaybeUninit<u8>; SCRATCH_BUF_SIZE], |
1076 | table: &[u8; 256], |
1077 | ) -> Result<HdrName<'a>, InvalidHeaderName> { |
1078 | match data.len() { |
1079 | 0 => Err(InvalidHeaderName::new()), |
1080 | len @ 1..=SCRATCH_BUF_SIZE => { |
1081 | // Read from data into the buffer - transforming using `table` as we go |
1082 | data.iter() |
1083 | .zip(b.iter_mut()) |
1084 | .for_each(|(index, out)| *out = MaybeUninit::new(table[*index as usize])); |
1085 | // Safety: len bytes of b were just initialized. |
1086 | let name: &'a [u8] = unsafe { slice_assume_init(&b[0..len]) }; |
1087 | match StandardHeader::from_bytes(name) { |
1088 | Some(sh) => Ok(sh.into()), |
1089 | None => { |
1090 | if name.contains(&0) { |
1091 | Err(InvalidHeaderName::new()) |
1092 | } else { |
1093 | Ok(HdrName::custom(name, true)) |
1094 | } |
1095 | } |
1096 | } |
1097 | } |
1098 | SCRATCH_BUF_OVERFLOW..=super::MAX_HEADER_NAME_LEN => Ok(HdrName::custom(data, false)), |
1099 | _ => Err(InvalidHeaderName::new()), |
1100 | } |
1101 | } |
1102 | |
1103 | impl<'a> From<StandardHeader> for HdrName<'a> { |
1104 | fn from(hdr: StandardHeader) -> HdrName<'a> { |
1105 | HdrName { |
1106 | inner: Repr::Standard(hdr), |
1107 | } |
1108 | } |
1109 | } |
1110 | |
1111 | impl HeaderName { |
1112 | /// Converts a slice of bytes to an HTTP header name. |
1113 | /// |
1114 | /// This function normalizes the input. |
1115 | pub fn from_bytes(src: &[u8]) -> Result<HeaderName, InvalidHeaderName> { |
1116 | let mut buf = uninit_u8_array(); |
1117 | // Precondition: HEADER_CHARS is a valid table for parse_hdr(). |
1118 | match parse_hdr(src, &mut buf, &HEADER_CHARS)?.inner { |
1119 | Repr::Standard(std) => Ok(std.into()), |
1120 | Repr::Custom(MaybeLower { buf, lower: true }) => { |
1121 | let buf = Bytes::copy_from_slice(buf); |
1122 | // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. |
1123 | let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; |
1124 | Ok(Custom(val).into()) |
1125 | } |
1126 | Repr::Custom(MaybeLower { buf, lower: false }) => { |
1127 | use bytes::BufMut; |
1128 | let mut dst = BytesMut::with_capacity(buf.len()); |
1129 | |
1130 | for b in buf.iter() { |
1131 | // HEADER_CHARS maps all bytes to valid single-byte UTF-8 |
1132 | let b = HEADER_CHARS[*b as usize]; |
1133 | |
1134 | if b == 0 { |
1135 | return Err(InvalidHeaderName::new()); |
1136 | } |
1137 | |
1138 | dst.put_u8(b); |
1139 | } |
1140 | |
1141 | // Safety: the loop above maps all bytes in buf to valid single byte |
1142 | // UTF-8 before copying them into dst. This means that dst (and hence |
1143 | // dst.freeze()) is valid UTF-8. |
1144 | let val = unsafe { ByteStr::from_utf8_unchecked(dst.freeze()) }; |
1145 | |
1146 | Ok(Custom(val).into()) |
1147 | } |
1148 | } |
1149 | } |
1150 | |
1151 | /// Converts a slice of bytes to an HTTP header name. |
1152 | /// |
1153 | /// This function expects the input to only contain lowercase characters. |
1154 | /// This is useful when decoding HTTP/2.0 or HTTP/3.0 headers. Both |
1155 | /// require that all headers be represented in lower case. |
1156 | /// |
1157 | /// # Examples |
1158 | /// |
1159 | /// ``` |
1160 | /// # use http::header::*; |
1161 | /// |
1162 | /// // Parsing a lower case header |
1163 | /// let hdr = HeaderName::from_lowercase(b"content-length" ).unwrap(); |
1164 | /// assert_eq!(CONTENT_LENGTH, hdr); |
1165 | /// |
1166 | /// // Parsing a header that contains uppercase characters |
1167 | /// assert!(HeaderName::from_lowercase(b"Content-Length" ).is_err()); |
1168 | /// ``` |
1169 | pub fn from_lowercase(src: &[u8]) -> Result<HeaderName, InvalidHeaderName> { |
1170 | let mut buf = uninit_u8_array(); |
1171 | // Precondition: HEADER_CHARS_H2 is a valid table for parse_hdr() |
1172 | match parse_hdr(src, &mut buf, &HEADER_CHARS_H2)?.inner { |
1173 | Repr::Standard(std) => Ok(std.into()), |
1174 | Repr::Custom(MaybeLower { buf, lower: true }) => { |
1175 | let buf = Bytes::copy_from_slice(buf); |
1176 | // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. |
1177 | let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; |
1178 | Ok(Custom(val).into()) |
1179 | } |
1180 | Repr::Custom(MaybeLower { buf, lower: false }) => { |
1181 | for &b in buf.iter() { |
1182 | // HEADER_CHARS_H2 maps all bytes that are not valid single-byte |
1183 | // UTF-8 to 0 so this check returns an error for invalid UTF-8. |
1184 | if HEADER_CHARS_H2[b as usize] == 0 { |
1185 | return Err(InvalidHeaderName::new()); |
1186 | } |
1187 | } |
1188 | |
1189 | let buf = Bytes::copy_from_slice(buf); |
1190 | // Safety: the loop above checks that each byte of buf (either |
1191 | // version) is valid UTF-8. |
1192 | let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; |
1193 | Ok(Custom(val).into()) |
1194 | } |
1195 | } |
1196 | } |
1197 | |
1198 | /// Converts a static string to a HTTP header name. |
1199 | /// |
1200 | /// This function requires the static string to only contain lowercase |
1201 | /// characters, numerals and symbols, as per the HTTP/2.0 specification |
1202 | /// and header names internal representation within this library. |
1203 | /// |
1204 | /// # Panics |
1205 | /// |
1206 | /// This function panics when the static string is a invalid header. |
1207 | /// |
1208 | /// Until [Allow panicking in constants](https://github.com/rust-lang/rfcs/pull/2345) |
1209 | /// makes its way into stable, the panic message at compile-time is |
1210 | /// going to look cryptic, but should at least point at your header value: |
1211 | /// |
1212 | /// ```text |
1213 | /// error: any use of this value will cause an error |
1214 | /// --> http/src/header/name.rs:1241:13 |
1215 | /// | |
1216 | /// 1241 | ([] as [u8; 0])[0]; // Invalid header name |
1217 | /// | ^^^^^^^^^^^^^^^^^^ |
1218 | /// | | |
1219 | /// | index out of bounds: the length is 0 but the index is 0 |
1220 | /// | inside `http::HeaderName::from_static` at http/src/header/name.rs:1241:13 |
1221 | /// | inside `INVALID_NAME` at src/main.rs:3:34 |
1222 | /// | |
1223 | /// ::: src/main.rs:3:1 |
1224 | /// | |
1225 | /// 3 | const INVALID_NAME: HeaderName = HeaderName::from_static("Capitalized"); |
1226 | /// | ------------------------------------------------------------------------ |
1227 | /// ``` |
1228 | /// |
1229 | /// # Examples |
1230 | /// |
1231 | /// ``` |
1232 | /// # use http::header::*; |
1233 | /// // Parsing a standard header |
1234 | /// let hdr = HeaderName::from_static("content-length" ); |
1235 | /// assert_eq!(CONTENT_LENGTH, hdr); |
1236 | /// |
1237 | /// // Parsing a custom header |
1238 | /// let CUSTOM_HEADER: &'static str = "custom-header" ; |
1239 | /// |
1240 | /// let a = HeaderName::from_lowercase(b"custom-header" ).unwrap(); |
1241 | /// let b = HeaderName::from_static(CUSTOM_HEADER); |
1242 | /// assert_eq!(a, b); |
1243 | /// ``` |
1244 | /// |
1245 | /// ```should_panic |
1246 | /// # use http::header::*; |
1247 | /// # |
1248 | /// // Parsing a header that contains invalid symbols(s): |
1249 | /// HeaderName::from_static("content{}{}length" ); // This line panics! |
1250 | /// |
1251 | /// // Parsing a header that contains invalid uppercase characters. |
1252 | /// let a = HeaderName::from_static("foobar" ); |
1253 | /// let b = HeaderName::from_static("FOOBAR" ); // This line panics! |
1254 | /// ``` |
1255 | #[allow (unconditional_panic)] // required for the panic circumvention |
1256 | pub const fn from_static(src: &'static str) -> HeaderName { |
1257 | let name_bytes = src.as_bytes(); |
1258 | if let Some(standard) = StandardHeader::from_bytes(name_bytes) { |
1259 | return HeaderName { |
1260 | inner: Repr::Standard(standard), |
1261 | }; |
1262 | } |
1263 | |
1264 | if name_bytes.is_empty() || name_bytes.len() > super::MAX_HEADER_NAME_LEN || { |
1265 | let mut i = 0; |
1266 | loop { |
1267 | if i >= name_bytes.len() { |
1268 | break false; |
1269 | } else if HEADER_CHARS_H2[name_bytes[i] as usize] == 0 { |
1270 | break true; |
1271 | } |
1272 | i += 1; |
1273 | } |
1274 | } { |
1275 | // TODO: When msrv is bumped to larger than 1.57, this should be |
1276 | // replaced with `panic!` macro. |
1277 | // https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html#panic-in-const-contexts |
1278 | // |
1279 | // See the panics section of this method's document for details. |
1280 | #[allow (clippy::no_effect, clippy::out_of_bounds_indexing)] |
1281 | ([] as [u8; 0])[0]; // Invalid header name |
1282 | } |
1283 | |
1284 | HeaderName { |
1285 | inner: Repr::Custom(Custom(ByteStr::from_static(src))), |
1286 | } |
1287 | } |
1288 | |
1289 | /// Returns a `str` representation of the header. |
1290 | /// |
1291 | /// The returned string will always be lower case. |
1292 | #[inline ] |
1293 | pub fn as_str(&self) -> &str { |
1294 | match self.inner { |
1295 | Repr::Standard(v) => v.as_str(), |
1296 | Repr::Custom(ref v) => &v.0, |
1297 | } |
1298 | } |
1299 | |
1300 | pub(super) fn into_bytes(self) -> Bytes { |
1301 | self.inner.into() |
1302 | } |
1303 | } |
1304 | |
1305 | impl FromStr for HeaderName { |
1306 | type Err = InvalidHeaderName; |
1307 | |
1308 | fn from_str(s: &str) -> Result<HeaderName, InvalidHeaderName> { |
1309 | HeaderName::from_bytes(s.as_bytes()).map_err(|_| InvalidHeaderName { _priv: () }) |
1310 | } |
1311 | } |
1312 | |
1313 | impl AsRef<str> for HeaderName { |
1314 | fn as_ref(&self) -> &str { |
1315 | self.as_str() |
1316 | } |
1317 | } |
1318 | |
1319 | impl AsRef<[u8]> for HeaderName { |
1320 | fn as_ref(&self) -> &[u8] { |
1321 | self.as_str().as_bytes() |
1322 | } |
1323 | } |
1324 | |
1325 | impl Borrow<str> for HeaderName { |
1326 | fn borrow(&self) -> &str { |
1327 | self.as_str() |
1328 | } |
1329 | } |
1330 | |
1331 | impl fmt::Debug for HeaderName { |
1332 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
1333 | fmt::Debug::fmt(self.as_str(), f:fmt) |
1334 | } |
1335 | } |
1336 | |
1337 | impl fmt::Display for HeaderName { |
1338 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
1339 | fmt::Display::fmt(self.as_str(), f:fmt) |
1340 | } |
1341 | } |
1342 | |
1343 | impl InvalidHeaderName { |
1344 | pub(super) fn new() -> InvalidHeaderName { |
1345 | InvalidHeaderName { _priv: () } |
1346 | } |
1347 | } |
1348 | |
1349 | impl<'a> From<&'a HeaderName> for HeaderName { |
1350 | fn from(src: &'a HeaderName) -> HeaderName { |
1351 | src.clone() |
1352 | } |
1353 | } |
1354 | |
1355 | #[doc (hidden)] |
1356 | impl<T> From<Repr<T>> for Bytes |
1357 | where |
1358 | T: Into<Bytes>, |
1359 | { |
1360 | fn from(repr: Repr<T>) -> Bytes { |
1361 | match repr { |
1362 | Repr::Standard(header: StandardHeader) => Bytes::from_static(header.as_str().as_bytes()), |
1363 | Repr::Custom(header: T) => header.into(), |
1364 | } |
1365 | } |
1366 | } |
1367 | |
1368 | impl From<Custom> for Bytes { |
1369 | #[inline ] |
1370 | fn from(Custom(inner: ByteStr): Custom) -> Bytes { |
1371 | Bytes::from(inner) |
1372 | } |
1373 | } |
1374 | |
1375 | impl<'a> TryFrom<&'a str> for HeaderName { |
1376 | type Error = InvalidHeaderName; |
1377 | #[inline ] |
1378 | fn try_from(s: &'a str) -> Result<Self, Self::Error> { |
1379 | Self::from_bytes(src:s.as_bytes()) |
1380 | } |
1381 | } |
1382 | |
1383 | impl<'a> TryFrom<&'a String> for HeaderName { |
1384 | type Error = InvalidHeaderName; |
1385 | #[inline ] |
1386 | fn try_from(s: &'a String) -> Result<Self, Self::Error> { |
1387 | Self::from_bytes(src:s.as_bytes()) |
1388 | } |
1389 | } |
1390 | |
1391 | impl<'a> TryFrom<&'a [u8]> for HeaderName { |
1392 | type Error = InvalidHeaderName; |
1393 | #[inline ] |
1394 | fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> { |
1395 | Self::from_bytes(src:s) |
1396 | } |
1397 | } |
1398 | |
1399 | impl TryFrom<String> for HeaderName { |
1400 | type Error = InvalidHeaderName; |
1401 | |
1402 | #[inline ] |
1403 | fn try_from(s: String) -> Result<Self, Self::Error> { |
1404 | Self::from_bytes(src:s.as_bytes()) |
1405 | } |
1406 | } |
1407 | |
1408 | impl TryFrom<Vec<u8>> for HeaderName { |
1409 | type Error = InvalidHeaderName; |
1410 | |
1411 | #[inline ] |
1412 | fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> { |
1413 | Self::from_bytes(&vec) |
1414 | } |
1415 | } |
1416 | |
1417 | #[doc (hidden)] |
1418 | impl From<StandardHeader> for HeaderName { |
1419 | fn from(src: StandardHeader) -> HeaderName { |
1420 | HeaderName { |
1421 | inner: Repr::Standard(src), |
1422 | } |
1423 | } |
1424 | } |
1425 | |
1426 | #[doc (hidden)] |
1427 | impl From<Custom> for HeaderName { |
1428 | fn from(src: Custom) -> HeaderName { |
1429 | HeaderName { |
1430 | inner: Repr::Custom(src), |
1431 | } |
1432 | } |
1433 | } |
1434 | |
1435 | impl<'a> PartialEq<&'a HeaderName> for HeaderName { |
1436 | #[inline ] |
1437 | fn eq(&self, other: &&'a HeaderName) -> bool { |
1438 | *self == **other |
1439 | } |
1440 | } |
1441 | |
1442 | impl<'a> PartialEq<HeaderName> for &'a HeaderName { |
1443 | #[inline ] |
1444 | fn eq(&self, other: &HeaderName) -> bool { |
1445 | *other == *self |
1446 | } |
1447 | } |
1448 | |
1449 | impl PartialEq<str> for HeaderName { |
1450 | /// Performs a case-insensitive comparison of the string against the header |
1451 | /// name |
1452 | /// |
1453 | /// # Examples |
1454 | /// |
1455 | /// ``` |
1456 | /// use http::header::CONTENT_LENGTH; |
1457 | /// |
1458 | /// assert_eq!(CONTENT_LENGTH, "content-length" ); |
1459 | /// assert_eq!(CONTENT_LENGTH, "Content-Length" ); |
1460 | /// assert_ne!(CONTENT_LENGTH, "content length" ); |
1461 | /// ``` |
1462 | #[inline ] |
1463 | fn eq(&self, other: &str) -> bool { |
1464 | eq_ignore_ascii_case(self.as_ref(), s:other.as_bytes()) |
1465 | } |
1466 | } |
1467 | |
1468 | impl PartialEq<HeaderName> for str { |
1469 | /// Performs a case-insensitive comparison of the string against the header |
1470 | /// name |
1471 | /// |
1472 | /// # Examples |
1473 | /// |
1474 | /// ``` |
1475 | /// use http::header::CONTENT_LENGTH; |
1476 | /// |
1477 | /// assert_eq!(CONTENT_LENGTH, "content-length" ); |
1478 | /// assert_eq!(CONTENT_LENGTH, "Content-Length" ); |
1479 | /// assert_ne!(CONTENT_LENGTH, "content length" ); |
1480 | /// ``` |
1481 | #[inline ] |
1482 | fn eq(&self, other: &HeaderName) -> bool { |
1483 | *other == *self |
1484 | } |
1485 | } |
1486 | |
1487 | impl<'a> PartialEq<&'a str> for HeaderName { |
1488 | /// Performs a case-insensitive comparison of the string against the header |
1489 | /// name |
1490 | #[inline ] |
1491 | fn eq(&self, other: &&'a str) -> bool { |
1492 | *self == **other |
1493 | } |
1494 | } |
1495 | |
1496 | impl<'a> PartialEq<HeaderName> for &'a str { |
1497 | /// Performs a case-insensitive comparison of the string against the header |
1498 | /// name |
1499 | #[inline ] |
1500 | fn eq(&self, other: &HeaderName) -> bool { |
1501 | *other == *self |
1502 | } |
1503 | } |
1504 | |
1505 | impl fmt::Debug for InvalidHeaderName { |
1506 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1507 | fDebugStruct<'_, '_>.debug_struct(name:"InvalidHeaderName" ) |
1508 | // skip _priv noise |
1509 | .finish() |
1510 | } |
1511 | } |
1512 | |
1513 | impl fmt::Display for InvalidHeaderName { |
1514 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1515 | f.write_str(data:"invalid HTTP header name" ) |
1516 | } |
1517 | } |
1518 | |
1519 | impl Error for InvalidHeaderName {} |
1520 | |
1521 | // ===== HdrName ===== |
1522 | |
1523 | impl<'a> HdrName<'a> { |
1524 | // Precondition: if lower then buf is valid UTF-8 |
1525 | fn custom(buf: &'a [u8], lower: bool) -> HdrName<'a> { |
1526 | HdrName { |
1527 | // Invariant (on MaybeLower): follows from the precondition |
1528 | inner: Repr::Custom(MaybeLower { buf, lower }), |
1529 | } |
1530 | } |
1531 | |
1532 | pub fn from_bytes<F, U>(hdr: &[u8], f: F) -> Result<U, InvalidHeaderName> |
1533 | where |
1534 | F: FnOnce(HdrName<'_>) -> U, |
1535 | { |
1536 | let mut buf = uninit_u8_array(); |
1537 | // Precondition: HEADER_CHARS is a valid table for parse_hdr(). |
1538 | let hdr = parse_hdr(hdr, &mut buf, &HEADER_CHARS)?; |
1539 | Ok(f(hdr)) |
1540 | } |
1541 | |
1542 | pub fn from_static<F, U>(hdr: &'static str, f: F) -> U |
1543 | where |
1544 | F: FnOnce(HdrName<'_>) -> U, |
1545 | { |
1546 | let mut buf = uninit_u8_array(); |
1547 | let hdr = |
1548 | // Precondition: HEADER_CHARS is a valid table for parse_hdr(). |
1549 | parse_hdr(hdr.as_bytes(), &mut buf, &HEADER_CHARS).expect("static str is invalid name" ); |
1550 | f(hdr) |
1551 | } |
1552 | } |
1553 | |
1554 | #[doc (hidden)] |
1555 | impl<'a> From<HdrName<'a>> for HeaderName { |
1556 | fn from(src: HdrName<'a>) -> HeaderName { |
1557 | match src.inner { |
1558 | Repr::Standard(s) => HeaderName { |
1559 | inner: Repr::Standard(s), |
1560 | }, |
1561 | Repr::Custom(maybe_lower) => { |
1562 | if maybe_lower.lower { |
1563 | let buf = Bytes::copy_from_slice(maybe_lower.buf); |
1564 | // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. |
1565 | let byte_str = unsafe { ByteStr::from_utf8_unchecked(buf) }; |
1566 | |
1567 | HeaderName { |
1568 | inner: Repr::Custom(Custom(byte_str)), |
1569 | } |
1570 | } else { |
1571 | use bytes::BufMut; |
1572 | let mut dst = BytesMut::with_capacity(maybe_lower.buf.len()); |
1573 | |
1574 | for b in maybe_lower.buf.iter() { |
1575 | // HEADER_CHARS maps each byte to a valid single-byte UTF-8 |
1576 | // codepoint. |
1577 | dst.put_u8(HEADER_CHARS[*b as usize]); |
1578 | } |
1579 | |
1580 | // Safety: the loop above maps each byte of maybe_lower.buf to a |
1581 | // valid single-byte UTF-8 codepoint before copying it into dst. |
1582 | // dst (and hence dst.freeze()) is thus valid UTF-8. |
1583 | let buf = unsafe { ByteStr::from_utf8_unchecked(dst.freeze()) }; |
1584 | |
1585 | HeaderName { |
1586 | inner: Repr::Custom(Custom(buf)), |
1587 | } |
1588 | } |
1589 | } |
1590 | } |
1591 | } |
1592 | } |
1593 | |
1594 | #[doc (hidden)] |
1595 | impl<'a> PartialEq<HdrName<'a>> for HeaderName { |
1596 | #[inline ] |
1597 | fn eq(&self, other: &HdrName<'a>) -> bool { |
1598 | match self.inner { |
1599 | Repr::Standard(a: StandardHeader) => match other.inner { |
1600 | Repr::Standard(b: StandardHeader) => a == b, |
1601 | _ => false, |
1602 | }, |
1603 | Repr::Custom(Custom(ref a: &ByteStr)) => match other.inner { |
1604 | Repr::Custom(ref b: &MaybeLower<'a>) => { |
1605 | if b.lower { |
1606 | a.as_bytes() == b.buf |
1607 | } else { |
1608 | eq_ignore_ascii_case(lower:a.as_bytes(), s:b.buf) |
1609 | } |
1610 | } |
1611 | _ => false, |
1612 | }, |
1613 | } |
1614 | } |
1615 | } |
1616 | |
1617 | // ===== Custom ===== |
1618 | |
1619 | impl Hash for Custom { |
1620 | #[inline ] |
1621 | fn hash<H: Hasher>(&self, hasher: &mut H) { |
1622 | hasher.write(self.0.as_bytes()) |
1623 | } |
1624 | } |
1625 | |
1626 | // ===== MaybeLower ===== |
1627 | |
1628 | impl<'a> Hash for MaybeLower<'a> { |
1629 | #[inline ] |
1630 | fn hash<H: Hasher>(&self, hasher: &mut H) { |
1631 | if self.lower { |
1632 | hasher.write(self.buf); |
1633 | } else { |
1634 | for &b: u8 in self.buf { |
1635 | hasher.write(&[HEADER_CHARS[b as usize]]); |
1636 | } |
1637 | } |
1638 | } |
1639 | } |
1640 | |
1641 | // Assumes that the left hand side is already lower case |
1642 | #[inline ] |
1643 | fn eq_ignore_ascii_case(lower: &[u8], s: &[u8]) -> bool { |
1644 | if lower.len() != s.len() { |
1645 | return false; |
1646 | } |
1647 | |
1648 | lowerimpl Iterator |
1649 | .iter() |
1650 | .zip(s) |
1651 | .all(|(a: &u8, b: &u8)| *a == HEADER_CHARS[*b as usize]) |
1652 | } |
1653 | |
1654 | // Utility functions for MaybeUninit<>. These are drawn from unstable API's on |
1655 | // MaybeUninit<> itself. |
1656 | const SCRATCH_BUF_SIZE: usize = 64; |
1657 | const SCRATCH_BUF_OVERFLOW: usize = SCRATCH_BUF_SIZE + 1; |
1658 | |
1659 | fn uninit_u8_array() -> [MaybeUninit<u8>; SCRATCH_BUF_SIZE] { |
1660 | let arr: MaybeUninit<[MaybeUninit<…>; 64]> = MaybeUninit::<[MaybeUninit<u8>; SCRATCH_BUF_SIZE]>::uninit(); |
1661 | // Safety: assume_init() is claiming that an array of MaybeUninit<> |
1662 | // has been initialized, but MaybeUninit<>'s do not require initialization. |
1663 | unsafe { arr.assume_init() } |
1664 | } |
1665 | |
1666 | // Assuming all the elements are initialized, get a slice of them. |
1667 | // |
1668 | // Safety: All elements of `slice` must be initialized to prevent |
1669 | // undefined behavior. |
1670 | unsafe fn slice_assume_init<T>(slice: &[MaybeUninit<T>]) -> &[T] { |
1671 | &*(slice as *const [MaybeUninit<T>] as *const [T]) |
1672 | } |
1673 | |
1674 | #[cfg (test)] |
1675 | mod tests { |
1676 | use self::StandardHeader::Vary; |
1677 | use super::*; |
1678 | |
1679 | #[test ] |
1680 | fn test_bounds() { |
1681 | fn check_bounds<T: Sync + Send>() {} |
1682 | check_bounds::<HeaderName>(); |
1683 | } |
1684 | |
1685 | #[test ] |
1686 | fn test_parse_invalid_headers() { |
1687 | for i in 0..128 { |
1688 | let hdr = vec![1u8; i]; |
1689 | assert!( |
1690 | HeaderName::from_bytes(&hdr).is_err(), |
1691 | "{} invalid header chars did not fail" , |
1692 | i |
1693 | ); |
1694 | } |
1695 | } |
1696 | |
1697 | const ONE_TOO_LONG: &[u8] = &[b'a' ; super::super::MAX_HEADER_NAME_LEN + 1]; |
1698 | |
1699 | #[test ] |
1700 | fn test_invalid_name_lengths() { |
1701 | assert!( |
1702 | HeaderName::from_bytes(&[]).is_err(), |
1703 | "zero-length header name is an error" , |
1704 | ); |
1705 | |
1706 | let long = &ONE_TOO_LONG[0..super::super::MAX_HEADER_NAME_LEN]; |
1707 | |
1708 | let long_str = std::str::from_utf8(long).unwrap(); |
1709 | assert_eq!(HeaderName::from_static(long_str), long_str); // shouldn't panic! |
1710 | |
1711 | assert!( |
1712 | HeaderName::from_bytes(long).is_ok(), |
1713 | "max header name length is ok" , |
1714 | ); |
1715 | assert!( |
1716 | HeaderName::from_bytes(ONE_TOO_LONG).is_err(), |
1717 | "longer than max header name length is an error" , |
1718 | ); |
1719 | } |
1720 | |
1721 | #[test ] |
1722 | #[should_panic ] |
1723 | fn test_static_invalid_name_lengths() { |
1724 | // Safety: ONE_TOO_LONG contains only the UTF-8 safe, single-byte codepoint b'a'. |
1725 | let _ = HeaderName::from_static(unsafe { std::str::from_utf8_unchecked(ONE_TOO_LONG) }); |
1726 | } |
1727 | |
1728 | #[test ] |
1729 | fn test_from_hdr_name() { |
1730 | use self::StandardHeader::Vary; |
1731 | |
1732 | let name = HeaderName::from(HdrName { |
1733 | inner: Repr::Standard(Vary), |
1734 | }); |
1735 | |
1736 | assert_eq!(name.inner, Repr::Standard(Vary)); |
1737 | |
1738 | let name = HeaderName::from(HdrName { |
1739 | inner: Repr::Custom(MaybeLower { |
1740 | buf: b"hello-world" , |
1741 | lower: true, |
1742 | }), |
1743 | }); |
1744 | |
1745 | assert_eq!( |
1746 | name.inner, |
1747 | Repr::Custom(Custom(ByteStr::from_static("hello-world" ))) |
1748 | ); |
1749 | |
1750 | let name = HeaderName::from(HdrName { |
1751 | inner: Repr::Custom(MaybeLower { |
1752 | buf: b"Hello-World" , |
1753 | lower: false, |
1754 | }), |
1755 | }); |
1756 | |
1757 | assert_eq!( |
1758 | name.inner, |
1759 | Repr::Custom(Custom(ByteStr::from_static("hello-world" ))) |
1760 | ); |
1761 | } |
1762 | |
1763 | #[test ] |
1764 | fn test_eq_hdr_name() { |
1765 | use self::StandardHeader::Vary; |
1766 | |
1767 | let a = HeaderName { |
1768 | inner: Repr::Standard(Vary), |
1769 | }; |
1770 | let b = HdrName { |
1771 | inner: Repr::Standard(Vary), |
1772 | }; |
1773 | |
1774 | assert_eq!(a, b); |
1775 | |
1776 | let a = HeaderName { |
1777 | inner: Repr::Custom(Custom(ByteStr::from_static("vaary" ))), |
1778 | }; |
1779 | assert_ne!(a, b); |
1780 | |
1781 | let b = HdrName { |
1782 | inner: Repr::Custom(MaybeLower { |
1783 | buf: b"vaary" , |
1784 | lower: true, |
1785 | }), |
1786 | }; |
1787 | |
1788 | assert_eq!(a, b); |
1789 | |
1790 | let b = HdrName { |
1791 | inner: Repr::Custom(MaybeLower { |
1792 | buf: b"vaary" , |
1793 | lower: false, |
1794 | }), |
1795 | }; |
1796 | |
1797 | assert_eq!(a, b); |
1798 | |
1799 | let b = HdrName { |
1800 | inner: Repr::Custom(MaybeLower { |
1801 | buf: b"VAARY" , |
1802 | lower: false, |
1803 | }), |
1804 | }; |
1805 | |
1806 | assert_eq!(a, b); |
1807 | |
1808 | let a = HeaderName { |
1809 | inner: Repr::Standard(Vary), |
1810 | }; |
1811 | assert_ne!(a, b); |
1812 | } |
1813 | |
1814 | #[test ] |
1815 | fn test_from_static_std() { |
1816 | let a = HeaderName { |
1817 | inner: Repr::Standard(Vary), |
1818 | }; |
1819 | |
1820 | let b = HeaderName::from_static("vary" ); |
1821 | assert_eq!(a, b); |
1822 | |
1823 | let b = HeaderName::from_static("vaary" ); |
1824 | assert_ne!(a, b); |
1825 | } |
1826 | |
1827 | #[test ] |
1828 | #[should_panic ] |
1829 | fn test_from_static_std_uppercase() { |
1830 | HeaderName::from_static("Vary" ); |
1831 | } |
1832 | |
1833 | #[test ] |
1834 | #[should_panic ] |
1835 | fn test_from_static_std_symbol() { |
1836 | HeaderName::from_static("vary{}" ); |
1837 | } |
1838 | |
1839 | // MaybeLower { lower: true } |
1840 | #[test ] |
1841 | fn test_from_static_custom_short() { |
1842 | let a = HeaderName { |
1843 | inner: Repr::Custom(Custom(ByteStr::from_static("customheader" ))), |
1844 | }; |
1845 | let b = HeaderName::from_static("customheader" ); |
1846 | assert_eq!(a, b); |
1847 | } |
1848 | |
1849 | #[test ] |
1850 | #[should_panic ] |
1851 | fn test_from_static_custom_short_uppercase() { |
1852 | HeaderName::from_static("custom header" ); |
1853 | } |
1854 | |
1855 | #[test ] |
1856 | #[should_panic ] |
1857 | fn test_from_static_custom_short_symbol() { |
1858 | HeaderName::from_static("CustomHeader" ); |
1859 | } |
1860 | |
1861 | // MaybeLower { lower: false } |
1862 | #[test ] |
1863 | fn test_from_static_custom_long() { |
1864 | let a = HeaderName { |
1865 | inner: Repr::Custom(Custom(ByteStr::from_static( |
1866 | "longer-than-63--thisheaderislongerthansixtythreecharactersandthushandleddifferent" , |
1867 | ))), |
1868 | }; |
1869 | let b = HeaderName::from_static( |
1870 | "longer-than-63--thisheaderislongerthansixtythreecharactersandthushandleddifferent" , |
1871 | ); |
1872 | assert_eq!(a, b); |
1873 | } |
1874 | |
1875 | #[test ] |
1876 | #[should_panic ] |
1877 | fn test_from_static_custom_long_uppercase() { |
1878 | HeaderName::from_static( |
1879 | "Longer-Than-63--ThisHeaderIsLongerThanSixtyThreeCharactersAndThusHandledDifferent" , |
1880 | ); |
1881 | } |
1882 | |
1883 | #[test ] |
1884 | #[should_panic ] |
1885 | fn test_from_static_custom_long_symbol() { |
1886 | HeaderName::from_static( |
1887 | "longer-than-63--thisheader{}{}{}{}islongerthansixtythreecharactersandthushandleddifferent" |
1888 | ); |
1889 | } |
1890 | |
1891 | #[test ] |
1892 | fn test_from_static_custom_single_char() { |
1893 | let a = HeaderName { |
1894 | inner: Repr::Custom(Custom(ByteStr::from_static("a" ))), |
1895 | }; |
1896 | let b = HeaderName::from_static("a" ); |
1897 | assert_eq!(a, b); |
1898 | } |
1899 | |
1900 | #[test ] |
1901 | #[should_panic ] |
1902 | fn test_from_static_empty() { |
1903 | HeaderName::from_static("" ); |
1904 | } |
1905 | |
1906 | #[test ] |
1907 | fn test_all_tokens() { |
1908 | HeaderName::from_static("!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyz" ); |
1909 | } |
1910 | |
1911 | #[test ] |
1912 | fn test_from_lowercase() { |
1913 | HeaderName::from_lowercase(&[0; 10]).unwrap_err(); |
1914 | HeaderName::from_lowercase(&[b'A' ; 10]).unwrap_err(); |
1915 | HeaderName::from_lowercase(&[0x1; 10]).unwrap_err(); |
1916 | HeaderName::from_lowercase(&[0xFF; 10]).unwrap_err(); |
1917 | //HeaderName::from_lowercase(&[0; 100]).unwrap_err(); |
1918 | HeaderName::from_lowercase(&[b'A' ; 100]).unwrap_err(); |
1919 | HeaderName::from_lowercase(&[0x1; 100]).unwrap_err(); |
1920 | HeaderName::from_lowercase(&[0xFF; 100]).unwrap_err(); |
1921 | } |
1922 | } |
1923 | |