1 | use crate::byte_str::ByteStr; |
2 | use bytes::{Bytes, BytesMut}; |
3 | |
4 | use std::borrow::Borrow; |
5 | use std::error::Error; |
6 | use std::convert::{TryFrom}; |
7 | use std::hash::{Hash, Hasher}; |
8 | use std::mem::MaybeUninit; |
9 | use std::str::FromStr; |
10 | use std::fmt; |
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 encryption, 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 | /// Content-Types that are acceptable for the response. |
680 | (LastModified, LAST_MODIFIED, b"last-modified" ); |
681 | |
682 | /// Allows the server to point an interested client to another resource |
683 | /// containing metadata about the requested resource. |
684 | (Link, LINK, b"link" ); |
685 | |
686 | /// Indicates the URL to redirect a page to. |
687 | /// |
688 | /// The Location response header indicates the URL to redirect a page to. It |
689 | /// only provides a meaning when served with a 3xx status response. |
690 | /// |
691 | /// The HTTP method used to make the new request to fetch the page pointed |
692 | /// to by Location depends of the original method and of the kind of |
693 | /// redirection: |
694 | /// |
695 | /// * If 303 (See Also) responses always lead to the use of a GET method, |
696 | /// 307 (Temporary Redirect) and 308 (Permanent Redirect) don't change the |
697 | /// method used in the original request; |
698 | /// |
699 | /// * 301 (Permanent Redirect) and 302 (Found) doesn't change the method |
700 | /// most of the time, though older user-agents may (so you basically don't |
701 | /// know). |
702 | /// |
703 | /// All responses with one of these status codes send a Location header. |
704 | /// |
705 | /// Beside redirect response, messages with 201 (Created) status also |
706 | /// include the Location header. It indicates the URL to the newly created |
707 | /// resource. |
708 | /// |
709 | /// Location and Content-Location are different: Location indicates the |
710 | /// target of a redirection (or the URL of a newly created resource), while |
711 | /// Content-Location indicates the direct URL to use to access the resource |
712 | /// when content negotiation happened, without the need of further content |
713 | /// negotiation. Location is a header associated with the response, while |
714 | /// Content-Location is associated with the entity returned. |
715 | (Location, LOCATION, b"location" ); |
716 | |
717 | /// Indicates the max number of intermediaries the request should be sent |
718 | /// through. |
719 | (MaxForwards, MAX_FORWARDS, b"max-forwards" ); |
720 | |
721 | /// Indicates where a fetch originates from. |
722 | /// |
723 | /// It doesn't include any path information, but only the server name. It is |
724 | /// sent with CORS requests, as well as with POST requests. It is similar to |
725 | /// the Referer header, but, unlike this header, it doesn't disclose the |
726 | /// whole path. |
727 | (Origin, ORIGIN, b"origin" ); |
728 | |
729 | /// HTTP/1.0 header usually used for backwards compatibility. |
730 | /// |
731 | /// The Pragma HTTP/1.0 general header is an implementation-specific header |
732 | /// that may have various effects along the request-response chain. It is |
733 | /// used for backwards compatibility with HTTP/1.0 caches where the |
734 | /// Cache-Control HTTP/1.1 header is not yet present. |
735 | (Pragma, PRAGMA, b"pragma" ); |
736 | |
737 | /// Defines the authentication method that should be used to gain access to |
738 | /// a proxy. |
739 | /// |
740 | /// Unlike `www-authenticate`, the `proxy-authenticate` header field applies |
741 | /// only to the next outbound client on the response chain. This is because |
742 | /// only the client that chose a given proxy is likely to have the |
743 | /// credentials necessary for authentication. However, when multiple proxies |
744 | /// are used within the same administrative domain, such as office and |
745 | /// regional caching proxies within a large corporate network, it is common |
746 | /// for credentials to be generated by the user agent and passed through the |
747 | /// hierarchy until consumed. Hence, in such a configuration, it will appear |
748 | /// as if Proxy-Authenticate is being forwarded because each proxy will send |
749 | /// the same challenge set. |
750 | /// |
751 | /// The `proxy-authenticate` header is sent along with a `407 Proxy |
752 | /// Authentication Required`. |
753 | (ProxyAuthenticate, PROXY_AUTHENTICATE, b"proxy-authenticate" ); |
754 | |
755 | /// Contains the credentials to authenticate a user agent to a proxy server. |
756 | /// |
757 | /// This header is usually included after the server has responded with a |
758 | /// 407 Proxy Authentication Required status and the Proxy-Authenticate |
759 | /// header. |
760 | (ProxyAuthorization, PROXY_AUTHORIZATION, b"proxy-authorization" ); |
761 | |
762 | /// Associates a specific cryptographic public key with a certain server. |
763 | /// |
764 | /// This decreases the risk of MITM attacks with forged certificates. If one |
765 | /// or several keys are pinned and none of them are used by the server, the |
766 | /// browser will not accept the response as legitimate, and will not display |
767 | /// it. |
768 | (PublicKeyPins, PUBLIC_KEY_PINS, b"public-key-pins" ); |
769 | |
770 | /// Sends reports of pinning violation to the report-uri specified in the |
771 | /// header. |
772 | /// |
773 | /// Unlike `Public-Key-Pins`, this header still allows browsers to connect |
774 | /// to the server if the pinning is violated. |
775 | (PublicKeyPinsReportOnly, PUBLIC_KEY_PINS_REPORT_ONLY, b"public-key-pins-report-only" ); |
776 | |
777 | /// Indicates the part of a document that the server should return. |
778 | /// |
779 | /// Several parts can be requested with one Range header at once, and the |
780 | /// server may send back these ranges in a multipart document. If the server |
781 | /// sends back ranges, it uses the 206 Partial Content for the response. If |
782 | /// the ranges are invalid, the server returns the 416 Range Not Satisfiable |
783 | /// error. The server can also ignore the Range header and return the whole |
784 | /// document with a 200 status code. |
785 | (Range, RANGE, b"range" ); |
786 | |
787 | /// Contains the address of the previous web page from which a link to the |
788 | /// currently requested page was followed. |
789 | /// |
790 | /// The Referer header allows servers to identify where people are visiting |
791 | /// them from and may use that data for analytics, logging, or optimized |
792 | /// caching, for example. |
793 | (Referer, REFERER, b"referer" ); |
794 | |
795 | /// Governs which referrer information should be included with requests |
796 | /// made. |
797 | (ReferrerPolicy, REFERRER_POLICY, b"referrer-policy" ); |
798 | |
799 | /// Informs the web browser that the current page or frame should be |
800 | /// refreshed. |
801 | (Refresh, REFRESH, b"refresh" ); |
802 | |
803 | /// The Retry-After response HTTP header indicates how long the user agent |
804 | /// should wait before making a follow-up request. There are two main cases |
805 | /// this header is used: |
806 | /// |
807 | /// * When sent with a 503 (Service Unavailable) response, it indicates how |
808 | /// long the service is expected to be unavailable. |
809 | /// |
810 | /// * When sent with a redirect response, such as 301 (Moved Permanently), |
811 | /// it indicates the minimum time that the user agent is asked to wait |
812 | /// before issuing the redirected request. |
813 | (RetryAfter, RETRY_AFTER, b"retry-after" ); |
814 | |
815 | /// The |Sec-WebSocket-Accept| header field is used in the WebSocket |
816 | /// opening handshake. It is sent from the server to the client to |
817 | /// confirm that the server is willing to initiate the WebSocket |
818 | /// connection. |
819 | (SecWebSocketAccept, SEC_WEBSOCKET_ACCEPT, b"sec-websocket-accept" ); |
820 | |
821 | /// The |Sec-WebSocket-Extensions| header field is used in the WebSocket |
822 | /// opening handshake. It is initially sent from the client to the |
823 | /// server, and then subsequently sent from the server to the client, to |
824 | /// agree on a set of protocol-level extensions to use for the duration |
825 | /// of the connection. |
826 | (SecWebSocketExtensions, SEC_WEBSOCKET_EXTENSIONS, b"sec-websocket-extensions" ); |
827 | |
828 | /// The |Sec-WebSocket-Key| header field is used in the WebSocket opening |
829 | /// handshake. It is sent from the client to the server to provide part |
830 | /// of the information used by the server to prove that it received a |
831 | /// valid WebSocket opening handshake. This helps ensure that the server |
832 | /// does not accept connections from non-WebSocket clients (e.g., HTTP |
833 | /// clients) that are being abused to send data to unsuspecting WebSocket |
834 | /// servers. |
835 | (SecWebSocketKey, SEC_WEBSOCKET_KEY, b"sec-websocket-key" ); |
836 | |
837 | /// The |Sec-WebSocket-Protocol| header field is used in the WebSocket |
838 | /// opening handshake. It is sent from the client to the server and back |
839 | /// from the server to the client to confirm the subprotocol of the |
840 | /// connection. This enables scripts to both select a subprotocol and be |
841 | /// sure that the server agreed to serve that subprotocol. |
842 | (SecWebSocketProtocol, SEC_WEBSOCKET_PROTOCOL, b"sec-websocket-protocol" ); |
843 | |
844 | /// The |Sec-WebSocket-Version| header field is used in the WebSocket |
845 | /// opening handshake. It is sent from the client to the server to |
846 | /// indicate the protocol version of the connection. This enables |
847 | /// servers to correctly interpret the opening handshake and subsequent |
848 | /// data being sent from the data, and close the connection if the server |
849 | /// cannot interpret that data in a safe manner. |
850 | (SecWebSocketVersion, SEC_WEBSOCKET_VERSION, b"sec-websocket-version" ); |
851 | |
852 | /// Contains information about the software used by the origin server to |
853 | /// handle the request. |
854 | /// |
855 | /// Overly long and detailed Server values should be avoided as they |
856 | /// potentially reveal internal implementation details that might make it |
857 | /// (slightly) easier for attackers to find and exploit known security |
858 | /// holes. |
859 | (Server, SERVER, b"server" ); |
860 | |
861 | /// Used to send cookies from the server to the user agent. |
862 | (SetCookie, SET_COOKIE, b"set-cookie" ); |
863 | |
864 | /// Tells the client to communicate with HTTPS instead of using HTTP. |
865 | (StrictTransportSecurity, STRICT_TRANSPORT_SECURITY, b"strict-transport-security" ); |
866 | |
867 | /// Informs the server of transfer encodings willing to be accepted as part |
868 | /// of the response. |
869 | /// |
870 | /// See also the Transfer-Encoding response header for more details on |
871 | /// transfer encodings. Note that chunked is always acceptable for HTTP/1.1 |
872 | /// recipients and you that don't have to specify "chunked" using the TE |
873 | /// header. However, it is useful for setting if the client is accepting |
874 | /// trailer fields in a chunked transfer coding using the "trailers" value. |
875 | (Te, TE, b"te" ); |
876 | |
877 | /// Allows the sender to include additional fields at the end of chunked |
878 | /// messages. |
879 | (Trailer, TRAILER, b"trailer" ); |
880 | |
881 | /// Specifies the form of encoding used to safely transfer the entity to the |
882 | /// client. |
883 | /// |
884 | /// `transfer-encoding` is a hop-by-hop header, that is applying to a |
885 | /// message between two nodes, not to a resource itself. Each segment of a |
886 | /// multi-node connection can use different `transfer-encoding` values. If |
887 | /// you want to compress data over the whole connection, use the end-to-end |
888 | /// header `content-encoding` header instead. |
889 | /// |
890 | /// When present on a response to a `HEAD` request that has no body, it |
891 | /// indicates the value that would have applied to the corresponding `GET` |
892 | /// message. |
893 | (TransferEncoding, TRANSFER_ENCODING, b"transfer-encoding" ); |
894 | |
895 | /// Contains a string that allows identifying the requesting client's |
896 | /// software. |
897 | (UserAgent, USER_AGENT, b"user-agent" ); |
898 | |
899 | /// Used as part of the exchange to upgrade the protocol. |
900 | (Upgrade, UPGRADE, b"upgrade" ); |
901 | |
902 | /// Sends a signal to the server expressing the client’s preference for an |
903 | /// encrypted and authenticated response. |
904 | (UpgradeInsecureRequests, UPGRADE_INSECURE_REQUESTS, b"upgrade-insecure-requests" ); |
905 | |
906 | /// Determines how to match future requests with cached responses. |
907 | /// |
908 | /// The `vary` HTTP response header determines how to match future request |
909 | /// headers to decide whether a cached response can be used rather than |
910 | /// requesting a fresh one from the origin server. It is used by the server |
911 | /// to indicate which headers it used when selecting a representation of a |
912 | /// resource in a content negotiation algorithm. |
913 | /// |
914 | /// The `vary` header should be set on a 304 Not Modified response exactly |
915 | /// like it would have been set on an equivalent 200 OK response. |
916 | (Vary, VARY, b"vary" ); |
917 | |
918 | /// Added by proxies to track routing. |
919 | /// |
920 | /// The `via` general header is added by proxies, both forward and reverse |
921 | /// proxies, and can appear in the request headers and the response headers. |
922 | /// It is used for tracking message forwards, avoiding request loops, and |
923 | /// identifying the protocol capabilities of senders along the |
924 | /// request/response chain. |
925 | (Via, VIA, b"via" ); |
926 | |
927 | /// General HTTP header contains information about possible problems with |
928 | /// the status of the message. |
929 | /// |
930 | /// More than one `warning` header may appear in a response. Warning header |
931 | /// fields can in general be applied to any message, however some warn-codes |
932 | /// are specific to caches and can only be applied to response messages. |
933 | (Warning, WARNING, b"warning" ); |
934 | |
935 | /// Defines the authentication method that should be used to gain access to |
936 | /// a resource. |
937 | (WwwAuthenticate, WWW_AUTHENTICATE, b"www-authenticate" ); |
938 | |
939 | /// Marker used by the server to indicate that the MIME types advertised in |
940 | /// the `content-type` headers should not be changed and be followed. |
941 | /// |
942 | /// This allows to opt-out of MIME type sniffing, or, in other words, it is |
943 | /// a way to say that the webmasters knew what they were doing. |
944 | /// |
945 | /// This header was introduced by Microsoft in IE 8 as a way for webmasters |
946 | /// to block content sniffing that was happening and could transform |
947 | /// non-executable MIME types into executable MIME types. Since then, other |
948 | /// browsers have introduced it, even if their MIME sniffing algorithms were |
949 | /// less aggressive. |
950 | /// |
951 | /// Site security testers usually expect this header to be set. |
952 | (XContentTypeOptions, X_CONTENT_TYPE_OPTIONS, b"x-content-type-options" ); |
953 | |
954 | /// Controls DNS prefetching. |
955 | /// |
956 | /// The `x-dns-prefetch-control` HTTP response header controls DNS |
957 | /// prefetching, a feature by which browsers proactively perform domain name |
958 | /// resolution on both links that the user may choose to follow as well as |
959 | /// URLs for items referenced by the document, including images, CSS, |
960 | /// JavaScript, and so forth. |
961 | /// |
962 | /// This prefetching is performed in the background, so that the DNS is |
963 | /// likely to have been resolved by the time the referenced items are |
964 | /// needed. This reduces latency when the user clicks a link. |
965 | (XDnsPrefetchControl, X_DNS_PREFETCH_CONTROL, b"x-dns-prefetch-control" ); |
966 | |
967 | /// Indicates whether or not a browser should be allowed to render a page in |
968 | /// a frame. |
969 | /// |
970 | /// Sites can use this to avoid clickjacking attacks, by ensuring that their |
971 | /// content is not embedded into other sites. |
972 | /// |
973 | /// The added security is only provided if the user accessing the document |
974 | /// is using a browser supporting `x-frame-options`. |
975 | (XFrameOptions, X_FRAME_OPTIONS, b"x-frame-options" ); |
976 | |
977 | /// Stop pages from loading when an XSS attack is detected. |
978 | /// |
979 | /// The HTTP X-XSS-Protection response header is a feature of Internet |
980 | /// Explorer, Chrome and Safari that stops pages from loading when they |
981 | /// detect reflected cross-site scripting (XSS) attacks. Although these |
982 | /// protections are largely unnecessary in modern browsers when sites |
983 | /// implement a strong Content-Security-Policy that disables the use of |
984 | /// inline JavaScript ('unsafe-inline'), they can still provide protections |
985 | /// for users of older web browsers that don't yet support CSP. |
986 | (XXssProtection, X_XSS_PROTECTION, b"x-xss-protection" ); |
987 | } |
988 | |
989 | /// Valid header name characters |
990 | /// |
991 | /// ```not_rust |
992 | /// field-name = token |
993 | /// separators = "(" | ")" | "<" | ">" | "@" |
994 | /// | "," | ";" | ":" | "\" | <"> |
995 | /// | "/" | "[" | "]" | "?" | "=" |
996 | /// | "{" | "}" | SP | HT |
997 | /// token = 1*tchar |
998 | /// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" |
999 | /// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" |
1000 | /// / DIGIT / ALPHA |
1001 | /// ; any VCHAR, except delimiters |
1002 | /// ``` |
1003 | // HEADER_CHARS maps every byte that is 128 or larger to 0 so everything that is |
1004 | // mapped by HEADER_CHARS, maps to a valid single-byte UTF-8 codepoint. |
1005 | const HEADER_CHARS: [u8; 256] = [ |
1006 | // 0 1 2 3 4 5 6 7 8 9 |
1007 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x |
1008 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x |
1009 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x |
1010 | 0, 0, 0, b'!' , b'"' , b'#' , b'$' , b'%' , b'&' , b' \'' , // 3x |
1011 | 0, 0, b'*' , b'+' , 0, b'-' , b'.' , 0, b'0' , b'1' , // 4x |
1012 | b'2' , b'3' , b'4' , b'5' , b'6' , b'7' , b'8' , b'9' , 0, 0, // 5x |
1013 | 0, 0, 0, 0, 0, b'a' , b'b' , b'c' , b'd' , b'e' , // 6x |
1014 | b'f' , b'g' , b'h' , b'i' , b'j' , b'k' , b'l' , b'm' , b'n' , b'o' , // 7x |
1015 | b'p' , b'q' , b'r' , b's' , b't' , b'u' , b'v' , b'w' , b'x' , b'y' , // 8x |
1016 | b'z' , 0, 0, 0, b'^' , b'_' , b'`' , b'a' , b'b' , b'c' , // 9x |
1017 | b'd' , b'e' , b'f' , b'g' , b'h' , b'i' , b'j' , b'k' , b'l' , b'm' , // 10x |
1018 | b'n' , b'o' , b'p' , b'q' , b'r' , b's' , b't' , b'u' , b'v' , b'w' , // 11x |
1019 | b'x' , b'y' , b'z' , 0, b'|' , 0, b'~' , 0, 0, 0, // 12x |
1020 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 13x |
1021 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x |
1022 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15x |
1023 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16x |
1024 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17x |
1025 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18x |
1026 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19x |
1027 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20x |
1028 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21x |
1029 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22x |
1030 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23x |
1031 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24x |
1032 | 0, 0, 0, 0, 0, 0 // 25x |
1033 | ]; |
1034 | |
1035 | /// Valid header name characters for HTTP/2.0 and HTTP/3.0 |
1036 | // HEADER_CHARS_H2 maps every byte that is 128 or larger to 0 so everything that is |
1037 | // mapped by HEADER_CHARS_H2, maps to a valid single-byte UTF-8 codepoint. |
1038 | const HEADER_CHARS_H2: [u8; 256] = [ |
1039 | // 0 1 2 3 4 5 6 7 8 9 |
1040 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x |
1041 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x |
1042 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x |
1043 | 0, 0, 0, b'!' , b'"' , b'#' , b'$' , b'%' , b'&' , b' \'' , // 3x |
1044 | 0, 0, b'*' , b'+' , 0, b'-' , b'.' , 0, b'0' , b'1' , // 4x |
1045 | b'2' , b'3' , b'4' , b'5' , b'6' , b'7' , b'8' , b'9' , 0, 0, // 5x |
1046 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x |
1047 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7x |
1048 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x |
1049 | 0, 0, 0, 0, b'^' , b'_' , b'`' , b'a' , b'b' , b'c' , // 9x |
1050 | b'd' , b'e' , b'f' , b'g' , b'h' , b'i' , b'j' , b'k' , b'l' , b'm' , // 10x |
1051 | b'n' , b'o' , b'p' , b'q' , b'r' , b's' , b't' , b'u' , b'v' , b'w' , // 11x |
1052 | b'x' , b'y' , b'z' , 0, b'|' , 0, b'~' , 0, 0, 0, // 12x |
1053 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 13x |
1054 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x |
1055 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15x |
1056 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16x |
1057 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17x |
1058 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18x |
1059 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19x |
1060 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20x |
1061 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21x |
1062 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22x |
1063 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23x |
1064 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24x |
1065 | 0, 0, 0, 0, 0, 0 // 25x |
1066 | ]; |
1067 | |
1068 | fn parse_hdr<'a>( |
1069 | data: &'a [u8], |
1070 | b: &'a mut [MaybeUninit<u8>; SCRATCH_BUF_SIZE], |
1071 | table: &[u8; 256], |
1072 | ) -> Result<HdrName<'a>, InvalidHeaderName> { |
1073 | match data.len() { |
1074 | 0 => Err(InvalidHeaderName::new()), |
1075 | len @ 1..=SCRATCH_BUF_SIZE => { |
1076 | // Read from data into the buffer - transforming using `table` as we go |
1077 | data.iter() |
1078 | .zip(b.iter_mut()) |
1079 | .for_each(|(index, out)| *out = MaybeUninit::new(table[*index as usize])); |
1080 | // Safety: len bytes of b were just initialized. |
1081 | let name: &'a [u8] = unsafe { slice_assume_init(&b[0..len]) }; |
1082 | match StandardHeader::from_bytes(name) { |
1083 | Some(sh) => Ok(sh.into()), |
1084 | None => { |
1085 | if name.contains(&0) { |
1086 | Err(InvalidHeaderName::new()) |
1087 | } else { |
1088 | Ok(HdrName::custom(name, true)) |
1089 | } |
1090 | } |
1091 | } |
1092 | } |
1093 | SCRATCH_BUF_OVERFLOW..=super::MAX_HEADER_NAME_LEN => Ok(HdrName::custom(data, false)), |
1094 | _ => Err(InvalidHeaderName::new()), |
1095 | } |
1096 | } |
1097 | |
1098 | |
1099 | |
1100 | impl<'a> From<StandardHeader> for HdrName<'a> { |
1101 | fn from(hdr: StandardHeader) -> HdrName<'a> { |
1102 | HdrName { inner: Repr::Standard(hdr) } |
1103 | } |
1104 | } |
1105 | |
1106 | impl HeaderName { |
1107 | /// Converts a slice of bytes to an HTTP header name. |
1108 | /// |
1109 | /// This function normalizes the input. |
1110 | pub fn from_bytes(src: &[u8]) -> Result<HeaderName, InvalidHeaderName> { |
1111 | let mut buf = uninit_u8_array(); |
1112 | // Precondition: HEADER_CHARS is a valid table for parse_hdr(). |
1113 | match parse_hdr(src, &mut buf, &HEADER_CHARS)?.inner { |
1114 | Repr::Standard(std) => Ok(std.into()), |
1115 | Repr::Custom(MaybeLower { buf, lower: true }) => { |
1116 | let buf = Bytes::copy_from_slice(buf); |
1117 | // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. |
1118 | let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; |
1119 | Ok(Custom(val).into()) |
1120 | } |
1121 | Repr::Custom(MaybeLower { buf, lower: false }) => { |
1122 | use bytes::{BufMut}; |
1123 | let mut dst = BytesMut::with_capacity(buf.len()); |
1124 | |
1125 | for b in buf.iter() { |
1126 | // HEADER_CHARS maps all bytes to valid single-byte UTF-8 |
1127 | let b = HEADER_CHARS[*b as usize]; |
1128 | |
1129 | if b == 0 { |
1130 | return Err(InvalidHeaderName::new()); |
1131 | } |
1132 | |
1133 | dst.put_u8(b); |
1134 | } |
1135 | |
1136 | // Safety: the loop above maps all bytes in buf to valid single byte |
1137 | // UTF-8 before copying them into dst. This means that dst (and hence |
1138 | // dst.freeze()) is valid UTF-8. |
1139 | let val = unsafe { ByteStr::from_utf8_unchecked(dst.freeze()) }; |
1140 | |
1141 | Ok(Custom(val).into()) |
1142 | } |
1143 | } |
1144 | } |
1145 | |
1146 | /// Converts a slice of bytes to an HTTP header name. |
1147 | /// |
1148 | /// This function expects the input to only contain lowercase characters. |
1149 | /// This is useful when decoding HTTP/2.0 or HTTP/3.0 headers. Both |
1150 | /// require that all headers be represented in lower case. |
1151 | /// |
1152 | /// # Examples |
1153 | /// |
1154 | /// ``` |
1155 | /// # use http::header::*; |
1156 | /// |
1157 | /// // Parsing a lower case header |
1158 | /// let hdr = HeaderName::from_lowercase(b"content-length" ).unwrap(); |
1159 | /// assert_eq!(CONTENT_LENGTH, hdr); |
1160 | /// |
1161 | /// // Parsing a header that contains uppercase characters |
1162 | /// assert!(HeaderName::from_lowercase(b"Content-Length" ).is_err()); |
1163 | /// ``` |
1164 | pub fn from_lowercase(src: &[u8]) -> Result<HeaderName, InvalidHeaderName> { |
1165 | let mut buf = uninit_u8_array(); |
1166 | // Precondition: HEADER_CHARS_H2 is a valid table for parse_hdr() |
1167 | match parse_hdr(src, &mut buf, &HEADER_CHARS_H2)?.inner { |
1168 | Repr::Standard(std) => Ok(std.into()), |
1169 | Repr::Custom(MaybeLower { buf, lower: true }) => { |
1170 | let buf = Bytes::copy_from_slice(buf); |
1171 | // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. |
1172 | let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; |
1173 | Ok(Custom(val).into()) |
1174 | } |
1175 | Repr::Custom(MaybeLower { buf, lower: false }) => { |
1176 | for &b in buf.iter() { |
1177 | // HEADER_CHARS_H2 maps all bytes that are not valid single-byte |
1178 | // UTF-8 to 0 so this check returns an error for invalid UTF-8. |
1179 | if HEADER_CHARS_H2[b as usize] == 0 { |
1180 | return Err(InvalidHeaderName::new()); |
1181 | } |
1182 | } |
1183 | |
1184 | let buf = Bytes::copy_from_slice(buf); |
1185 | // Safety: the loop above checks that each byte of buf (either |
1186 | // version) is valid UTF-8. |
1187 | let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; |
1188 | Ok(Custom(val).into()) |
1189 | } |
1190 | } |
1191 | } |
1192 | |
1193 | /// Converts a static string to a HTTP header name. |
1194 | /// |
1195 | /// This function requires the static string to only contain lowercase |
1196 | /// characters, numerals and symbols, as per the HTTP/2.0 specification |
1197 | /// and header names internal representation within this library. |
1198 | /// |
1199 | /// # Panics |
1200 | /// |
1201 | /// This function panics when the static string is a invalid header. |
1202 | /// |
1203 | /// Until [Allow panicking in constants](https://github.com/rust-lang/rfcs/pull/2345) |
1204 | /// makes its way into stable, the panic message at compile-time is |
1205 | /// going to look cryptic, but should at least point at your header value: |
1206 | /// |
1207 | /// ```text |
1208 | /// error: any use of this value will cause an error |
1209 | /// --> http/src/header/name.rs:1241:13 |
1210 | /// | |
1211 | /// 1241 | ([] as [u8; 0])[0]; // Invalid header name |
1212 | /// | ^^^^^^^^^^^^^^^^^^ |
1213 | /// | | |
1214 | /// | index out of bounds: the length is 0 but the index is 0 |
1215 | /// | inside `http::HeaderName::from_static` at http/src/header/name.rs:1241:13 |
1216 | /// | inside `INVALID_NAME` at src/main.rs:3:34 |
1217 | /// | |
1218 | /// ::: src/main.rs:3:1 |
1219 | /// | |
1220 | /// 3 | const INVALID_NAME: HeaderName = HeaderName::from_static("Capitalized"); |
1221 | /// | ------------------------------------------------------------------------ |
1222 | /// ``` |
1223 | /// |
1224 | /// # Examples |
1225 | /// |
1226 | /// ``` |
1227 | /// # use http::header::*; |
1228 | /// // Parsing a standard header |
1229 | /// let hdr = HeaderName::from_static("content-length" ); |
1230 | /// assert_eq!(CONTENT_LENGTH, hdr); |
1231 | /// |
1232 | /// // Parsing a custom header |
1233 | /// let CUSTOM_HEADER: &'static str = "custom-header" ; |
1234 | /// |
1235 | /// let a = HeaderName::from_lowercase(b"custom-header" ).unwrap(); |
1236 | /// let b = HeaderName::from_static(CUSTOM_HEADER); |
1237 | /// assert_eq!(a, b); |
1238 | /// ``` |
1239 | /// |
1240 | /// ```should_panic |
1241 | /// # use http::header::*; |
1242 | /// # |
1243 | /// // Parsing a header that contains invalid symbols(s): |
1244 | /// HeaderName::from_static("content{}{}length" ); // This line panics! |
1245 | /// |
1246 | /// // Parsing a header that contains invalid uppercase characters. |
1247 | /// let a = HeaderName::from_static("foobar" ); |
1248 | /// let b = HeaderName::from_static("FOOBAR" ); // This line panics! |
1249 | /// ``` |
1250 | #[allow (unconditional_panic)] // required for the panic circumvention |
1251 | pub const fn from_static(src: &'static str) -> HeaderName { |
1252 | let name_bytes = src.as_bytes(); |
1253 | if let Some(standard) = StandardHeader::from_bytes(name_bytes) { |
1254 | return HeaderName{ |
1255 | inner: Repr::Standard(standard), |
1256 | }; |
1257 | } |
1258 | |
1259 | if name_bytes.len() == 0 || name_bytes.len() > super::MAX_HEADER_NAME_LEN || { |
1260 | let mut i = 0; |
1261 | loop { |
1262 | if i >= name_bytes.len() { |
1263 | break false; |
1264 | } else if HEADER_CHARS_H2[name_bytes[i] as usize] == 0 { |
1265 | break true; |
1266 | } |
1267 | i += 1; |
1268 | } |
1269 | } { |
1270 | ([] as [u8; 0])[0]; // Invalid header name |
1271 | } |
1272 | |
1273 | HeaderName { |
1274 | inner: Repr::Custom(Custom(ByteStr::from_static(src))) |
1275 | } |
1276 | } |
1277 | |
1278 | /// Returns a `str` representation of the header. |
1279 | /// |
1280 | /// The returned string will always be lower case. |
1281 | #[inline ] |
1282 | pub fn as_str(&self) -> &str { |
1283 | match self.inner { |
1284 | Repr::Standard(v) => v.as_str(), |
1285 | Repr::Custom(ref v) => &*v.0, |
1286 | } |
1287 | } |
1288 | |
1289 | pub(super) fn into_bytes(self) -> Bytes { |
1290 | self.inner.into() |
1291 | } |
1292 | } |
1293 | |
1294 | impl FromStr for HeaderName { |
1295 | type Err = InvalidHeaderName; |
1296 | |
1297 | fn from_str(s: &str) -> Result<HeaderName, InvalidHeaderName> { |
1298 | HeaderName::from_bytes(s.as_bytes()).map_err(|_| InvalidHeaderName { _priv: () }) |
1299 | } |
1300 | } |
1301 | |
1302 | impl AsRef<str> for HeaderName { |
1303 | fn as_ref(&self) -> &str { |
1304 | self.as_str() |
1305 | } |
1306 | } |
1307 | |
1308 | impl AsRef<[u8]> for HeaderName { |
1309 | fn as_ref(&self) -> &[u8] { |
1310 | self.as_str().as_bytes() |
1311 | } |
1312 | } |
1313 | |
1314 | impl Borrow<str> for HeaderName { |
1315 | fn borrow(&self) -> &str { |
1316 | self.as_str() |
1317 | } |
1318 | } |
1319 | |
1320 | impl fmt::Debug for HeaderName { |
1321 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
1322 | fmt::Debug::fmt(self.as_str(), f:fmt) |
1323 | } |
1324 | } |
1325 | |
1326 | impl fmt::Display for HeaderName { |
1327 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
1328 | fmt::Display::fmt(self.as_str(), f:fmt) |
1329 | } |
1330 | } |
1331 | |
1332 | impl InvalidHeaderName { |
1333 | pub(super) fn new() -> InvalidHeaderName { |
1334 | InvalidHeaderName { _priv: () } |
1335 | } |
1336 | } |
1337 | |
1338 | impl<'a> From<&'a HeaderName> for HeaderName { |
1339 | fn from(src: &'a HeaderName) -> HeaderName { |
1340 | src.clone() |
1341 | } |
1342 | } |
1343 | |
1344 | #[doc (hidden)] |
1345 | impl<T> From<Repr<T>> for Bytes |
1346 | where |
1347 | T: Into<Bytes>, |
1348 | { |
1349 | fn from(repr: Repr<T>) -> Bytes { |
1350 | match repr { |
1351 | Repr::Standard(header: StandardHeader) => Bytes::from_static(header.as_str().as_bytes()), |
1352 | Repr::Custom(header: T) => header.into(), |
1353 | } |
1354 | } |
1355 | } |
1356 | |
1357 | impl From<Custom> for Bytes { |
1358 | #[inline ] |
1359 | fn from(Custom(inner: ByteStr): Custom) -> Bytes { |
1360 | Bytes::from(inner) |
1361 | } |
1362 | } |
1363 | |
1364 | impl<'a> TryFrom<&'a str> for HeaderName { |
1365 | type Error = InvalidHeaderName; |
1366 | #[inline ] |
1367 | fn try_from(s: &'a str) -> Result<Self, Self::Error> { |
1368 | Self::from_bytes(src:s.as_bytes()) |
1369 | } |
1370 | } |
1371 | |
1372 | impl<'a> TryFrom<&'a String> for HeaderName { |
1373 | type Error = InvalidHeaderName; |
1374 | #[inline ] |
1375 | fn try_from(s: &'a String) -> Result<Self, Self::Error> { |
1376 | Self::from_bytes(src:s.as_bytes()) |
1377 | } |
1378 | } |
1379 | |
1380 | impl<'a> TryFrom<&'a [u8]> for HeaderName { |
1381 | type Error = InvalidHeaderName; |
1382 | #[inline ] |
1383 | fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> { |
1384 | Self::from_bytes(src:s) |
1385 | } |
1386 | } |
1387 | |
1388 | impl TryFrom<String> for HeaderName { |
1389 | type Error = InvalidHeaderName; |
1390 | |
1391 | #[inline ] |
1392 | fn try_from(s: String) -> Result<Self, Self::Error> { |
1393 | Self::from_bytes(src:s.as_bytes()) |
1394 | } |
1395 | } |
1396 | |
1397 | impl TryFrom<Vec<u8>> for HeaderName { |
1398 | type Error = InvalidHeaderName; |
1399 | |
1400 | #[inline ] |
1401 | fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> { |
1402 | Self::from_bytes(&vec) |
1403 | } |
1404 | } |
1405 | |
1406 | #[doc (hidden)] |
1407 | impl From<StandardHeader> for HeaderName { |
1408 | fn from(src: StandardHeader) -> HeaderName { |
1409 | HeaderName { |
1410 | inner: Repr::Standard(src), |
1411 | } |
1412 | } |
1413 | } |
1414 | |
1415 | #[doc (hidden)] |
1416 | impl From<Custom> for HeaderName { |
1417 | fn from(src: Custom) -> HeaderName { |
1418 | HeaderName { |
1419 | inner: Repr::Custom(src), |
1420 | } |
1421 | } |
1422 | } |
1423 | |
1424 | impl<'a> PartialEq<&'a HeaderName> for HeaderName { |
1425 | #[inline ] |
1426 | fn eq(&self, other: &&'a HeaderName) -> bool { |
1427 | *self == **other |
1428 | } |
1429 | } |
1430 | |
1431 | impl<'a> PartialEq<HeaderName> for &'a HeaderName { |
1432 | #[inline ] |
1433 | fn eq(&self, other: &HeaderName) -> bool { |
1434 | *other == *self |
1435 | } |
1436 | } |
1437 | |
1438 | impl PartialEq<str> for HeaderName { |
1439 | /// Performs a case-insensitive comparison of the string against the header |
1440 | /// name |
1441 | /// |
1442 | /// # Examples |
1443 | /// |
1444 | /// ``` |
1445 | /// use http::header::CONTENT_LENGTH; |
1446 | /// |
1447 | /// assert_eq!(CONTENT_LENGTH, "content-length" ); |
1448 | /// assert_eq!(CONTENT_LENGTH, "Content-Length" ); |
1449 | /// assert_ne!(CONTENT_LENGTH, "content length" ); |
1450 | /// ``` |
1451 | #[inline ] |
1452 | fn eq(&self, other: &str) -> bool { |
1453 | eq_ignore_ascii_case(self.as_ref(), s:other.as_bytes()) |
1454 | } |
1455 | } |
1456 | |
1457 | impl PartialEq<HeaderName> for str { |
1458 | /// Performs a case-insensitive comparison of the string against the header |
1459 | /// name |
1460 | /// |
1461 | /// # Examples |
1462 | /// |
1463 | /// ``` |
1464 | /// use http::header::CONTENT_LENGTH; |
1465 | /// |
1466 | /// assert_eq!(CONTENT_LENGTH, "content-length" ); |
1467 | /// assert_eq!(CONTENT_LENGTH, "Content-Length" ); |
1468 | /// assert_ne!(CONTENT_LENGTH, "content length" ); |
1469 | /// ``` |
1470 | #[inline ] |
1471 | fn eq(&self, other: &HeaderName) -> bool { |
1472 | *other == *self |
1473 | } |
1474 | } |
1475 | |
1476 | impl<'a> PartialEq<&'a str> for HeaderName { |
1477 | /// Performs a case-insensitive comparison of the string against the header |
1478 | /// name |
1479 | #[inline ] |
1480 | fn eq(&self, other: &&'a str) -> bool { |
1481 | *self == **other |
1482 | } |
1483 | } |
1484 | |
1485 | impl<'a> PartialEq<HeaderName> for &'a str { |
1486 | /// Performs a case-insensitive comparison of the string against the header |
1487 | /// name |
1488 | #[inline ] |
1489 | fn eq(&self, other: &HeaderName) -> bool { |
1490 | *other == *self |
1491 | } |
1492 | } |
1493 | |
1494 | impl fmt::Debug for InvalidHeaderName { |
1495 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1496 | fDebugStruct<'_, '_>.debug_struct(name:"InvalidHeaderName" ) |
1497 | // skip _priv noise |
1498 | .finish() |
1499 | } |
1500 | } |
1501 | |
1502 | impl fmt::Display for InvalidHeaderName { |
1503 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1504 | f.write_str(data:"invalid HTTP header name" ) |
1505 | } |
1506 | } |
1507 | |
1508 | impl Error for InvalidHeaderName {} |
1509 | |
1510 | // ===== HdrName ===== |
1511 | |
1512 | impl<'a> HdrName<'a> { |
1513 | // Precondition: if lower then buf is valid UTF-8 |
1514 | fn custom(buf: &'a [u8], lower: bool) -> HdrName<'a> { |
1515 | HdrName { |
1516 | // Invariant (on MaybeLower): follows from the precondition |
1517 | inner: Repr::Custom(MaybeLower { |
1518 | buf: buf, |
1519 | lower: lower, |
1520 | }), |
1521 | } |
1522 | } |
1523 | |
1524 | pub fn from_bytes<F, U>(hdr: &[u8], f: F) -> Result<U, InvalidHeaderName> |
1525 | where F: FnOnce(HdrName<'_>) -> U, |
1526 | { |
1527 | let mut buf = uninit_u8_array(); |
1528 | // Precondition: HEADER_CHARS is a valid table for parse_hdr(). |
1529 | let hdr = parse_hdr(hdr, &mut buf, &HEADER_CHARS)?; |
1530 | Ok(f(hdr)) |
1531 | } |
1532 | |
1533 | pub fn from_static<F, U>(hdr: &'static str, f: F) -> U |
1534 | where |
1535 | F: FnOnce(HdrName<'_>) -> U, |
1536 | { |
1537 | let mut buf = uninit_u8_array(); |
1538 | let hdr = |
1539 | // Precondition: HEADER_CHARS is a valid table for parse_hdr(). |
1540 | parse_hdr(hdr.as_bytes(), &mut buf, &HEADER_CHARS).expect("static str is invalid name" ); |
1541 | f(hdr) |
1542 | } |
1543 | } |
1544 | |
1545 | #[doc (hidden)] |
1546 | impl<'a> From<HdrName<'a>> for HeaderName { |
1547 | fn from(src: HdrName<'a>) -> HeaderName { |
1548 | match src.inner { |
1549 | Repr::Standard(s) => HeaderName { |
1550 | inner: Repr::Standard(s), |
1551 | }, |
1552 | Repr::Custom(maybe_lower) => { |
1553 | if maybe_lower.lower { |
1554 | let buf = Bytes::copy_from_slice(&maybe_lower.buf[..]); |
1555 | // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. |
1556 | let byte_str = unsafe { ByteStr::from_utf8_unchecked(buf) }; |
1557 | |
1558 | HeaderName { |
1559 | inner: Repr::Custom(Custom(byte_str)), |
1560 | } |
1561 | } else { |
1562 | use bytes::BufMut; |
1563 | let mut dst = BytesMut::with_capacity(maybe_lower.buf.len()); |
1564 | |
1565 | for b in maybe_lower.buf.iter() { |
1566 | // HEADER_CHARS maps each byte to a valid single-byte UTF-8 |
1567 | // codepoint. |
1568 | dst.put_u8(HEADER_CHARS[*b as usize]); |
1569 | } |
1570 | |
1571 | // Safety: the loop above maps each byte of maybe_lower.buf to a |
1572 | // valid single-byte UTF-8 codepoint before copying it into dst. |
1573 | // dst (and hence dst.freeze()) is thus valid UTF-8. |
1574 | let buf = unsafe { ByteStr::from_utf8_unchecked(dst.freeze()) }; |
1575 | |
1576 | HeaderName { |
1577 | inner: Repr::Custom(Custom(buf)), |
1578 | } |
1579 | } |
1580 | } |
1581 | } |
1582 | } |
1583 | } |
1584 | |
1585 | #[doc (hidden)] |
1586 | impl<'a> PartialEq<HdrName<'a>> for HeaderName { |
1587 | #[inline ] |
1588 | fn eq(&self, other: &HdrName<'a>) -> bool { |
1589 | match self.inner { |
1590 | Repr::Standard(a: StandardHeader) => match other.inner { |
1591 | Repr::Standard(b: StandardHeader) => a == b, |
1592 | _ => false, |
1593 | }, |
1594 | Repr::Custom(Custom(ref a: &ByteStr)) => match other.inner { |
1595 | Repr::Custom(ref b: &MaybeLower<'_>) => { |
1596 | if b.lower { |
1597 | a.as_bytes() == b.buf |
1598 | } else { |
1599 | eq_ignore_ascii_case(lower:a.as_bytes(), s:b.buf) |
1600 | } |
1601 | } |
1602 | _ => false, |
1603 | }, |
1604 | } |
1605 | } |
1606 | } |
1607 | |
1608 | // ===== Custom ===== |
1609 | |
1610 | impl Hash for Custom { |
1611 | #[inline ] |
1612 | fn hash<H: Hasher>(&self, hasher: &mut H) { |
1613 | hasher.write(self.0.as_bytes()) |
1614 | } |
1615 | } |
1616 | |
1617 | // ===== MaybeLower ===== |
1618 | |
1619 | impl<'a> Hash for MaybeLower<'a> { |
1620 | #[inline ] |
1621 | fn hash<H: Hasher>(&self, hasher: &mut H) { |
1622 | if self.lower { |
1623 | hasher.write(self.buf); |
1624 | } else { |
1625 | for &b: u8 in self.buf { |
1626 | hasher.write(&[HEADER_CHARS[b as usize]]); |
1627 | } |
1628 | } |
1629 | } |
1630 | } |
1631 | |
1632 | // Assumes that the left hand side is already lower case |
1633 | #[inline ] |
1634 | fn eq_ignore_ascii_case(lower: &[u8], s: &[u8]) -> bool { |
1635 | if lower.len() != s.len() { |
1636 | return false; |
1637 | } |
1638 | |
1639 | lower.iter().zip(s).all(|(a: &u8, b: &u8)| { |
1640 | *a == HEADER_CHARS[*b as usize] |
1641 | }) |
1642 | } |
1643 | |
1644 | // Utility functions for MaybeUninit<>. These are drawn from unstable API's on |
1645 | // MaybeUninit<> itself. |
1646 | const SCRATCH_BUF_SIZE: usize = 64; |
1647 | const SCRATCH_BUF_OVERFLOW: usize = SCRATCH_BUF_SIZE + 1; |
1648 | |
1649 | fn uninit_u8_array() -> [MaybeUninit<u8>; SCRATCH_BUF_SIZE] { |
1650 | let arr: MaybeUninit<[MaybeUninit<…>; 64]> = MaybeUninit::<[MaybeUninit<u8>; SCRATCH_BUF_SIZE]>::uninit(); |
1651 | // Safety: assume_init() is claiming that an array of MaybeUninit<> |
1652 | // has been initilized, but MaybeUninit<>'s do not require initilizaton. |
1653 | unsafe { arr.assume_init() } |
1654 | } |
1655 | |
1656 | // Assuming all the elements are initilized, get a slice of them. |
1657 | // |
1658 | // Safety: All elements of `slice` must be initilized to prevent |
1659 | // undefined behavior. |
1660 | unsafe fn slice_assume_init<T>(slice: &[MaybeUninit<T>]) -> &[T] { |
1661 | &*(slice as *const [MaybeUninit<T>] as *const [T]) |
1662 | } |
1663 | |
1664 | #[cfg (test)] |
1665 | mod tests { |
1666 | use super::*; |
1667 | use self::StandardHeader::Vary; |
1668 | |
1669 | #[test ] |
1670 | fn test_bounds() { |
1671 | fn check_bounds<T: Sync + Send>() {} |
1672 | check_bounds::<HeaderName>(); |
1673 | } |
1674 | |
1675 | #[test ] |
1676 | fn test_parse_invalid_headers() { |
1677 | for i in 0..128 { |
1678 | let hdr = vec![1u8; i]; |
1679 | assert!(HeaderName::from_bytes(&hdr).is_err(), " {} invalid header chars did not fail" , i); |
1680 | } |
1681 | } |
1682 | |
1683 | const ONE_TOO_LONG: &[u8] = &[b'a' ; super::super::MAX_HEADER_NAME_LEN+1]; |
1684 | |
1685 | #[test ] |
1686 | fn test_invalid_name_lengths() { |
1687 | assert!( |
1688 | HeaderName::from_bytes(&[]).is_err(), |
1689 | "zero-length header name is an error" , |
1690 | ); |
1691 | |
1692 | let long = &ONE_TOO_LONG[0..super::super::MAX_HEADER_NAME_LEN]; |
1693 | |
1694 | let long_str = std::str::from_utf8(long).unwrap(); |
1695 | assert_eq!(HeaderName::from_static(long_str), long_str); // shouldn't panic! |
1696 | |
1697 | assert!( |
1698 | HeaderName::from_bytes(long).is_ok(), |
1699 | "max header name length is ok" , |
1700 | ); |
1701 | assert!( |
1702 | HeaderName::from_bytes(ONE_TOO_LONG).is_err(), |
1703 | "longer than max header name length is an error" , |
1704 | ); |
1705 | } |
1706 | |
1707 | #[test ] |
1708 | #[should_panic ] |
1709 | fn test_static_invalid_name_lengths() { |
1710 | // Safety: ONE_TOO_LONG contains only the UTF-8 safe, single-byte codepoint b'a'. |
1711 | let _ = HeaderName::from_static(unsafe { std::str::from_utf8_unchecked(ONE_TOO_LONG) }); |
1712 | } |
1713 | |
1714 | #[test ] |
1715 | fn test_from_hdr_name() { |
1716 | use self::StandardHeader::Vary; |
1717 | |
1718 | let name = HeaderName::from(HdrName { |
1719 | inner: Repr::Standard(Vary), |
1720 | }); |
1721 | |
1722 | assert_eq!(name.inner, Repr::Standard(Vary)); |
1723 | |
1724 | let name = HeaderName::from(HdrName { |
1725 | inner: Repr::Custom(MaybeLower { |
1726 | buf: b"hello-world" , |
1727 | lower: true, |
1728 | }), |
1729 | }); |
1730 | |
1731 | assert_eq!(name.inner, Repr::Custom(Custom(ByteStr::from_static("hello-world" )))); |
1732 | |
1733 | let name = HeaderName::from(HdrName { |
1734 | inner: Repr::Custom(MaybeLower { |
1735 | buf: b"Hello-World" , |
1736 | lower: false, |
1737 | }), |
1738 | }); |
1739 | |
1740 | assert_eq!(name.inner, Repr::Custom(Custom(ByteStr::from_static("hello-world" )))); |
1741 | } |
1742 | |
1743 | #[test ] |
1744 | fn test_eq_hdr_name() { |
1745 | use self::StandardHeader::Vary; |
1746 | |
1747 | let a = HeaderName { inner: Repr::Standard(Vary) }; |
1748 | let b = HdrName { inner: Repr::Standard(Vary) }; |
1749 | |
1750 | assert_eq!(a, b); |
1751 | |
1752 | let a = HeaderName { inner: Repr::Custom(Custom(ByteStr::from_static("vaary" ))) }; |
1753 | assert_ne!(a, b); |
1754 | |
1755 | let b = HdrName { inner: Repr::Custom(MaybeLower { |
1756 | buf: b"vaary" , |
1757 | lower: true, |
1758 | })}; |
1759 | |
1760 | assert_eq!(a, b); |
1761 | |
1762 | let b = HdrName { inner: Repr::Custom(MaybeLower { |
1763 | buf: b"vaary" , |
1764 | lower: false, |
1765 | })}; |
1766 | |
1767 | assert_eq!(a, b); |
1768 | |
1769 | let b = HdrName { inner: Repr::Custom(MaybeLower { |
1770 | buf: b"VAARY" , |
1771 | lower: false, |
1772 | })}; |
1773 | |
1774 | assert_eq!(a, b); |
1775 | |
1776 | let a = HeaderName { inner: Repr::Standard(Vary) }; |
1777 | assert_ne!(a, b); |
1778 | } |
1779 | |
1780 | #[test ] |
1781 | fn test_from_static_std() { |
1782 | let a = HeaderName { inner: Repr::Standard(Vary) }; |
1783 | |
1784 | let b = HeaderName::from_static("vary" ); |
1785 | assert_eq!(a, b); |
1786 | |
1787 | let b = HeaderName::from_static("vaary" ); |
1788 | assert_ne!(a, b); |
1789 | } |
1790 | |
1791 | #[test ] |
1792 | #[should_panic ] |
1793 | fn test_from_static_std_uppercase() { |
1794 | HeaderName::from_static("Vary" ); |
1795 | } |
1796 | |
1797 | #[test ] |
1798 | #[should_panic ] |
1799 | fn test_from_static_std_symbol() { |
1800 | HeaderName::from_static("vary{}" ); |
1801 | } |
1802 | |
1803 | // MaybeLower { lower: true } |
1804 | #[test ] |
1805 | fn test_from_static_custom_short() { |
1806 | let a = HeaderName { inner: Repr::Custom(Custom(ByteStr::from_static("customheader" ))) }; |
1807 | let b = HeaderName::from_static("customheader" ); |
1808 | assert_eq!(a, b); |
1809 | } |
1810 | |
1811 | #[test ] |
1812 | #[should_panic ] |
1813 | fn test_from_static_custom_short_uppercase() { |
1814 | HeaderName::from_static("custom header" ); |
1815 | } |
1816 | |
1817 | #[test ] |
1818 | #[should_panic ] |
1819 | fn test_from_static_custom_short_symbol() { |
1820 | HeaderName::from_static("CustomHeader" ); |
1821 | } |
1822 | |
1823 | // MaybeLower { lower: false } |
1824 | #[test ] |
1825 | fn test_from_static_custom_long() { |
1826 | let a = HeaderName { inner: Repr::Custom(Custom(ByteStr::from_static( |
1827 | "longer-than-63--thisheaderislongerthansixtythreecharactersandthushandleddifferent" |
1828 | ))) }; |
1829 | let b = HeaderName::from_static( |
1830 | "longer-than-63--thisheaderislongerthansixtythreecharactersandthushandleddifferent" |
1831 | ); |
1832 | assert_eq!(a, b); |
1833 | } |
1834 | |
1835 | #[test ] |
1836 | #[should_panic ] |
1837 | fn test_from_static_custom_long_uppercase() { |
1838 | HeaderName::from_static( |
1839 | "Longer-Than-63--ThisHeaderIsLongerThanSixtyThreeCharactersAndThusHandledDifferent" |
1840 | ); |
1841 | } |
1842 | |
1843 | #[test ] |
1844 | #[should_panic ] |
1845 | fn test_from_static_custom_long_symbol() { |
1846 | HeaderName::from_static( |
1847 | "longer-than-63--thisheader{}{}{}{}islongerthansixtythreecharactersandthushandleddifferent" |
1848 | ); |
1849 | } |
1850 | |
1851 | #[test ] |
1852 | fn test_from_static_custom_single_char() { |
1853 | let a = HeaderName { inner: Repr::Custom(Custom(ByteStr::from_static("a" ))) }; |
1854 | let b = HeaderName::from_static("a" ); |
1855 | assert_eq!(a, b); |
1856 | } |
1857 | |
1858 | #[test ] |
1859 | #[should_panic ] |
1860 | fn test_from_static_empty() { |
1861 | HeaderName::from_static("" ); |
1862 | } |
1863 | |
1864 | #[test ] |
1865 | fn test_all_tokens() { |
1866 | HeaderName::from_static("!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyz" ); |
1867 | } |
1868 | |
1869 | #[test ] |
1870 | fn test_from_lowercase() { |
1871 | HeaderName::from_lowercase(&[0; 10]).unwrap_err(); |
1872 | HeaderName::from_lowercase(&[b'A' ; 10]).unwrap_err(); |
1873 | HeaderName::from_lowercase(&[0x1; 10]).unwrap_err(); |
1874 | HeaderName::from_lowercase(&[0xFF; 10]).unwrap_err(); |
1875 | //HeaderName::from_lowercase(&[0; 100]).unwrap_err(); |
1876 | HeaderName::from_lowercase(&[b'A' ; 100]).unwrap_err(); |
1877 | HeaderName::from_lowercase(&[0x1; 100]).unwrap_err(); |
1878 | HeaderName::from_lowercase(&[0xFF; 100]).unwrap_err(); |
1879 | } |
1880 | } |
1881 | |