RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.
This commit is contained in:
1
zeroidc/vendor/http/.cargo-checksum.json
vendored
Normal file
1
zeroidc/vendor/http/.cargo-checksum.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"files":{"CHANGELOG.md":"9da8137ba524bbfc6ad9caed8b2f1919b7ff3477b9fdffa65f6d6da499e13528","Cargo.toml":"9b964a1a24b7f8e43b0f183b12577299db8be2621026dfc1926a8ff2b8ea020d","LICENSE-APACHE":"8bb1b50b0e5c9399ae33bd35fab2769010fa6c14e8860c729a52295d84896b7a","LICENSE-MIT":"dc91f8200e4b2a1f9261035d4c18c33c246911a6c0f7b543d75347e61b249cff","README.md":"2b08369b8ce261843a84103642fd4d8b1ab556af6d6397dbc78c19f7025d255a","benches/header_map/basic.rs":"7aa1f667be9b7fd2aab249a6ca6cfa2f06229c79d3406a8354d07bf9ccf09fd8","benches/header_map/mod.rs":"c1a4c7291b94d2477c1aa6721a5cfbd3dc63242baf14b836138273830cc67990","benches/header_map/vec_map.rs":"48f4eb7a90b6f0844a445924cf24c80acfaf76f909acca4492bfeb9b1ea9e4a4","benches/header_name.rs":"0c6ea9862807f7973b9e6577ef8959b3f41da04837cde326faa73d028caeb420","benches/header_name2.rs":"b19a4e9ca8ffe19e415bd2b498055c89da3ab773ea04b973747fc8517cfe7d2b","benches/header_value.rs":"7aa588f2155c513b7aebfe0b1cc7f7c6b0a699fafd817fe2fee8e46fb131bd76","benches/method.rs":"1913b8c95ecf542e9e50e20ff645f4024036e9b89f04a83e06303bf423053508","benches/uri.rs":"fbc88c25bc27d39162c674c162d1e40c8139452fbfb891a7caebb3c225baa1ae","src/byte_str.rs":"4767ad7bb6e5cda64a63e00ec549c1318e9305acb64d22d5cfbe54b8540b425b","src/convert.rs":"a31a4351cd3ee36a58ff4f5b30ce2c8967cde8486faea2d2673a8f8cb74b3204","src/error.rs":"8163a0b4f659a0f0070559568a7b553a5370553a4570bb496811ca3b978046bf","src/extensions.rs":"5f85c3e1eef53d0fcbd4a24a6c13828790dac74ad60f71cad365e14d39b196a6","src/header/map.rs":"353e7c213933945119d774a655dd822a3afc05b08c832707691910a647c1231c","src/header/mod.rs":"b91ee39f0f76aba6a2ca4e818126d0185272c150c29a43dfbec60891d213c45a","src/header/name.rs":"ce12e22ee9812ee3ffb07b45d8b02aa64ee6aa4c32dc434c76e1b4e02d81b8b8","src/header/value.rs":"ffea8236f38178fa3dd600b893d1eb8b698e3a052aaad2dbdda4a14e1b3c7108","src/lib.rs":"ebd9065bb9f2a3cef9c70a9c43619e2c767c00a33c145997fdbd1a731ba8bbfe","src/method.rs":"1de55021d29174c462ed60042d0d263276af131a25c4d942c91afb09622644f8","src/request.rs":"8c76af67f41c62774c298016ab562a1ab0e8f6d9ab5617e0294a10c0200e5f74","src/response.rs":"5a17fafd73e6d51f2fe43af26143aa2390384c09045d8b634b68515118665bdb","src/status.rs":"fd9d1c1670bde5f94934ff2a9fa9c7f2db5bbe32a750e4e202bf2775b5c5cac3","src/uri/authority.rs":"4df1371b3bd751dd8b5c4b88a4dc67eac3c691748992223e78d7eb859832ebe0","src/uri/builder.rs":"875506b3a603a6e35557548ed0cf3beb7de0a4d1c898316e7293f3bc2ffb05c5","src/uri/mod.rs":"a70ef96edd8b7920b404a88a5d7bf09b382bf24b93e5d28dfc82b83bd77e2ce7","src/uri/path.rs":"72a300e800a85dd9901a18dae3d3f0beacb783472d66a70615486194f43cd118","src/uri/port.rs":"a30793678abc96e833d026d96f060244183ab631e19eafbbad8e4643c7bb9d86","src/uri/scheme.rs":"59e6f12d3e1e1ee982e68a4a6556f25e94073ca3d77c372b6d8d71daf8f62f2a","src/uri/tests.rs":"61f88b73490c2442ec12cb0829aa1ddd28f1bce874b4fc6dd7a544c80280aeb1","src/version.rs":"623ef60a450203b051f3457e2f095508b66aaaa799b1447fb1b34d92cb2e7d62","tests/header_map.rs":"b81993a9042c21fd64114fb099b1666ea4685246f4f1c76bac72b825b6a3741b","tests/header_map_fuzz.rs":"a71387a8e1a3906f713a00cde443790cd7a86b7e37cafa2f9ed4d432cbf97026","tests/status_code.rs":"4c1bd08baffa6265aad5e837b189c269a3bef9031984b37980c24a8c671ac22c"},"package":"75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"}
|
||||
199
zeroidc/vendor/http/CHANGELOG.md
vendored
Normal file
199
zeroidc/vendor/http/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
# 0.2.8 (June 6, 2022)
|
||||
|
||||
* Fix internal usage of uninitialized memory to use `MaybeUninit` inside `HeaderName`.
|
||||
|
||||
# 0.2.7 (April 28, 2022)
|
||||
|
||||
* MSRV bumped to `1.49`.
|
||||
* Add `extend()` method to `Extensions`.
|
||||
* Add `From<Authority>` and `From<PathAndQuery>` impls for `Uri`.
|
||||
* Make `HeaderName::from_static` a `const fn`.
|
||||
|
||||
# 0.2.6 (December 30, 2021)
|
||||
|
||||
* Upgrade internal `itoa` dependency to 1.0.
|
||||
|
||||
# 0.2.5 (September 21, 2021)
|
||||
|
||||
* Add `is_empty()` and `len()` methods to `Extensions`.
|
||||
* Add `version_ref()` method to `request::Builder`.
|
||||
* Implement `TryFrom<Vec<u8>>` and `TryFrom<String>` for `Authority`, `Uri`, `PathAndQuery`, and `HeaderName`.
|
||||
* Make `HeaderValue::from_static` a `const fn`.
|
||||
|
||||
# 0.2.4 (April 4, 2021)
|
||||
|
||||
* Fix `Uri` parsing to allow `{`, `"`, and `}` in paths.
|
||||
|
||||
# 0.2.3 (January 7, 2021)
|
||||
|
||||
* Upgrade internal (private) `bytes` dependency to 1.0.
|
||||
|
||||
# 0.2.2 (December 14, 2020)
|
||||
|
||||
* Fix (potential double) panic of (`HeaderMap`) `OccupiedEntry::remove_entry` and
|
||||
`remove_entry_mult` when multiple values are present. ([#446], [#449] dekellum)
|
||||
* Safety audits of (priv) `ByteStr` and refactor of `Authority` ([#408], [#414] sbosnick)
|
||||
* Fix `HeaderName` to error instead of panic when input is too long ([#432] [#433] acfoltzer)
|
||||
* Allow `StatusCode` to encode values 100-999 without error. Use of the
|
||||
unclassified range 600-999 remains discouraged. ([#144], [#438], [#443] quininer dekellum)
|
||||
* Add `String` and `&String` fallible conversions to `PathAndQuery` ([#450] mkindahl)
|
||||
* Fix `Authority` (and `Uri`) to error instead of panic on unbalanced brackets
|
||||
([#435], [#445] aeryz)
|
||||
|
||||
# 0.2.1 (March 25, 2020)
|
||||
|
||||
* Add `extensions_ref` and `extensions_mut` to `request::Builder` and `response::Builder`.
|
||||
|
||||
# 0.2.0 (December 2, 2019)
|
||||
|
||||
* Add `Version::HTTP_3` constant.
|
||||
* Add `HeaderValue::from_maybe_shared`, `HeaderValue::from_maybe_shared_unchecked`, `Uri::from_maybe_shared`, `Authority::from_maybe_shared`, and `PathAndQuery::from_maybe_shared`.
|
||||
* Change `request::Builder`, `response::Builder`, and `uri::Builder` to use by-value methods instead of by-ref.
|
||||
* Change from `HttpTryFrom` trait to `std::convert::TryFrom`.
|
||||
* Change `HeaderMap::entry` to no longer return a `Result`.
|
||||
* Change `HeaderMap::drain` iterator to match the behavior of `IntoIter`.
|
||||
* Change `Authority::port` to return an `Option<Port>` instead of `Option<u16>`.
|
||||
* Change `Uri::scheme` to return `Option<&Scheme>` instead of `Option<&str>`.
|
||||
* Change `Uri::authority` to return `Option<&Authority>` instead of `Option<&str>`.
|
||||
* Remove `InvalidUriBytes`, `InvalidHeaderNameBytes`, and `InvalidHeaderValueBytes` error types.
|
||||
* Remove `HeaderValue::from_shared`, `HeaderValue::from_shared_unchecked`, `Uri::from_shared`, `Authority::from_shared`, `Scheme::from_shared`, and `PathAndQuery::from_shared`.
|
||||
* Remove `Authority::port_part`.
|
||||
* Remove `Uri::scheme_part` and `Uri::authority_part`.
|
||||
|
||||
# 0.1.20 (November 26, 2019)
|
||||
|
||||
* Fix possible double-free if `header::Drain` iterator is `std::mem::forgot`en (#357).
|
||||
* Fix possible data race if multiple `header::ValueDrain`s are iterated on different threads (#362).
|
||||
* Fix `HeaderMap::reserve` capacity overflows (#360).
|
||||
* Fix parsing long authority-form `Uri`s (#351).
|
||||
|
||||
# 0.1.19 (October 15, 2019)
|
||||
|
||||
* Allow `%` in IPv6 addresses in `Uri` (#343).
|
||||
|
||||
# 0.1.18 (July 26, 2019)
|
||||
|
||||
* Fix compilation of `HeaderName` parsing on WASM targets (#324).
|
||||
* Implement `HttpTryFrom<HashMap>` for `HeaderMap` (#326).
|
||||
* Export `http::header::HeaderValue` as `http::HeaderValue`.
|
||||
|
||||
# 0.1.17 (April 5, 2019)
|
||||
|
||||
* Add `Error::inner_ref()` to view the kind of error (#303)
|
||||
* Add `headers_ref()` and `headers_mut()` methods to `request::Builder` and `response::Builder` (#293)
|
||||
|
||||
# 0.1.16 (February 19, 2019)
|
||||
|
||||
* Fix `Uri` to permit more characters in the `path` (#296)
|
||||
|
||||
# 0.1.15 (January 22, 2019)
|
||||
|
||||
* Fix `Uri::host()` to include brackets of IPv6 literals (#292)
|
||||
* Add `scheme_str` and `port_u16` methods to `Uri` (#287)
|
||||
* Add `method_ref`, `uri_ref`, and `headers_ref` to `request::Builder` (#284)
|
||||
|
||||
# 0.1.14 (November 21, 2018)
|
||||
|
||||
* Add `Port` struct (#252, #255, #265)
|
||||
* Introduce `Uri` builder (#219)
|
||||
* Empty `Method` no longer considered valid (#262)
|
||||
* Fix `Uri` equality when terminating question mark is present (#270)
|
||||
* Allow % character in userinfo (#269)
|
||||
* Support additional tokens for header names (#271)
|
||||
* Export `http::headers::{IterMut, ValuesMut}` (#278)
|
||||
|
||||
# 0.1.13 (September 14, 2018)
|
||||
|
||||
* impl `fmt::Display` for `HeaderName` (#249)
|
||||
* Fix `uri::Authority` parsing when there is no host after an `@` (#248)
|
||||
* Fix `Uri` parsing to allow more characters in query strings (#247)
|
||||
|
||||
# 0.1.12 (September 7, 2018)
|
||||
|
||||
* Fix `HeaderValue` parsing to allow HTABs (#244)
|
||||
|
||||
# 0.1.11 (September 5, 2018)
|
||||
|
||||
* Add `From<&Self>` for `HeaderValue`, `Method`, and `StatusCode` (#238)
|
||||
* Add `Uri::from_static` (#240)
|
||||
|
||||
# 0.1.10 (August 8, 2018)
|
||||
|
||||
* `impl HttpTryFrom<String>` for HeaderValue (#236)
|
||||
|
||||
# 0.1.9 (August 7, 2018)
|
||||
|
||||
* Fix double percent encoding (#233)
|
||||
* Add additional HttpTryFrom impls (#234)
|
||||
|
||||
# 0.1.8 (July 23, 2018)
|
||||
|
||||
* Add fuller set of `PartialEq` for `Method` (#221)
|
||||
* Reduce size of `HeaderMap` by using `Box<[Entry]>` instea of `Vec` (#224)
|
||||
* Reduce size of `Extensions` by storing as `Option<Box<AnyMap>>` (#227)
|
||||
* Implement `Iterator::size_hint` for most iterators in `header` (#226)
|
||||
|
||||
# 0.1.7 (June 22, 2018)
|
||||
|
||||
* Add `From<uN> for HeaderValue` for most integer types (#218).
|
||||
* Add `Uri::into_parts()` inherent method (same as `Parts::from(uri)`) (#214).
|
||||
* Fix converting `Uri`s in authority-form to `Parts` and then back into `Uri` (#216).
|
||||
* Fix `Authority` parsing to reject multiple port sections (#215).
|
||||
* Fix parsing 1 character authority-form `Uri`s into illegal forms (#220).
|
||||
|
||||
# 0.1.6 (June 13, 2018)
|
||||
|
||||
* Add `HeaderName::from_static()` constructor (#195).
|
||||
* Add `Authority::from_static()` constructor (#186).
|
||||
* Implement `From<HeaderName>` for `HeaderValue` (#184).
|
||||
* Fix duplicate keys when iterating over `header::Keys` (#201).
|
||||
|
||||
# 0.1.5 (February 28, 2018)
|
||||
|
||||
* Add websocket handshake related header constants (#162).
|
||||
* Parsing `Authority` with an empty string now returns an error (#164).
|
||||
* Implement `PartialEq<u16>` for `StatusCode` (#153).
|
||||
* Implement `HttpTryFrom<&Uri>` for `Uri` (#165).
|
||||
* Implement `FromStr` for `Method` (#167).
|
||||
* Implement `HttpTryFrom<String>` for `Uri` (#171).
|
||||
* Add `into_body` fns to `Request` and `Response` (#172).
|
||||
* Fix `Request::options` (#177).
|
||||
|
||||
# 0.1.4 (January 4, 2018)
|
||||
|
||||
* Add PathAndQuery::from_static (#148).
|
||||
* Impl PartialOrd / PartialEq for Authority and PathAndQuery (#150).
|
||||
* Add `map` fn to `Request` and `Response` (#151).
|
||||
|
||||
# 0.1.3 (December 11, 2017)
|
||||
|
||||
* Add `Scheme` associated consts for common protos.
|
||||
|
||||
# 0.1.2 (November 29, 2017)
|
||||
|
||||
* Add Uri accessor for scheme part.
|
||||
* Fix Uri parsing bug (#134)
|
||||
|
||||
# 0.1.1 (October 9, 2017)
|
||||
|
||||
* Provide Uri accessors for parts (#129)
|
||||
* Add Request builder helpers. (#123)
|
||||
* Misc performance improvements (#126)
|
||||
|
||||
# 0.1.0 (September 8, 2017)
|
||||
|
||||
* Initial release.
|
||||
|
||||
[#144]: https://github.com/hyperium/http/issues/144
|
||||
[#408]: https://github.com/hyperium/http/pull/408
|
||||
[#414]: https://github.com/hyperium/http/pull/414
|
||||
[#432]: https://github.com/hyperium/http/issues/432
|
||||
[#433]: https://github.com/hyperium/http/pull/433
|
||||
[#438]: https://github.com/hyperium/http/pull/438
|
||||
[#443]: https://github.com/hyperium/http/pull/443
|
||||
[#446]: https://github.com/hyperium/http/issues/446
|
||||
[#449]: https://github.com/hyperium/http/pull/449
|
||||
[#450]: https://github.com/hyperium/http/pull/450
|
||||
[#435]: https://github.com/hyperium/http/issues/435
|
||||
[#445]: https://github.com/hyperium/http/pull/445
|
||||
|
||||
88
zeroidc/vendor/http/Cargo.toml
vendored
Normal file
88
zeroidc/vendor/http/Cargo.toml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
rust-version = "1.49.0"
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
authors = [
|
||||
"Alex Crichton <alex@alexcrichton.com>",
|
||||
"Carl Lerche <me@carllerche.com>",
|
||||
"Sean McArthur <sean@seanmonstar.com>",
|
||||
]
|
||||
description = """
|
||||
A set of types for representing HTTP requests and responses.
|
||||
"""
|
||||
documentation = "https://docs.rs/http"
|
||||
readme = "README.md"
|
||||
keywords = ["http"]
|
||||
categories = ["web-programming"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/hyperium/http"
|
||||
|
||||
[[bench]]
|
||||
name = "header_map"
|
||||
path = "benches/header_map/mod.rs"
|
||||
|
||||
[[bench]]
|
||||
name = "header_name"
|
||||
path = "benches/header_name.rs"
|
||||
|
||||
[[bench]]
|
||||
name = "header_name2"
|
||||
path = "benches/header_name2.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "header_value"
|
||||
path = "benches/header_value.rs"
|
||||
|
||||
[[bench]]
|
||||
name = "method"
|
||||
path = "benches/method.rs"
|
||||
|
||||
[[bench]]
|
||||
name = "uri"
|
||||
path = "benches/uri.rs"
|
||||
|
||||
[dependencies.bytes]
|
||||
version = "1"
|
||||
|
||||
[dependencies.fnv]
|
||||
version = "1.0.5"
|
||||
|
||||
[dependencies.itoa]
|
||||
version = "1"
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3.2"
|
||||
|
||||
[dev-dependencies.doc-comment]
|
||||
version = "0.3"
|
||||
|
||||
[dev-dependencies.indexmap]
|
||||
version = "1.0"
|
||||
|
||||
[dev-dependencies.quickcheck]
|
||||
version = "0.9.0"
|
||||
|
||||
[dev-dependencies.rand]
|
||||
version = "0.7.0"
|
||||
|
||||
[dev-dependencies.seahash]
|
||||
version = "3.0.5"
|
||||
|
||||
[dev-dependencies.serde]
|
||||
version = "1.0"
|
||||
|
||||
[dev-dependencies.serde_json]
|
||||
version = "1.0"
|
||||
201
zeroidc/vendor/http/LICENSE-APACHE
vendored
Normal file
201
zeroidc/vendor/http/LICENSE-APACHE
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2017 http-rs authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
25
zeroidc/vendor/http/LICENSE-MIT
vendored
Normal file
25
zeroidc/vendor/http/LICENSE-MIT
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2017 http-rs authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
80
zeroidc/vendor/http/README.md
vendored
Normal file
80
zeroidc/vendor/http/README.md
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
# HTTP
|
||||
|
||||
A general purpose library of common HTTP types
|
||||
|
||||
[](https://github.com/hyperium/http/actions?query=workflow%3ACI)
|
||||
[](https://crates.io/crates/http)
|
||||
[][dox]
|
||||
|
||||
More information about this crate can be found in the [crate
|
||||
documentation][dox].
|
||||
|
||||
[dox]: https://docs.rs/http
|
||||
|
||||
## Usage
|
||||
|
||||
To use `http`, first add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
http = "0.2"
|
||||
```
|
||||
|
||||
Next, add this to your crate:
|
||||
|
||||
```rust
|
||||
use http::{Request, Response};
|
||||
|
||||
fn main() {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Create an HTTP request:
|
||||
|
||||
```rust
|
||||
use http::Request;
|
||||
|
||||
fn main() {
|
||||
let request = Request::builder()
|
||||
.uri("https://www.rust-lang.org/")
|
||||
.header("User-Agent", "awesome/1.0")
|
||||
.body(())
|
||||
.unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
Create an HTTP response:
|
||||
|
||||
```rust
|
||||
use http::{Response, StatusCode};
|
||||
|
||||
fn main() {
|
||||
let response = Response::builder()
|
||||
.status(StatusCode::MOVED_PERMANENTLY)
|
||||
.header("Location", "https://www.rust-lang.org/install.html")
|
||||
.body(())
|
||||
.unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
# Supported Rust Versions
|
||||
|
||||
This project follows the [Tokio MSRV][msrv] and is currently set to `1.49`.
|
||||
|
||||
[msrv]: https://github.com/tokio-rs/tokio/#supported-rust-versions
|
||||
|
||||
# License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
|
||||
|
||||
# Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
595
zeroidc/vendor/http/benches/header_map/basic.rs
vendored
Normal file
595
zeroidc/vendor/http/benches/header_map/basic.rs
vendored
Normal file
@@ -0,0 +1,595 @@
|
||||
macro_rules! bench {
|
||||
($name:ident($map:ident, $b:ident) $body:expr) => {
|
||||
mod $name {
|
||||
#[allow(unused_imports)]
|
||||
use super::custom_hdr;
|
||||
use fnv::FnvHasher;
|
||||
use http::header::*;
|
||||
use seahash::SeaHasher;
|
||||
use std::hash::BuildHasherDefault;
|
||||
#[allow(unused_imports)]
|
||||
use test::{self, Bencher};
|
||||
|
||||
#[bench]
|
||||
fn header_map($b: &mut Bencher) {
|
||||
let $map = || HeaderMap::default();
|
||||
$body
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn order_map_fnv($b: &mut Bencher) {
|
||||
use indexmap::IndexMap;
|
||||
let $map = || IndexMap::<_, _, BuildHasherDefault<FnvHasher>>::default();
|
||||
$body
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn vec_map($b: &mut Bencher) {
|
||||
use crate::vec_map::VecMap;
|
||||
|
||||
let $map = || VecMap::with_capacity(0);
|
||||
$body
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn order_map_seahash($b: &mut Bencher) {
|
||||
use indexmap::IndexMap;
|
||||
let $map = || IndexMap::<_, _, BuildHasherDefault<SeaHasher>>::default();
|
||||
$body
|
||||
}
|
||||
|
||||
/*
|
||||
#[bench]
|
||||
fn order_map_siphash($b: &mut Bencher) {
|
||||
use indexmap::IndexMap;
|
||||
let $map = || IndexMap::new();
|
||||
$body
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn std_map_siphash($b: &mut Bencher) {
|
||||
use std::collections::HashMap;
|
||||
let $map = || HashMap::new();
|
||||
$body
|
||||
}
|
||||
*/
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bench!(new_insert_get_host(new_map, b) {
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
h.insert(HOST, "hyper.rs");
|
||||
test::black_box(h.get(&HOST));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_4_std_get_30(new_map, b) {
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for i in 0..4 {
|
||||
h.insert(super::STD[i].clone(), "foo");
|
||||
}
|
||||
|
||||
for i in 0..30 {
|
||||
test::black_box(h.get(&super::STD[i % 4]));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_6_std_get_6(new_map, b) {
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for i in 0..6 {
|
||||
h.insert(super::STD[i].clone(), "foo");
|
||||
}
|
||||
|
||||
for i in 0..6 {
|
||||
test::black_box(h.get(&super::STD[i % 4]));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/*
|
||||
bench!(insert_remove_host(new_map, b) {
|
||||
let mut h = new_map();
|
||||
|
||||
b.iter(|| {
|
||||
test::black_box(h.insert(HOST, "hyper.rs"));
|
||||
test::black_box(h.remove(&HOST));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_insert_host(new_map, b) {
|
||||
let mut h = new_map();
|
||||
|
||||
b.iter(|| {
|
||||
test::black_box(h.insert(HOST, "hyper.rs"));
|
||||
test::black_box(h.insert(HOST, "hyper.rs"));
|
||||
})
|
||||
});
|
||||
*/
|
||||
|
||||
bench!(get_10_of_20_std(new_map, b) {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in super::STD[10..30].iter() {
|
||||
h.insert(hdr.clone(), hdr.as_str().to_string());
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
for hdr in &super::STD[10..20] {
|
||||
test::black_box(h.get(hdr));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
bench!(get_100_std(new_map, b) {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in super::STD.iter() {
|
||||
h.insert(hdr.clone(), hdr.as_str().to_string());
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
for i in 0..100 {
|
||||
test::black_box(h.get(&super::STD[i % super::STD.len()]));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_8_get_1_std(new_map, b) {
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &super::STD[0..8] {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&super::STD[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_10_get_1_std(new_map, b) {
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &super::STD[0..10] {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&super::STD[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_20_get_1_std(new_map, b) {
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &super::STD[0..20] {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&super::STD[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(get_10_custom_short(new_map, b) {
|
||||
let hdrs = custom_hdr(20);
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
h.insert(hdr.clone(), hdr.as_str().to_string());
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
for hdr in &hdrs[..10] {
|
||||
test::black_box(h.get(hdr));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_10_get_1_custom_short(new_map, b) {
|
||||
let hdrs = custom_hdr(10);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&hdrs[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_10_get_1_custom_med(new_map, b) {
|
||||
let hdrs = super::med_custom_hdr(10);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&hdrs[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_10_get_1_custom_long(new_map, b) {
|
||||
let hdrs = super::long_custom_hdr(10);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&hdrs[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_10_get_1_custom_very_long(new_map, b) {
|
||||
let hdrs = super::very_long_custom_hdr(10);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&hdrs[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_20_get_1_custom_short(new_map, b) {
|
||||
let hdrs = custom_hdr(20);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&hdrs[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_20_get_1_custom_med(new_map, b) {
|
||||
let hdrs = super::med_custom_hdr(20);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&hdrs[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_20_get_1_custom_long(new_map, b) {
|
||||
let hdrs = super::long_custom_hdr(20);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&hdrs[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(set_20_get_1_custom_very_long(new_map, b) {
|
||||
let hdrs = super::very_long_custom_hdr(20);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
|
||||
test::black_box(h.get(&hdrs[0]));
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_all_std_headers(new_map, b) {
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in super::STD {
|
||||
test::black_box(h.insert(hdr.clone(), "foo"));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_79_custom_std_headers(new_map, b) {
|
||||
let hdrs = super::custom_std(79);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
h.insert(hdr.clone(), "foo");
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_100_custom_headers(new_map, b) {
|
||||
let hdrs = custom_hdr(100);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
test::black_box(h.insert(hdr.clone(), "foo"));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_500_custom_headers(new_map, b) {
|
||||
let hdrs = custom_hdr(500);
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for hdr in &hdrs {
|
||||
test::black_box(h.insert(hdr.clone(), "foo"));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_one_15_char_header(new_map, b) {
|
||||
let hdr: HeaderName = "abcd-abcd-abcde"
|
||||
.parse().unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
h.insert(hdr.clone(), "hello");
|
||||
test::black_box(h);
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_one_25_char_header(new_map, b) {
|
||||
let hdr: HeaderName = "abcd-abcd-abcd-abcd-abcde"
|
||||
.parse().unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
h.insert(hdr.clone(), "hello");
|
||||
test::black_box(h);
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_one_50_char_header(new_map, b) {
|
||||
let hdr: HeaderName = "abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcde"
|
||||
.parse().unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
h.insert(hdr.clone(), "hello");
|
||||
test::black_box(h);
|
||||
})
|
||||
});
|
||||
|
||||
bench!(insert_one_100_char_header(new_map, b) {
|
||||
let hdr: HeaderName = "abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcdeabcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcde"
|
||||
.parse().unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
h.insert(hdr.clone(), "hello");
|
||||
test::black_box(h);
|
||||
})
|
||||
});
|
||||
|
||||
const HN_HDRS: [(&'static str, &'static str); 11] = [
|
||||
("Date", "Fri, 27 Jan 2017 23:00:00 GMT"),
|
||||
("Content-Type", "text/html; charset=utf-8"),
|
||||
("Transfer-Encoding", "chunked"),
|
||||
("Connection", "keep-alive"),
|
||||
("Set-Cookie", "__cfduid=dbdfbbe3822b61cb8750ba37d894022151485558000; expires=Sat, 27-Jan-18 23:00:00 GMT; path=/; domain=.ycombinator.com; HttpOnly"),
|
||||
("Vary", "Accept-Encoding"),
|
||||
("Cache-Control", "private"),
|
||||
("X-Frame-Options", "DENY"),
|
||||
("Strict-Transport-Security", "max-age=31556900; includeSubDomains"),
|
||||
("Server", "cloudflare-nginx"),
|
||||
("CF-RAY", "327fd1809f3c1baf-SEA"),
|
||||
];
|
||||
|
||||
bench!(hn_hdrs_set_8_get_many(new_map, b) {
|
||||
let hdrs: Vec<(HeaderName, &'static str)> = super::HN_HDRS[..8].iter()
|
||||
.map(|&(name, val)| (name.parse().unwrap(), val))
|
||||
.collect();
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for &(ref name, val) in hdrs.iter() {
|
||||
h.insert(name.clone(), val);
|
||||
}
|
||||
|
||||
for _ in 0..15 {
|
||||
test::black_box(h.get(&CONTENT_LENGTH));
|
||||
test::black_box(h.get(&VARY));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
bench!(hn_hdrs_set_8_get_miss(new_map, b) {
|
||||
let hdrs: Vec<(HeaderName, &'static str)> = super::HN_HDRS[..8].iter()
|
||||
.map(|&(name, val)| (name.parse().unwrap(), val))
|
||||
.collect();
|
||||
|
||||
let miss: HeaderName = "x-wat".parse().unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for &(ref name, val) in hdrs.iter() {
|
||||
h.insert(name.clone(), val);
|
||||
}
|
||||
|
||||
test::black_box(h.get(&CONTENT_LENGTH));
|
||||
test::black_box(h.get(&miss));
|
||||
});
|
||||
});
|
||||
|
||||
bench!(hn_hdrs_set_11_get_with_miss(new_map, b) {
|
||||
let hdrs: Vec<(HeaderName, &'static str)> = super::HN_HDRS.iter()
|
||||
.map(|&(name, val)| (name.parse().unwrap(), val))
|
||||
.collect();
|
||||
|
||||
let miss: HeaderName = "x-wat".parse().unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
let mut h = new_map();
|
||||
|
||||
for &(ref name, val) in hdrs.iter() {
|
||||
h.insert(name.clone(), val);
|
||||
}
|
||||
|
||||
for _ in 0..10 {
|
||||
test::black_box(h.get(&CONTENT_LENGTH));
|
||||
test::black_box(h.get(&VARY));
|
||||
test::black_box(h.get(&miss));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
use http::header::*;
|
||||
|
||||
fn custom_hdr(n: usize) -> Vec<HeaderName> {
|
||||
(0..n)
|
||||
.map(|i| {
|
||||
let s = format!("x-custom-{}", i);
|
||||
s.parse().unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn med_custom_hdr(n: usize) -> Vec<HeaderName> {
|
||||
(0..n)
|
||||
.map(|i| {
|
||||
let s = format!("content-length-{}", i);
|
||||
s.parse().unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn long_custom_hdr(n: usize) -> Vec<HeaderName> {
|
||||
(0..n)
|
||||
.map(|i| {
|
||||
let s = format!("access-control-allow-headers-{}", i);
|
||||
s.parse().unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn very_long_custom_hdr(n: usize) -> Vec<HeaderName> {
|
||||
(0..n)
|
||||
.map(|i| {
|
||||
let s = format!("access-control-allow-access-control-allow-headers-{}", i);
|
||||
s.parse().unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn custom_std(n: usize) -> Vec<HeaderName> {
|
||||
(0..n)
|
||||
.map(|i| {
|
||||
let s = format!("{}-{}", STD[i % STD.len()].as_str(), i);
|
||||
s.parse().unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
const STD: &'static [HeaderName] = &[
|
||||
ACCEPT,
|
||||
ACCEPT_CHARSET,
|
||||
ACCEPT_ENCODING,
|
||||
ACCEPT_LANGUAGE,
|
||||
ACCEPT_RANGES,
|
||||
ACCESS_CONTROL_ALLOW_CREDENTIALS,
|
||||
ACCESS_CONTROL_ALLOW_HEADERS,
|
||||
ACCESS_CONTROL_ALLOW_METHODS,
|
||||
ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||
ACCESS_CONTROL_EXPOSE_HEADERS,
|
||||
ACCESS_CONTROL_MAX_AGE,
|
||||
ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
ACCESS_CONTROL_REQUEST_METHOD,
|
||||
AGE,
|
||||
ALLOW,
|
||||
ALT_SVC,
|
||||
AUTHORIZATION,
|
||||
CACHE_CONTROL,
|
||||
CONNECTION,
|
||||
CONTENT_DISPOSITION,
|
||||
CONTENT_ENCODING,
|
||||
CONTENT_LANGUAGE,
|
||||
CONTENT_LENGTH,
|
||||
CONTENT_LOCATION,
|
||||
CONTENT_RANGE,
|
||||
CONTENT_SECURITY_POLICY,
|
||||
CONTENT_SECURITY_POLICY_REPORT_ONLY,
|
||||
CONTENT_TYPE,
|
||||
COOKIE,
|
||||
DNT,
|
||||
DATE,
|
||||
ETAG,
|
||||
EXPECT,
|
||||
EXPIRES,
|
||||
FORWARDED,
|
||||
FROM,
|
||||
HOST,
|
||||
IF_MATCH,
|
||||
IF_MODIFIED_SINCE,
|
||||
IF_NONE_MATCH,
|
||||
IF_RANGE,
|
||||
IF_UNMODIFIED_SINCE,
|
||||
LAST_MODIFIED,
|
||||
LINK,
|
||||
LOCATION,
|
||||
MAX_FORWARDS,
|
||||
ORIGIN,
|
||||
PRAGMA,
|
||||
PROXY_AUTHENTICATE,
|
||||
PROXY_AUTHORIZATION,
|
||||
PUBLIC_KEY_PINS,
|
||||
PUBLIC_KEY_PINS_REPORT_ONLY,
|
||||
RANGE,
|
||||
REFERER,
|
||||
REFERRER_POLICY,
|
||||
REFRESH,
|
||||
RETRY_AFTER,
|
||||
SERVER,
|
||||
SET_COOKIE,
|
||||
STRICT_TRANSPORT_SECURITY,
|
||||
TE,
|
||||
TRAILER,
|
||||
TRANSFER_ENCODING,
|
||||
USER_AGENT,
|
||||
UPGRADE,
|
||||
UPGRADE_INSECURE_REQUESTS,
|
||||
VARY,
|
||||
VIA,
|
||||
WARNING,
|
||||
WWW_AUTHENTICATE,
|
||||
X_CONTENT_TYPE_OPTIONS,
|
||||
X_DNS_PREFETCH_CONTROL,
|
||||
X_FRAME_OPTIONS,
|
||||
X_XSS_PROTECTION,
|
||||
];
|
||||
6
zeroidc/vendor/http/benches/header_map/mod.rs
vendored
Normal file
6
zeroidc/vendor/http/benches/header_map/mod.rs
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
mod basic;
|
||||
mod vec_map;
|
||||
107
zeroidc/vendor/http/benches/header_map/vec_map.rs
vendored
Normal file
107
zeroidc/vendor/http/benches/header_map/vec_map.rs
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VecMap<K, V> {
|
||||
vec: Vec<(K, V)>,
|
||||
}
|
||||
|
||||
impl<K: PartialEq, V> VecMap<K, V> {
|
||||
#[inline]
|
||||
pub fn with_capacity(cap: usize) -> VecMap<K, V> {
|
||||
VecMap {
|
||||
vec: Vec::with_capacity(cap),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn insert(&mut self, key: K, value: V) {
|
||||
match self.find(&key) {
|
||||
Some(pos) => self.vec[pos] = (key, value),
|
||||
None => self.vec.push((key, value)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
|
||||
match self.find(&key) {
|
||||
Some(pos) => Entry::Occupied(OccupiedEntry {
|
||||
vec: self,
|
||||
pos: pos,
|
||||
}),
|
||||
None => Entry::Vacant(VacantEntry {
|
||||
vec: self,
|
||||
key: key,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get<K2: PartialEq<K> + ?Sized>(&self, key: &K2) -> Option<&V> {
|
||||
self.find(key).map(move |pos| &self.vec[pos].1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mut<K2: PartialEq<K> + ?Sized>(&mut self, key: &K2) -> Option<&mut V> {
|
||||
self.find(key).map(move |pos| &mut self.vec[pos].1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains_key<K2: PartialEq<K> + ?Sized>(&self, key: &K2) -> bool {
|
||||
self.find(key).is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.vec.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> ::std::slice::Iter<'_, (K, V)> {
|
||||
self.vec.iter()
|
||||
}
|
||||
#[inline]
|
||||
pub fn remove<K2: PartialEq<K> + ?Sized>(&mut self, key: &K2) -> Option<V> {
|
||||
self.find(key)
|
||||
.map(|pos| self.vec.remove(pos))
|
||||
.map(|(_, v)| v)
|
||||
}
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.vec.clear();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn find<K2: PartialEq<K> + ?Sized>(&self, key: &K2) -> Option<usize> {
|
||||
self.vec.iter().position(|entry| key == &entry.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Entry<'a, K: 'a, V: 'a> {
|
||||
Vacant(VacantEntry<'a, K, V>),
|
||||
Occupied(OccupiedEntry<'a, K, V>),
|
||||
}
|
||||
|
||||
pub struct VacantEntry<'a, K, V> {
|
||||
vec: &'a mut VecMap<K, V>,
|
||||
key: K,
|
||||
}
|
||||
|
||||
impl<'a, K, V> VacantEntry<'a, K, V> {
|
||||
pub fn insert(self, val: V) -> &'a mut V {
|
||||
let vec = self.vec;
|
||||
vec.vec.push((self.key, val));
|
||||
let pos = vec.vec.len() - 1;
|
||||
&mut vec.vec[pos].1
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OccupiedEntry<'a, K, V> {
|
||||
vec: &'a mut VecMap<K, V>,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<'a, K, V> OccupiedEntry<'a, K, V> {
|
||||
pub fn into_mut(self) -> &'a mut V {
|
||||
&mut self.vec.vec[self.pos].1
|
||||
}
|
||||
}
|
||||
296
zeroidc/vendor/http/benches/header_name.rs
vendored
Normal file
296
zeroidc/vendor/http/benches/header_name.rs
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate bytes;
|
||||
extern crate http;
|
||||
extern crate test;
|
||||
|
||||
use http::header::HeaderName;
|
||||
use test::Bencher;
|
||||
|
||||
fn make_all_known_headers() -> Vec<Vec<u8>> {
|
||||
// Standard request headers
|
||||
vec![b"A-IM".to_vec(),
|
||||
b"Accept".to_vec(),
|
||||
b"Accept-Charset".to_vec(),
|
||||
b"Accept-Datetime".to_vec(),
|
||||
b"Accept-Encoding".to_vec(),
|
||||
b"Accept-Language".to_vec(),
|
||||
b"Access-Control-Request-Method".to_vec(),
|
||||
b"Authorization".to_vec(),
|
||||
b"Cache-Control".to_vec(),
|
||||
b"Connection".to_vec(),
|
||||
b"Permanent".to_vec(),
|
||||
b"Content-Length".to_vec(),
|
||||
b"Content-MD5".to_vec(),
|
||||
b"Content-Type".to_vec(),
|
||||
b"Cookie".to_vec(),
|
||||
b"Date".to_vec(),
|
||||
b"Expect".to_vec(),
|
||||
b"Forwarded".to_vec(),
|
||||
b"From".to_vec(),
|
||||
b"Host".to_vec(),
|
||||
b"Permanent".to_vec(),
|
||||
b"HTTP2-Settings".to_vec(),
|
||||
b"If-Match".to_vec(),
|
||||
b"If-Modified-Since".to_vec(),
|
||||
b"If-None-Match".to_vec(),
|
||||
b"If-Range".to_vec(),
|
||||
b"If-Unmodified-Since".to_vec(),
|
||||
b"Max-Forwards".to_vec(),
|
||||
b"Origin".to_vec(),
|
||||
b"Pragma".to_vec(),
|
||||
b"Proxy-Authorization".to_vec(),
|
||||
b"Range".to_vec(),
|
||||
b"Referer".to_vec(),
|
||||
b"TE".to_vec(),
|
||||
b"User-Agent".to_vec(),
|
||||
b"Upgrade".to_vec(),
|
||||
b"Via".to_vec(),
|
||||
b"Warning".to_vec(),
|
||||
// common_non_standard
|
||||
b"Upgrade-Insecure-Requests".to_vec(),
|
||||
b"Upgrade-Insecure-Requests".to_vec(),
|
||||
b"X-Requested-With".to_vec(),
|
||||
b"DNT".to_vec(),
|
||||
b"X-Forwarded-For".to_vec(),
|
||||
b"X-Forwarded-Host".to_vec(),
|
||||
b"X-Forwarded-Proto".to_vec(),
|
||||
b"Front-End-Https".to_vec(),
|
||||
b"X-Http-Method-Override".to_vec(),
|
||||
b"X-ATT-DeviceId".to_vec(),
|
||||
b"X-Wap-Profile".to_vec(),
|
||||
b"Proxy-Connection".to_vec(),
|
||||
b"X-UIDH".to_vec(),
|
||||
b"X-Csrf-Token".to_vec(),
|
||||
b"X-Request-ID".to_vec(),
|
||||
b"X-Correlation-ID".to_vec(),
|
||||
b"Save-Data".to_vec(),
|
||||
// standard_response_headers
|
||||
b"Accept-Patch".to_vec(),
|
||||
b"Accept-Ranges".to_vec(),
|
||||
b"Access-Control-Allow-Credentials".to_vec(),
|
||||
b"Access-Control-Allow-Headers".to_vec(),
|
||||
b"Access-Control-Allow-Methods".to_vec(),
|
||||
b"Access-Control-Allow-Origin".to_vec(),
|
||||
b"Access-Control-Expose-Headers".to_vec(),
|
||||
b"Access-Control-Max-Age".to_vec(),
|
||||
b"Age".to_vec(),
|
||||
b"Allow".to_vec(),
|
||||
b"Alt-Svc".to_vec(),
|
||||
b"Cache-Control".to_vec(),
|
||||
b"Connection".to_vec(),
|
||||
b"Content-Disposition".to_vec(),
|
||||
b"Content-Encoding".to_vec(),
|
||||
b"Content-Language".to_vec(),
|
||||
b"Content-Length".to_vec(),
|
||||
b"Content-Location".to_vec(),
|
||||
b"Content-MD5".to_vec(),
|
||||
b"Content-Range".to_vec(),
|
||||
b"Content-Type".to_vec(),
|
||||
b"Date".to_vec(),
|
||||
b"Delta-Base".to_vec(),
|
||||
b"ETag".to_vec(),
|
||||
b"Expires".to_vec(),
|
||||
b"IM".to_vec(),
|
||||
b"Last-Modified".to_vec(),
|
||||
b"Link".to_vec(),
|
||||
b"Location".to_vec(),
|
||||
b"P3P".to_vec(),
|
||||
b"Permanent".to_vec(),
|
||||
b"Pragma".to_vec(),
|
||||
b"Proxy-Authenticate".to_vec(),
|
||||
b"Public-Key-Pins".to_vec(),
|
||||
b"Retry-After".to_vec(),
|
||||
b"Server".to_vec(),
|
||||
b"Set-Cookie".to_vec(),
|
||||
b"Strict-Transport-Security".to_vec(),
|
||||
b"Tk".to_vec(),
|
||||
b"Trailer".to_vec(),
|
||||
b"Transfer-Encoding".to_vec(),
|
||||
b"Upgrade".to_vec(),
|
||||
b"Vary".to_vec(),
|
||||
b"Via".to_vec(),
|
||||
b"Warning".to_vec(),
|
||||
b"WWW-Authenticate".to_vec(),
|
||||
b"X-Frame-Options".to_vec(),
|
||||
// common_non_standard_response
|
||||
b"Content-Security-Policy".to_vec(),
|
||||
b"Refresh".to_vec(),
|
||||
b"Status".to_vec(),
|
||||
b"Timing-Allow-Origin".to_vec(),
|
||||
b"X-Content-Duration".to_vec(),
|
||||
b"X-Content-Security-Policy".to_vec(),
|
||||
b"X-Content-Type-Options".to_vec(),
|
||||
b"X-Correlation-ID".to_vec(),
|
||||
b"X-Powered-By".to_vec(),
|
||||
b"X-Request-ID".to_vec(),
|
||||
b"X-UA-Compatible".to_vec(),
|
||||
b"X-WebKit-CSP".to_vec(),
|
||||
b"X-XSS-Protection".to_vec(),
|
||||
]
|
||||
}
|
||||
|
||||
static ALL_KNOWN_HEADERS: &[&str] = &[
|
||||
// Standard request headers
|
||||
"a-im",
|
||||
"accept",
|
||||
"accept-charset",
|
||||
"accept-datetime",
|
||||
"accept-encoding",
|
||||
"accept-language",
|
||||
"access-control-request-method",
|
||||
"authorization",
|
||||
"cache-control",
|
||||
"connection",
|
||||
"permanent",
|
||||
"content-length",
|
||||
"content-md5",
|
||||
"content-type",
|
||||
"cookie",
|
||||
"date",
|
||||
"expect",
|
||||
"forwarded",
|
||||
"from",
|
||||
"host",
|
||||
"permanent",
|
||||
"http2-settings",
|
||||
"if-match",
|
||||
"if-modified-since",
|
||||
"if-none-match",
|
||||
"if-range",
|
||||
"if-unmodified-since",
|
||||
"max-forwards",
|
||||
"origin",
|
||||
"pragma",
|
||||
"proxy-authorization",
|
||||
"range",
|
||||
"referer",
|
||||
"te",
|
||||
"user-agent",
|
||||
"upgrade",
|
||||
"via",
|
||||
"warning",
|
||||
// common_non_standard
|
||||
"upgrade-insecure-requests",
|
||||
"upgrade-insecure-requests",
|
||||
"x-requested-with",
|
||||
"dnt",
|
||||
"x-forwarded-for",
|
||||
"x-forwarded-host",
|
||||
"x-forwarded-proto",
|
||||
"front-end-https",
|
||||
"x-http-method-override",
|
||||
"x-att-deviceid",
|
||||
"x-wap-profile",
|
||||
"proxy-connection",
|
||||
"x-uidh",
|
||||
"x-csrf-token",
|
||||
"x-request-id",
|
||||
"x-correlation-id",
|
||||
"save-data",
|
||||
// standard_response_headers
|
||||
"accept-patch",
|
||||
"accept-ranges",
|
||||
"access-control-allow-credentials",
|
||||
"access-control-allow-headers",
|
||||
"access-control-allow-methods",
|
||||
"access-control-allow-origin",
|
||||
"access-control-expose-headers",
|
||||
"access-control-max-age",
|
||||
"age",
|
||||
"allow",
|
||||
"alt-svc",
|
||||
"cache-control",
|
||||
"connection",
|
||||
"content-disposition",
|
||||
"content-encoding",
|
||||
"content-language",
|
||||
"content-length",
|
||||
"content-location",
|
||||
"content-md5",
|
||||
"content-range",
|
||||
"content-type",
|
||||
"date",
|
||||
"delta-base",
|
||||
"etag",
|
||||
"expires",
|
||||
"im",
|
||||
"last-modified",
|
||||
"link",
|
||||
"location",
|
||||
"p3p",
|
||||
"permanent",
|
||||
"pragma",
|
||||
"proxy-authenticate",
|
||||
"public-key-pins",
|
||||
"retry-after",
|
||||
"server",
|
||||
"set-cookie",
|
||||
"strict-transport-security",
|
||||
"tk",
|
||||
"trailer",
|
||||
"transfer-encoding",
|
||||
"upgrade",
|
||||
"vary",
|
||||
"via",
|
||||
"warning",
|
||||
"www-authenticate",
|
||||
"x-frame-options",
|
||||
// common_non_standard_response
|
||||
"content-security-policy",
|
||||
"refresh",
|
||||
"status",
|
||||
"timing-allow-origin",
|
||||
"x-content-duration",
|
||||
"x-content-security-policy",
|
||||
"x-content-type-options",
|
||||
"x-correlation-id",
|
||||
"x-powered-by",
|
||||
"x-request-id",
|
||||
"x-ua-compatible",
|
||||
"x-webkit-csp",
|
||||
"x-xss-protection",
|
||||
];
|
||||
|
||||
#[bench]
|
||||
fn header_name_easy(b: &mut Bencher) {
|
||||
let name = b"Content-type";
|
||||
b.iter(|| {
|
||||
HeaderName::from_bytes(&name[..]).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn header_name_custom(b: &mut Bencher) {
|
||||
let name = b"Foo-Bar-Baz-Blah";
|
||||
b.iter(|| {
|
||||
HeaderName::from_bytes(&name[..]).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn header_name_bad(b: &mut Bencher) {
|
||||
let name = b"bad header name";
|
||||
b.iter(|| {
|
||||
HeaderName::from_bytes(&name[..]).expect_err("Bad header name");
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn header_name_various(b: &mut Bencher) {
|
||||
let all_known_headers = make_all_known_headers();
|
||||
b.iter(|| {
|
||||
for name in &all_known_headers{
|
||||
HeaderName::from_bytes(name.as_slice()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn header_name_from_static(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
for name in ALL_KNOWN_HEADERS {
|
||||
HeaderName::from_static(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
52
zeroidc/vendor/http/benches/header_name2.rs
vendored
Normal file
52
zeroidc/vendor/http/benches/header_name2.rs
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId,Criterion, Throughput};
|
||||
use http::header::HeaderName;
|
||||
|
||||
// This is a list of some of the standard headers ordered by increasing size.
|
||||
// It has exactly one standard header per size (some sizes don't have a standard
|
||||
// header).
|
||||
const STANDARD_HEADERS_BY_SIZE: &[&str] = &[
|
||||
"te",
|
||||
"age",
|
||||
"date",
|
||||
"allow",
|
||||
"accept",
|
||||
"alt-svc",
|
||||
"if-match",
|
||||
"forwarded",
|
||||
"connection",
|
||||
"retry-after",
|
||||
"content-type",
|
||||
"accept-ranges",
|
||||
"accept-charset",
|
||||
"accept-encoding",
|
||||
"content-encoding",
|
||||
"if-modified-since",
|
||||
"proxy-authenticate",
|
||||
"content-disposition",
|
||||
"sec-websocket-accept",
|
||||
"sec-websocket-version",
|
||||
"access-control-max-age",
|
||||
"content-security-policy",
|
||||
"sec-websocket-extensions",
|
||||
"strict-transport-security",
|
||||
"access-control-allow-origin",
|
||||
"access-control-allow-headers",
|
||||
"access-control-expose-headers",
|
||||
"access-control-request-headers",
|
||||
"access-control-allow-credentials",
|
||||
"content-security-policy-report-only",
|
||||
];
|
||||
|
||||
fn header_name_by_size(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("std_hdr");
|
||||
for name in STANDARD_HEADERS_BY_SIZE {
|
||||
group.throughput(Throughput::Bytes(name.len() as u64));
|
||||
group.bench_with_input(BenchmarkId::from_parameter(name), name, |b, name| {
|
||||
b.iter(|| HeaderName::from_static(name) );
|
||||
});
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, header_name_by_size);
|
||||
criterion_main!(benches);
|
||||
46
zeroidc/vendor/http/benches/header_value.rs
vendored
Normal file
46
zeroidc/vendor/http/benches/header_value.rs
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use bytes::Bytes;
|
||||
use http::HeaderValue;
|
||||
use test::Bencher;
|
||||
|
||||
static SHORT: &'static [u8] = b"localhost";
|
||||
static LONG: &'static [u8] = b"Mozilla/5.0 (X11; CrOS x86_64 9592.71.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.80 Safari/537.36";
|
||||
|
||||
#[bench]
|
||||
fn from_shared_short(b: &mut Bencher) {
|
||||
b.bytes = SHORT.len() as u64;
|
||||
let bytes = Bytes::from_static(SHORT);
|
||||
b.iter(|| {
|
||||
HeaderValue::from_maybe_shared(bytes.clone()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn from_shared_long(b: &mut Bencher) {
|
||||
b.bytes = LONG.len() as u64;
|
||||
let bytes = Bytes::from_static(LONG);
|
||||
b.iter(|| {
|
||||
HeaderValue::from_maybe_shared(bytes.clone()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn from_shared_unchecked_short(b: &mut Bencher) {
|
||||
b.bytes = SHORT.len() as u64;
|
||||
let bytes = Bytes::from_static(SHORT);
|
||||
b.iter(|| unsafe {
|
||||
HeaderValue::from_maybe_shared_unchecked(bytes.clone());
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn from_shared_unchecked_long(b: &mut Bencher) {
|
||||
b.bytes = LONG.len() as u64;
|
||||
let bytes = Bytes::from_static(LONG);
|
||||
b.iter(|| unsafe {
|
||||
HeaderValue::from_maybe_shared_unchecked(bytes.clone());
|
||||
});
|
||||
}
|
||||
40
zeroidc/vendor/http/benches/method.rs
vendored
Normal file
40
zeroidc/vendor/http/benches/method.rs
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use http::method::Method;
|
||||
use test::Bencher;
|
||||
|
||||
fn make_all_methods() -> Vec<Vec<u8>> {
|
||||
vec![
|
||||
b"OPTIONS".to_vec(),
|
||||
b"GET".to_vec(),
|
||||
b"POST".to_vec(),
|
||||
b"PUT".to_vec(),
|
||||
b"DELETE".to_vec(),
|
||||
b"HEAD".to_vec(),
|
||||
b"TRACE".to_vec(),
|
||||
b"CONNECT".to_vec(),
|
||||
b"PATCH".to_vec(),
|
||||
b"CUSTOM_SHORT".to_vec(),
|
||||
b"CUSTOM_LONG_METHOD".to_vec(),
|
||||
]
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn method_easy(b: &mut Bencher) {
|
||||
let name = b"GET";
|
||||
b.iter(|| {
|
||||
Method::from_bytes(&name[..]).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn method_various(b: &mut Bencher) {
|
||||
let all_methods = make_all_methods();
|
||||
b.iter(|| {
|
||||
for name in &all_methods {
|
||||
Method::from_bytes(name.as_slice()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
32
zeroidc/vendor/http/benches/uri.rs
vendored
Normal file
32
zeroidc/vendor/http/benches/uri.rs
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use http::Uri;
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn uri_parse_slash(b: &mut Bencher) {
|
||||
b.bytes = 1;
|
||||
b.iter(|| {
|
||||
"/".parse::<Uri>().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn uri_parse_relative_medium(b: &mut Bencher) {
|
||||
let s = "/wp-content/uploads/2010/03/hello-kitty-darth-vader-pink.jpg";
|
||||
b.bytes = s.len() as u64;
|
||||
b.iter(|| {
|
||||
s.parse::<Uri>().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn uri_parse_relative_query(b: &mut Bencher) {
|
||||
let s = "/wp-content/uploads/2010/03/hello-kitty-darth-vader-pink.jpg?foo={bar}|baz%13%11quux";
|
||||
b.bytes = s.len() as u64;
|
||||
b.iter(|| {
|
||||
s.parse::<Uri>().unwrap();
|
||||
});
|
||||
}
|
||||
85
zeroidc/vendor/http/src/byte_str.rs
vendored
Normal file
85
zeroidc/vendor/http/src/byte_str.rs
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
use bytes::Bytes;
|
||||
|
||||
use std::{ops, str};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub(crate) struct ByteStr {
|
||||
// Invariant: bytes contains valid UTF-8
|
||||
bytes: Bytes,
|
||||
}
|
||||
|
||||
impl ByteStr {
|
||||
#[inline]
|
||||
pub fn new() -> ByteStr {
|
||||
ByteStr {
|
||||
// Invariant: the empty slice is trivially valid UTF-8.
|
||||
bytes: Bytes::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn from_static(val: &'static str) -> ByteStr {
|
||||
ByteStr {
|
||||
// Invariant: val is a str so contains vaid UTF-8.
|
||||
bytes: Bytes::from_static(val.as_bytes()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// ## Panics
|
||||
/// In a debug build this will panic if `bytes` is not valid UTF-8.
|
||||
///
|
||||
/// ## Safety
|
||||
/// `bytes` must contain valid UTF-8. In a release build it is undefined
|
||||
/// behaviour to call this with `bytes` that is not valid UTF-8.
|
||||
pub unsafe fn from_utf8_unchecked(bytes: Bytes) -> ByteStr {
|
||||
if cfg!(debug_assertions) {
|
||||
match str::from_utf8(&bytes) {
|
||||
Ok(_) => (),
|
||||
Err(err) => panic!(
|
||||
"ByteStr::from_utf8_unchecked() with invalid bytes; error = {}, bytes = {:?}",
|
||||
err, bytes
|
||||
),
|
||||
}
|
||||
}
|
||||
// Invariant: assumed by the safety requirements of this function.
|
||||
ByteStr { bytes: bytes }
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for ByteStr {
|
||||
type Target = str;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &str {
|
||||
let b: &[u8] = self.bytes.as_ref();
|
||||
// Safety: the invariant of `bytes` is that it contains valid UTF-8.
|
||||
unsafe { str::from_utf8_unchecked(b) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ByteStr {
|
||||
#[inline]
|
||||
fn from(src: String) -> ByteStr {
|
||||
ByteStr {
|
||||
// Invariant: src is a String so contains valid UTF-8.
|
||||
bytes: Bytes::from(src),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for ByteStr {
|
||||
#[inline]
|
||||
fn from(src: &'a str) -> ByteStr {
|
||||
ByteStr {
|
||||
// Invariant: src is a str so contains valid UTF-8.
|
||||
bytes: Bytes::copy_from_slice(src.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ByteStr> for Bytes {
|
||||
fn from(src: ByteStr) -> Self {
|
||||
src.bytes
|
||||
}
|
||||
}
|
||||
17
zeroidc/vendor/http/src/convert.rs
vendored
Normal file
17
zeroidc/vendor/http/src/convert.rs
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
macro_rules! if_downcast_into {
|
||||
($in_ty:ty, $out_ty:ty, $val:ident, $body:expr) => ({
|
||||
if std::any::TypeId::of::<$in_ty>() == std::any::TypeId::of::<$out_ty>() {
|
||||
// Store the value in an `Option` so we can `take`
|
||||
// it after casting to `&mut dyn Any`.
|
||||
let mut slot = Some($val);
|
||||
// Re-write the `$val` ident with the downcasted value.
|
||||
let $val = (&mut slot as &mut dyn std::any::Any)
|
||||
.downcast_mut::<Option<$out_ty>>()
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap();
|
||||
// Run the $body in scope of the replaced val.
|
||||
$body
|
||||
}
|
||||
})
|
||||
}
|
||||
149
zeroidc/vendor/http/src/error.rs
vendored
Normal file
149
zeroidc/vendor/http/src/error.rs
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::result;
|
||||
|
||||
use crate::header;
|
||||
use crate::method;
|
||||
use crate::status;
|
||||
use crate::uri;
|
||||
|
||||
/// A generic "error" for HTTP connections
|
||||
///
|
||||
/// This error type is less specific than the error returned from other
|
||||
/// functions in this crate, but all other errors can be converted to this
|
||||
/// error. Consumers of this crate can typically consume and work with this form
|
||||
/// of error for conversions with the `?` operator.
|
||||
pub struct Error {
|
||||
inner: ErrorKind,
|
||||
}
|
||||
|
||||
/// A `Result` typedef to use with the `http::Error` type
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
enum ErrorKind {
|
||||
StatusCode(status::InvalidStatusCode),
|
||||
Method(method::InvalidMethod),
|
||||
Uri(uri::InvalidUri),
|
||||
UriParts(uri::InvalidUriParts),
|
||||
HeaderName(header::InvalidHeaderName),
|
||||
HeaderValue(header::InvalidHeaderValue),
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_tuple("http::Error")
|
||||
// Skip the noise of the ErrorKind enum
|
||||
.field(&self.get_ref())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self.get_ref(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Return true if the underlying error has the same type as T.
|
||||
pub fn is<T: error::Error + 'static>(&self) -> bool {
|
||||
self.get_ref().is::<T>()
|
||||
}
|
||||
|
||||
/// Return a reference to the lower level, inner error.
|
||||
pub fn get_ref(&self) -> &(dyn error::Error + 'static) {
|
||||
use self::ErrorKind::*;
|
||||
|
||||
match self.inner {
|
||||
StatusCode(ref e) => e,
|
||||
Method(ref e) => e,
|
||||
Uri(ref e) => e,
|
||||
UriParts(ref e) => e,
|
||||
HeaderName(ref e) => e,
|
||||
HeaderValue(ref e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
// Return any available cause from the inner error. Note the inner error is
|
||||
// not itself the cause.
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
self.get_ref().source()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<status::InvalidStatusCode> for Error {
|
||||
fn from(err: status::InvalidStatusCode) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::StatusCode(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<method::InvalidMethod> for Error {
|
||||
fn from(err: method::InvalidMethod) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::Method(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uri::InvalidUri> for Error {
|
||||
fn from(err: uri::InvalidUri) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::Uri(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uri::InvalidUriParts> for Error {
|
||||
fn from(err: uri::InvalidUriParts) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::UriParts(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<header::InvalidHeaderName> for Error {
|
||||
fn from(err: header::InvalidHeaderName) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::HeaderName(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<header::InvalidHeaderValue> for Error {
|
||||
fn from(err: header::InvalidHeaderValue) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::HeaderValue(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for Error {
|
||||
fn from(err: std::convert::Infallible) -> Error {
|
||||
match err {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn inner_error_is_invalid_status_code() {
|
||||
if let Err(e) = status::StatusCode::from_u16(6666) {
|
||||
let err: Error = e.into();
|
||||
let ie = err.get_ref();
|
||||
assert!(!ie.is::<header::InvalidHeaderValue>());
|
||||
assert!(ie.is::<status::InvalidStatusCode>());
|
||||
ie.downcast_ref::<status::InvalidStatusCode>().unwrap();
|
||||
|
||||
assert!(!err.is::<header::InvalidHeaderValue>());
|
||||
assert!(err.is::<status::InvalidStatusCode>());
|
||||
} else {
|
||||
panic!("Bad status allowed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
250
zeroidc/vendor/http/src/extensions.rs
vendored
Normal file
250
zeroidc/vendor/http/src/extensions.rs
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::hash::{BuildHasherDefault, Hasher};
|
||||
|
||||
type AnyMap = HashMap<TypeId, Box<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>;
|
||||
|
||||
// With TypeIds as keys, there's no need to hash them. They are already hashes
|
||||
// themselves, coming from the compiler. The IdHasher just holds the u64 of
|
||||
// the TypeId, and then returns it, instead of doing any bit fiddling.
|
||||
#[derive(Default)]
|
||||
struct IdHasher(u64);
|
||||
|
||||
impl Hasher for IdHasher {
|
||||
fn write(&mut self, _: &[u8]) {
|
||||
unreachable!("TypeId calls write_u64");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u64(&mut self, id: u64) {
|
||||
self.0 = id;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn finish(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A type map of protocol extensions.
|
||||
///
|
||||
/// `Extensions` can be used by `Request` and `Response` to store
|
||||
/// extra data derived from the underlying protocol.
|
||||
#[derive(Default)]
|
||||
pub struct Extensions {
|
||||
// If extensions are never used, no need to carry around an empty HashMap.
|
||||
// That's 3 words. Instead, this is only 1 word.
|
||||
map: Option<Box<AnyMap>>,
|
||||
}
|
||||
|
||||
impl Extensions {
|
||||
/// Create an empty `Extensions`.
|
||||
#[inline]
|
||||
pub fn new() -> Extensions {
|
||||
Extensions { map: None }
|
||||
}
|
||||
|
||||
/// Insert a type into this `Extensions`.
|
||||
///
|
||||
/// If a extension of this type already existed, it will
|
||||
/// be returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// assert!(ext.insert(5i32).is_none());
|
||||
/// assert!(ext.insert(4u8).is_none());
|
||||
/// assert_eq!(ext.insert(9i32), Some(5i32));
|
||||
/// ```
|
||||
pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
|
||||
self.map
|
||||
.get_or_insert_with(|| Box::new(HashMap::default()))
|
||||
.insert(TypeId::of::<T>(), Box::new(val))
|
||||
.and_then(|boxed| {
|
||||
(boxed as Box<dyn Any + 'static>)
|
||||
.downcast()
|
||||
.ok()
|
||||
.map(|boxed| *boxed)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a reference to a type previously inserted on this `Extensions`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// assert!(ext.get::<i32>().is_none());
|
||||
/// ext.insert(5i32);
|
||||
///
|
||||
/// assert_eq!(ext.get::<i32>(), Some(&5i32));
|
||||
/// ```
|
||||
pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
|
||||
self.map
|
||||
.as_ref()
|
||||
.and_then(|map| map.get(&TypeId::of::<T>()))
|
||||
.and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref())
|
||||
}
|
||||
|
||||
/// Get a mutable reference to a type previously inserted on this `Extensions`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// ext.insert(String::from("Hello"));
|
||||
/// ext.get_mut::<String>().unwrap().push_str(" World");
|
||||
///
|
||||
/// assert_eq!(ext.get::<String>().unwrap(), "Hello World");
|
||||
/// ```
|
||||
pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
|
||||
self.map
|
||||
.as_mut()
|
||||
.and_then(|map| map.get_mut(&TypeId::of::<T>()))
|
||||
.and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut())
|
||||
}
|
||||
|
||||
/// Remove a type from this `Extensions`.
|
||||
///
|
||||
/// If a extension of this type existed, it will be returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// ext.insert(5i32);
|
||||
/// assert_eq!(ext.remove::<i32>(), Some(5i32));
|
||||
/// assert!(ext.get::<i32>().is_none());
|
||||
/// ```
|
||||
pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<T> {
|
||||
self.map
|
||||
.as_mut()
|
||||
.and_then(|map| map.remove(&TypeId::of::<T>()))
|
||||
.and_then(|boxed| {
|
||||
(boxed as Box<dyn Any + 'static>)
|
||||
.downcast()
|
||||
.ok()
|
||||
.map(|boxed| *boxed)
|
||||
})
|
||||
}
|
||||
|
||||
/// Clear the `Extensions` of all inserted extensions.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// ext.insert(5i32);
|
||||
/// ext.clear();
|
||||
///
|
||||
/// assert!(ext.get::<i32>().is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
if let Some(ref mut map) = self.map {
|
||||
map.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether the extension set is empty or not.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// assert!(ext.is_empty());
|
||||
/// ext.insert(5i32);
|
||||
/// assert!(!ext.is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.map
|
||||
.as_ref()
|
||||
.map_or(true, |map| map.is_empty())
|
||||
}
|
||||
|
||||
/// Get the numer of extensions available.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// assert_eq!(ext.len(), 0);
|
||||
/// ext.insert(5i32);
|
||||
/// assert_eq!(ext.len(), 1);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.map
|
||||
.as_ref()
|
||||
.map_or(0, |map| map.len())
|
||||
}
|
||||
|
||||
/// Extends `self` with another `Extensions`.
|
||||
///
|
||||
/// If an instance of a specific type exists in both, the one in `self` is overwritten with the
|
||||
/// one from `other`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext_a = Extensions::new();
|
||||
/// ext_a.insert(8u8);
|
||||
/// ext_a.insert(16u16);
|
||||
///
|
||||
/// let mut ext_b = Extensions::new();
|
||||
/// ext_b.insert(4u8);
|
||||
/// ext_b.insert("hello");
|
||||
///
|
||||
/// ext_a.extend(ext_b);
|
||||
/// assert_eq!(ext_a.len(), 3);
|
||||
/// assert_eq!(ext_a.get::<u8>(), Some(&4u8));
|
||||
/// assert_eq!(ext_a.get::<u16>(), Some(&16u16));
|
||||
/// assert_eq!(ext_a.get::<&'static str>().copied(), Some("hello"));
|
||||
/// ```
|
||||
pub fn extend(&mut self, other: Self) {
|
||||
if let Some(other) = other.map {
|
||||
if let Some(map) = &mut self.map {
|
||||
map.extend(*other);
|
||||
} else {
|
||||
self.map = Some(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Extensions {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Extensions").finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extensions() {
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct MyType(i32);
|
||||
|
||||
let mut extensions = Extensions::new();
|
||||
|
||||
extensions.insert(5i32);
|
||||
extensions.insert(MyType(10));
|
||||
|
||||
assert_eq!(extensions.get(), Some(&5i32));
|
||||
assert_eq!(extensions.get_mut(), Some(&mut 5i32));
|
||||
|
||||
assert_eq!(extensions.remove::<i32>(), Some(5i32));
|
||||
assert!(extensions.get::<i32>().is_none());
|
||||
|
||||
assert_eq!(extensions.get::<bool>(), None);
|
||||
assert_eq!(extensions.get(), Some(&MyType(10)));
|
||||
}
|
||||
3496
zeroidc/vendor/http/src/header/map.rs
vendored
Normal file
3496
zeroidc/vendor/http/src/header/map.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
172
zeroidc/vendor/http/src/header/mod.rs
vendored
Normal file
172
zeroidc/vendor/http/src/header/mod.rs
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
//! HTTP header types
|
||||
//!
|
||||
//! The module provides [`HeaderName`], [`HeaderMap`], and a number of types
|
||||
//! used for interacting with `HeaderMap`. These types allow representing both
|
||||
//! HTTP/1 and HTTP/2 headers.
|
||||
//!
|
||||
//! # `HeaderName`
|
||||
//!
|
||||
//! The `HeaderName` type represents both standard header names as well as
|
||||
//! custom header names. The type handles the case insensitive nature of header
|
||||
//! names and is used as the key portion of `HeaderMap`. Header names are
|
||||
//! normalized to lower case. In other words, when creating a `HeaderName` with
|
||||
//! a string, even if upper case characters are included, when getting a string
|
||||
//! representation of the `HeaderName`, it will be all lower case. This allows
|
||||
//! for faster `HeaderMap` comparison operations.
|
||||
//!
|
||||
//! The internal representation is optimized to efficiently handle the cases
|
||||
//! most commonly encountered when working with HTTP. Standard header names are
|
||||
//! special cased and are represented internally as an enum. Short custom
|
||||
//! headers will be stored directly in the `HeaderName` struct and will not
|
||||
//! incur any allocation overhead, however longer strings will require an
|
||||
//! allocation for storage.
|
||||
//!
|
||||
//! ## Limitations
|
||||
//!
|
||||
//! `HeaderName` has a max length of 32,768 for header names. Attempting to
|
||||
//! parse longer names will result in a panic.
|
||||
//!
|
||||
//! # `HeaderMap`
|
||||
//!
|
||||
//! `HeaderMap` is a map structure of header names highly optimized for use
|
||||
//! cases common with HTTP. It is a [multimap] structure, where each header name
|
||||
//! may have multiple associated header values. Given this, some of the APIs
|
||||
//! diverge from [`HashMap`].
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! Just like `HashMap` in Rust's stdlib, `HeaderMap` is based on [Robin Hood
|
||||
//! hashing]. This algorithm tends to reduce the worst case search times in the
|
||||
//! table and enables high load factors without seriously affecting performance.
|
||||
//! Internally, keys and values are stored in vectors. As such, each insertion
|
||||
//! will not incur allocation overhead. However, once the underlying vector
|
||||
//! storage is full, a larger vector must be allocated and all values copied.
|
||||
//!
|
||||
//! ## Deterministic ordering
|
||||
//!
|
||||
//! Unlike Rust's `HashMap`, values in `HeaderMap` are deterministically
|
||||
//! ordered. Roughly, values are ordered by insertion. This means that a
|
||||
//! function that deterministically operates on a header map can rely on the
|
||||
//! iteration order to remain consistent across processes and platforms.
|
||||
//!
|
||||
//! ## Adaptive hashing
|
||||
//!
|
||||
//! `HeaderMap` uses an adaptive hashing strategy in order to efficiently handle
|
||||
//! most common cases. All standard headers have statically computed hash values
|
||||
//! which removes the need to perform any hashing of these headers at runtime.
|
||||
//! The default hash function emphasizes performance over robustness. However,
|
||||
//! `HeaderMap` detects high collision rates and switches to a secure hash
|
||||
//! function in those events. The threshold is set such that only denial of
|
||||
//! service attacks should trigger it.
|
||||
//!
|
||||
//! ## Limitations
|
||||
//!
|
||||
//! `HeaderMap` can store a maximum of 32,768 headers (header name / value
|
||||
//! pairs). Attempting to insert more will result in a panic.
|
||||
//!
|
||||
//! [`HeaderName`]: struct.HeaderName.html
|
||||
//! [`HeaderMap`]: struct.HeaderMap.html
|
||||
//! [multimap]: https://en.wikipedia.org/wiki/Multimap
|
||||
//! [`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
|
||||
//! [Robin Hood hashing]: https://en.wikipedia.org/wiki/Hash_table#Robin_Hood_hashing
|
||||
|
||||
mod map;
|
||||
mod name;
|
||||
mod value;
|
||||
|
||||
pub use self::map::{
|
||||
AsHeaderName, Drain, Entry, GetAll, HeaderMap, IntoHeaderName, IntoIter, Iter, IterMut, Keys,
|
||||
OccupiedEntry, VacantEntry, ValueDrain, ValueIter, ValueIterMut, Values, ValuesMut,
|
||||
};
|
||||
pub use self::name::{HeaderName, InvalidHeaderName};
|
||||
pub use self::value::{HeaderValue, InvalidHeaderValue, ToStrError};
|
||||
|
||||
// Use header name constants
|
||||
pub use self::name::{
|
||||
ACCEPT,
|
||||
ACCEPT_CHARSET,
|
||||
ACCEPT_ENCODING,
|
||||
ACCEPT_LANGUAGE,
|
||||
ACCEPT_RANGES,
|
||||
ACCESS_CONTROL_ALLOW_CREDENTIALS,
|
||||
ACCESS_CONTROL_ALLOW_HEADERS,
|
||||
ACCESS_CONTROL_ALLOW_METHODS,
|
||||
ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||
ACCESS_CONTROL_EXPOSE_HEADERS,
|
||||
ACCESS_CONTROL_MAX_AGE,
|
||||
ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
ACCESS_CONTROL_REQUEST_METHOD,
|
||||
AGE,
|
||||
ALLOW,
|
||||
ALT_SVC,
|
||||
AUTHORIZATION,
|
||||
CACHE_CONTROL,
|
||||
CONNECTION,
|
||||
CONTENT_DISPOSITION,
|
||||
CONTENT_ENCODING,
|
||||
CONTENT_LANGUAGE,
|
||||
CONTENT_LENGTH,
|
||||
CONTENT_LOCATION,
|
||||
CONTENT_RANGE,
|
||||
CONTENT_SECURITY_POLICY,
|
||||
CONTENT_SECURITY_POLICY_REPORT_ONLY,
|
||||
CONTENT_TYPE,
|
||||
COOKIE,
|
||||
DNT,
|
||||
DATE,
|
||||
ETAG,
|
||||
EXPECT,
|
||||
EXPIRES,
|
||||
FORWARDED,
|
||||
FROM,
|
||||
HOST,
|
||||
IF_MATCH,
|
||||
IF_MODIFIED_SINCE,
|
||||
IF_NONE_MATCH,
|
||||
IF_RANGE,
|
||||
IF_UNMODIFIED_SINCE,
|
||||
LAST_MODIFIED,
|
||||
LINK,
|
||||
LOCATION,
|
||||
MAX_FORWARDS,
|
||||
ORIGIN,
|
||||
PRAGMA,
|
||||
PROXY_AUTHENTICATE,
|
||||
PROXY_AUTHORIZATION,
|
||||
PUBLIC_KEY_PINS,
|
||||
PUBLIC_KEY_PINS_REPORT_ONLY,
|
||||
RANGE,
|
||||
REFERER,
|
||||
REFERRER_POLICY,
|
||||
REFRESH,
|
||||
RETRY_AFTER,
|
||||
SEC_WEBSOCKET_ACCEPT,
|
||||
SEC_WEBSOCKET_EXTENSIONS,
|
||||
SEC_WEBSOCKET_KEY,
|
||||
SEC_WEBSOCKET_PROTOCOL,
|
||||
SEC_WEBSOCKET_VERSION,
|
||||
SERVER,
|
||||
SET_COOKIE,
|
||||
STRICT_TRANSPORT_SECURITY,
|
||||
TE,
|
||||
TRAILER,
|
||||
TRANSFER_ENCODING,
|
||||
UPGRADE,
|
||||
UPGRADE_INSECURE_REQUESTS,
|
||||
USER_AGENT,
|
||||
VARY,
|
||||
VIA,
|
||||
WARNING,
|
||||
WWW_AUTHENTICATE,
|
||||
X_CONTENT_TYPE_OPTIONS,
|
||||
X_DNS_PREFETCH_CONTROL,
|
||||
X_FRAME_OPTIONS,
|
||||
X_XSS_PROTECTION,
|
||||
};
|
||||
|
||||
/// Maximum length of a header name
|
||||
///
|
||||
/// Generally, 64kb for a header name is WAY too much than would ever be needed
|
||||
/// in practice. Restricting it to this size enables using `u16` values to
|
||||
/// represent offsets when dealing with header names.
|
||||
const MAX_HEADER_NAME_LEN: usize = (1 << 16) - 1;
|
||||
1856
zeroidc/vendor/http/src/header/name.rs
vendored
Normal file
1856
zeroidc/vendor/http/src/header/name.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
795
zeroidc/vendor/http/src/header/value.rs
vendored
Normal file
795
zeroidc/vendor/http/src/header/value.rs
vendored
Normal file
@@ -0,0 +1,795 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error;
|
||||
use std::fmt::Write;
|
||||
use std::str::FromStr;
|
||||
use std::{cmp, fmt, mem, str};
|
||||
|
||||
use crate::header::name::HeaderName;
|
||||
|
||||
/// Represents an HTTP header field value.
|
||||
///
|
||||
/// In practice, HTTP header field values are usually valid ASCII. However, the
|
||||
/// HTTP spec allows for a header value to contain opaque bytes as well. In this
|
||||
/// case, the header field value is not able to be represented as a string.
|
||||
///
|
||||
/// To handle this, the `HeaderValue` is useable as a type and can be compared
|
||||
/// with strings and implements `Debug`. A `to_str` fn is provided that returns
|
||||
/// an `Err` if the header value contains non visible ascii characters.
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct HeaderValue {
|
||||
inner: Bytes,
|
||||
is_sensitive: bool,
|
||||
}
|
||||
|
||||
/// A possible error when converting a `HeaderValue` from a string or byte
|
||||
/// slice.
|
||||
pub struct InvalidHeaderValue {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
/// A possible error when converting a `HeaderValue` to a string representation.
|
||||
///
|
||||
/// Header field values may contain opaque bytes, in which case it is not
|
||||
/// possible to represent the value as a string.
|
||||
#[derive(Debug)]
|
||||
pub struct ToStrError {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl HeaderValue {
|
||||
/// Convert a static string to a `HeaderValue`.
|
||||
///
|
||||
/// This function will not perform any copying, however the string is
|
||||
/// checked to ensure that no invalid characters are present. Only visible
|
||||
/// ASCII characters (32-127) are permitted.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the argument contains invalid header value
|
||||
/// characters.
|
||||
///
|
||||
/// Until [Allow panicking in constants](https://github.com/rust-lang/rfcs/pull/2345)
|
||||
/// makes its way into stable, the panic message at compile-time is
|
||||
/// going to look cryptic, but should at least point at your header value:
|
||||
///
|
||||
/// ```text
|
||||
/// error: any use of this value will cause an error
|
||||
/// --> http/src/header/value.rs:67:17
|
||||
/// |
|
||||
/// 67 | ([] as [u8; 0])[0]; // Invalid header value
|
||||
/// | ^^^^^^^^^^^^^^^^^^
|
||||
/// | |
|
||||
/// | index out of bounds: the length is 0 but the index is 0
|
||||
/// | inside `HeaderValue::from_static` at http/src/header/value.rs:67:17
|
||||
/// | inside `INVALID_HEADER` at src/main.rs:73:33
|
||||
/// |
|
||||
/// ::: src/main.rs:73:1
|
||||
/// |
|
||||
/// 73 | const INVALID_HEADER: HeaderValue = HeaderValue::from_static("жsome value");
|
||||
/// | ----------------------------------------------------------------------------
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_static("hello");
|
||||
/// assert_eq!(val, "hello");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(unconditional_panic)] // required for the panic circumvention
|
||||
pub const fn from_static(src: &'static str) -> HeaderValue {
|
||||
let bytes = src.as_bytes();
|
||||
let mut i = 0;
|
||||
while i < bytes.len() {
|
||||
if !is_visible_ascii(bytes[i]) {
|
||||
([] as [u8; 0])[0]; // Invalid header value
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
HeaderValue {
|
||||
inner: Bytes::from_static(bytes),
|
||||
is_sensitive: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert a string to a `HeaderValue`.
|
||||
///
|
||||
/// If the argument contains invalid header value characters, an error is
|
||||
/// returned. Only visible ASCII characters (32-127) are permitted. Use
|
||||
/// `from_bytes` to create a `HeaderValue` that includes opaque octets
|
||||
/// (128-255).
|
||||
///
|
||||
/// This function is intended to be replaced in the future by a `TryFrom`
|
||||
/// implementation once the trait is stabilized in std.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_str("hello").unwrap();
|
||||
/// assert_eq!(val, "hello");
|
||||
/// ```
|
||||
///
|
||||
/// An invalid value
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_str("\n");
|
||||
/// assert!(val.is_err());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_str(src: &str) -> Result<HeaderValue, InvalidHeaderValue> {
|
||||
HeaderValue::try_from_generic(src, |s| Bytes::copy_from_slice(s.as_bytes()))
|
||||
}
|
||||
|
||||
/// Converts a HeaderName into a HeaderValue
|
||||
///
|
||||
/// Since every valid HeaderName is a valid HeaderValue this is done infallibly.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::{HeaderValue, HeaderName};
|
||||
/// # use http::header::ACCEPT;
|
||||
/// let val = HeaderValue::from_name(ACCEPT);
|
||||
/// assert_eq!(val, HeaderValue::from_bytes(b"accept").unwrap());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_name(name: HeaderName) -> HeaderValue {
|
||||
name.into()
|
||||
}
|
||||
|
||||
/// Attempt to convert a byte slice to a `HeaderValue`.
|
||||
///
|
||||
/// If the argument contains invalid header value bytes, an error is
|
||||
/// returned. Only byte values between 32 and 255 (inclusive) are permitted,
|
||||
/// excluding byte 127 (DEL).
|
||||
///
|
||||
/// This function is intended to be replaced in the future by a `TryFrom`
|
||||
/// implementation once the trait is stabilized in std.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_bytes(b"hello\xfa").unwrap();
|
||||
/// assert_eq!(val, &b"hello\xfa"[..]);
|
||||
/// ```
|
||||
///
|
||||
/// An invalid value
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_bytes(b"\n");
|
||||
/// assert!(val.is_err());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_bytes(src: &[u8]) -> Result<HeaderValue, InvalidHeaderValue> {
|
||||
HeaderValue::try_from_generic(src, Bytes::copy_from_slice)
|
||||
}
|
||||
|
||||
/// Attempt to convert a `Bytes` buffer to a `HeaderValue`.
|
||||
///
|
||||
/// This will try to prevent a copy if the type passed is the type used
|
||||
/// internally, and will copy the data if it is not.
|
||||
pub fn from_maybe_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue>
|
||||
where
|
||||
T: AsRef<[u8]> + 'static,
|
||||
{
|
||||
if_downcast_into!(T, Bytes, src, {
|
||||
return HeaderValue::from_shared(src);
|
||||
});
|
||||
|
||||
HeaderValue::from_bytes(src.as_ref())
|
||||
}
|
||||
|
||||
/// Convert a `Bytes` directly into a `HeaderValue` without validating.
|
||||
///
|
||||
/// This function does NOT validate that illegal bytes are not contained
|
||||
/// within the buffer.
|
||||
pub unsafe fn from_maybe_shared_unchecked<T>(src: T) -> HeaderValue
|
||||
where
|
||||
T: AsRef<[u8]> + 'static,
|
||||
{
|
||||
if cfg!(debug_assertions) {
|
||||
match HeaderValue::from_maybe_shared(src) {
|
||||
Ok(val) => val,
|
||||
Err(_err) => {
|
||||
panic!("HeaderValue::from_maybe_shared_unchecked() with invalid bytes");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if_downcast_into!(T, Bytes, src, {
|
||||
return HeaderValue {
|
||||
inner: src,
|
||||
is_sensitive: false,
|
||||
};
|
||||
});
|
||||
|
||||
let src = Bytes::copy_from_slice(src.as_ref());
|
||||
HeaderValue {
|
||||
inner: src,
|
||||
is_sensitive: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValue> {
|
||||
HeaderValue::try_from_generic(src, std::convert::identity)
|
||||
}
|
||||
|
||||
fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>(src: T, into: F) -> Result<HeaderValue, InvalidHeaderValue> {
|
||||
for &b in src.as_ref() {
|
||||
if !is_valid(b) {
|
||||
return Err(InvalidHeaderValue { _priv: () });
|
||||
}
|
||||
}
|
||||
Ok(HeaderValue {
|
||||
inner: into(src),
|
||||
is_sensitive: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Yields a `&str` slice if the `HeaderValue` only contains visible ASCII
|
||||
/// chars.
|
||||
///
|
||||
/// This function will perform a scan of the header value, checking all the
|
||||
/// characters.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_static("hello");
|
||||
/// assert_eq!(val.to_str().unwrap(), "hello");
|
||||
/// ```
|
||||
pub fn to_str(&self) -> Result<&str, ToStrError> {
|
||||
let bytes = self.as_ref();
|
||||
|
||||
for &b in bytes {
|
||||
if !is_visible_ascii(b) {
|
||||
return Err(ToStrError { _priv: () });
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { Ok(str::from_utf8_unchecked(bytes)) }
|
||||
}
|
||||
|
||||
/// Returns the length of `self`.
|
||||
///
|
||||
/// This length is in bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_static("hello");
|
||||
/// assert_eq!(val.len(), 5);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.as_ref().len()
|
||||
}
|
||||
|
||||
/// Returns true if the `HeaderValue` has a length of zero bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_static("");
|
||||
/// assert!(val.is_empty());
|
||||
///
|
||||
/// let val = HeaderValue::from_static("hello");
|
||||
/// assert!(!val.is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Converts a `HeaderValue` to a byte slice.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_static("hello");
|
||||
/// assert_eq!(val.as_bytes(), b"hello");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.as_ref()
|
||||
}
|
||||
|
||||
/// Mark that the header value represents sensitive information.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let mut val = HeaderValue::from_static("my secret");
|
||||
///
|
||||
/// val.set_sensitive(true);
|
||||
/// assert!(val.is_sensitive());
|
||||
///
|
||||
/// val.set_sensitive(false);
|
||||
/// assert!(!val.is_sensitive());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn set_sensitive(&mut self, val: bool) {
|
||||
self.is_sensitive = val;
|
||||
}
|
||||
|
||||
/// Returns `true` if the value represents sensitive data.
|
||||
///
|
||||
/// Sensitive data could represent passwords or other data that should not
|
||||
/// be stored on disk or in memory. By marking header values as sensitive,
|
||||
/// components using this crate can be instructed to treat them with special
|
||||
/// care for security reasons. For example, caches can avoid storing
|
||||
/// sensitive values, and HPACK encoders used by HTTP/2.0 implementations
|
||||
/// can choose not to compress them.
|
||||
///
|
||||
/// Additionally, sensitive values will be masked by the `Debug`
|
||||
/// implementation of `HeaderValue`.
|
||||
///
|
||||
/// Note that sensitivity is not factored into equality or ordering.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let mut val = HeaderValue::from_static("my secret");
|
||||
///
|
||||
/// val.set_sensitive(true);
|
||||
/// assert!(val.is_sensitive());
|
||||
///
|
||||
/// val.set_sensitive(false);
|
||||
/// assert!(!val.is_sensitive());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_sensitive(&self) -> bool {
|
||||
self.is_sensitive
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for HeaderValue {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for HeaderValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_sensitive {
|
||||
f.write_str("Sensitive")
|
||||
} else {
|
||||
f.write_str("\"")?;
|
||||
let mut from = 0;
|
||||
let bytes = self.as_bytes();
|
||||
for (i, &b) in bytes.iter().enumerate() {
|
||||
if !is_visible_ascii(b) || b == b'"' {
|
||||
if from != i {
|
||||
f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?;
|
||||
}
|
||||
if b == b'"' {
|
||||
f.write_str("\\\"")?;
|
||||
} else {
|
||||
write!(f, "\\x{:x}", b)?;
|
||||
}
|
||||
from = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?;
|
||||
f.write_str("\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HeaderName> for HeaderValue {
|
||||
#[inline]
|
||||
fn from(h: HeaderName) -> HeaderValue {
|
||||
HeaderValue {
|
||||
inner: h.into_bytes(),
|
||||
is_sensitive: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_integers {
|
||||
($($name:ident: $t:ident => $max_len:expr),*) => {$(
|
||||
impl From<$t> for HeaderValue {
|
||||
fn from(num: $t) -> HeaderValue {
|
||||
let mut buf = if mem::size_of::<BytesMut>() - 1 < $max_len {
|
||||
// On 32bit platforms, BytesMut max inline size
|
||||
// is 15 bytes, but the $max_len could be bigger.
|
||||
//
|
||||
// The likelihood of the number *actually* being
|
||||
// that big is very small, so only allocate
|
||||
// if the number needs that space.
|
||||
//
|
||||
// The largest decimal number in 15 digits:
|
||||
// It wold be 10.pow(15) - 1, but this is a constant
|
||||
// version.
|
||||
if num as u64 > 999_999_999_999_999_999 {
|
||||
BytesMut::with_capacity($max_len)
|
||||
} else {
|
||||
// fits inline...
|
||||
BytesMut::new()
|
||||
}
|
||||
} else {
|
||||
// full value fits inline, so don't allocate!
|
||||
BytesMut::new()
|
||||
};
|
||||
let _ = buf.write_str(::itoa::Buffer::new().format(num));
|
||||
HeaderValue {
|
||||
inner: buf.freeze(),
|
||||
is_sensitive: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn $name() {
|
||||
let n: $t = 55;
|
||||
let val = HeaderValue::from(n);
|
||||
assert_eq!(val, &n.to_string());
|
||||
|
||||
let n = ::std::$t::MAX;
|
||||
let val = HeaderValue::from(n);
|
||||
assert_eq!(val, &n.to_string());
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
from_integers! {
|
||||
// integer type => maximum decimal length
|
||||
|
||||
// u8 purposely left off... HeaderValue::from(b'3') could be confusing
|
||||
from_u16: u16 => 5,
|
||||
from_i16: i16 => 6,
|
||||
from_u32: u32 => 10,
|
||||
from_i32: i32 => 11,
|
||||
from_u64: u64 => 20,
|
||||
from_i64: i64 => 20
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
from_integers! {
|
||||
from_usize: usize => 5,
|
||||
from_isize: isize => 6
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
from_integers! {
|
||||
from_usize: usize => 10,
|
||||
from_isize: isize => 11
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
from_integers! {
|
||||
from_usize: usize => 20,
|
||||
from_isize: isize => 20
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod from_header_name_tests {
|
||||
use super::*;
|
||||
use crate::header::map::HeaderMap;
|
||||
use crate::header::name;
|
||||
|
||||
#[test]
|
||||
fn it_can_insert_header_name_as_header_value() {
|
||||
let mut map = HeaderMap::new();
|
||||
map.insert(name::UPGRADE, name::SEC_WEBSOCKET_PROTOCOL.into());
|
||||
map.insert(
|
||||
name::ACCEPT,
|
||||
name::HeaderName::from_bytes(b"hello-world").unwrap().into(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
map.get(name::UPGRADE).unwrap(),
|
||||
HeaderValue::from_bytes(b"sec-websocket-protocol").unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
map.get(name::ACCEPT).unwrap(),
|
||||
HeaderValue::from_bytes(b"hello-world").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for HeaderValue {
|
||||
type Err = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<HeaderValue, Self::Err> {
|
||||
HeaderValue::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a HeaderValue> for HeaderValue {
|
||||
#[inline]
|
||||
fn from(t: &'a HeaderValue) -> Self {
|
||||
t.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a str) -> Result<Self, Self::Error> {
|
||||
t.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a String> for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
#[inline]
|
||||
fn try_from(s: &'a String) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
HeaderValue::from_bytes(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: String) -> Result<Self, Self::Error> {
|
||||
HeaderValue::from_shared(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
HeaderValue::from_shared(vec.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod try_from_header_name_tests {
|
||||
use super::*;
|
||||
use crate::header::name;
|
||||
|
||||
#[test]
|
||||
fn it_converts_using_try_from() {
|
||||
assert_eq!(
|
||||
HeaderValue::try_from(name::UPGRADE).unwrap(),
|
||||
HeaderValue::from_bytes(b"upgrade").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const fn is_visible_ascii(b: u8) -> bool {
|
||||
b >= 32 && b < 127 || b == b'\t'
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_valid(b: u8) -> bool {
|
||||
b >= 32 && b != 127 || b == b'\t'
|
||||
}
|
||||
|
||||
impl fmt::Debug for InvalidHeaderValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("InvalidHeaderValue")
|
||||
// skip _priv noise
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InvalidHeaderValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("failed to parse header value")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for InvalidHeaderValue {}
|
||||
|
||||
impl fmt::Display for ToStrError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("failed to convert header to a str")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ToStrError {}
|
||||
|
||||
// ===== PartialEq / PartialOrd =====
|
||||
|
||||
impl PartialEq for HeaderValue {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for HeaderValue {}
|
||||
|
||||
impl PartialOrd for HeaderValue {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
self.inner.partial_cmp(&other.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for HeaderValue {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.inner.cmp(&other.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for HeaderValue {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.inner == other.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<[u8]> for HeaderValue {
|
||||
#[inline]
|
||||
fn eq(&self, other: &[u8]) -> bool {
|
||||
self.inner == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<str> for HeaderValue {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
|
||||
(*self.inner).partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<[u8]> for HeaderValue {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
|
||||
(*self.inner).partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<HeaderValue> for str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<HeaderValue> for [u8] {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<HeaderValue> for str {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<HeaderValue> for [u8] {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
self.partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for HeaderValue {
|
||||
#[inline]
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
*self == &other[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<String> for HeaderValue {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
self.inner.partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<HeaderValue> for String {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<HeaderValue> for String {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<HeaderValue> for &'a HeaderValue {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
**self == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<HeaderValue> for &'a HeaderValue {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
(**self).partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue
|
||||
where
|
||||
HeaderValue: PartialEq<T>,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'a T) -> bool {
|
||||
*self == **other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue
|
||||
where
|
||||
HeaderValue: PartialOrd<T>,
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
|
||||
self.partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<HeaderValue> for &'a str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<HeaderValue> for &'a str {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from() {
|
||||
HeaderValue::try_from(vec![127]).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let cases = &[
|
||||
("hello", "\"hello\""),
|
||||
("hello \"world\"", "\"hello \\\"world\\\"\""),
|
||||
("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""),
|
||||
];
|
||||
|
||||
for &(value, expected) in cases {
|
||||
let val = HeaderValue::from_bytes(value.as_bytes()).unwrap();
|
||||
let actual = format!("{:?}", val);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
let mut sensitive = HeaderValue::from_static("password");
|
||||
sensitive.set_sensitive(true);
|
||||
assert_eq!("Sensitive", format!("{:?}", sensitive));
|
||||
}
|
||||
211
zeroidc/vendor/http/src/lib.rs
vendored
Normal file
211
zeroidc/vendor/http/src/lib.rs
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
#![doc(html_root_url = "https://docs.rs/http/0.2.8")]
|
||||
|
||||
//! A general purpose library of common HTTP types
|
||||
//!
|
||||
//! This crate is a general purpose library for common types found when working
|
||||
//! with the HTTP protocol. You'll find `Request` and `Response` types for
|
||||
//! working as either a client or a server as well as all of their components.
|
||||
//! Notably you'll find `Uri` for what a `Request` is requesting, a `Method`
|
||||
//! for how it's being requested, a `StatusCode` for what sort of response came
|
||||
//! back, a `Version` for how this was communicated, and
|
||||
//! `HeaderName`/`HeaderValue` definitions to get grouped in a `HeaderMap` to
|
||||
//! work with request/response headers.
|
||||
//!
|
||||
//! You will notably *not* find an implementation of sending requests or
|
||||
//! spinning up a server in this crate. It's intended that this crate is the
|
||||
//! "standard library" for HTTP clients and servers without dictating any
|
||||
//! particular implementation. Note that this crate is still early on in its
|
||||
//! lifecycle so the support libraries that integrate with the `http` crate are
|
||||
//! a work in progress! Stay tuned and we'll be sure to highlight crates here
|
||||
//! in the future.
|
||||
//!
|
||||
//! ## Requests and Responses
|
||||
//!
|
||||
//! Perhaps the main two types in this crate are the `Request` and `Response`
|
||||
//! types. A `Request` could either be constructed to get sent off as a client
|
||||
//! or it can also be received to generate a `Response` for a server. Similarly
|
||||
//! as a client a `Response` is what you get after sending a `Request`, whereas
|
||||
//! on a server you'll be manufacturing a `Response` to send back to the client.
|
||||
//!
|
||||
//! Each type has a number of accessors for the component fields. For as a
|
||||
//! server you might want to inspect a requests URI to dispatch it:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::{Request, Response};
|
||||
//!
|
||||
//! fn response(req: Request<()>) -> http::Result<Response<()>> {
|
||||
//! match req.uri().path() {
|
||||
//! "/" => index(req),
|
||||
//! "/foo" => foo(req),
|
||||
//! "/bar" => bar(req),
|
||||
//! _ => not_found(req),
|
||||
//! }
|
||||
//! }
|
||||
//! # fn index(_req: Request<()>) -> http::Result<Response<()>> { panic!() }
|
||||
//! # fn foo(_req: Request<()>) -> http::Result<Response<()>> { panic!() }
|
||||
//! # fn bar(_req: Request<()>) -> http::Result<Response<()>> { panic!() }
|
||||
//! # fn not_found(_req: Request<()>) -> http::Result<Response<()>> { panic!() }
|
||||
//! ```
|
||||
//!
|
||||
//! On a `Request` you'll also find accessors like `method` to return a
|
||||
//! `Method` and `headers` to inspect the various headers. A `Response`
|
||||
//! has similar methods for headers, the status code, etc.
|
||||
//!
|
||||
//! In addition to getters, request/response types also have mutable accessors
|
||||
//! to edit the request/response:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::{HeaderValue, Response, StatusCode};
|
||||
//! use http::header::CONTENT_TYPE;
|
||||
//!
|
||||
//! fn add_server_headers<T>(response: &mut Response<T>) {
|
||||
//! response.headers_mut()
|
||||
//! .insert(CONTENT_TYPE, HeaderValue::from_static("text/html"));
|
||||
//! *response.status_mut() = StatusCode::OK;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! And finally, one of the most important aspects of requests/responses, the
|
||||
//! body! The `Request` and `Response` types in this crate are *generic* in
|
||||
//! what their body is. This allows downstream libraries to use different
|
||||
//! representations such as `Request<Vec<u8>>`, `Response<impl Read>`,
|
||||
//! `Request<impl Stream<Item = Vec<u8>, Error = _>>`, or even
|
||||
//! `Response<MyCustomType>` where the custom type was deserialized from JSON.
|
||||
//!
|
||||
//! The body representation is intentionally flexible to give downstream
|
||||
//! libraries maximal flexibility in implementing the body as appropriate.
|
||||
//!
|
||||
//! ## HTTP Headers
|
||||
//!
|
||||
//! Another major piece of functionality in this library is HTTP header
|
||||
//! interpretation and generation. The `HeaderName` type serves as a way to
|
||||
//! define header *names*, or what's to the left of the colon. A `HeaderValue`
|
||||
//! conversely is the header *value*, or what's to the right of a colon.
|
||||
//!
|
||||
//! For example, if you have an HTTP request that looks like:
|
||||
//!
|
||||
//! ```http
|
||||
//! GET /foo HTTP/1.1
|
||||
//! Accept: text/html
|
||||
//! ```
|
||||
//!
|
||||
//! Then `"Accept"` is a `HeaderName` while `"text/html"` is a `HeaderValue`.
|
||||
//! Each of these is a dedicated type to allow for a number of interesting
|
||||
//! optimizations and to also encode the static guarantees of each type. For
|
||||
//! example a `HeaderName` is always a valid `&str`, but a `HeaderValue` may
|
||||
//! not be valid UTF-8.
|
||||
//!
|
||||
//! The most common header names are already defined for you as constant values
|
||||
//! in the `header` module of this crate. For example:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::header::{self, HeaderName};
|
||||
//!
|
||||
//! let name: HeaderName = header::ACCEPT;
|
||||
//! assert_eq!(name.as_str(), "accept");
|
||||
//! ```
|
||||
//!
|
||||
//! You can, however, also parse header names from strings:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::header::{self, HeaderName};
|
||||
//!
|
||||
//! let name = "Accept".parse::<HeaderName>().unwrap();
|
||||
//! assert_eq!(name, header::ACCEPT);
|
||||
//! ```
|
||||
//!
|
||||
//! Header values can be created from string literals through the `from_static`
|
||||
//! function:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::HeaderValue;
|
||||
//!
|
||||
//! let value = HeaderValue::from_static("text/html");
|
||||
//! assert_eq!(value.as_bytes(), b"text/html");
|
||||
//! ```
|
||||
//!
|
||||
//! And header values can also be parsed like names:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::HeaderValue;
|
||||
//!
|
||||
//! let value = "text/html";
|
||||
//! let value = value.parse::<HeaderValue>().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! Most HTTP requests and responses tend to come with more than one header, so
|
||||
//! it's not too useful to just work with names and values only! This crate also
|
||||
//! provides a `HeaderMap` type which is a specialized hash map for keys as
|
||||
//! `HeaderName` and generic values. This type, like header names, is optimized
|
||||
//! for common usage but should continue to scale with your needs over time.
|
||||
//!
|
||||
//! # URIs
|
||||
//!
|
||||
//! Each HTTP `Request` has an associated URI with it. This may just be a path
|
||||
//! like `/index.html` but it could also be an absolute URL such as
|
||||
//! `https://www.rust-lang.org/index.html`. A `URI` has a number of accessors to
|
||||
//! interpret it:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::Uri;
|
||||
//! use http::uri::Scheme;
|
||||
//!
|
||||
//! let uri = "https://www.rust-lang.org/index.html".parse::<Uri>().unwrap();
|
||||
//!
|
||||
//! assert_eq!(uri.scheme(), Some(&Scheme::HTTPS));
|
||||
//! assert_eq!(uri.host(), Some("www.rust-lang.org"));
|
||||
//! assert_eq!(uri.path(), "/index.html");
|
||||
//! assert_eq!(uri.query(), None);
|
||||
//! ```
|
||||
|
||||
#![deny(warnings, missing_docs, missing_debug_implementations)]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate doc_comment;
|
||||
|
||||
#[cfg(test)]
|
||||
doctest!("../README.md");
|
||||
|
||||
#[macro_use]
|
||||
mod convert;
|
||||
|
||||
pub mod header;
|
||||
pub mod method;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod status;
|
||||
pub mod uri;
|
||||
pub mod version;
|
||||
|
||||
mod byte_str;
|
||||
mod error;
|
||||
mod extensions;
|
||||
|
||||
pub use crate::error::{Error, Result};
|
||||
pub use crate::extensions::Extensions;
|
||||
#[doc(no_inline)]
|
||||
pub use crate::header::{HeaderMap, HeaderValue};
|
||||
pub use crate::method::Method;
|
||||
pub use crate::request::Request;
|
||||
pub use crate::response::Response;
|
||||
pub use crate::status::StatusCode;
|
||||
pub use crate::uri::Uri;
|
||||
pub use crate::version::Version;
|
||||
|
||||
fn _assert_types() {
|
||||
fn assert_send<T: Send>() {}
|
||||
fn assert_sync<T: Sync>() {}
|
||||
|
||||
assert_send::<Request<()>>();
|
||||
assert_send::<Response<()>>();
|
||||
|
||||
assert_sync::<Request<()>>();
|
||||
assert_sync::<Response<()>>();
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
/// Private trait to this crate to prevent traits from being implemented in
|
||||
/// downstream crates.
|
||||
pub trait Sealed {}
|
||||
}
|
||||
473
zeroidc/vendor/http/src/method.rs
vendored
Normal file
473
zeroidc/vendor/http/src/method.rs
vendored
Normal file
@@ -0,0 +1,473 @@
|
||||
//! The HTTP request method
|
||||
//!
|
||||
//! This module contains HTTP-method related structs and errors and such. The
|
||||
//! main type of this module, `Method`, is also reexported at the root of the
|
||||
//! crate as `http::Method` and is intended for import through that location
|
||||
//! primarily.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use http::Method;
|
||||
//!
|
||||
//! assert_eq!(Method::GET, Method::from_bytes(b"GET").unwrap());
|
||||
//! assert!(Method::GET.is_idempotent());
|
||||
//! assert_eq!(Method::POST.as_str(), "POST");
|
||||
//! ```
|
||||
|
||||
use self::Inner::*;
|
||||
use self::extension::{InlineExtension, AllocatedExtension};
|
||||
|
||||
use std::convert::AsRef;
|
||||
use std::error::Error;
|
||||
use std::str::FromStr;
|
||||
use std::convert::TryFrom;
|
||||
use std::{fmt, str};
|
||||
|
||||
/// The Request Method (VERB)
|
||||
///
|
||||
/// This type also contains constants for a number of common HTTP methods such
|
||||
/// as GET, POST, etc.
|
||||
///
|
||||
/// Currently includes 8 variants representing the 8 methods defined in
|
||||
/// [RFC 7230](https://tools.ietf.org/html/rfc7231#section-4.1), plus PATCH,
|
||||
/// and an Extension variant for all extensions.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use http::Method;
|
||||
///
|
||||
/// assert_eq!(Method::GET, Method::from_bytes(b"GET").unwrap());
|
||||
/// assert!(Method::GET.is_idempotent());
|
||||
/// assert_eq!(Method::POST.as_str(), "POST");
|
||||
/// ```
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Method(Inner);
|
||||
|
||||
/// A possible error value when converting `Method` from bytes.
|
||||
pub struct InvalidMethod {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
enum Inner {
|
||||
Options,
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Head,
|
||||
Trace,
|
||||
Connect,
|
||||
Patch,
|
||||
// If the extension is short enough, store it inline
|
||||
ExtensionInline(InlineExtension),
|
||||
// Otherwise, allocate it
|
||||
ExtensionAllocated(AllocatedExtension),
|
||||
}
|
||||
|
||||
|
||||
impl Method {
|
||||
/// GET
|
||||
pub const GET: Method = Method(Get);
|
||||
|
||||
/// POST
|
||||
pub const POST: Method = Method(Post);
|
||||
|
||||
/// PUT
|
||||
pub const PUT: Method = Method(Put);
|
||||
|
||||
/// DELETE
|
||||
pub const DELETE: Method = Method(Delete);
|
||||
|
||||
/// HEAD
|
||||
pub const HEAD: Method = Method(Head);
|
||||
|
||||
/// OPTIONS
|
||||
pub const OPTIONS: Method = Method(Options);
|
||||
|
||||
/// CONNECT
|
||||
pub const CONNECT: Method = Method(Connect);
|
||||
|
||||
/// PATCH
|
||||
pub const PATCH: Method = Method(Patch);
|
||||
|
||||
/// TRACE
|
||||
pub const TRACE: Method = Method(Trace);
|
||||
|
||||
/// Converts a slice of bytes to an HTTP method.
|
||||
pub fn from_bytes(src: &[u8]) -> Result<Method, InvalidMethod> {
|
||||
match src.len() {
|
||||
0 => Err(InvalidMethod::new()),
|
||||
3 => match src {
|
||||
b"GET" => Ok(Method(Get)),
|
||||
b"PUT" => Ok(Method(Put)),
|
||||
_ => Method::extension_inline(src),
|
||||
},
|
||||
4 => match src {
|
||||
b"POST" => Ok(Method(Post)),
|
||||
b"HEAD" => Ok(Method(Head)),
|
||||
_ => Method::extension_inline(src),
|
||||
},
|
||||
5 => match src {
|
||||
b"PATCH" => Ok(Method(Patch)),
|
||||
b"TRACE" => Ok(Method(Trace)),
|
||||
_ => Method::extension_inline(src),
|
||||
},
|
||||
6 => match src {
|
||||
b"DELETE" => Ok(Method(Delete)),
|
||||
_ => Method::extension_inline(src),
|
||||
},
|
||||
7 => match src {
|
||||
b"OPTIONS" => Ok(Method(Options)),
|
||||
b"CONNECT" => Ok(Method(Connect)),
|
||||
_ => Method::extension_inline(src),
|
||||
},
|
||||
_ => {
|
||||
if src.len() < InlineExtension::MAX {
|
||||
Method::extension_inline(src)
|
||||
} else {
|
||||
let allocated = AllocatedExtension::new(src)?;
|
||||
|
||||
Ok(Method(ExtensionAllocated(allocated)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extension_inline(src: &[u8]) -> Result<Method, InvalidMethod> {
|
||||
let inline = InlineExtension::new(src)?;
|
||||
|
||||
Ok(Method(ExtensionInline(inline)))
|
||||
}
|
||||
|
||||
/// Whether a method is considered "safe", meaning the request is
|
||||
/// essentially read-only.
|
||||
///
|
||||
/// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.1)
|
||||
/// for more words.
|
||||
pub fn is_safe(&self) -> bool {
|
||||
match self.0 {
|
||||
Get | Head | Options | Trace => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a method is considered "idempotent", meaning the request has
|
||||
/// the same result if executed multiple times.
|
||||
///
|
||||
/// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.2) for
|
||||
/// more words.
|
||||
pub fn is_idempotent(&self) -> bool {
|
||||
match self.0 {
|
||||
Put | Delete => true,
|
||||
_ => self.is_safe(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a &str representation of the HTTP method
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self.0 {
|
||||
Options => "OPTIONS",
|
||||
Get => "GET",
|
||||
Post => "POST",
|
||||
Put => "PUT",
|
||||
Delete => "DELETE",
|
||||
Head => "HEAD",
|
||||
Trace => "TRACE",
|
||||
Connect => "CONNECT",
|
||||
Patch => "PATCH",
|
||||
ExtensionInline(ref inline) => inline.as_str(),
|
||||
ExtensionAllocated(ref allocated) => allocated.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Method {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a Method> for Method {
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'a Method) -> bool {
|
||||
self == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<Method> for &'a Method {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Method) -> bool {
|
||||
*self == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for Method {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_ref() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Method> for str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Method) -> bool {
|
||||
self == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for Method {
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'a str) -> bool {
|
||||
self.as_ref() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<Method> for &'a str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Method) -> bool {
|
||||
*self == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Method {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Method {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.write_str(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Method {
|
||||
#[inline]
|
||||
fn default() -> Method {
|
||||
Method::GET
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Method> for Method {
|
||||
#[inline]
|
||||
fn from(t: &'a Method) -> Self {
|
||||
t.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Method {
|
||||
type Error = InvalidMethod;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
Method::from_bytes(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Method {
|
||||
type Error = InvalidMethod;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a str) -> Result<Self, Self::Error> {
|
||||
TryFrom::try_from(t.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Method {
|
||||
type Err = InvalidMethod;
|
||||
|
||||
#[inline]
|
||||
fn from_str(t: &str) -> Result<Self, Self::Err> {
|
||||
TryFrom::try_from(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl InvalidMethod {
|
||||
fn new() -> InvalidMethod {
|
||||
InvalidMethod { _priv: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for InvalidMethod {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("InvalidMethod")
|
||||
// skip _priv noise
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InvalidMethod {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("invalid HTTP method")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for InvalidMethod {}
|
||||
|
||||
mod extension {
|
||||
use super::InvalidMethod;
|
||||
use std::str;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
// Invariant: the first self.1 bytes of self.0 are valid UTF-8.
|
||||
pub struct InlineExtension([u8; InlineExtension::MAX], u8);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
// Invariant: self.0 contains valid UTF-8.
|
||||
pub struct AllocatedExtension(Box<[u8]>);
|
||||
|
||||
impl InlineExtension {
|
||||
// Method::from_bytes() assumes this is at least 7
|
||||
pub const MAX: usize = 15;
|
||||
|
||||
pub fn new(src: &[u8]) -> Result<InlineExtension, InvalidMethod> {
|
||||
let mut data: [u8; InlineExtension::MAX] = Default::default();
|
||||
|
||||
write_checked(src, &mut data)?;
|
||||
|
||||
// Invariant: write_checked ensures that the first src.len() bytes
|
||||
// of data are valid UTF-8.
|
||||
Ok(InlineExtension(data, src.len() as u8))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
let InlineExtension(ref data, len) = self;
|
||||
// Safety: the invariant of InlineExtension ensures that the first
|
||||
// len bytes of data contain valid UTF-8.
|
||||
unsafe {str::from_utf8_unchecked(&data[..*len as usize])}
|
||||
}
|
||||
}
|
||||
|
||||
impl AllocatedExtension {
|
||||
pub fn new(src: &[u8]) -> Result<AllocatedExtension, InvalidMethod> {
|
||||
let mut data: Vec<u8> = vec![0; src.len()];
|
||||
|
||||
write_checked(src, &mut data)?;
|
||||
|
||||
// Invariant: data is exactly src.len() long and write_checked
|
||||
// ensures that the first src.len() bytes of data are valid UTF-8.
|
||||
Ok(AllocatedExtension(data.into_boxed_slice()))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
// Safety: the invariant of AllocatedExtension ensures that self.0
|
||||
// contains valid UTF-8.
|
||||
unsafe {str::from_utf8_unchecked(&self.0)}
|
||||
}
|
||||
}
|
||||
|
||||
// From the HTTP spec section 5.1.1, the HTTP method is case-sensitive and can
|
||||
// contain the following characters:
|
||||
//
|
||||
// ```
|
||||
// method = token
|
||||
// token = 1*tchar
|
||||
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
||||
// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
|
||||
// ```
|
||||
//
|
||||
// https://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01#Method
|
||||
//
|
||||
// Note that this definition means that any &[u8] that consists solely of valid
|
||||
// characters is also valid UTF-8 because the valid method characters are a
|
||||
// subset of the valid 1 byte UTF-8 encoding.
|
||||
const METHOD_CHARS: [u8; 256] = [
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 1x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 2x
|
||||
b'\0', b'\0', b'\0', b'!', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 3x
|
||||
b'\0', b'\0', b'*', b'+', b'\0', b'-', b'.', b'\0', b'0', b'1', // 4x
|
||||
b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'\0', b'\0', // 5x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'A', b'B', b'C', b'D', b'E', // 6x
|
||||
b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', // 7x
|
||||
b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', // 8x
|
||||
b'Z', b'\0', b'\0', b'\0', b'^', b'_', b'`', b'a', b'b', b'c', // 9x
|
||||
b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', // 10x
|
||||
b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', // 11x
|
||||
b'x', b'y', b'z', b'\0', b'|', b'\0', b'~', b'\0', b'\0', b'\0', // 12x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 13x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 14x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 15x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 16x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 17x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 18x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 19x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 20x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 21x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 22x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 23x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 24x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0' // 25x
|
||||
];
|
||||
|
||||
// write_checked ensures (among other things) that the first src.len() bytes
|
||||
// of dst are valid UTF-8
|
||||
fn write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod> {
|
||||
for (i, &b) in src.iter().enumerate() {
|
||||
let b = METHOD_CHARS[b as usize];
|
||||
|
||||
if b == 0 {
|
||||
return Err(InvalidMethod::new());
|
||||
}
|
||||
|
||||
dst[i] = b;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_method_eq() {
|
||||
assert_eq!(Method::GET, Method::GET);
|
||||
assert_eq!(Method::GET, "GET");
|
||||
assert_eq!(&Method::GET, "GET");
|
||||
|
||||
assert_eq!("GET", Method::GET);
|
||||
assert_eq!("GET", &Method::GET);
|
||||
|
||||
assert_eq!(&Method::GET, Method::GET);
|
||||
assert_eq!(Method::GET, &Method::GET);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_method() {
|
||||
assert!(Method::from_str("").is_err());
|
||||
assert!(Method::from_bytes(b"").is_err());
|
||||
assert!(Method::from_bytes(&[0xC0]).is_err()); // invalid utf-8
|
||||
assert!(Method::from_bytes(&[0x10]).is_err()); // invalid method characters
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_idempotent() {
|
||||
assert!(Method::OPTIONS.is_idempotent());
|
||||
assert!(Method::GET.is_idempotent());
|
||||
assert!(Method::PUT.is_idempotent());
|
||||
assert!(Method::DELETE.is_idempotent());
|
||||
assert!(Method::HEAD.is_idempotent());
|
||||
assert!(Method::TRACE.is_idempotent());
|
||||
|
||||
assert!(!Method::POST.is_idempotent());
|
||||
assert!(!Method::CONNECT.is_idempotent());
|
||||
assert!(!Method::PATCH.is_idempotent());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extention_method() {
|
||||
assert_eq!(Method::from_str("WOW").unwrap(), "WOW");
|
||||
assert_eq!(Method::from_str("wOw!!").unwrap(), "wOw!!");
|
||||
|
||||
let long_method = "This_is_a_very_long_method.It_is_valid_but_unlikely.";
|
||||
assert_eq!(Method::from_str(&long_method).unwrap(), long_method);
|
||||
}
|
||||
}
|
||||
1096
zeroidc/vendor/http/src/request.rs
vendored
Normal file
1096
zeroidc/vendor/http/src/request.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
799
zeroidc/vendor/http/src/response.rs
vendored
Normal file
799
zeroidc/vendor/http/src/response.rs
vendored
Normal file
@@ -0,0 +1,799 @@
|
||||
//! HTTP response types.
|
||||
//!
|
||||
//! This module contains structs related to HTTP responses, notably the
|
||||
//! `Response` type itself as well as a builder to create responses. Typically
|
||||
//! you'll import the `http::Response` type rather than reaching into this
|
||||
//! module itself.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Creating a `Response` to return
|
||||
//!
|
||||
//! ```
|
||||
//! use http::{Request, Response, StatusCode};
|
||||
//!
|
||||
//! fn respond_to(req: Request<()>) -> http::Result<Response<()>> {
|
||||
//! let mut builder = Response::builder()
|
||||
//! .header("Foo", "Bar")
|
||||
//! .status(StatusCode::OK);
|
||||
//!
|
||||
//! if req.headers().contains_key("Another-Header") {
|
||||
//! builder = builder.header("Another-Header", "Ack");
|
||||
//! }
|
||||
//!
|
||||
//! builder.body(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! A simple 404 handler
|
||||
//!
|
||||
//! ```
|
||||
//! use http::{Request, Response, StatusCode};
|
||||
//!
|
||||
//! fn not_found(_req: Request<()>) -> http::Result<Response<()>> {
|
||||
//! Response::builder()
|
||||
//! .status(StatusCode::NOT_FOUND)
|
||||
//! .body(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Or otherwise inspecting the result of a request:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use http::{Request, Response};
|
||||
//!
|
||||
//! fn get(url: &str) -> http::Result<Response<()>> {
|
||||
//! // ...
|
||||
//! # panic!()
|
||||
//! }
|
||||
//!
|
||||
//! let response = get("https://www.rust-lang.org/").unwrap();
|
||||
//!
|
||||
//! if !response.status().is_success() {
|
||||
//! panic!("failed to get a successful response status!");
|
||||
//! }
|
||||
//!
|
||||
//! if let Some(date) = response.headers().get("Date") {
|
||||
//! // we've got a `Date` header!
|
||||
//! }
|
||||
//!
|
||||
//! let body = response.body();
|
||||
//! // ...
|
||||
//! ```
|
||||
|
||||
use std::any::Any;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
|
||||
use crate::header::{HeaderMap, HeaderName, HeaderValue};
|
||||
use crate::status::StatusCode;
|
||||
use crate::version::Version;
|
||||
use crate::{Extensions, Result};
|
||||
|
||||
/// Represents an HTTP response
|
||||
///
|
||||
/// An HTTP response consists of a head and a potentially optional body. The body
|
||||
/// component is generic, enabling arbitrary types to represent the HTTP body.
|
||||
/// For example, the body could be `Vec<u8>`, a `Stream` of byte chunks, or a
|
||||
/// value that has been deserialized.
|
||||
///
|
||||
/// Typically you'll work with responses on the client side as the result of
|
||||
/// sending a `Request` and on the server you'll be generating a `Response` to
|
||||
/// send back to the client.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a `Response` to return
|
||||
///
|
||||
/// ```
|
||||
/// use http::{Request, Response, StatusCode};
|
||||
///
|
||||
/// fn respond_to(req: Request<()>) -> http::Result<Response<()>> {
|
||||
/// let mut builder = Response::builder()
|
||||
/// .header("Foo", "Bar")
|
||||
/// .status(StatusCode::OK);
|
||||
///
|
||||
/// if req.headers().contains_key("Another-Header") {
|
||||
/// builder = builder.header("Another-Header", "Ack");
|
||||
/// }
|
||||
///
|
||||
/// builder.body(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// A simple 404 handler
|
||||
///
|
||||
/// ```
|
||||
/// use http::{Request, Response, StatusCode};
|
||||
///
|
||||
/// fn not_found(_req: Request<()>) -> http::Result<Response<()>> {
|
||||
/// Response::builder()
|
||||
/// .status(StatusCode::NOT_FOUND)
|
||||
/// .body(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Or otherwise inspecting the result of a request:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use http::{Request, Response};
|
||||
///
|
||||
/// fn get(url: &str) -> http::Result<Response<()>> {
|
||||
/// // ...
|
||||
/// # panic!()
|
||||
/// }
|
||||
///
|
||||
/// let response = get("https://www.rust-lang.org/").unwrap();
|
||||
///
|
||||
/// if !response.status().is_success() {
|
||||
/// panic!("failed to get a successful response status!");
|
||||
/// }
|
||||
///
|
||||
/// if let Some(date) = response.headers().get("Date") {
|
||||
/// // we've got a `Date` header!
|
||||
/// }
|
||||
///
|
||||
/// let body = response.body();
|
||||
/// // ...
|
||||
/// ```
|
||||
///
|
||||
/// Deserialize a response of bytes via json:
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate serde;
|
||||
/// # extern crate serde_json;
|
||||
/// # extern crate http;
|
||||
/// use http::Response;
|
||||
/// use serde::de;
|
||||
///
|
||||
/// fn deserialize<T>(res: Response<Vec<u8>>) -> serde_json::Result<Response<T>>
|
||||
/// where for<'de> T: de::Deserialize<'de>,
|
||||
/// {
|
||||
/// let (parts, body) = res.into_parts();
|
||||
/// let body = serde_json::from_slice(&body)?;
|
||||
/// Ok(Response::from_parts(parts, body))
|
||||
/// }
|
||||
/// #
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// Or alternatively, serialize the body of a response to json
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate serde;
|
||||
/// # extern crate serde_json;
|
||||
/// # extern crate http;
|
||||
/// use http::Response;
|
||||
/// use serde::ser;
|
||||
///
|
||||
/// fn serialize<T>(res: Response<T>) -> serde_json::Result<Response<Vec<u8>>>
|
||||
/// where T: ser::Serialize,
|
||||
/// {
|
||||
/// let (parts, body) = res.into_parts();
|
||||
/// let body = serde_json::to_vec(&body)?;
|
||||
/// Ok(Response::from_parts(parts, body))
|
||||
/// }
|
||||
/// #
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
pub struct Response<T> {
|
||||
head: Parts,
|
||||
body: T,
|
||||
}
|
||||
|
||||
/// Component parts of an HTTP `Response`
|
||||
///
|
||||
/// The HTTP response head consists of a status, version, and a set of
|
||||
/// header fields.
|
||||
pub struct Parts {
|
||||
/// The response's status
|
||||
pub status: StatusCode,
|
||||
|
||||
/// The response's version
|
||||
pub version: Version,
|
||||
|
||||
/// The response's headers
|
||||
pub headers: HeaderMap<HeaderValue>,
|
||||
|
||||
/// The response's extensions
|
||||
pub extensions: Extensions,
|
||||
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
/// An HTTP response builder
|
||||
///
|
||||
/// This type can be used to construct an instance of `Response` through a
|
||||
/// builder-like pattern.
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
inner: Result<Parts>,
|
||||
}
|
||||
|
||||
impl Response<()> {
|
||||
/// Creates a new builder-style object to manufacture a `Response`
|
||||
///
|
||||
/// This method returns an instance of `Builder` which can be used to
|
||||
/// create a `Response`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response = Response::builder()
|
||||
/// .status(200)
|
||||
/// .header("X-Custom-Foo", "Bar")
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn builder() -> Builder {
|
||||
Builder::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Response<T> {
|
||||
/// Creates a new blank `Response` with the body
|
||||
///
|
||||
/// The component ports of this response will be set to their default, e.g.
|
||||
/// the ok status, no headers, etc.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response = Response::new("hello world");
|
||||
///
|
||||
/// assert_eq!(response.status(), StatusCode::OK);
|
||||
/// assert_eq!(*response.body(), "hello world");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new(body: T) -> Response<T> {
|
||||
Response {
|
||||
head: Parts::new(),
|
||||
body: body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Response` with the given head and body
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response = Response::new("hello world");
|
||||
/// let (mut parts, body) = response.into_parts();
|
||||
///
|
||||
/// parts.status = StatusCode::BAD_REQUEST;
|
||||
/// let response = Response::from_parts(parts, body);
|
||||
///
|
||||
/// assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||
/// assert_eq!(*response.body(), "hello world");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_parts(parts: Parts, body: T) -> Response<T> {
|
||||
Response {
|
||||
head: parts,
|
||||
body: body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `StatusCode`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<()> = Response::default();
|
||||
/// assert_eq!(response.status(), StatusCode::OK);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn status(&self) -> StatusCode {
|
||||
self.head.status
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the associated `StatusCode`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let mut response: Response<()> = Response::default();
|
||||
/// *response.status_mut() = StatusCode::CREATED;
|
||||
/// assert_eq!(response.status(), StatusCode::CREATED);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn status_mut(&mut self) -> &mut StatusCode {
|
||||
&mut self.head.status
|
||||
}
|
||||
|
||||
/// Returns a reference to the associated version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<()> = Response::default();
|
||||
/// assert_eq!(response.version(), Version::HTTP_11);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn version(&self) -> Version {
|
||||
self.head.version
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the associated version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let mut response: Response<()> = Response::default();
|
||||
/// *response.version_mut() = Version::HTTP_2;
|
||||
/// assert_eq!(response.version(), Version::HTTP_2);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn version_mut(&mut self) -> &mut Version {
|
||||
&mut self.head.version
|
||||
}
|
||||
|
||||
/// Returns a reference to the associated header field map.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<()> = Response::default();
|
||||
/// assert!(response.headers().is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn headers(&self) -> &HeaderMap<HeaderValue> {
|
||||
&self.head.headers
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the associated header field map.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// # use http::header::*;
|
||||
/// let mut response: Response<()> = Response::default();
|
||||
/// response.headers_mut().insert(HOST, HeaderValue::from_static("world"));
|
||||
/// assert!(!response.headers().is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue> {
|
||||
&mut self.head.headers
|
||||
}
|
||||
|
||||
/// Returns a reference to the associated extensions.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<()> = Response::default();
|
||||
/// assert!(response.extensions().get::<i32>().is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn extensions(&self) -> &Extensions {
|
||||
&self.head.extensions
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the associated extensions.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// # use http::header::*;
|
||||
/// let mut response: Response<()> = Response::default();
|
||||
/// response.extensions_mut().insert("hello");
|
||||
/// assert_eq!(response.extensions().get(), Some(&"hello"));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn extensions_mut(&mut self) -> &mut Extensions {
|
||||
&mut self.head.extensions
|
||||
}
|
||||
|
||||
/// Returns a reference to the associated HTTP body.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<String> = Response::default();
|
||||
/// assert!(response.body().is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn body(&self) -> &T {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the associated HTTP body.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let mut response: Response<String> = Response::default();
|
||||
/// response.body_mut().push_str("hello world");
|
||||
/// assert!(!response.body().is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn body_mut(&mut self) -> &mut T {
|
||||
&mut self.body
|
||||
}
|
||||
|
||||
/// Consumes the response, returning just the body.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Response;
|
||||
/// let response = Response::new(10);
|
||||
/// let body = response.into_body();
|
||||
/// assert_eq!(body, 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn into_body(self) -> T {
|
||||
self.body
|
||||
}
|
||||
|
||||
/// Consumes the response returning the head and body parts.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<()> = Response::default();
|
||||
/// let (parts, body) = response.into_parts();
|
||||
/// assert_eq!(parts.status, StatusCode::OK);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn into_parts(self) -> (Parts, T) {
|
||||
(self.head, self.body)
|
||||
}
|
||||
|
||||
/// Consumes the response returning a new response with body mapped to the
|
||||
/// return type of the passed in function.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response = Response::builder().body("some string").unwrap();
|
||||
/// let mapped_response: Response<&[u8]> = response.map(|b| {
|
||||
/// assert_eq!(b, "some string");
|
||||
/// b.as_bytes()
|
||||
/// });
|
||||
/// assert_eq!(mapped_response.body(), &"some string".as_bytes());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn map<F, U>(self, f: F) -> Response<U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
Response {
|
||||
body: f(self.body),
|
||||
head: self.head,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Default for Response<T> {
|
||||
#[inline]
|
||||
fn default() -> Response<T> {
|
||||
Response::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for Response<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Response")
|
||||
.field("status", &self.status())
|
||||
.field("version", &self.version())
|
||||
.field("headers", self.headers())
|
||||
// omits Extensions because not useful
|
||||
.field("body", self.body())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Parts {
|
||||
/// Creates a new default instance of `Parts`
|
||||
fn new() -> Parts {
|
||||
Parts {
|
||||
status: StatusCode::default(),
|
||||
version: Version::default(),
|
||||
headers: HeaderMap::default(),
|
||||
extensions: Extensions::default(),
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Parts {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Parts")
|
||||
.field("status", &self.status)
|
||||
.field("version", &self.version)
|
||||
.field("headers", &self.headers)
|
||||
// omits Extensions because not useful
|
||||
// omits _priv because not useful
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Creates a new default instance of `Builder` to construct either a
|
||||
/// `Head` or a `Response`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let response = response::Builder::new()
|
||||
/// .status(200)
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new() -> Builder {
|
||||
Builder::default()
|
||||
}
|
||||
|
||||
/// Set the HTTP status for this response.
|
||||
///
|
||||
/// This function will configure the HTTP status code of the `Response` that
|
||||
/// will be returned from `Builder::build`.
|
||||
///
|
||||
/// By default this is `200`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let response = Response::builder()
|
||||
/// .status(200)
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn status<T>(self, status: T) -> Builder
|
||||
where
|
||||
StatusCode: TryFrom<T>,
|
||||
<StatusCode as TryFrom<T>>::Error: Into<crate::Error>,
|
||||
{
|
||||
self.and_then(move |mut head| {
|
||||
head.status = TryFrom::try_from(status).map_err(Into::into)?;
|
||||
Ok(head)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the HTTP version for this response.
|
||||
///
|
||||
/// This function will configure the HTTP version of the `Response` that
|
||||
/// will be returned from `Builder::build`.
|
||||
///
|
||||
/// By default this is HTTP/1.1
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let response = Response::builder()
|
||||
/// .version(Version::HTTP_2)
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn version(self, version: Version) -> Builder {
|
||||
self.and_then(move |mut head| {
|
||||
head.version = version;
|
||||
Ok(head)
|
||||
})
|
||||
}
|
||||
|
||||
/// Appends a header to this response builder.
|
||||
///
|
||||
/// This function will append the provided key/value as a header to the
|
||||
/// internal `HeaderMap` being constructed. Essentially this is equivalent
|
||||
/// to calling `HeaderMap::append`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// # use http::header::HeaderValue;
|
||||
///
|
||||
/// let response = Response::builder()
|
||||
/// .header("Content-Type", "text/html")
|
||||
/// .header("X-Custom-Foo", "bar")
|
||||
/// .header("content-length", 0)
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn header<K, V>(self, key: K, value: V) -> Builder
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<crate::Error>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<crate::Error>,
|
||||
{
|
||||
self.and_then(move |mut head| {
|
||||
let name = <HeaderName as TryFrom<K>>::try_from(key).map_err(Into::into)?;
|
||||
let value = <HeaderValue as TryFrom<V>>::try_from(value).map_err(Into::into)?;
|
||||
head.headers.append(name, value);
|
||||
Ok(head)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get header on this response builder.
|
||||
///
|
||||
/// When builder has error returns None.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Response;
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let res = Response::builder()
|
||||
/// .header("Accept", "text/html")
|
||||
/// .header("X-Custom-Foo", "bar");
|
||||
/// let headers = res.headers_ref().unwrap();
|
||||
/// assert_eq!( headers["Accept"], "text/html" );
|
||||
/// assert_eq!( headers["X-Custom-Foo"], "bar" );
|
||||
/// ```
|
||||
pub fn headers_ref(&self) -> Option<&HeaderMap<HeaderValue>> {
|
||||
self.inner.as_ref().ok().map(|h| &h.headers)
|
||||
}
|
||||
|
||||
/// Get header on this response builder.
|
||||
/// when builder has error returns None
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// # use http::header::HeaderValue;
|
||||
/// # use http::response::Builder;
|
||||
/// let mut res = Response::builder();
|
||||
/// {
|
||||
/// let headers = res.headers_mut().unwrap();
|
||||
/// headers.insert("Accept", HeaderValue::from_static("text/html"));
|
||||
/// headers.insert("X-Custom-Foo", HeaderValue::from_static("bar"));
|
||||
/// }
|
||||
/// let headers = res.headers_ref().unwrap();
|
||||
/// assert_eq!( headers["Accept"], "text/html" );
|
||||
/// assert_eq!( headers["X-Custom-Foo"], "bar" );
|
||||
/// ```
|
||||
pub fn headers_mut(&mut self) -> Option<&mut HeaderMap<HeaderValue>> {
|
||||
self.inner.as_mut().ok().map(|h| &mut h.headers)
|
||||
}
|
||||
|
||||
/// Adds an extension to this builder
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let response = Response::builder()
|
||||
/// .extension("My Extension")
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
///
|
||||
/// assert_eq!(response.extensions().get::<&'static str>(),
|
||||
/// Some(&"My Extension"));
|
||||
/// ```
|
||||
pub fn extension<T>(self, extension: T) -> Builder
|
||||
where
|
||||
T: Any + Send + Sync + 'static,
|
||||
{
|
||||
self.and_then(move |mut head| {
|
||||
head.extensions.insert(extension);
|
||||
Ok(head)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a reference to the extensions for this response builder.
|
||||
///
|
||||
/// If the builder has an error, this returns `None`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Response;
|
||||
/// let res = Response::builder().extension("My Extension").extension(5u32);
|
||||
/// let extensions = res.extensions_ref().unwrap();
|
||||
/// assert_eq!(extensions.get::<&'static str>(), Some(&"My Extension"));
|
||||
/// assert_eq!(extensions.get::<u32>(), Some(&5u32));
|
||||
/// ```
|
||||
pub fn extensions_ref(&self) -> Option<&Extensions> {
|
||||
self.inner.as_ref().ok().map(|h| &h.extensions)
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the extensions for this response builder.
|
||||
///
|
||||
/// If the builder has an error, this returns `None`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Response;
|
||||
/// let mut res = Response::builder().extension("My Extension");
|
||||
/// let mut extensions = res.extensions_mut().unwrap();
|
||||
/// assert_eq!(extensions.get::<&'static str>(), Some(&"My Extension"));
|
||||
/// extensions.insert(5u32);
|
||||
/// assert_eq!(extensions.get::<u32>(), Some(&5u32));
|
||||
/// ```
|
||||
pub fn extensions_mut(&mut self) -> Option<&mut Extensions> {
|
||||
self.inner.as_mut().ok().map(|h| &mut h.extensions)
|
||||
}
|
||||
|
||||
/// "Consumes" this builder, using the provided `body` to return a
|
||||
/// constructed `Response`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may return an error if any previously configured argument
|
||||
/// failed to parse or get converted to the internal representation. For
|
||||
/// example if an invalid `head` was specified via `header("Foo",
|
||||
/// "Bar\r\n")` the error will be returned when this function is called
|
||||
/// rather than when `header` was called.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let response = Response::builder()
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn body<T>(self, body: T) -> Result<Response<T>> {
|
||||
self.inner.map(move |head| {
|
||||
Response {
|
||||
head,
|
||||
body,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
fn and_then<F>(self, func: F) -> Self
|
||||
where
|
||||
F: FnOnce(Parts) -> Result<Parts>
|
||||
{
|
||||
Builder {
|
||||
inner: self.inner.and_then(func),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
#[inline]
|
||||
fn default() -> Builder {
|
||||
Builder {
|
||||
inner: Ok(Parts::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_can_map_a_body_from_one_type_to_another() {
|
||||
let response = Response::builder().body("some string").unwrap();
|
||||
let mapped_response = response.map(|s| {
|
||||
assert_eq!(s, "some string");
|
||||
123u32
|
||||
});
|
||||
assert_eq!(mapped_response.body(), &123u32);
|
||||
}
|
||||
}
|
||||
588
zeroidc/vendor/http/src/status.rs
vendored
Normal file
588
zeroidc/vendor/http/src/status.rs
vendored
Normal file
@@ -0,0 +1,588 @@
|
||||
//! HTTP status codes
|
||||
//!
|
||||
//! This module contains HTTP-status code related structs an errors. The main
|
||||
//! type in this module is `StatusCode` which is not intended to be used through
|
||||
//! this module but rather the `http::StatusCode` type.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use http::StatusCode;
|
||||
//!
|
||||
//! assert_eq!(StatusCode::from_u16(200).unwrap(), StatusCode::OK);
|
||||
//! assert_eq!(StatusCode::NOT_FOUND, 404);
|
||||
//! assert!(StatusCode::OK.is_success());
|
||||
//! ```
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::num::NonZeroU16;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// An HTTP status code (`status-code` in RFC 7230 et al.).
|
||||
///
|
||||
/// Constants are provided for known status codes, including those in the IANA
|
||||
/// [HTTP Status Code Registry](
|
||||
/// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml).
|
||||
///
|
||||
/// Status code values in the range 100-999 (inclusive) are supported by this
|
||||
/// type. Values in the range 100-599 are semantically classified by the most
|
||||
/// significant digit. See [`StatusCode::is_success`], etc. Values above 599
|
||||
/// are unclassified but allowed for legacy compatibility, though their use is
|
||||
/// discouraged. Applications may interpret such values as protocol errors.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use http::StatusCode;
|
||||
///
|
||||
/// assert_eq!(StatusCode::from_u16(200).unwrap(), StatusCode::OK);
|
||||
/// assert_eq!(StatusCode::NOT_FOUND.as_u16(), 404);
|
||||
/// assert!(StatusCode::OK.is_success());
|
||||
/// ```
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct StatusCode(NonZeroU16);
|
||||
|
||||
/// A possible error value when converting a `StatusCode` from a `u16` or `&str`
|
||||
///
|
||||
/// This error indicates that the supplied input was not a valid number, was less
|
||||
/// than 100, or was greater than 999.
|
||||
pub struct InvalidStatusCode {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl StatusCode {
|
||||
/// Converts a u16 to a status code.
|
||||
///
|
||||
/// The function validates the correctness of the supplied u16. It must be
|
||||
/// greater or equal to 100 and less than 1000.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use http::StatusCode;
|
||||
///
|
||||
/// let ok = StatusCode::from_u16(200).unwrap();
|
||||
/// assert_eq!(ok, StatusCode::OK);
|
||||
///
|
||||
/// let err = StatusCode::from_u16(99);
|
||||
/// assert!(err.is_err());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_u16(src: u16) -> Result<StatusCode, InvalidStatusCode> {
|
||||
if src < 100 || src >= 1000 {
|
||||
return Err(InvalidStatusCode::new());
|
||||
}
|
||||
|
||||
NonZeroU16::new(src)
|
||||
.map(StatusCode)
|
||||
.ok_or_else(InvalidStatusCode::new)
|
||||
}
|
||||
|
||||
/// Converts a &[u8] to a status code
|
||||
pub fn from_bytes(src: &[u8]) -> Result<StatusCode, InvalidStatusCode> {
|
||||
if src.len() != 3 {
|
||||
return Err(InvalidStatusCode::new());
|
||||
}
|
||||
|
||||
let a = src[0].wrapping_sub(b'0') as u16;
|
||||
let b = src[1].wrapping_sub(b'0') as u16;
|
||||
let c = src[2].wrapping_sub(b'0') as u16;
|
||||
|
||||
if a == 0 || a > 9 || b > 9 || c > 9 {
|
||||
return Err(InvalidStatusCode::new());
|
||||
}
|
||||
|
||||
let status = (a * 100) + (b * 10) + c;
|
||||
NonZeroU16::new(status)
|
||||
.map(StatusCode)
|
||||
.ok_or_else(InvalidStatusCode::new)
|
||||
}
|
||||
|
||||
/// Returns the `u16` corresponding to this `StatusCode`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This is the same as the `From<StatusCode>` implementation, but
|
||||
/// included as an inherent method because that implementation doesn't
|
||||
/// appear in rustdocs, as well as a way to force the type instead of
|
||||
/// relying on inference.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let status = http::StatusCode::OK;
|
||||
/// assert_eq!(status.as_u16(), 200);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_u16(&self) -> u16 {
|
||||
(*self).into()
|
||||
}
|
||||
|
||||
/// Returns a &str representation of the `StatusCode`
|
||||
///
|
||||
/// The return value only includes a numerical representation of the
|
||||
/// status code. The canonical reason is not included.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let status = http::StatusCode::OK;
|
||||
/// assert_eq!(status.as_str(), "200");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
let offset = (self.0.get() - 100) as usize;
|
||||
let offset = offset * 3;
|
||||
|
||||
// Invariant: self has checked range [100, 999] and CODE_DIGITS is
|
||||
// ASCII-only, of length 900 * 3 = 2700 bytes
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{ &CODE_DIGITS[offset..offset+3] }
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
unsafe { CODE_DIGITS.get_unchecked(offset..offset+3) }
|
||||
}
|
||||
|
||||
/// Get the standardised `reason-phrase` for this status code.
|
||||
///
|
||||
/// This is mostly here for servers writing responses, but could potentially have application
|
||||
/// at other times.
|
||||
///
|
||||
/// The reason phrase is defined as being exclusively for human readers. You should avoid
|
||||
/// deriving any meaning from it at all costs.
|
||||
///
|
||||
/// Bear in mind also that in HTTP/2.0 and HTTP/3.0 the reason phrase is abolished from
|
||||
/// transmission, and so this canonical reason phrase really is the only reason phrase you’ll
|
||||
/// find.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let status = http::StatusCode::OK;
|
||||
/// assert_eq!(status.canonical_reason(), Some("OK"));
|
||||
/// ```
|
||||
pub fn canonical_reason(&self) -> Option<&'static str> {
|
||||
canonical_reason(self.0.get())
|
||||
}
|
||||
|
||||
/// Check if status is within 100-199.
|
||||
#[inline]
|
||||
pub fn is_informational(&self) -> bool {
|
||||
200 > self.0.get() && self.0.get() >= 100
|
||||
}
|
||||
|
||||
/// Check if status is within 200-299.
|
||||
#[inline]
|
||||
pub fn is_success(&self) -> bool {
|
||||
300 > self.0.get() && self.0.get() >= 200
|
||||
}
|
||||
|
||||
/// Check if status is within 300-399.
|
||||
#[inline]
|
||||
pub fn is_redirection(&self) -> bool {
|
||||
400 > self.0.get() && self.0.get() >= 300
|
||||
}
|
||||
|
||||
/// Check if status is within 400-499.
|
||||
#[inline]
|
||||
pub fn is_client_error(&self) -> bool {
|
||||
500 > self.0.get() && self.0.get() >= 400
|
||||
}
|
||||
|
||||
/// Check if status is within 500-599.
|
||||
#[inline]
|
||||
pub fn is_server_error(&self) -> bool {
|
||||
600 > self.0.get() && self.0.get() >= 500
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for StatusCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats the status code, *including* the canonical reason.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::StatusCode;
|
||||
/// assert_eq!(format!("{}", StatusCode::OK), "200 OK");
|
||||
/// ```
|
||||
impl fmt::Display for StatusCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} {}",
|
||||
u16::from(*self),
|
||||
self.canonical_reason().unwrap_or("<unknown status code>")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for StatusCode {
|
||||
#[inline]
|
||||
fn default() -> StatusCode {
|
||||
StatusCode::OK
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<u16> for StatusCode {
|
||||
#[inline]
|
||||
fn eq(&self, other: &u16) -> bool {
|
||||
self.as_u16() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<StatusCode> for u16 {
|
||||
#[inline]
|
||||
fn eq(&self, other: &StatusCode) -> bool {
|
||||
*self == other.as_u16()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StatusCode> for u16 {
|
||||
#[inline]
|
||||
fn from(status: StatusCode) -> u16 {
|
||||
status.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for StatusCode {
|
||||
type Err = InvalidStatusCode;
|
||||
|
||||
fn from_str(s: &str) -> Result<StatusCode, InvalidStatusCode> {
|
||||
StatusCode::from_bytes(s.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a StatusCode> for StatusCode {
|
||||
#[inline]
|
||||
fn from(t: &'a StatusCode) -> Self {
|
||||
t.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for StatusCode {
|
||||
type Error = InvalidStatusCode;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
StatusCode::from_bytes(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for StatusCode {
|
||||
type Error = InvalidStatusCode;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a str) -> Result<Self, Self::Error> {
|
||||
t.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for StatusCode {
|
||||
type Error = InvalidStatusCode;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: u16) -> Result<Self, Self::Error> {
|
||||
StatusCode::from_u16(t)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! status_codes {
|
||||
(
|
||||
$(
|
||||
$(#[$docs:meta])*
|
||||
($num:expr, $konst:ident, $phrase:expr);
|
||||
)+
|
||||
) => {
|
||||
impl StatusCode {
|
||||
$(
|
||||
$(#[$docs])*
|
||||
pub const $konst: StatusCode = StatusCode(unsafe { NonZeroU16::new_unchecked($num) });
|
||||
)+
|
||||
|
||||
}
|
||||
|
||||
fn canonical_reason(num: u16) -> Option<&'static str> {
|
||||
match num {
|
||||
$(
|
||||
$num => Some($phrase),
|
||||
)+
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status_codes! {
|
||||
/// 100 Continue
|
||||
/// [[RFC7231, Section 6.2.1](https://tools.ietf.org/html/rfc7231#section-6.2.1)]
|
||||
(100, CONTINUE, "Continue");
|
||||
/// 101 Switching Protocols
|
||||
/// [[RFC7231, Section 6.2.2](https://tools.ietf.org/html/rfc7231#section-6.2.2)]
|
||||
(101, SWITCHING_PROTOCOLS, "Switching Protocols");
|
||||
/// 102 Processing
|
||||
/// [[RFC2518](https://tools.ietf.org/html/rfc2518)]
|
||||
(102, PROCESSING, "Processing");
|
||||
|
||||
/// 200 OK
|
||||
/// [[RFC7231, Section 6.3.1](https://tools.ietf.org/html/rfc7231#section-6.3.1)]
|
||||
(200, OK, "OK");
|
||||
/// 201 Created
|
||||
/// [[RFC7231, Section 6.3.2](https://tools.ietf.org/html/rfc7231#section-6.3.2)]
|
||||
(201, CREATED, "Created");
|
||||
/// 202 Accepted
|
||||
/// [[RFC7231, Section 6.3.3](https://tools.ietf.org/html/rfc7231#section-6.3.3)]
|
||||
(202, ACCEPTED, "Accepted");
|
||||
/// 203 Non-Authoritative Information
|
||||
/// [[RFC7231, Section 6.3.4](https://tools.ietf.org/html/rfc7231#section-6.3.4)]
|
||||
(203, NON_AUTHORITATIVE_INFORMATION, "Non Authoritative Information");
|
||||
/// 204 No Content
|
||||
/// [[RFC7231, Section 6.3.5](https://tools.ietf.org/html/rfc7231#section-6.3.5)]
|
||||
(204, NO_CONTENT, "No Content");
|
||||
/// 205 Reset Content
|
||||
/// [[RFC7231, Section 6.3.6](https://tools.ietf.org/html/rfc7231#section-6.3.6)]
|
||||
(205, RESET_CONTENT, "Reset Content");
|
||||
/// 206 Partial Content
|
||||
/// [[RFC7233, Section 4.1](https://tools.ietf.org/html/rfc7233#section-4.1)]
|
||||
(206, PARTIAL_CONTENT, "Partial Content");
|
||||
/// 207 Multi-Status
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
(207, MULTI_STATUS, "Multi-Status");
|
||||
/// 208 Already Reported
|
||||
/// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
|
||||
(208, ALREADY_REPORTED, "Already Reported");
|
||||
|
||||
/// 226 IM Used
|
||||
/// [[RFC3229](https://tools.ietf.org/html/rfc3229)]
|
||||
(226, IM_USED, "IM Used");
|
||||
|
||||
/// 300 Multiple Choices
|
||||
/// [[RFC7231, Section 6.4.1](https://tools.ietf.org/html/rfc7231#section-6.4.1)]
|
||||
(300, MULTIPLE_CHOICES, "Multiple Choices");
|
||||
/// 301 Moved Permanently
|
||||
/// [[RFC7231, Section 6.4.2](https://tools.ietf.org/html/rfc7231#section-6.4.2)]
|
||||
(301, MOVED_PERMANENTLY, "Moved Permanently");
|
||||
/// 302 Found
|
||||
/// [[RFC7231, Section 6.4.3](https://tools.ietf.org/html/rfc7231#section-6.4.3)]
|
||||
(302, FOUND, "Found");
|
||||
/// 303 See Other
|
||||
/// [[RFC7231, Section 6.4.4](https://tools.ietf.org/html/rfc7231#section-6.4.4)]
|
||||
(303, SEE_OTHER, "See Other");
|
||||
/// 304 Not Modified
|
||||
/// [[RFC7232, Section 4.1](https://tools.ietf.org/html/rfc7232#section-4.1)]
|
||||
(304, NOT_MODIFIED, "Not Modified");
|
||||
/// 305 Use Proxy
|
||||
/// [[RFC7231, Section 6.4.5](https://tools.ietf.org/html/rfc7231#section-6.4.5)]
|
||||
(305, USE_PROXY, "Use Proxy");
|
||||
/// 307 Temporary Redirect
|
||||
/// [[RFC7231, Section 6.4.7](https://tools.ietf.org/html/rfc7231#section-6.4.7)]
|
||||
(307, TEMPORARY_REDIRECT, "Temporary Redirect");
|
||||
/// 308 Permanent Redirect
|
||||
/// [[RFC7238](https://tools.ietf.org/html/rfc7238)]
|
||||
(308, PERMANENT_REDIRECT, "Permanent Redirect");
|
||||
|
||||
/// 400 Bad Request
|
||||
/// [[RFC7231, Section 6.5.1](https://tools.ietf.org/html/rfc7231#section-6.5.1)]
|
||||
(400, BAD_REQUEST, "Bad Request");
|
||||
/// 401 Unauthorized
|
||||
/// [[RFC7235, Section 3.1](https://tools.ietf.org/html/rfc7235#section-3.1)]
|
||||
(401, UNAUTHORIZED, "Unauthorized");
|
||||
/// 402 Payment Required
|
||||
/// [[RFC7231, Section 6.5.2](https://tools.ietf.org/html/rfc7231#section-6.5.2)]
|
||||
(402, PAYMENT_REQUIRED, "Payment Required");
|
||||
/// 403 Forbidden
|
||||
/// [[RFC7231, Section 6.5.3](https://tools.ietf.org/html/rfc7231#section-6.5.3)]
|
||||
(403, FORBIDDEN, "Forbidden");
|
||||
/// 404 Not Found
|
||||
/// [[RFC7231, Section 6.5.4](https://tools.ietf.org/html/rfc7231#section-6.5.4)]
|
||||
(404, NOT_FOUND, "Not Found");
|
||||
/// 405 Method Not Allowed
|
||||
/// [[RFC7231, Section 6.5.5](https://tools.ietf.org/html/rfc7231#section-6.5.5)]
|
||||
(405, METHOD_NOT_ALLOWED, "Method Not Allowed");
|
||||
/// 406 Not Acceptable
|
||||
/// [[RFC7231, Section 6.5.6](https://tools.ietf.org/html/rfc7231#section-6.5.6)]
|
||||
(406, NOT_ACCEPTABLE, "Not Acceptable");
|
||||
/// 407 Proxy Authentication Required
|
||||
/// [[RFC7235, Section 3.2](https://tools.ietf.org/html/rfc7235#section-3.2)]
|
||||
(407, PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required");
|
||||
/// 408 Request Timeout
|
||||
/// [[RFC7231, Section 6.5.7](https://tools.ietf.org/html/rfc7231#section-6.5.7)]
|
||||
(408, REQUEST_TIMEOUT, "Request Timeout");
|
||||
/// 409 Conflict
|
||||
/// [[RFC7231, Section 6.5.8](https://tools.ietf.org/html/rfc7231#section-6.5.8)]
|
||||
(409, CONFLICT, "Conflict");
|
||||
/// 410 Gone
|
||||
/// [[RFC7231, Section 6.5.9](https://tools.ietf.org/html/rfc7231#section-6.5.9)]
|
||||
(410, GONE, "Gone");
|
||||
/// 411 Length Required
|
||||
/// [[RFC7231, Section 6.5.10](https://tools.ietf.org/html/rfc7231#section-6.5.10)]
|
||||
(411, LENGTH_REQUIRED, "Length Required");
|
||||
/// 412 Precondition Failed
|
||||
/// [[RFC7232, Section 4.2](https://tools.ietf.org/html/rfc7232#section-4.2)]
|
||||
(412, PRECONDITION_FAILED, "Precondition Failed");
|
||||
/// 413 Payload Too Large
|
||||
/// [[RFC7231, Section 6.5.11](https://tools.ietf.org/html/rfc7231#section-6.5.11)]
|
||||
(413, PAYLOAD_TOO_LARGE, "Payload Too Large");
|
||||
/// 414 URI Too Long
|
||||
/// [[RFC7231, Section 6.5.12](https://tools.ietf.org/html/rfc7231#section-6.5.12)]
|
||||
(414, URI_TOO_LONG, "URI Too Long");
|
||||
/// 415 Unsupported Media Type
|
||||
/// [[RFC7231, Section 6.5.13](https://tools.ietf.org/html/rfc7231#section-6.5.13)]
|
||||
(415, UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
|
||||
/// 416 Range Not Satisfiable
|
||||
/// [[RFC7233, Section 4.4](https://tools.ietf.org/html/rfc7233#section-4.4)]
|
||||
(416, RANGE_NOT_SATISFIABLE, "Range Not Satisfiable");
|
||||
/// 417 Expectation Failed
|
||||
/// [[RFC7231, Section 6.5.14](https://tools.ietf.org/html/rfc7231#section-6.5.14)]
|
||||
(417, EXPECTATION_FAILED, "Expectation Failed");
|
||||
/// 418 I'm a teapot
|
||||
/// [curiously not registered by IANA but [RFC2324](https://tools.ietf.org/html/rfc2324)]
|
||||
(418, IM_A_TEAPOT, "I'm a teapot");
|
||||
|
||||
/// 421 Misdirected Request
|
||||
/// [RFC7540, Section 9.1.2](http://tools.ietf.org/html/rfc7540#section-9.1.2)
|
||||
(421, MISDIRECTED_REQUEST, "Misdirected Request");
|
||||
/// 422 Unprocessable Entity
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
(422, UNPROCESSABLE_ENTITY, "Unprocessable Entity");
|
||||
/// 423 Locked
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
(423, LOCKED, "Locked");
|
||||
/// 424 Failed Dependency
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
(424, FAILED_DEPENDENCY, "Failed Dependency");
|
||||
|
||||
/// 426 Upgrade Required
|
||||
/// [[RFC7231, Section 6.5.15](https://tools.ietf.org/html/rfc7231#section-6.5.15)]
|
||||
(426, UPGRADE_REQUIRED, "Upgrade Required");
|
||||
|
||||
/// 428 Precondition Required
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
(428, PRECONDITION_REQUIRED, "Precondition Required");
|
||||
/// 429 Too Many Requests
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
(429, TOO_MANY_REQUESTS, "Too Many Requests");
|
||||
|
||||
/// 431 Request Header Fields Too Large
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
(431, REQUEST_HEADER_FIELDS_TOO_LARGE, "Request Header Fields Too Large");
|
||||
|
||||
/// 451 Unavailable For Legal Reasons
|
||||
/// [[RFC7725](http://tools.ietf.org/html/rfc7725)]
|
||||
(451, UNAVAILABLE_FOR_LEGAL_REASONS, "Unavailable For Legal Reasons");
|
||||
|
||||
/// 500 Internal Server Error
|
||||
/// [[RFC7231, Section 6.6.1](https://tools.ietf.org/html/rfc7231#section-6.6.1)]
|
||||
(500, INTERNAL_SERVER_ERROR, "Internal Server Error");
|
||||
/// 501 Not Implemented
|
||||
/// [[RFC7231, Section 6.6.2](https://tools.ietf.org/html/rfc7231#section-6.6.2)]
|
||||
(501, NOT_IMPLEMENTED, "Not Implemented");
|
||||
/// 502 Bad Gateway
|
||||
/// [[RFC7231, Section 6.6.3](https://tools.ietf.org/html/rfc7231#section-6.6.3)]
|
||||
(502, BAD_GATEWAY, "Bad Gateway");
|
||||
/// 503 Service Unavailable
|
||||
/// [[RFC7231, Section 6.6.4](https://tools.ietf.org/html/rfc7231#section-6.6.4)]
|
||||
(503, SERVICE_UNAVAILABLE, "Service Unavailable");
|
||||
/// 504 Gateway Timeout
|
||||
/// [[RFC7231, Section 6.6.5](https://tools.ietf.org/html/rfc7231#section-6.6.5)]
|
||||
(504, GATEWAY_TIMEOUT, "Gateway Timeout");
|
||||
/// 505 HTTP Version Not Supported
|
||||
/// [[RFC7231, Section 6.6.6](https://tools.ietf.org/html/rfc7231#section-6.6.6)]
|
||||
(505, HTTP_VERSION_NOT_SUPPORTED, "HTTP Version Not Supported");
|
||||
/// 506 Variant Also Negotiates
|
||||
/// [[RFC2295](https://tools.ietf.org/html/rfc2295)]
|
||||
(506, VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates");
|
||||
/// 507 Insufficient Storage
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
(507, INSUFFICIENT_STORAGE, "Insufficient Storage");
|
||||
/// 508 Loop Detected
|
||||
/// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
|
||||
(508, LOOP_DETECTED, "Loop Detected");
|
||||
|
||||
/// 510 Not Extended
|
||||
/// [[RFC2774](https://tools.ietf.org/html/rfc2774)]
|
||||
(510, NOT_EXTENDED, "Not Extended");
|
||||
/// 511 Network Authentication Required
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
(511, NETWORK_AUTHENTICATION_REQUIRED, "Network Authentication Required");
|
||||
}
|
||||
|
||||
impl InvalidStatusCode {
|
||||
fn new() -> InvalidStatusCode {
|
||||
InvalidStatusCode {
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for InvalidStatusCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("InvalidStatusCode")
|
||||
// skip _priv noise
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InvalidStatusCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("invalid status code")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for InvalidStatusCode {}
|
||||
|
||||
// A string of packed 3-ASCII-digit status code values for the supported range
|
||||
// of [100, 999] (900 codes, 2700 bytes).
|
||||
const CODE_DIGITS: &'static str = "\
|
||||
100101102103104105106107108109110111112113114115116117118119\
|
||||
120121122123124125126127128129130131132133134135136137138139\
|
||||
140141142143144145146147148149150151152153154155156157158159\
|
||||
160161162163164165166167168169170171172173174175176177178179\
|
||||
180181182183184185186187188189190191192193194195196197198199\
|
||||
200201202203204205206207208209210211212213214215216217218219\
|
||||
220221222223224225226227228229230231232233234235236237238239\
|
||||
240241242243244245246247248249250251252253254255256257258259\
|
||||
260261262263264265266267268269270271272273274275276277278279\
|
||||
280281282283284285286287288289290291292293294295296297298299\
|
||||
300301302303304305306307308309310311312313314315316317318319\
|
||||
320321322323324325326327328329330331332333334335336337338339\
|
||||
340341342343344345346347348349350351352353354355356357358359\
|
||||
360361362363364365366367368369370371372373374375376377378379\
|
||||
380381382383384385386387388389390391392393394395396397398399\
|
||||
400401402403404405406407408409410411412413414415416417418419\
|
||||
420421422423424425426427428429430431432433434435436437438439\
|
||||
440441442443444445446447448449450451452453454455456457458459\
|
||||
460461462463464465466467468469470471472473474475476477478479\
|
||||
480481482483484485486487488489490491492493494495496497498499\
|
||||
500501502503504505506507508509510511512513514515516517518519\
|
||||
520521522523524525526527528529530531532533534535536537538539\
|
||||
540541542543544545546547548549550551552553554555556557558559\
|
||||
560561562563564565566567568569570571572573574575576577578579\
|
||||
580581582583584585586587588589590591592593594595596597598599\
|
||||
600601602603604605606607608609610611612613614615616617618619\
|
||||
620621622623624625626627628629630631632633634635636637638639\
|
||||
640641642643644645646647648649650651652653654655656657658659\
|
||||
660661662663664665666667668669670671672673674675676677678679\
|
||||
680681682683684685686687688689690691692693694695696697698699\
|
||||
700701702703704705706707708709710711712713714715716717718719\
|
||||
720721722723724725726727728729730731732733734735736737738739\
|
||||
740741742743744745746747748749750751752753754755756757758759\
|
||||
760761762763764765766767768769770771772773774775776777778779\
|
||||
780781782783784785786787788789790791792793794795796797798799\
|
||||
800801802803804805806807808809810811812813814815816817818819\
|
||||
820821822823824825826827828829830831832833834835836837838839\
|
||||
840841842843844845846847848849850851852853854855856857858859\
|
||||
860861862863864865866867868869870871872873874875876877878879\
|
||||
880881882883884885886887888889890891892893894895896897898899\
|
||||
900901902903904905906907908909910911912913914915916917918919\
|
||||
920921922923924925926927928929930931932933934935936937938939\
|
||||
940941942943944945946947948949950951952953954955956957958959\
|
||||
960961962963964965966967968969970971972973974975976977978979\
|
||||
980981982983984985986987988989990991992993994995996997998999";
|
||||
671
zeroidc/vendor/http/src/uri/authority.rs
vendored
Normal file
671
zeroidc/vendor/http/src/uri/authority.rs
vendored
Normal file
@@ -0,0 +1,671 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::str::FromStr;
|
||||
use std::{cmp, fmt, str};
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
use super::{ErrorKind, InvalidUri, Port, URI_CHARS};
|
||||
use crate::byte_str::ByteStr;
|
||||
|
||||
/// Represents the authority component of a URI.
|
||||
#[derive(Clone)]
|
||||
pub struct Authority {
|
||||
pub(super) data: ByteStr,
|
||||
}
|
||||
|
||||
impl Authority {
|
||||
pub(super) fn empty() -> Self {
|
||||
Authority {
|
||||
data: ByteStr::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Not public while `bytes` is unstable.
|
||||
pub(super) fn from_shared(s: Bytes) -> Result<Self, InvalidUri> {
|
||||
// Precondition on create_authority: trivially satisfied by the
|
||||
// identity clousre
|
||||
create_authority(s, |s| s)
|
||||
}
|
||||
|
||||
/// Attempt to convert an `Authority` from a static string.
|
||||
///
|
||||
/// This function will not perform any copying, and the string will be
|
||||
/// checked if it is empty or contains an invalid character.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the argument contains invalid characters or
|
||||
/// is empty.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority = Authority::from_static("example.com");
|
||||
/// assert_eq!(authority.host(), "example.com");
|
||||
/// ```
|
||||
pub fn from_static(src: &'static str) -> Self {
|
||||
Authority::from_shared(Bytes::from_static(src.as_bytes()))
|
||||
.expect("static str is not valid authority")
|
||||
}
|
||||
|
||||
/// Attempt to convert a `Bytes` buffer to a `Authority`.
|
||||
///
|
||||
/// This will try to prevent a copy if the type passed is the type used
|
||||
/// internally, and will copy the data if it is not.
|
||||
pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
|
||||
where
|
||||
T: AsRef<[u8]> + 'static,
|
||||
{
|
||||
if_downcast_into!(T, Bytes, src, {
|
||||
return Authority::from_shared(src);
|
||||
});
|
||||
|
||||
Authority::try_from(src.as_ref())
|
||||
}
|
||||
|
||||
// Note: this may return an *empty* Authority. You might want `parse_non_empty`.
|
||||
// Postcondition: for all Ok() returns, s[..ret.unwrap()] is valid UTF-8 where
|
||||
// ret is the return value.
|
||||
pub(super) fn parse(s: &[u8]) -> Result<usize, InvalidUri> {
|
||||
let mut colon_cnt = 0;
|
||||
let mut start_bracket = false;
|
||||
let mut end_bracket = false;
|
||||
let mut has_percent = false;
|
||||
let mut end = s.len();
|
||||
let mut at_sign_pos = None;
|
||||
|
||||
// Among other things, this loop checks that every byte in s up to the
|
||||
// first '/', '?', or '#' is a valid URI character (or in some contexts,
|
||||
// a '%'). This means that each such byte is a valid single-byte UTF-8
|
||||
// code point.
|
||||
for (i, &b) in s.iter().enumerate() {
|
||||
match URI_CHARS[b as usize] {
|
||||
b'/' | b'?' | b'#' => {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
b':' => {
|
||||
colon_cnt += 1;
|
||||
}
|
||||
b'[' => {
|
||||
if has_percent || start_bracket {
|
||||
// Something other than the userinfo has a `%`, so reject it.
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
start_bracket = true;
|
||||
}
|
||||
b']' => {
|
||||
if end_bracket {
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
end_bracket = true;
|
||||
|
||||
// Those were part of an IPv6 hostname, so forget them...
|
||||
colon_cnt = 0;
|
||||
has_percent = false;
|
||||
}
|
||||
b'@' => {
|
||||
at_sign_pos = Some(i);
|
||||
|
||||
// Those weren't a port colon, but part of the
|
||||
// userinfo, so it needs to be forgotten.
|
||||
colon_cnt = 0;
|
||||
has_percent = false;
|
||||
}
|
||||
0 if b == b'%' => {
|
||||
// Per https://tools.ietf.org/html/rfc3986#section-3.2.1 and
|
||||
// https://url.spec.whatwg.org/#authority-state
|
||||
// the userinfo can have a percent-encoded username and password,
|
||||
// so record that a `%` was found. If this turns out to be
|
||||
// part of the userinfo, this flag will be cleared.
|
||||
// Also per https://tools.ietf.org/html/rfc6874, percent-encoding can
|
||||
// be used to indicate a zone identifier.
|
||||
// If the flag hasn't been cleared at the end, that means this
|
||||
// was part of the hostname (and not part of an IPv6 address), and
|
||||
// will fail with an error.
|
||||
has_percent = true;
|
||||
}
|
||||
0 => {
|
||||
return Err(ErrorKind::InvalidUriChar.into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if start_bracket ^ end_bracket {
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
|
||||
if colon_cnt > 1 {
|
||||
// Things like 'localhost:8080:3030' are rejected.
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
|
||||
if end > 0 && at_sign_pos == Some(end - 1) {
|
||||
// If there's nothing after an `@`, this is bonkers.
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
|
||||
if has_percent {
|
||||
// Something after the userinfo has a `%`, so reject it.
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
|
||||
Ok(end)
|
||||
}
|
||||
|
||||
// Parse bytes as an Authority, not allowing an empty string.
|
||||
//
|
||||
// This should be used by functions that allow a user to parse
|
||||
// an `Authority` by itself.
|
||||
//
|
||||
// Postcondition: for all Ok() returns, s[..ret.unwrap()] is valid UTF-8 where
|
||||
// ret is the return value.
|
||||
fn parse_non_empty(s: &[u8]) -> Result<usize, InvalidUri> {
|
||||
if s.is_empty() {
|
||||
return Err(ErrorKind::Empty.into());
|
||||
}
|
||||
Authority::parse(s)
|
||||
}
|
||||
|
||||
/// Get the host of this `Authority`.
|
||||
///
|
||||
/// The host subcomponent of authority is identified by an IP literal
|
||||
/// encapsulated within square brackets, an IPv4 address in dotted- decimal
|
||||
/// form, or a registered name. The host subcomponent is **case-insensitive**.
|
||||
///
|
||||
/// ```notrust
|
||||
/// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
|
||||
/// |---------|
|
||||
/// |
|
||||
/// host
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let authority: Authority = "example.org:80".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(authority.host(), "example.org");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn host(&self) -> &str {
|
||||
host(self.as_str())
|
||||
}
|
||||
|
||||
/// Get the port part of this `Authority`.
|
||||
///
|
||||
/// The port subcomponent of authority is designated by an optional port
|
||||
/// number following the host and delimited from it by a single colon (":")
|
||||
/// character. It can be turned into a decimal port number with the `as_u16`
|
||||
/// method or as a `str` with the `as_str` method.
|
||||
///
|
||||
/// ```notrust
|
||||
/// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
|
||||
/// |-|
|
||||
/// |
|
||||
/// port
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Authority with port
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "example.org:80".parse().unwrap();
|
||||
///
|
||||
/// let port = authority.port().unwrap();
|
||||
/// assert_eq!(port.as_u16(), 80);
|
||||
/// assert_eq!(port.as_str(), "80");
|
||||
/// ```
|
||||
///
|
||||
/// Authority without port
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "example.org".parse().unwrap();
|
||||
///
|
||||
/// assert!(authority.port().is_none());
|
||||
/// ```
|
||||
pub fn port(&self) -> Option<Port<&str>> {
|
||||
let bytes = self.as_str();
|
||||
bytes
|
||||
.rfind(":")
|
||||
.and_then(|i| Port::from_str(&bytes[i + 1..]).ok())
|
||||
}
|
||||
|
||||
/// Get the port of this `Authority` as a `u16`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "example.org:80".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(authority.port_u16(), Some(80));
|
||||
/// ```
|
||||
pub fn port_u16(&self) -> Option<u16> {
|
||||
self.port().and_then(|p| Some(p.as_u16()))
|
||||
}
|
||||
|
||||
/// Return a str representation of the authority
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.data[..]
|
||||
}
|
||||
}
|
||||
|
||||
// Purposefully not public while `bytes` is unstable.
|
||||
// impl TryFrom<Bytes> for Authority
|
||||
|
||||
impl AsRef<str> for Authority {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Authority {
|
||||
fn eq(&self, other: &Authority) -> bool {
|
||||
self.data.eq_ignore_ascii_case(&other.data)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Authority {}
|
||||
|
||||
/// Case-insensitive equality
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "HELLO.com".parse().unwrap();
|
||||
/// assert_eq!(authority, "hello.coM");
|
||||
/// assert_eq!("hello.com", authority);
|
||||
/// ```
|
||||
impl PartialEq<str> for Authority {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.data.eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Authority> for str {
|
||||
fn eq(&self, other: &Authority) -> bool {
|
||||
self.eq_ignore_ascii_case(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<Authority> for &'a str {
|
||||
fn eq(&self, other: &Authority) -> bool {
|
||||
self.eq_ignore_ascii_case(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for Authority {
|
||||
fn eq(&self, other: &&'a str) -> bool {
|
||||
self.data.eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for Authority {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.data.eq_ignore_ascii_case(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Authority> for String {
|
||||
fn eq(&self, other: &Authority) -> bool {
|
||||
self.as_str().eq_ignore_ascii_case(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// Case-insensitive ordering
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "DEF.com".parse().unwrap();
|
||||
/// assert!(authority < "ghi.com");
|
||||
/// assert!(authority > "abc.com");
|
||||
/// ```
|
||||
impl PartialOrd for Authority {
|
||||
fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
|
||||
let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<str> for Authority {
|
||||
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
|
||||
let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<Authority> for str {
|
||||
fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
|
||||
let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<Authority> for &'a str {
|
||||
fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
|
||||
let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<&'a str> for Authority {
|
||||
fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
|
||||
let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<String> for Authority {
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<Authority> for String {
|
||||
fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
|
||||
let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
/// Case-insensitive hashing
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// # use std::hash::{Hash, Hasher};
|
||||
/// # use std::collections::hash_map::DefaultHasher;
|
||||
///
|
||||
/// let a: Authority = "HELLO.com".parse().unwrap();
|
||||
/// let b: Authority = "hello.coM".parse().unwrap();
|
||||
///
|
||||
/// let mut s = DefaultHasher::new();
|
||||
/// a.hash(&mut s);
|
||||
/// let a = s.finish();
|
||||
///
|
||||
/// let mut s = DefaultHasher::new();
|
||||
/// b.hash(&mut s);
|
||||
/// let b = s.finish();
|
||||
///
|
||||
/// assert_eq!(a, b);
|
||||
/// ```
|
||||
impl Hash for Authority {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
self.data.len().hash(state);
|
||||
for &b in self.data.as_bytes() {
|
||||
state.write_u8(b.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Authority {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
// parse first, and only turn into Bytes if valid
|
||||
|
||||
// Preconditon on create_authority: copy_from_slice() copies all of
|
||||
// bytes from the [u8] parameter into a new Bytes
|
||||
create_authority(s, |s| Bytes::copy_from_slice(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Authority {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||||
TryFrom::try_from(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for Authority {
|
||||
type Error = InvalidUri;
|
||||
|
||||
#[inline]
|
||||
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Authority::from_shared(vec.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Authority {
|
||||
type Error = InvalidUri;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: String) -> Result<Self, Self::Error> {
|
||||
Authority::from_shared(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Authority {
|
||||
type Err = InvalidUri;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, InvalidUri> {
|
||||
TryFrom::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Authority {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Authority {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
fn host(auth: &str) -> &str {
|
||||
let host_port = auth
|
||||
.rsplitn(2, '@')
|
||||
.next()
|
||||
.expect("split always has at least 1 item");
|
||||
|
||||
if host_port.as_bytes()[0] == b'[' {
|
||||
let i = host_port
|
||||
.find(']')
|
||||
.expect("parsing should validate brackets");
|
||||
// ..= ranges aren't available in 1.20, our minimum Rust version...
|
||||
&host_port[0..i + 1]
|
||||
} else {
|
||||
host_port
|
||||
.split(':')
|
||||
.next()
|
||||
.expect("split always has at least 1 item")
|
||||
}
|
||||
}
|
||||
|
||||
// Precondition: f converts all of the bytes in the passed in B into the
|
||||
// returned Bytes.
|
||||
fn create_authority<B, F>(b: B, f: F) -> Result<Authority, InvalidUri>
|
||||
where
|
||||
B: AsRef<[u8]>,
|
||||
F: FnOnce(B) -> Bytes,
|
||||
{
|
||||
let s = b.as_ref();
|
||||
let authority_end = Authority::parse_non_empty(s)?;
|
||||
|
||||
if authority_end != s.len() {
|
||||
return Err(ErrorKind::InvalidUriChar.into());
|
||||
}
|
||||
|
||||
let bytes = f(b);
|
||||
|
||||
Ok(Authority {
|
||||
// Safety: the postcondition on parse_non_empty() and the check against
|
||||
// s.len() ensure that b is valid UTF-8. The precondition on f ensures
|
||||
// that this is carried through to bytes.
|
||||
data: unsafe { ByteStr::from_utf8_unchecked(bytes) },
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_empty_string_is_error() {
|
||||
let err = Authority::parse_non_empty(b"").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::Empty);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_to_self_of_same_authority() {
|
||||
let authority1: Authority = "example.com".parse().unwrap();
|
||||
let authority2: Authority = "EXAMPLE.COM".parse().unwrap();
|
||||
assert_eq!(authority1, authority2);
|
||||
assert_eq!(authority2, authority1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_to_self_of_different_authority() {
|
||||
let authority1: Authority = "example.com".parse().unwrap();
|
||||
let authority2: Authority = "test.com".parse().unwrap();
|
||||
assert_ne!(authority1, authority2);
|
||||
assert_ne!(authority2, authority1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_a_str() {
|
||||
let authority: Authority = "example.com".parse().unwrap();
|
||||
assert_eq!(&authority, "EXAMPLE.com");
|
||||
assert_eq!("EXAMPLE.com", &authority);
|
||||
assert_eq!(authority, "EXAMPLE.com");
|
||||
assert_eq!("EXAMPLE.com", authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_static_equates_with_a_str() {
|
||||
let authority = Authority::from_static("example.com");
|
||||
assert_eq!(authority, "example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_with_a_str_of_a_different_authority() {
|
||||
let authority: Authority = "example.com".parse().unwrap();
|
||||
assert_ne!(&authority, "test.com");
|
||||
assert_ne!("test.com", &authority);
|
||||
assert_ne!(authority, "test.com");
|
||||
assert_ne!("test.com", authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_a_string() {
|
||||
let authority: Authority = "example.com".parse().unwrap();
|
||||
assert_eq!(authority, "EXAMPLE.com".to_string());
|
||||
assert_eq!("EXAMPLE.com".to_string(), authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_a_string_of_a_different_authority() {
|
||||
let authority: Authority = "example.com".parse().unwrap();
|
||||
assert_ne!(authority, "test.com".to_string());
|
||||
assert_ne!("test.com".to_string(), authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_to_self() {
|
||||
let authority1: Authority = "abc.com".parse().unwrap();
|
||||
let authority2: Authority = "def.com".parse().unwrap();
|
||||
assert!(authority1 < authority2);
|
||||
assert!(authority2 > authority1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_with_a_str() {
|
||||
let authority: Authority = "def.com".parse().unwrap();
|
||||
// with ref
|
||||
assert!(&authority < "ghi.com");
|
||||
assert!("ghi.com" > &authority);
|
||||
assert!(&authority > "abc.com");
|
||||
assert!("abc.com" < &authority);
|
||||
|
||||
// no ref
|
||||
assert!(authority < "ghi.com");
|
||||
assert!("ghi.com" > authority);
|
||||
assert!(authority > "abc.com");
|
||||
assert!("abc.com" < authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_with_a_string() {
|
||||
let authority: Authority = "def.com".parse().unwrap();
|
||||
assert!(authority < "ghi.com".to_string());
|
||||
assert!("ghi.com".to_string() > authority);
|
||||
assert!(authority > "abc.com".to_string());
|
||||
assert!("abc.com".to_string() < authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allows_percent_in_userinfo() {
|
||||
let authority_str = "a%2f:b%2f@example.com";
|
||||
let authority: Authority = authority_str.parse().unwrap();
|
||||
assert_eq!(authority, authority_str);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_percent_in_hostname() {
|
||||
let err = Authority::parse_non_empty(b"example%2f.com").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidAuthority);
|
||||
|
||||
let err = Authority::parse_non_empty(b"a%2f:b%2f@example%2f.com").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidAuthority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allows_percent_in_ipv6_address() {
|
||||
let authority_str = "[fe80::1:2:3:4%25eth0]";
|
||||
let result: Authority = authority_str.parse().unwrap();
|
||||
assert_eq!(result, authority_str);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_percent_outside_ipv6_address() {
|
||||
let err = Authority::parse_non_empty(b"1234%20[fe80::1:2:3:4]").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidAuthority);
|
||||
|
||||
let err = Authority::parse_non_empty(b"[fe80::1:2:3:4]%20").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidAuthority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_invalid_utf8() {
|
||||
let err = Authority::try_from([0xc0u8].as_ref()).unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidUriChar);
|
||||
|
||||
let err = Authority::from_shared(Bytes::from_static([0xc0u8].as_ref()))
|
||||
.unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidUriChar);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_invalid_use_of_brackets() {
|
||||
let err = Authority::parse_non_empty(b"[]@[").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidAuthority);
|
||||
}
|
||||
}
|
||||
197
zeroidc/vendor/http/src/uri/builder.rs
vendored
Normal file
197
zeroidc/vendor/http/src/uri/builder.rs
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use super::{Authority, Parts, PathAndQuery, Scheme};
|
||||
use crate::Uri;
|
||||
|
||||
/// A builder for `Uri`s.
|
||||
///
|
||||
/// This type can be used to construct an instance of `Uri`
|
||||
/// through a builder pattern.
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
parts: Result<Parts, crate::Error>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Creates a new default instance of `Builder` to construct a `Uri`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let uri = uri::Builder::new()
|
||||
/// .scheme("https")
|
||||
/// .authority("hyper.rs")
|
||||
/// .path_and_query("/")
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new() -> Builder {
|
||||
Builder::default()
|
||||
}
|
||||
|
||||
/// Set the `Scheme` for this URI.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let mut builder = uri::Builder::new();
|
||||
/// builder.scheme("https");
|
||||
/// ```
|
||||
pub fn scheme<T>(self, scheme: T) -> Self
|
||||
where
|
||||
Scheme: TryFrom<T>,
|
||||
<Scheme as TryFrom<T>>::Error: Into<crate::Error>,
|
||||
{
|
||||
self.map(move |mut parts| {
|
||||
let scheme = scheme.try_into().map_err(Into::into)?;
|
||||
parts.scheme = Some(scheme);
|
||||
Ok(parts)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the `Authority` for this URI.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let uri = uri::Builder::new()
|
||||
/// .authority("tokio.rs")
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn authority<T>(self, auth: T) -> Self
|
||||
where
|
||||
Authority: TryFrom<T>,
|
||||
<Authority as TryFrom<T>>::Error: Into<crate::Error>,
|
||||
{
|
||||
self.map(move |mut parts| {
|
||||
let auth = auth.try_into().map_err(Into::into)?;
|
||||
parts.authority = Some(auth);
|
||||
Ok(parts)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the `PathAndQuery` for this URI.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let uri = uri::Builder::new()
|
||||
/// .path_and_query("/hello?foo=bar")
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn path_and_query<T>(self, p_and_q: T) -> Self
|
||||
where
|
||||
PathAndQuery: TryFrom<T>,
|
||||
<PathAndQuery as TryFrom<T>>::Error: Into<crate::Error>,
|
||||
{
|
||||
self.map(move |mut parts| {
|
||||
let p_and_q = p_and_q.try_into().map_err(Into::into)?;
|
||||
parts.path_and_query = Some(p_and_q);
|
||||
Ok(parts)
|
||||
})
|
||||
}
|
||||
|
||||
/// Consumes this builder, and tries to construct a valid `Uri` from
|
||||
/// the configured pieces.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may return an error if any previously configured argument
|
||||
/// failed to parse or get converted to the internal representation. For
|
||||
/// example if an invalid `scheme` was specified via `scheme("!@#%/^")`
|
||||
/// the error will be returned when this function is called rather than
|
||||
/// when `scheme` was called.
|
||||
///
|
||||
/// Additionally, the various forms of URI require certain combinations of
|
||||
/// parts to be set to be valid. If the parts don't fit into any of the
|
||||
/// valid forms of URI, a new error is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let uri = Uri::builder()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn build(self) -> Result<Uri, crate::Error> {
|
||||
let parts = self.parts?;
|
||||
Uri::from_parts(parts).map_err(Into::into)
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
fn map<F>(self, func: F) -> Self
|
||||
where
|
||||
F: FnOnce(Parts) -> Result<Parts, crate::Error>,
|
||||
{
|
||||
|
||||
Builder {
|
||||
parts: self.parts.and_then(func),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
#[inline]
|
||||
fn default() -> Builder {
|
||||
Builder {
|
||||
parts: Ok(Parts::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn build_from_str() {
|
||||
let uri = Builder::new()
|
||||
.scheme(Scheme::HTTP)
|
||||
.authority("hyper.rs")
|
||||
.path_and_query("/foo?a=1")
|
||||
.build()
|
||||
.unwrap();
|
||||
assert_eq!(uri.scheme_str(), Some("http"));
|
||||
assert_eq!(uri.authority().unwrap().host(), "hyper.rs");
|
||||
assert_eq!(uri.path(), "/foo");
|
||||
assert_eq!(uri.query(), Some("a=1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_from_string() {
|
||||
for i in 1..10 {
|
||||
let uri = Builder::new()
|
||||
.path_and_query(format!("/foo?a={}", i))
|
||||
.build()
|
||||
.unwrap();
|
||||
let expected_query = format!("a={}", i);
|
||||
assert_eq!(uri.path(), "/foo");
|
||||
assert_eq!(uri.query(), Some(expected_query.as_str()));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_from_string_ref() {
|
||||
for i in 1..10 {
|
||||
let p_a_q = format!("/foo?a={}", i);
|
||||
let uri = Builder::new().path_and_query(&p_a_q).build().unwrap();
|
||||
let expected_query = format!("a={}", i);
|
||||
assert_eq!(uri.path(), "/foo");
|
||||
assert_eq!(uri.query(), Some(expected_query.as_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
1118
zeroidc/vendor/http/src/uri/mod.rs
vendored
Normal file
1118
zeroidc/vendor/http/src/uri/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
558
zeroidc/vendor/http/src/uri/path.rs
vendored
Normal file
558
zeroidc/vendor/http/src/uri/path.rs
vendored
Normal file
@@ -0,0 +1,558 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
use std::{cmp, fmt, str};
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
use super::{ErrorKind, InvalidUri};
|
||||
use crate::byte_str::ByteStr;
|
||||
|
||||
/// Represents the path component of a URI
|
||||
#[derive(Clone)]
|
||||
pub struct PathAndQuery {
|
||||
pub(super) data: ByteStr,
|
||||
pub(super) query: u16,
|
||||
}
|
||||
|
||||
const NONE: u16 = ::std::u16::MAX;
|
||||
|
||||
impl PathAndQuery {
|
||||
// Not public while `bytes` is unstable.
|
||||
pub(super) fn from_shared(mut src: Bytes) -> Result<Self, InvalidUri> {
|
||||
let mut query = NONE;
|
||||
let mut fragment = None;
|
||||
|
||||
// block for iterator borrow
|
||||
{
|
||||
let mut iter = src.as_ref().iter().enumerate();
|
||||
|
||||
// path ...
|
||||
for (i, &b) in &mut iter {
|
||||
// See https://url.spec.whatwg.org/#path-state
|
||||
match b {
|
||||
b'?' => {
|
||||
debug_assert_eq!(query, NONE);
|
||||
query = i as u16;
|
||||
break;
|
||||
}
|
||||
b'#' => {
|
||||
fragment = Some(i);
|
||||
break;
|
||||
}
|
||||
|
||||
// This is the range of bytes that don't need to be
|
||||
// percent-encoded in the path. If it should have been
|
||||
// percent-encoded, then error.
|
||||
0x21 |
|
||||
0x24..=0x3B |
|
||||
0x3D |
|
||||
0x40..=0x5F |
|
||||
0x61..=0x7A |
|
||||
0x7C |
|
||||
0x7E => {},
|
||||
|
||||
// These are code points that are supposed to be
|
||||
// percent-encoded in the path but there are clients
|
||||
// out there sending them as is and httparse accepts
|
||||
// to parse those requests, so they are allowed here
|
||||
// for parity.
|
||||
//
|
||||
// For reference, those are code points that are used
|
||||
// to send requests with JSON directly embedded in
|
||||
// the URI path. Yes, those things happen for real.
|
||||
b'"' |
|
||||
b'{' | b'}' => {},
|
||||
|
||||
_ => return Err(ErrorKind::InvalidUriChar.into()),
|
||||
}
|
||||
}
|
||||
|
||||
// query ...
|
||||
if query != NONE {
|
||||
for (i, &b) in iter {
|
||||
match b {
|
||||
// While queries *should* be percent-encoded, most
|
||||
// bytes are actually allowed...
|
||||
// See https://url.spec.whatwg.org/#query-state
|
||||
//
|
||||
// Allowed: 0x21 / 0x24 - 0x3B / 0x3D / 0x3F - 0x7E
|
||||
0x21 |
|
||||
0x24..=0x3B |
|
||||
0x3D |
|
||||
0x3F..=0x7E => {},
|
||||
|
||||
b'#' => {
|
||||
fragment = Some(i);
|
||||
break;
|
||||
}
|
||||
|
||||
_ => return Err(ErrorKind::InvalidUriChar.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(i) = fragment {
|
||||
src.truncate(i);
|
||||
}
|
||||
|
||||
Ok(PathAndQuery {
|
||||
data: unsafe { ByteStr::from_utf8_unchecked(src) },
|
||||
query: query,
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert a `PathAndQuery` from a static string.
|
||||
///
|
||||
/// This function will not perform any copying, however the string is
|
||||
/// checked to ensure that it is valid.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the argument is an invalid path and query.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let v = PathAndQuery::from_static("/hello?world");
|
||||
///
|
||||
/// assert_eq!(v.path(), "/hello");
|
||||
/// assert_eq!(v.query(), Some("world"));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_static(src: &'static str) -> Self {
|
||||
let src = Bytes::from_static(src.as_bytes());
|
||||
|
||||
PathAndQuery::from_shared(src).unwrap()
|
||||
}
|
||||
|
||||
/// Attempt to convert a `Bytes` buffer to a `PathAndQuery`.
|
||||
///
|
||||
/// This will try to prevent a copy if the type passed is the type used
|
||||
/// internally, and will copy the data if it is not.
|
||||
pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
|
||||
where
|
||||
T: AsRef<[u8]> + 'static,
|
||||
{
|
||||
if_downcast_into!(T, Bytes, src, {
|
||||
return PathAndQuery::from_shared(src);
|
||||
});
|
||||
|
||||
PathAndQuery::try_from(src.as_ref())
|
||||
}
|
||||
|
||||
pub(super) fn empty() -> Self {
|
||||
PathAndQuery {
|
||||
data: ByteStr::new(),
|
||||
query: NONE,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn slash() -> Self {
|
||||
PathAndQuery {
|
||||
data: ByteStr::from_static("/"),
|
||||
query: NONE,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn star() -> Self {
|
||||
PathAndQuery {
|
||||
data: ByteStr::from_static("*"),
|
||||
query: NONE,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path component
|
||||
///
|
||||
/// The path component is **case sensitive**.
|
||||
///
|
||||
/// ```notrust
|
||||
/// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
|
||||
/// |--------|
|
||||
/// |
|
||||
/// path
|
||||
/// ```
|
||||
///
|
||||
/// If the URI is `*` then the path component is equal to `*`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
///
|
||||
/// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(path_and_query.path(), "/hello/world");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn path(&self) -> &str {
|
||||
let ret = if self.query == NONE {
|
||||
&self.data[..]
|
||||
} else {
|
||||
&self.data[..self.query as usize]
|
||||
};
|
||||
|
||||
if ret.is_empty() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
/// Returns the query string component
|
||||
///
|
||||
/// The query component contains non-hierarchical data that, along with data
|
||||
/// in the path component, serves to identify a resource within the scope of
|
||||
/// the URI's scheme and naming authority (if any). The query component is
|
||||
/// indicated by the first question mark ("?") character and terminated by a
|
||||
/// number sign ("#") character or by the end of the URI.
|
||||
///
|
||||
/// ```notrust
|
||||
/// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
|
||||
/// |-------------------|
|
||||
/// |
|
||||
/// query
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// With a query string component
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let path_and_query: PathAndQuery = "/hello/world?key=value&foo=bar".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(path_and_query.query(), Some("key=value&foo=bar"));
|
||||
/// ```
|
||||
///
|
||||
/// Without a query string component
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
|
||||
///
|
||||
/// assert!(path_and_query.query().is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn query(&self) -> Option<&str> {
|
||||
if self.query == NONE {
|
||||
None
|
||||
} else {
|
||||
let i = self.query + 1;
|
||||
Some(&self.data[i as usize..])
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path and query as a string component.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// With a query string component
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let path_and_query: PathAndQuery = "/hello/world?key=value&foo=bar".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(path_and_query.as_str(), "/hello/world?key=value&foo=bar");
|
||||
/// ```
|
||||
///
|
||||
/// Without a query string component
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(path_and_query.as_str(), "/hello/world");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
let ret = &self.data[..];
|
||||
if ret.is_empty() {
|
||||
return "/";
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for PathAndQuery {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
PathAndQuery::from_shared(Bytes::copy_from_slice(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for PathAndQuery {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||||
TryFrom::try_from(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Vec<u8>> for PathAndQuery {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
PathAndQuery::from_shared(vec.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for PathAndQuery {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
PathAndQuery::from_shared(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&String> for PathAndQuery {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &String) -> Result<Self, Self::Error> {
|
||||
TryFrom::try_from(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PathAndQuery {
|
||||
type Err = InvalidUri;
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<Self, InvalidUri> {
|
||||
TryFrom::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PathAndQuery {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PathAndQuery {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if !self.data.is_empty() {
|
||||
match self.data.as_bytes()[0] {
|
||||
b'/' | b'*' => write!(fmt, "{}", &self.data[..]),
|
||||
_ => write!(fmt, "/{}", &self.data[..]),
|
||||
}
|
||||
} else {
|
||||
write!(fmt, "/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== PartialEq / PartialOrd =====
|
||||
|
||||
impl PartialEq for PathAndQuery {
|
||||
#[inline]
|
||||
fn eq(&self, other: &PathAndQuery) -> bool {
|
||||
self.data == other.data
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for PathAndQuery {}
|
||||
|
||||
impl PartialEq<str> for PathAndQuery {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<PathAndQuery> for &'a str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &PathAndQuery) -> bool {
|
||||
self == &other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for PathAndQuery {
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'a str) -> bool {
|
||||
self.as_str() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<PathAndQuery> for str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &PathAndQuery) -> bool {
|
||||
self == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for PathAndQuery {
|
||||
#[inline]
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<PathAndQuery> for String {
|
||||
#[inline]
|
||||
fn eq(&self, other: &PathAndQuery) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for PathAndQuery {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<str> for PathAndQuery {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
|
||||
self.as_str().partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<PathAndQuery> for str {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
|
||||
self.partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<&'a str> for PathAndQuery {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
|
||||
self.as_str().partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<PathAndQuery> for &'a str {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
|
||||
self.partial_cmp(&other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<String> for PathAndQuery {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<PathAndQuery> for String {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn equal_to_self_of_same_path() {
|
||||
let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
let p2: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
assert_eq!(p1, p2);
|
||||
assert_eq!(p2, p1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_to_self_of_different_path() {
|
||||
let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
let p2: PathAndQuery = "/world&foo=bar".parse().unwrap();
|
||||
assert_ne!(p1, p2);
|
||||
assert_ne!(p2, p1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_a_str() {
|
||||
let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
assert_eq!(&path_and_query, "/hello/world&foo=bar");
|
||||
assert_eq!("/hello/world&foo=bar", &path_and_query);
|
||||
assert_eq!(path_and_query, "/hello/world&foo=bar");
|
||||
assert_eq!("/hello/world&foo=bar", path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_with_a_str_of_a_different_path() {
|
||||
let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
// as a reference
|
||||
assert_ne!(&path_and_query, "/hello&foo=bar");
|
||||
assert_ne!("/hello&foo=bar", &path_and_query);
|
||||
// without reference
|
||||
assert_ne!(path_and_query, "/hello&foo=bar");
|
||||
assert_ne!("/hello&foo=bar", path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_a_string() {
|
||||
let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
assert_eq!(path_and_query, "/hello/world&foo=bar".to_string());
|
||||
assert_eq!("/hello/world&foo=bar".to_string(), path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_with_a_string_of_a_different_path() {
|
||||
let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
assert_ne!(path_and_query, "/hello&foo=bar".to_string());
|
||||
assert_ne!("/hello&foo=bar".to_string(), path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_to_self() {
|
||||
let p1: PathAndQuery = "/a/world&foo=bar".parse().unwrap();
|
||||
let p2: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
|
||||
assert!(p1 < p2);
|
||||
assert!(p2 > p1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_with_a_str() {
|
||||
let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
|
||||
// by ref
|
||||
assert!(&path_and_query < "/c/world&foo=bar");
|
||||
assert!("/c/world&foo=bar" > &path_and_query);
|
||||
assert!(&path_and_query > "/a/world&foo=bar");
|
||||
assert!("/a/world&foo=bar" < &path_and_query);
|
||||
|
||||
// by val
|
||||
assert!(path_and_query < "/c/world&foo=bar");
|
||||
assert!("/c/world&foo=bar" > path_and_query);
|
||||
assert!(path_and_query > "/a/world&foo=bar");
|
||||
assert!("/a/world&foo=bar" < path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_with_a_string() {
|
||||
let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
|
||||
assert!(path_and_query < "/c/world&foo=bar".to_string());
|
||||
assert!("/c/world&foo=bar".to_string() > path_and_query);
|
||||
assert!(path_and_query > "/a/world&foo=bar".to_string());
|
||||
assert!("/a/world&foo=bar".to_string() < path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_valid_percent_encodings() {
|
||||
assert_eq!("/a%20b", pq("/a%20b?r=1").path());
|
||||
assert_eq!("qr=%31", pq("/a/b?qr=%31").query().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_invalid_percent_encodings() {
|
||||
assert_eq!("/a%%b", pq("/a%%b?r=1").path());
|
||||
assert_eq!("/aaa%", pq("/aaa%").path());
|
||||
assert_eq!("/aaa%", pq("/aaa%?r=1").path());
|
||||
assert_eq!("/aa%2", pq("/aa%2").path());
|
||||
assert_eq!("/aa%2", pq("/aa%2?r=1").path());
|
||||
assert_eq!("qr=%3", pq("/a/b?qr=%3").query().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_is_fine() {
|
||||
assert_eq!(r#"/{"bread":"baguette"}"#, pq(r#"/{"bread":"baguette"}"#).path());
|
||||
}
|
||||
|
||||
fn pq(s: &str) -> PathAndQuery {
|
||||
s.parse().expect(&format!("parsing {}", s))
|
||||
}
|
||||
}
|
||||
151
zeroidc/vendor/http/src/uri/port.rs
vendored
Normal file
151
zeroidc/vendor/http/src/uri/port.rs
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
use std::fmt;
|
||||
|
||||
use super::{ErrorKind, InvalidUri};
|
||||
|
||||
/// The port component of a URI.
|
||||
pub struct Port<T> {
|
||||
port: u16,
|
||||
repr: T,
|
||||
}
|
||||
|
||||
impl<T> Port<T> {
|
||||
/// Returns the port number as a `u16`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Port as `u16`.
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "example.org:80".parse().unwrap();
|
||||
///
|
||||
/// let port = authority.port().unwrap();
|
||||
/// assert_eq!(port.as_u16(), 80);
|
||||
/// ```
|
||||
pub fn as_u16(&self) -> u16 {
|
||||
self.port
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Port<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
/// Converts a `str` to a port number.
|
||||
///
|
||||
/// The supplied `str` must be a valid u16.
|
||||
pub(crate) fn from_str(bytes: T) -> Result<Self, InvalidUri> {
|
||||
bytes
|
||||
.as_ref()
|
||||
.parse::<u16>()
|
||||
.map(|port| Port { port, repr: bytes })
|
||||
.map_err(|_| ErrorKind::InvalidPort.into())
|
||||
}
|
||||
|
||||
/// Returns the port number as a `str`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Port as `str`.
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "example.org:80".parse().unwrap();
|
||||
///
|
||||
/// let port = authority.port().unwrap();
|
||||
/// assert_eq!(port.as_str(), "80");
|
||||
/// ```
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.repr.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Port<T>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("Port").field(&self.port).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for Port<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Use `u16::fmt` so that it respects any formatting flags that
|
||||
// may have been set (like padding, align, etc).
|
||||
fmt::Display::fmt(&self.port, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Port<T>> for u16 {
|
||||
fn from(port: Port<T>) -> Self {
|
||||
port.as_u16()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<str> for Port<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> PartialEq<Port<U>> for Port<T> {
|
||||
fn eq(&self, other: &Port<U>) -> bool {
|
||||
self.port == other.port
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<u16> for Port<T> {
|
||||
fn eq(&self, other: &u16) -> bool {
|
||||
self.port == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<Port<T>> for u16 {
|
||||
fn eq(&self, other: &Port<T>) -> bool {
|
||||
other.port == *self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn partialeq_port() {
|
||||
let port_a = Port::from_str("8080").unwrap();
|
||||
let port_b = Port::from_str("8080").unwrap();
|
||||
assert_eq!(port_a, port_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partialeq_port_different_reprs() {
|
||||
let port_a = Port {
|
||||
repr: "8081",
|
||||
port: 8081,
|
||||
};
|
||||
let port_b = Port {
|
||||
repr: String::from("8081"),
|
||||
port: 8081,
|
||||
};
|
||||
assert_eq!(port_a, port_b);
|
||||
assert_eq!(port_b, port_a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partialeq_u16() {
|
||||
let port = Port::from_str("8080").unwrap();
|
||||
// test equals in both directions
|
||||
assert_eq!(port, 8080);
|
||||
assert_eq!(8080, port);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u16_from_port() {
|
||||
let port = Port::from_str("8080").unwrap();
|
||||
assert_eq!(8080, u16::from(port));
|
||||
}
|
||||
}
|
||||
363
zeroidc/vendor/http/src/uri/scheme.rs
vendored
Normal file
363
zeroidc/vendor/http/src/uri/scheme.rs
vendored
Normal file
@@ -0,0 +1,363 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::str::FromStr;
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
use super::{ErrorKind, InvalidUri};
|
||||
use crate::byte_str::ByteStr;
|
||||
|
||||
/// Represents the scheme component of a URI
|
||||
#[derive(Clone)]
|
||||
pub struct Scheme {
|
||||
pub(super) inner: Scheme2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) enum Scheme2<T = Box<ByteStr>> {
|
||||
None,
|
||||
Standard(Protocol),
|
||||
Other(T),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(super) enum Protocol {
|
||||
Http,
|
||||
Https,
|
||||
}
|
||||
|
||||
impl Scheme {
|
||||
/// HTTP protocol scheme
|
||||
pub const HTTP: Scheme = Scheme {
|
||||
inner: Scheme2::Standard(Protocol::Http),
|
||||
};
|
||||
|
||||
/// HTTP protocol over TLS.
|
||||
pub const HTTPS: Scheme = Scheme {
|
||||
inner: Scheme2::Standard(Protocol::Https),
|
||||
};
|
||||
|
||||
pub(super) fn empty() -> Self {
|
||||
Scheme {
|
||||
inner: Scheme2::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a str representation of the scheme
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let scheme: Scheme = "http".parse().unwrap();
|
||||
/// assert_eq!(scheme.as_str(), "http");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
use self::Protocol::*;
|
||||
use self::Scheme2::*;
|
||||
|
||||
match self.inner {
|
||||
Standard(Http) => "http",
|
||||
Standard(Https) => "https",
|
||||
Other(ref v) => &v[..],
|
||||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Scheme {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
use self::Scheme2::*;
|
||||
|
||||
match Scheme2::parse_exact(s)? {
|
||||
None => Err(ErrorKind::InvalidScheme.into()),
|
||||
Standard(p) => Ok(Standard(p).into()),
|
||||
Other(_) => {
|
||||
let bytes = Bytes::copy_from_slice(s);
|
||||
|
||||
// Safety: postcondition on parse_exact() means that s and
|
||||
// hence bytes are valid UTF-8.
|
||||
let string = unsafe { ByteStr::from_utf8_unchecked(bytes) };
|
||||
|
||||
Ok(Other(Box::new(string)).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Scheme {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||||
TryFrom::try_from(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Scheme {
|
||||
type Err = InvalidUri;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
TryFrom::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Scheme {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Scheme {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Scheme {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Scheme {
|
||||
fn eq(&self, other: &Scheme) -> bool {
|
||||
use self::Protocol::*;
|
||||
use self::Scheme2::*;
|
||||
|
||||
match (&self.inner, &other.inner) {
|
||||
(&Standard(Http), &Standard(Http)) => true,
|
||||
(&Standard(Https), &Standard(Https)) => true,
|
||||
(&Other(ref a), &Other(ref b)) => a.eq_ignore_ascii_case(b),
|
||||
(&None, _) | (_, &None) => unreachable!(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Scheme {}
|
||||
|
||||
/// Case-insensitive equality
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Scheme;
|
||||
/// let scheme: Scheme = "HTTP".parse().unwrap();
|
||||
/// assert_eq!(scheme, *"http");
|
||||
/// ```
|
||||
impl PartialEq<str> for Scheme {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str().eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Case-insensitive equality
|
||||
impl PartialEq<Scheme> for str {
|
||||
fn eq(&self, other: &Scheme) -> bool {
|
||||
other == self
|
||||
}
|
||||
}
|
||||
|
||||
/// Case-insensitive hashing
|
||||
impl Hash for Scheme {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
match self.inner {
|
||||
Scheme2::None => (),
|
||||
Scheme2::Standard(Protocol::Http) => state.write_u8(1),
|
||||
Scheme2::Standard(Protocol::Https) => state.write_u8(2),
|
||||
Scheme2::Other(ref other) => {
|
||||
other.len().hash(state);
|
||||
for &b in other.as_bytes() {
|
||||
state.write_u8(b.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Scheme2<T> {
|
||||
pub(super) fn is_none(&self) -> bool {
|
||||
match *self {
|
||||
Scheme2::None => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Require the scheme to not be too long in order to enable further
|
||||
// optimizations later.
|
||||
const MAX_SCHEME_LEN: usize = 64;
|
||||
|
||||
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
//
|
||||
// SCHEME_CHARS is a table of valid characters in the scheme part of a URI. An
|
||||
// entry in the table is 0 for invalid characters. For valid characters the
|
||||
// entry is itself (i.e. the entry for 43 is b'+' because b'+' == 43u8). An
|
||||
// important characteristic of this table is that all entries above 127 are
|
||||
// invalid. This makes all of the valid entries a valid single-byte UTF-8 code
|
||||
// point. This means that a slice of such valid entries is valid UTF-8.
|
||||
const SCHEME_CHARS: [u8; 256] = [
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3x
|
||||
0, 0, 0, b'+', 0, b'-', b'.', 0, b'0', b'1', // 4x
|
||||
b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', 0, // 5x
|
||||
0, 0, 0, 0, 0, b'A', b'B', b'C', b'D', b'E', // 6x
|
||||
b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', // 7x
|
||||
b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', // 8x
|
||||
b'Z', 0, 0, 0, 0, 0, 0, b'a', b'b', b'c', // 9x
|
||||
b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', // 10x
|
||||
b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', // 11x
|
||||
b'x', b'y', b'z', 0, 0, 0, b'~', 0, 0, 0, // 12x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 13x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24x
|
||||
0, 0, 0, 0, 0, 0 // 25x
|
||||
];
|
||||
|
||||
impl Scheme2<usize> {
|
||||
// Postcondition: On all Ok() returns, s is valid UTF-8
|
||||
fn parse_exact(s: &[u8]) -> Result<Scheme2<()>, InvalidUri> {
|
||||
match s {
|
||||
b"http" => Ok(Protocol::Http.into()),
|
||||
b"https" => Ok(Protocol::Https.into()),
|
||||
_ => {
|
||||
if s.len() > MAX_SCHEME_LEN {
|
||||
return Err(ErrorKind::SchemeTooLong.into());
|
||||
}
|
||||
|
||||
// check that each byte in s is a SCHEME_CHARS which implies
|
||||
// that it is a valid single byte UTF-8 code point.
|
||||
for &b in s {
|
||||
match SCHEME_CHARS[b as usize] {
|
||||
b':' => {
|
||||
// Don't want :// here
|
||||
return Err(ErrorKind::InvalidScheme.into());
|
||||
}
|
||||
0 => {
|
||||
return Err(ErrorKind::InvalidScheme.into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Scheme2::Other(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse(s: &[u8]) -> Result<Scheme2<usize>, InvalidUri> {
|
||||
if s.len() >= 7 {
|
||||
// Check for HTTP
|
||||
if s[..7].eq_ignore_ascii_case(b"http://") {
|
||||
// Prefix will be striped
|
||||
return Ok(Protocol::Http.into());
|
||||
}
|
||||
}
|
||||
|
||||
if s.len() >= 8 {
|
||||
// Check for HTTPs
|
||||
if s[..8].eq_ignore_ascii_case(b"https://") {
|
||||
return Ok(Protocol::Https.into());
|
||||
}
|
||||
}
|
||||
|
||||
if s.len() > 3 {
|
||||
for i in 0..s.len() {
|
||||
let b = s[i];
|
||||
|
||||
match SCHEME_CHARS[b as usize] {
|
||||
b':' => {
|
||||
// Not enough data remaining
|
||||
if s.len() < i + 3 {
|
||||
break;
|
||||
}
|
||||
|
||||
// Not a scheme
|
||||
if &s[i + 1..i + 3] != b"//" {
|
||||
break;
|
||||
}
|
||||
|
||||
if i > MAX_SCHEME_LEN {
|
||||
return Err(ErrorKind::SchemeTooLong.into());
|
||||
}
|
||||
|
||||
// Return scheme
|
||||
return Ok(Scheme2::Other(i));
|
||||
}
|
||||
// Invald scheme character, abort
|
||||
0 => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Scheme2::None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Protocol {
|
||||
pub(super) fn len(&self) -> usize {
|
||||
match *self {
|
||||
Protocol::Http => 4,
|
||||
Protocol::Https => 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Protocol> for Scheme2<T> {
|
||||
fn from(src: Protocol) -> Self {
|
||||
Scheme2::Standard(src)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<Scheme2> for Scheme {
|
||||
fn from(src: Scheme2) -> Self {
|
||||
Scheme { inner: src }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn scheme_eq_to_str() {
|
||||
assert_eq!(&scheme("http"), "http");
|
||||
assert_eq!(&scheme("https"), "https");
|
||||
assert_eq!(&scheme("ftp"), "ftp");
|
||||
assert_eq!(&scheme("my+funky+scheme"), "my+funky+scheme");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_scheme_is_error() {
|
||||
Scheme::try_from("my_funky_scheme").expect_err("Unexpectly valid Scheme");
|
||||
|
||||
// Invalid UTF-8
|
||||
Scheme::try_from([0xC0].as_ref()).expect_err("Unexpectly valid Scheme");
|
||||
}
|
||||
|
||||
fn scheme(s: &str) -> Scheme {
|
||||
s.parse().expect(&format!("Invalid scheme: {}", s))
|
||||
}
|
||||
}
|
||||
519
zeroidc/vendor/http/src/uri/tests.rs
vendored
Normal file
519
zeroidc/vendor/http/src/uri/tests.rs
vendored
Normal file
@@ -0,0 +1,519 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::{ErrorKind, InvalidUri, Port, Uri, URI_CHARS};
|
||||
|
||||
#[test]
|
||||
fn test_char_table() {
|
||||
for (i, &v) in URI_CHARS.iter().enumerate() {
|
||||
if v != 0 {
|
||||
assert_eq!(i, v as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! part {
|
||||
($s:expr) => {
|
||||
Some(&$s.parse().unwrap())
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! test_parse {
|
||||
(
|
||||
$test_name:ident,
|
||||
$str:expr,
|
||||
$alt:expr,
|
||||
$($method:ident = $value:expr,)*
|
||||
) => (
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
let orig_str = $str;
|
||||
let uri = match Uri::from_str(orig_str) {
|
||||
Ok(uri) => uri,
|
||||
Err(err) => {
|
||||
panic!("parse error {:?} from {:?}", err, orig_str);
|
||||
},
|
||||
};
|
||||
$(
|
||||
assert_eq!(uri.$method(), $value, "{}: uri = {:?}", stringify!($method), uri);
|
||||
)+
|
||||
assert_eq!(uri, orig_str, "partial eq to original str");
|
||||
assert_eq!(uri, uri.clone(), "clones are equal");
|
||||
|
||||
let new_str = uri.to_string();
|
||||
let new_uri = Uri::from_str(&new_str).expect("to_string output parses again as a Uri");
|
||||
assert_eq!(new_uri, orig_str, "round trip still equals original str");
|
||||
|
||||
const ALT: &'static [&'static str] = &$alt;
|
||||
|
||||
for &alt in ALT.iter() {
|
||||
let other: Uri = alt.parse().unwrap();
|
||||
assert_eq!(uri, *alt);
|
||||
assert_eq!(uri, other);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_path_and_query,
|
||||
"/some/path/here?and=then&hello#and-bye",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = None,
|
||||
path = "/some/path/here",
|
||||
query = Some("and=then&hello"),
|
||||
host = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_form,
|
||||
"http://127.0.0.1:61761/chunks",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1:61761"),
|
||||
path = "/chunks",
|
||||
query = None,
|
||||
host = Some("127.0.0.1"),
|
||||
port = Port::from_str("61761").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_form_without_path,
|
||||
"https://127.0.0.1:61761",
|
||||
["https://127.0.0.1:61761/"],
|
||||
|
||||
scheme = part!("https"),
|
||||
authority = part!("127.0.0.1:61761"),
|
||||
path = "/",
|
||||
query = None,
|
||||
host = Some("127.0.0.1"),
|
||||
port = Port::from_str("61761").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_asterisk_form,
|
||||
"*",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = None,
|
||||
path = "*",
|
||||
query = None,
|
||||
host = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_authority_no_port,
|
||||
"localhost",
|
||||
["LOCALHOST", "LocaLHOSt"],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("localhost"),
|
||||
path = "",
|
||||
query = None,
|
||||
port = None,
|
||||
host = Some("localhost"),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_authority_only_one_character_issue_197,
|
||||
"S",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("S"),
|
||||
path = "",
|
||||
query = None,
|
||||
port = None,
|
||||
host = Some("S"),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_authority_form,
|
||||
"localhost:3000",
|
||||
["localhosT:3000"],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("localhost:3000"),
|
||||
path = "",
|
||||
query = None,
|
||||
host = Some("localhost"),
|
||||
port = Port::from_str("3000").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_with_default_port_http,
|
||||
"http://127.0.0.1:80",
|
||||
["http://127.0.0.1:80/"],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1:80"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = Port::from_str("80").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_with_default_port_https,
|
||||
"https://127.0.0.1:443",
|
||||
["https://127.0.0.1:443/"],
|
||||
|
||||
scheme = part!("https"),
|
||||
authority = part!("127.0.0.1:443"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = Port::from_str("443").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_fragment_questionmark,
|
||||
"http://127.0.0.1/#?",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_path_with_terminating_questionmark,
|
||||
"http://127.0.0.1/path?",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1"),
|
||||
path = "/path",
|
||||
query = Some(""),
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_form_with_empty_path_and_nonempty_query,
|
||||
"http://127.0.0.1?foo=bar",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1"),
|
||||
path = "/",
|
||||
query = Some("foo=bar"),
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_form_with_empty_path_and_fragment_with_slash,
|
||||
"http://127.0.0.1#foo/bar",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_form_with_empty_path_and_fragment_with_questionmark,
|
||||
"http://127.0.0.1#foo?bar",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_long_host_with_no_scheme,
|
||||
"thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost"),
|
||||
path = "",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_long_host_with_port_and_no_scheme,
|
||||
"thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost:1234",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost:1234"),
|
||||
path = "",
|
||||
query = None,
|
||||
port = Port::from_str("1234").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_userinfo1,
|
||||
"http://a:b@127.0.0.1:1234/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("a:b@127.0.0.1:1234"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = Port::from_str("1234").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_userinfo2,
|
||||
"http://a:b@127.0.0.1/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("a:b@127.0.0.1"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_userinfo3,
|
||||
"http://a@127.0.0.1/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("a@127.0.0.1"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_userinfo_with_port,
|
||||
"user@localhost:3000",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("user@localhost:3000"),
|
||||
path = "",
|
||||
query = None,
|
||||
host = Some("localhost"),
|
||||
port = Port::from_str("3000").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_userinfo_pass_with_port,
|
||||
"user:pass@localhost:3000",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("user:pass@localhost:3000"),
|
||||
path = "",
|
||||
query = None,
|
||||
host = Some("localhost"),
|
||||
port = Port::from_str("3000").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_ipv6,
|
||||
"http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
|
||||
host = Some("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_ipv6_shorthand,
|
||||
"http://[::1]/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("[::1]"),
|
||||
host = Some("[::1]"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_ipv6_shorthand2,
|
||||
"http://[::]/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("[::]"),
|
||||
host = Some("[::]"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_ipv6_shorthand3,
|
||||
"http://[2001:db8::2:1]/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("[2001:db8::2:1]"),
|
||||
host = Some("[2001:db8::2:1]"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_ipv6_with_port,
|
||||
"http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008"),
|
||||
host = Some("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = Port::from_str("8008").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_percentage_encoded_path,
|
||||
"/echo/abcdefgh_i-j%20/abcdefg_i-j%20478",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = None,
|
||||
host = None,
|
||||
path = "/echo/abcdefgh_i-j%20/abcdefg_i-j%20478",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_path_permissive,
|
||||
"/foo=bar|baz\\^~%",
|
||||
[],
|
||||
|
||||
path = "/foo=bar|baz\\^~%",
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_query_permissive,
|
||||
"/?foo={bar|baz}\\^`",
|
||||
[],
|
||||
|
||||
query = Some("foo={bar|baz}\\^`"),
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uri_parse_error() {
|
||||
fn err(s: &str) {
|
||||
Uri::from_str(s).unwrap_err();
|
||||
}
|
||||
|
||||
err("http://");
|
||||
err("htt:p//host");
|
||||
err("hyper.rs/");
|
||||
err("hyper.rs?key=val");
|
||||
err("?key=val");
|
||||
err("localhost/");
|
||||
err("localhost?key=val");
|
||||
err("\0");
|
||||
err("http://[::1");
|
||||
err("http://::1]");
|
||||
err("localhost:8080:3030");
|
||||
err("@");
|
||||
err("http://username:password@/wut");
|
||||
|
||||
// illegal queries
|
||||
err("/?foo\rbar");
|
||||
err("/?foo\nbar");
|
||||
err("/?<");
|
||||
err("/?>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_uri_len() {
|
||||
let mut uri = vec![];
|
||||
uri.extend(b"http://localhost/");
|
||||
uri.extend(vec![b'a'; 70 * 1024]);
|
||||
|
||||
let uri = String::from_utf8(uri).unwrap();
|
||||
let res: Result<Uri, InvalidUri> = uri.parse();
|
||||
|
||||
assert_eq!(res.unwrap_err().0, ErrorKind::TooLong);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overflowing_scheme() {
|
||||
let mut uri = vec![];
|
||||
uri.extend(vec![b'a'; 256]);
|
||||
uri.extend(b"://localhost/");
|
||||
|
||||
let uri = String::from_utf8(uri).unwrap();
|
||||
let res: Result<Uri, InvalidUri> = uri.parse();
|
||||
|
||||
assert_eq!(res.unwrap_err().0, ErrorKind::SchemeTooLong);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_length_scheme() {
|
||||
let mut uri = vec![];
|
||||
uri.extend(vec![b'a'; 64]);
|
||||
uri.extend(b"://localhost/");
|
||||
|
||||
let uri = String::from_utf8(uri).unwrap();
|
||||
let uri: Uri = uri.parse().unwrap();
|
||||
|
||||
assert_eq!(uri.scheme_str().unwrap().len(), 64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uri_to_path_and_query() {
|
||||
let cases = vec![
|
||||
("/", "/"),
|
||||
("/foo?bar", "/foo?bar"),
|
||||
("/foo?bar#nope", "/foo?bar"),
|
||||
("http://hyper.rs", "/"),
|
||||
("http://hyper.rs/", "/"),
|
||||
("http://hyper.rs/path", "/path"),
|
||||
("http://hyper.rs?query", "/?query"),
|
||||
("*", "*"),
|
||||
];
|
||||
|
||||
for case in cases {
|
||||
let uri = Uri::from_str(case.0).unwrap();
|
||||
let s = uri.path_and_query().unwrap().to_string();
|
||||
|
||||
assert_eq!(s, case.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authority_uri_parts_round_trip() {
|
||||
let s = "hyper.rs";
|
||||
let uri = Uri::from_str(s).expect("first parse");
|
||||
assert_eq!(uri, s);
|
||||
assert_eq!(uri.to_string(), s);
|
||||
|
||||
let parts = uri.into_parts();
|
||||
let uri2 = Uri::from_parts(parts).expect("from_parts");
|
||||
assert_eq!(uri2, s);
|
||||
assert_eq!(uri2.to_string(), s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_eq_path_with_terminating_questionmark() {
|
||||
let a = "/path";
|
||||
let uri = Uri::from_str("/path?").expect("first parse");
|
||||
|
||||
assert_eq!(uri, a);
|
||||
}
|
||||
75
zeroidc/vendor/http/src/version.rs
vendored
Normal file
75
zeroidc/vendor/http/src/version.rs
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
//! HTTP version
|
||||
//!
|
||||
//! This module contains a definition of the `Version` type. The `Version`
|
||||
//! type is intended to be accessed through the root of the crate
|
||||
//! (`http::Version`) rather than this module.
|
||||
//!
|
||||
//! The `Version` type contains constants that represent the various versions
|
||||
//! of the HTTP protocol.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use http::Version;
|
||||
//!
|
||||
//! let http11 = Version::HTTP_11;
|
||||
//! let http2 = Version::HTTP_2;
|
||||
//! assert!(http11 != http2);
|
||||
//!
|
||||
//! println!("{:?}", http2);
|
||||
//! ```
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Represents a version of the HTTP spec.
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone, Eq, Ord, Hash)]
|
||||
pub struct Version(Http);
|
||||
|
||||
impl Version {
|
||||
/// `HTTP/0.9`
|
||||
pub const HTTP_09: Version = Version(Http::Http09);
|
||||
|
||||
/// `HTTP/1.0`
|
||||
pub const HTTP_10: Version = Version(Http::Http10);
|
||||
|
||||
/// `HTTP/1.1`
|
||||
pub const HTTP_11: Version = Version(Http::Http11);
|
||||
|
||||
/// `HTTP/2.0`
|
||||
pub const HTTP_2: Version = Version(Http::H2);
|
||||
|
||||
/// `HTTP/3.0`
|
||||
pub const HTTP_3: Version = Version(Http::H3);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone, Eq, Ord, Hash)]
|
||||
enum Http {
|
||||
Http09,
|
||||
Http10,
|
||||
Http11,
|
||||
H2,
|
||||
H3,
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
impl Default for Version {
|
||||
#[inline]
|
||||
fn default() -> Version {
|
||||
Version::HTTP_11
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Version {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use self::Http::*;
|
||||
|
||||
f.write_str(match self.0 {
|
||||
Http09 => "HTTP/0.9",
|
||||
Http10 => "HTTP/1.0",
|
||||
Http11 => "HTTP/1.1",
|
||||
H2 => "HTTP/2.0",
|
||||
H3 => "HTTP/3.0",
|
||||
__NonExhaustive => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
636
zeroidc/vendor/http/tests/header_map.rs
vendored
Normal file
636
zeroidc/vendor/http/tests/header_map.rs
vendored
Normal file
@@ -0,0 +1,636 @@
|
||||
use http::header::*;
|
||||
use http::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
assert!(headers.get("hello").is_none());
|
||||
|
||||
let name: HeaderName = "hello".parse().unwrap();
|
||||
|
||||
match headers.entry(&name) {
|
||||
Entry::Vacant(e) => {
|
||||
e.insert("world".parse().unwrap());
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
assert!(headers.get("hello").is_some());
|
||||
|
||||
match headers.entry(&name) {
|
||||
Entry::Occupied(mut e) => {
|
||||
assert_eq!(e.get(), &"world");
|
||||
|
||||
// Push another value
|
||||
e.append("zomg".parse().unwrap());
|
||||
|
||||
let mut i = e.iter();
|
||||
|
||||
assert_eq!(*i.next().unwrap(), "world");
|
||||
assert_eq!(*i.next().unwrap(), "zomg");
|
||||
assert!(i.next().is_none());
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn reserve_over_capacity() {
|
||||
// See https://github.com/hyperium/http/issues/352
|
||||
let mut headers = HeaderMap::<u32>::with_capacity(32);
|
||||
headers.reserve(50_000); // over MAX_SIZE
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_capacity_max() {
|
||||
// The largest capacity such that (cap + cap / 3) < MAX_SIZE.
|
||||
HeaderMap::<u32>::with_capacity(24_576);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn with_capacity_overflow() {
|
||||
HeaderMap::<u32>::with_capacity(24_577);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn reserve_overflow() {
|
||||
// See https://github.com/hyperium/http/issues/352
|
||||
let mut headers = HeaderMap::<u32>::with_capacity(0);
|
||||
headers.reserve(std::usize::MAX); // next_power_of_two overflows
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drain() {
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
// Insert a single value
|
||||
let name: HeaderName = "hello".parse().unwrap();
|
||||
headers.insert(name, "world".parse().unwrap());
|
||||
|
||||
{
|
||||
let mut iter = headers.drain();
|
||||
let (name, value) = iter.next().unwrap();
|
||||
assert_eq!(name.unwrap().as_str(), "hello");
|
||||
|
||||
assert_eq!(value, "world");
|
||||
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
|
||||
assert!(headers.is_empty());
|
||||
|
||||
// Insert two sequential values
|
||||
headers.insert(
|
||||
"hello".parse::<HeaderName>().unwrap(),
|
||||
"world".parse().unwrap(),
|
||||
);
|
||||
headers.insert(
|
||||
"zomg".parse::<HeaderName>().unwrap(),
|
||||
"bar".parse().unwrap(),
|
||||
);
|
||||
headers.append(
|
||||
"hello".parse::<HeaderName>().unwrap(),
|
||||
"world2".parse().unwrap(),
|
||||
);
|
||||
|
||||
// Drain...
|
||||
{
|
||||
let mut iter = headers.drain();
|
||||
|
||||
let (name, value) = iter.next().unwrap();
|
||||
assert_eq!(name.unwrap().as_str(), "hello");
|
||||
assert_eq!(value, "world");
|
||||
|
||||
let (name, value) = iter.next().unwrap();
|
||||
assert_eq!(name, None);
|
||||
assert_eq!(value, "world2");
|
||||
|
||||
let (name, value) = iter.next().unwrap();
|
||||
assert_eq!(name.unwrap().as_str(), "zomg");
|
||||
assert_eq!(value, "bar");
|
||||
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drain_drop_immediately() {
|
||||
// test mem::forgetting does not double-free
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("hello", "world".parse().unwrap());
|
||||
headers.insert("zomg", "bar".parse().unwrap());
|
||||
headers.append("hello", "world2".parse().unwrap());
|
||||
|
||||
let iter = headers.drain();
|
||||
assert_eq!(iter.size_hint(), (2, Some(3)));
|
||||
// not consuming `iter`
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drain_forget() {
|
||||
// test mem::forgetting does not double-free
|
||||
|
||||
let mut headers = HeaderMap::<HeaderValue>::new();
|
||||
headers.insert("hello", "world".parse().unwrap());
|
||||
headers.insert("zomg", "bar".parse().unwrap());
|
||||
|
||||
assert_eq!(headers.len(), 2);
|
||||
|
||||
{
|
||||
let mut iter = headers.drain();
|
||||
assert_eq!(iter.size_hint(), (2, Some(2)));
|
||||
let _ = iter.next().unwrap();
|
||||
std::mem::forget(iter);
|
||||
}
|
||||
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drain_entry() {
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
headers.insert(
|
||||
"hello".parse::<HeaderName>().unwrap(),
|
||||
"world".parse().unwrap(),
|
||||
);
|
||||
headers.insert(
|
||||
"zomg".parse::<HeaderName>().unwrap(),
|
||||
"foo".parse().unwrap(),
|
||||
);
|
||||
headers.append(
|
||||
"hello".parse::<HeaderName>().unwrap(),
|
||||
"world2".parse().unwrap(),
|
||||
);
|
||||
headers.insert(
|
||||
"more".parse::<HeaderName>().unwrap(),
|
||||
"words".parse().unwrap(),
|
||||
);
|
||||
headers.append(
|
||||
"more".parse::<HeaderName>().unwrap(),
|
||||
"insertions".parse().unwrap(),
|
||||
);
|
||||
assert_eq!(5, headers.len());
|
||||
|
||||
// Using insert_mult
|
||||
{
|
||||
let mut e = match headers.entry("hello") {
|
||||
Entry::Occupied(e) => e,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
let vals: Vec<_> = e.insert_mult("wat".parse().unwrap()).collect();
|
||||
assert_eq!(2, vals.len());
|
||||
assert_eq!(vals[0], "world");
|
||||
assert_eq!(vals[1], "world2");
|
||||
}
|
||||
|
||||
assert_eq!(5-2+1, headers.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eq() {
|
||||
let mut a = HeaderMap::new();
|
||||
let mut b = HeaderMap::new();
|
||||
|
||||
assert_eq!(a, b);
|
||||
|
||||
a.insert(
|
||||
"hello".parse::<HeaderName>().unwrap(),
|
||||
"world".parse().unwrap(),
|
||||
);
|
||||
assert_ne!(a, b);
|
||||
|
||||
b.insert(
|
||||
"hello".parse::<HeaderName>().unwrap(),
|
||||
"world".parse().unwrap(),
|
||||
);
|
||||
assert_eq!(a, b);
|
||||
|
||||
a.insert("foo".parse::<HeaderName>().unwrap(), "bar".parse().unwrap());
|
||||
a.append("foo".parse::<HeaderName>().unwrap(), "baz".parse().unwrap());
|
||||
assert_ne!(a, b);
|
||||
|
||||
b.insert("foo".parse::<HeaderName>().unwrap(), "bar".parse().unwrap());
|
||||
assert_ne!(a, b);
|
||||
|
||||
b.append("foo".parse::<HeaderName>().unwrap(), "baz".parse().unwrap());
|
||||
assert_eq!(a, b);
|
||||
|
||||
a.append("a".parse::<HeaderName>().unwrap(), "a".parse().unwrap());
|
||||
a.append("a".parse::<HeaderName>().unwrap(), "b".parse().unwrap());
|
||||
b.append("a".parse::<HeaderName>().unwrap(), "b".parse().unwrap());
|
||||
b.append("a".parse::<HeaderName>().unwrap(), "a".parse().unwrap());
|
||||
|
||||
assert_ne!(a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_header_name() {
|
||||
let mut m = HeaderMap::new();
|
||||
m.insert(HOST, "localhost".parse().unwrap());
|
||||
m.insert(&ACCEPT, "*/*".parse().unwrap());
|
||||
m.insert("connection", "keep-alive".parse().unwrap());
|
||||
|
||||
m.append(LOCATION, "/".parse().unwrap());
|
||||
m.append(&VIA, "bob".parse().unwrap());
|
||||
m.append("transfer-encoding", "chunked".parse().unwrap());
|
||||
|
||||
assert_eq!(m.len(), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn as_header_name() {
|
||||
let mut m = HeaderMap::new();
|
||||
let v: HeaderValue = "localhost".parse().unwrap();
|
||||
m.insert(HOST, v.clone());
|
||||
|
||||
let expected = Some(&v);
|
||||
|
||||
assert_eq!(m.get("host"), expected);
|
||||
assert_eq!(m.get(&HOST), expected);
|
||||
|
||||
let s = String::from("host");
|
||||
assert_eq!(m.get(&s), expected);
|
||||
assert_eq!(m.get(s.as_str()), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_all_std_headers() {
|
||||
let mut m = HeaderMap::new();
|
||||
|
||||
for (i, hdr) in STD.iter().enumerate() {
|
||||
m.insert(hdr.clone(), hdr.as_str().parse().unwrap());
|
||||
|
||||
for j in 0..(i + 1) {
|
||||
assert_eq!(m[&STD[j]], STD[j].as_str());
|
||||
}
|
||||
|
||||
if i != 0 {
|
||||
for j in (i + 1)..STD.len() {
|
||||
assert!(
|
||||
m.get(&STD[j]).is_none(),
|
||||
"contained {}; j={}",
|
||||
STD[j].as_str(),
|
||||
j
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_79_custom_std_headers() {
|
||||
let mut h = HeaderMap::new();
|
||||
let hdrs = custom_std(79);
|
||||
|
||||
for (i, hdr) in hdrs.iter().enumerate() {
|
||||
h.insert(hdr.clone(), hdr.as_str().parse().unwrap());
|
||||
|
||||
for j in 0..(i + 1) {
|
||||
assert_eq!(h[&hdrs[j]], hdrs[j].as_str());
|
||||
}
|
||||
|
||||
for j in (i + 1)..hdrs.len() {
|
||||
assert!(h.get(&hdrs[j]).is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_multiple_values() {
|
||||
let mut map = HeaderMap::new();
|
||||
|
||||
map.append(header::CONTENT_TYPE, "json".parse().unwrap());
|
||||
map.append(header::CONTENT_TYPE, "html".parse().unwrap());
|
||||
map.append(header::CONTENT_TYPE, "xml".parse().unwrap());
|
||||
|
||||
let vals = map
|
||||
.get_all(&header::CONTENT_TYPE)
|
||||
.iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(&vals, &[&"json", &"html", &"xml"]);
|
||||
}
|
||||
|
||||
fn custom_std(n: usize) -> Vec<HeaderName> {
|
||||
(0..n)
|
||||
.map(|i| {
|
||||
let s = format!("{}-{}", STD[i % STD.len()].as_str(), i);
|
||||
s.parse().unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
const STD: &'static [HeaderName] = &[
|
||||
ACCEPT,
|
||||
ACCEPT_CHARSET,
|
||||
ACCEPT_ENCODING,
|
||||
ACCEPT_LANGUAGE,
|
||||
ACCEPT_RANGES,
|
||||
ACCESS_CONTROL_ALLOW_CREDENTIALS,
|
||||
ACCESS_CONTROL_ALLOW_HEADERS,
|
||||
ACCESS_CONTROL_ALLOW_METHODS,
|
||||
ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||
ACCESS_CONTROL_EXPOSE_HEADERS,
|
||||
ACCESS_CONTROL_MAX_AGE,
|
||||
ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
ACCESS_CONTROL_REQUEST_METHOD,
|
||||
AGE,
|
||||
ALLOW,
|
||||
ALT_SVC,
|
||||
AUTHORIZATION,
|
||||
CACHE_CONTROL,
|
||||
CONNECTION,
|
||||
CONTENT_DISPOSITION,
|
||||
CONTENT_ENCODING,
|
||||
CONTENT_LANGUAGE,
|
||||
CONTENT_LENGTH,
|
||||
CONTENT_LOCATION,
|
||||
CONTENT_RANGE,
|
||||
CONTENT_SECURITY_POLICY,
|
||||
CONTENT_SECURITY_POLICY_REPORT_ONLY,
|
||||
CONTENT_TYPE,
|
||||
COOKIE,
|
||||
DNT,
|
||||
DATE,
|
||||
ETAG,
|
||||
EXPECT,
|
||||
EXPIRES,
|
||||
FORWARDED,
|
||||
FROM,
|
||||
HOST,
|
||||
IF_MATCH,
|
||||
IF_MODIFIED_SINCE,
|
||||
IF_NONE_MATCH,
|
||||
IF_RANGE,
|
||||
IF_UNMODIFIED_SINCE,
|
||||
LAST_MODIFIED,
|
||||
LINK,
|
||||
LOCATION,
|
||||
MAX_FORWARDS,
|
||||
ORIGIN,
|
||||
PRAGMA,
|
||||
PROXY_AUTHENTICATE,
|
||||
PROXY_AUTHORIZATION,
|
||||
PUBLIC_KEY_PINS,
|
||||
PUBLIC_KEY_PINS_REPORT_ONLY,
|
||||
RANGE,
|
||||
REFERER,
|
||||
REFERRER_POLICY,
|
||||
RETRY_AFTER,
|
||||
SERVER,
|
||||
SET_COOKIE,
|
||||
STRICT_TRANSPORT_SECURITY,
|
||||
TE,
|
||||
TRAILER,
|
||||
TRANSFER_ENCODING,
|
||||
USER_AGENT,
|
||||
UPGRADE,
|
||||
UPGRADE_INSECURE_REQUESTS,
|
||||
VARY,
|
||||
VIA,
|
||||
WARNING,
|
||||
WWW_AUTHENTICATE,
|
||||
X_CONTENT_TYPE_OPTIONS,
|
||||
X_DNS_PREFETCH_CONTROL,
|
||||
X_FRAME_OPTIONS,
|
||||
X_XSS_PROTECTION,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn get_invalid() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("foo", "bar".parse().unwrap());
|
||||
assert!(headers.get("Evil\r\nKey").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn insert_invalid() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("evil\r\nfoo", "bar".parse().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value_htab() {
|
||||
// RFC 7230 Section 3.2:
|
||||
// > field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||
HeaderValue::from_static("hello\tworld");
|
||||
HeaderValue::from_str("hello\tworld").unwrap();
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn remove_multiple_a() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(VIA, "1.1 example.com".parse().unwrap());
|
||||
headers.insert(SET_COOKIE, "cookie_1=value 1".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_2=value 2".parse().unwrap());
|
||||
headers.append(VIA, "1.1 other.com".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_3=value 3".parse().unwrap());
|
||||
headers.insert(VARY, "*".parse().unwrap());
|
||||
|
||||
assert_eq!(headers.len(), 6);
|
||||
|
||||
let cookie = headers.remove(SET_COOKIE);
|
||||
assert_eq!(cookie, Some("cookie_1=value 1".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 3);
|
||||
|
||||
let via = headers.remove(VIA);
|
||||
assert_eq!(via, Some("1.1 example.com".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 1);
|
||||
|
||||
let vary = headers.remove(VARY);
|
||||
assert_eq!(vary, Some("*".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_multiple_b() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(VIA, "1.1 example.com".parse().unwrap());
|
||||
headers.insert(SET_COOKIE, "cookie_1=value 1".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_2=value 2".parse().unwrap());
|
||||
headers.append(VIA, "1.1 other.com".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_3=value 3".parse().unwrap());
|
||||
headers.insert(VARY, "*".parse().unwrap());
|
||||
|
||||
assert_eq!(headers.len(), 6);
|
||||
|
||||
let vary = headers.remove(VARY);
|
||||
assert_eq!(vary, Some("*".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 5);
|
||||
|
||||
let via = headers.remove(VIA);
|
||||
assert_eq!(via, Some("1.1 example.com".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 3);
|
||||
|
||||
let cookie = headers.remove(SET_COOKIE);
|
||||
assert_eq!(cookie, Some("cookie_1=value 1".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_entry_multi_0() {
|
||||
let mut headers = HeaderMap::new();
|
||||
let cookies = remove_all_values(&mut headers, SET_COOKIE);
|
||||
assert_eq!(cookies.len(), 0);
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_entry_multi_0_others() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(VIA, "1.1 example.com".parse().unwrap());
|
||||
headers.append(VIA, "1.1 other.com".parse().unwrap());
|
||||
|
||||
let cookies = remove_all_values(&mut headers, SET_COOKIE);
|
||||
assert_eq!(cookies.len(), 0);
|
||||
assert_eq!(headers.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_entry_multi_1() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(SET_COOKIE, "cookie_1=value 1".parse().unwrap());
|
||||
|
||||
let cookies = remove_all_values(&mut headers, SET_COOKIE);
|
||||
assert_eq!(cookies.len(), 1);
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_entry_multi_1_other() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(SET_COOKIE, "cookie_1=value 1".parse().unwrap());
|
||||
headers.insert(VIA, "1.1 example.com".parse().unwrap());
|
||||
|
||||
let cookies = remove_all_values(&mut headers, SET_COOKIE);
|
||||
assert_eq!(cookies.len(), 1);
|
||||
assert_eq!(headers.len(), 1);
|
||||
|
||||
let vias = remove_all_values(&mut headers, VIA);
|
||||
assert_eq!(vias.len(), 1);
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
// For issue hyperimum/http#446
|
||||
#[test]
|
||||
fn remove_entry_multi_2() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(SET_COOKIE, "cookie_1=value 1".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_2=value 2".parse().unwrap());
|
||||
|
||||
let cookies = remove_all_values(&mut headers, SET_COOKIE);
|
||||
assert_eq!(cookies.len(), 2);
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_entry_multi_3() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(SET_COOKIE, "cookie_1=value 1".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_2=value 2".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_3=value 3".parse().unwrap());
|
||||
|
||||
let cookies = remove_all_values(&mut headers, SET_COOKIE);
|
||||
assert_eq!(cookies.len(), 3);
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_entry_multi_3_others() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(VIA, "1.1 example.com".parse().unwrap());
|
||||
headers.insert(SET_COOKIE, "cookie_1=value 1".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_2=value 2".parse().unwrap());
|
||||
headers.append(VIA, "1.1 other.com".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_3=value 3".parse().unwrap());
|
||||
headers.insert(VARY, "*".parse().unwrap());
|
||||
|
||||
let cookies = remove_all_values(&mut headers, SET_COOKIE);
|
||||
assert_eq!(cookies.len(), 3);
|
||||
assert_eq!(headers.len(), 3);
|
||||
|
||||
let vias = remove_all_values(&mut headers, VIA);
|
||||
assert_eq!(vias.len(), 2);
|
||||
assert_eq!(headers.len(), 1);
|
||||
|
||||
let varies = remove_all_values(&mut headers, VARY);
|
||||
assert_eq!(varies.len(), 1);
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
fn remove_all_values<K>(headers: &mut HeaderMap, key: K) -> Vec<HeaderValue>
|
||||
where K: IntoHeaderName
|
||||
{
|
||||
match headers.entry(key) {
|
||||
Entry::Occupied(e) => e.remove_entry_mult().1.collect(),
|
||||
Entry::Vacant(_) => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_entry_3_others_a() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(VIA, "1.1 example.com".parse().unwrap());
|
||||
headers.insert(SET_COOKIE, "cookie_1=value 1".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_2=value 2".parse().unwrap());
|
||||
headers.append(VIA, "1.1 other.com".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_3=value 3".parse().unwrap());
|
||||
headers.insert(VARY, "*".parse().unwrap());
|
||||
|
||||
assert_eq!(headers.len(), 6);
|
||||
|
||||
let cookie = remove_values(&mut headers, SET_COOKIE);
|
||||
assert_eq!(cookie, Some("cookie_1=value 1".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 3);
|
||||
|
||||
let via = remove_values(&mut headers, VIA);
|
||||
assert_eq!(via, Some("1.1 example.com".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 1);
|
||||
|
||||
let vary = remove_values(&mut headers, VARY);
|
||||
assert_eq!(vary, Some("*".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_entry_3_others_b() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(VIA, "1.1 example.com".parse().unwrap());
|
||||
headers.insert(SET_COOKIE, "cookie_1=value 1".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_2=value 2".parse().unwrap());
|
||||
headers.append(VIA, "1.1 other.com".parse().unwrap());
|
||||
headers.append(SET_COOKIE, "cookie_3=value 3".parse().unwrap());
|
||||
headers.insert(VARY, "*".parse().unwrap());
|
||||
|
||||
assert_eq!(headers.len(), 6);
|
||||
|
||||
let vary = remove_values(&mut headers, VARY);
|
||||
assert_eq!(vary, Some("*".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 5);
|
||||
|
||||
let via = remove_values(&mut headers, VIA);
|
||||
assert_eq!(via, Some("1.1 example.com".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 3);
|
||||
|
||||
let cookie = remove_values(&mut headers, SET_COOKIE);
|
||||
assert_eq!(cookie, Some("cookie_1=value 1".parse().unwrap()));
|
||||
assert_eq!(headers.len(), 0);
|
||||
}
|
||||
|
||||
fn remove_values<K>(headers: &mut HeaderMap, key: K) -> Option<HeaderValue>
|
||||
where K: IntoHeaderName
|
||||
{
|
||||
match headers.entry(key) {
|
||||
Entry::Occupied(e) => Some(e.remove_entry().1),
|
||||
Entry::Vacant(_) => None,
|
||||
}
|
||||
}
|
||||
372
zeroidc/vendor/http/tests/header_map_fuzz.rs
vendored
Normal file
372
zeroidc/vendor/http/tests/header_map_fuzz.rs
vendored
Normal file
@@ -0,0 +1,372 @@
|
||||
use http::header::*;
|
||||
use http::*;
|
||||
|
||||
use quickcheck::{Arbitrary, Gen, QuickCheck, TestResult};
|
||||
use rand::rngs::StdRng;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::{Rng, SeedableRng};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn header_map_fuzz() {
|
||||
fn prop(fuzz: Fuzz) -> TestResult {
|
||||
fuzz.run();
|
||||
TestResult::from_bool(true)
|
||||
}
|
||||
|
||||
QuickCheck::new().quickcheck(prop as fn(Fuzz) -> TestResult)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Fuzz {
|
||||
// The magic seed that makes the test case reproducible
|
||||
seed: [u8; 32],
|
||||
|
||||
// Actions to perform
|
||||
steps: Vec<Step>,
|
||||
|
||||
// Number of steps to drop
|
||||
reduce: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Weight {
|
||||
insert: usize,
|
||||
remove: usize,
|
||||
append: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Step {
|
||||
action: Action,
|
||||
expect: AltMap,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Action {
|
||||
Insert {
|
||||
name: HeaderName, // Name to insert
|
||||
val: HeaderValue, // Value to insert
|
||||
old: Option<HeaderValue>, // Old value
|
||||
},
|
||||
Append {
|
||||
name: HeaderName,
|
||||
val: HeaderValue,
|
||||
ret: bool,
|
||||
},
|
||||
Remove {
|
||||
name: HeaderName, // Name to remove
|
||||
val: Option<HeaderValue>, // Value to get
|
||||
},
|
||||
}
|
||||
|
||||
// An alternate implementation of HeaderMap backed by HashMap
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct AltMap {
|
||||
map: HashMap<HeaderName, Vec<HeaderValue>>,
|
||||
}
|
||||
|
||||
impl Fuzz {
|
||||
fn new(seed: [u8; 32]) -> Fuzz {
|
||||
// Seed the RNG
|
||||
let mut rng = StdRng::from_seed(seed);
|
||||
|
||||
let mut steps = vec![];
|
||||
let mut expect = AltMap::default();
|
||||
let num = rng.gen_range(5, 500);
|
||||
|
||||
let weight = Weight {
|
||||
insert: rng.gen_range(1, 10),
|
||||
remove: rng.gen_range(1, 10),
|
||||
append: rng.gen_range(1, 10),
|
||||
};
|
||||
|
||||
while steps.len() < num {
|
||||
steps.push(expect.gen_step(&weight, &mut rng));
|
||||
}
|
||||
|
||||
Fuzz {
|
||||
seed: seed,
|
||||
steps: steps,
|
||||
reduce: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn run(self) {
|
||||
// Create a new header map
|
||||
let mut map = HeaderMap::new();
|
||||
|
||||
// Number of steps to perform
|
||||
let take = self.steps.len() - self.reduce;
|
||||
|
||||
for step in self.steps.into_iter().take(take) {
|
||||
step.action.apply(&mut map);
|
||||
|
||||
step.expect.assert_identical(&map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Fuzz {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> Self {
|
||||
Fuzz::new(Rng::gen(g))
|
||||
}
|
||||
}
|
||||
|
||||
impl AltMap {
|
||||
fn gen_step(&mut self, weight: &Weight, rng: &mut StdRng) -> Step {
|
||||
let action = self.gen_action(weight, rng);
|
||||
|
||||
Step {
|
||||
action: action,
|
||||
expect: self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// This will also apply the action against `self`
|
||||
fn gen_action(&mut self, weight: &Weight, rng: &mut StdRng) -> Action {
|
||||
let sum = weight.insert + weight.remove + weight.append;
|
||||
|
||||
let mut num = rng.gen_range(0, sum);
|
||||
|
||||
if num < weight.insert {
|
||||
return self.gen_insert(rng);
|
||||
}
|
||||
|
||||
num -= weight.insert;
|
||||
|
||||
if num < weight.remove {
|
||||
return self.gen_remove(rng);
|
||||
}
|
||||
|
||||
num -= weight.remove;
|
||||
|
||||
if num < weight.append {
|
||||
return self.gen_append(rng);
|
||||
}
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn gen_insert(&mut self, rng: &mut StdRng) -> Action {
|
||||
let name = self.gen_name(4, rng);
|
||||
let val = gen_header_value(rng);
|
||||
let old = self.insert(name.clone(), val.clone());
|
||||
|
||||
Action::Insert {
|
||||
name: name,
|
||||
val: val,
|
||||
old: old,
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_remove(&mut self, rng: &mut StdRng) -> Action {
|
||||
let name = self.gen_name(-4, rng);
|
||||
let val = self.remove(&name);
|
||||
|
||||
Action::Remove {
|
||||
name: name,
|
||||
val: val,
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_append(&mut self, rng: &mut StdRng) -> Action {
|
||||
let name = self.gen_name(-5, rng);
|
||||
let val = gen_header_value(rng);
|
||||
|
||||
let vals = self.map.entry(name.clone()).or_insert(vec![]);
|
||||
|
||||
let ret = !vals.is_empty();
|
||||
vals.push(val.clone());
|
||||
|
||||
Action::Append {
|
||||
name: name,
|
||||
val: val,
|
||||
ret: ret,
|
||||
}
|
||||
}
|
||||
|
||||
/// Negative numbers weigh finding an existing header higher
|
||||
fn gen_name(&self, weight: i32, rng: &mut StdRng) -> HeaderName {
|
||||
let mut existing = rng.gen_ratio(1, weight.abs() as u32);
|
||||
|
||||
if weight < 0 {
|
||||
existing = !existing;
|
||||
}
|
||||
|
||||
if existing {
|
||||
// Existing header
|
||||
if let Some(name) = self.find_random_name(rng) {
|
||||
name
|
||||
} else {
|
||||
gen_header_name(rng)
|
||||
}
|
||||
} else {
|
||||
gen_header_name(rng)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_random_name(&self, rng: &mut StdRng) -> Option<HeaderName> {
|
||||
if self.map.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let n = rng.gen_range(0, self.map.len());
|
||||
self.map.keys().nth(n).map(Clone::clone)
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, name: HeaderName, val: HeaderValue) -> Option<HeaderValue> {
|
||||
let old = self.map.insert(name, vec![val]);
|
||||
old.and_then(|v| v.into_iter().next())
|
||||
}
|
||||
|
||||
fn remove(&mut self, name: &HeaderName) -> Option<HeaderValue> {
|
||||
self.map.remove(name).and_then(|v| v.into_iter().next())
|
||||
}
|
||||
|
||||
fn assert_identical(&self, other: &HeaderMap<HeaderValue>) {
|
||||
assert_eq!(self.map.len(), other.keys_len());
|
||||
|
||||
for (key, val) in &self.map {
|
||||
// Test get
|
||||
assert_eq!(other.get(key), val.get(0));
|
||||
|
||||
// Test get_all
|
||||
let vals = other.get_all(key);
|
||||
let actual: Vec<_> = vals.iter().collect();
|
||||
assert_eq!(&actual[..], &val[..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Action {
|
||||
fn apply(self, map: &mut HeaderMap<HeaderValue>) {
|
||||
match self {
|
||||
Action::Insert { name, val, old } => {
|
||||
let actual = map.insert(name, val);
|
||||
assert_eq!(actual, old);
|
||||
}
|
||||
Action::Remove { name, val } => {
|
||||
// Just to help track the state, load all associated values.
|
||||
let _ = map.get_all(&name).iter().collect::<Vec<_>>();
|
||||
|
||||
let actual = map.remove(&name);
|
||||
assert_eq!(actual, val);
|
||||
}
|
||||
Action::Append { name, val, ret } => {
|
||||
assert_eq!(ret, map.append(name, val));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_header_name(g: &mut StdRng) -> HeaderName {
|
||||
const STANDARD_HEADERS: &'static [HeaderName] = &[
|
||||
header::ACCEPT,
|
||||
header::ACCEPT_CHARSET,
|
||||
header::ACCEPT_ENCODING,
|
||||
header::ACCEPT_LANGUAGE,
|
||||
header::ACCEPT_RANGES,
|
||||
header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
|
||||
header::ACCESS_CONTROL_ALLOW_HEADERS,
|
||||
header::ACCESS_CONTROL_ALLOW_METHODS,
|
||||
header::ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||
header::ACCESS_CONTROL_EXPOSE_HEADERS,
|
||||
header::ACCESS_CONTROL_MAX_AGE,
|
||||
header::ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
header::ACCESS_CONTROL_REQUEST_METHOD,
|
||||
header::AGE,
|
||||
header::ALLOW,
|
||||
header::ALT_SVC,
|
||||
header::AUTHORIZATION,
|
||||
header::CACHE_CONTROL,
|
||||
header::CONNECTION,
|
||||
header::CONTENT_DISPOSITION,
|
||||
header::CONTENT_ENCODING,
|
||||
header::CONTENT_LANGUAGE,
|
||||
header::CONTENT_LENGTH,
|
||||
header::CONTENT_LOCATION,
|
||||
header::CONTENT_RANGE,
|
||||
header::CONTENT_SECURITY_POLICY,
|
||||
header::CONTENT_SECURITY_POLICY_REPORT_ONLY,
|
||||
header::CONTENT_TYPE,
|
||||
header::COOKIE,
|
||||
header::DNT,
|
||||
header::DATE,
|
||||
header::ETAG,
|
||||
header::EXPECT,
|
||||
header::EXPIRES,
|
||||
header::FORWARDED,
|
||||
header::FROM,
|
||||
header::HOST,
|
||||
header::IF_MATCH,
|
||||
header::IF_MODIFIED_SINCE,
|
||||
header::IF_NONE_MATCH,
|
||||
header::IF_RANGE,
|
||||
header::IF_UNMODIFIED_SINCE,
|
||||
header::LAST_MODIFIED,
|
||||
header::LINK,
|
||||
header::LOCATION,
|
||||
header::MAX_FORWARDS,
|
||||
header::ORIGIN,
|
||||
header::PRAGMA,
|
||||
header::PROXY_AUTHENTICATE,
|
||||
header::PROXY_AUTHORIZATION,
|
||||
header::PUBLIC_KEY_PINS,
|
||||
header::PUBLIC_KEY_PINS_REPORT_ONLY,
|
||||
header::RANGE,
|
||||
header::REFERER,
|
||||
header::REFERRER_POLICY,
|
||||
header::REFRESH,
|
||||
header::RETRY_AFTER,
|
||||
header::SEC_WEBSOCKET_ACCEPT,
|
||||
header::SEC_WEBSOCKET_EXTENSIONS,
|
||||
header::SEC_WEBSOCKET_KEY,
|
||||
header::SEC_WEBSOCKET_PROTOCOL,
|
||||
header::SEC_WEBSOCKET_VERSION,
|
||||
header::SERVER,
|
||||
header::SET_COOKIE,
|
||||
header::STRICT_TRANSPORT_SECURITY,
|
||||
header::TE,
|
||||
header::TRAILER,
|
||||
header::TRANSFER_ENCODING,
|
||||
header::UPGRADE,
|
||||
header::UPGRADE_INSECURE_REQUESTS,
|
||||
header::USER_AGENT,
|
||||
header::VARY,
|
||||
header::VIA,
|
||||
header::WARNING,
|
||||
header::WWW_AUTHENTICATE,
|
||||
header::X_CONTENT_TYPE_OPTIONS,
|
||||
header::X_DNS_PREFETCH_CONTROL,
|
||||
header::X_FRAME_OPTIONS,
|
||||
header::X_XSS_PROTECTION,
|
||||
];
|
||||
|
||||
if g.gen_ratio(1, 2) {
|
||||
STANDARD_HEADERS.choose(g).unwrap().clone()
|
||||
} else {
|
||||
let value = gen_string(g, 1, 25);
|
||||
HeaderName::from_bytes(value.as_bytes()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_header_value(g: &mut StdRng) -> HeaderValue {
|
||||
let value = gen_string(g, 0, 70);
|
||||
HeaderValue::from_bytes(value.as_bytes()).unwrap()
|
||||
}
|
||||
|
||||
fn gen_string(g: &mut StdRng, min: usize, max: usize) -> String {
|
||||
let bytes: Vec<_> = (min..max)
|
||||
.map(|_| {
|
||||
// Chars to pick from
|
||||
b"ABCDEFGHIJKLMNOPQRSTUVabcdefghilpqrstuvwxyz----"
|
||||
.choose(g)
|
||||
.unwrap()
|
||||
.clone()
|
||||
})
|
||||
.collect();
|
||||
|
||||
String::from_utf8(bytes).unwrap()
|
||||
}
|
||||
82
zeroidc/vendor/http/tests/status_code.rs
vendored
Normal file
82
zeroidc/vendor/http/tests/status_code.rs
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
use http::*;
|
||||
|
||||
#[test]
|
||||
fn from_bytes() {
|
||||
for ok in &[
|
||||
"100", "101", "199", "200", "250", "299", "321", "399", "499", "599", "600", "999"
|
||||
] {
|
||||
assert!(StatusCode::from_bytes(ok.as_bytes()).is_ok());
|
||||
}
|
||||
|
||||
for not_ok in &[
|
||||
"0", "00", "10", "40", "99", "000", "010", "099", "1000", "1999",
|
||||
] {
|
||||
assert!(StatusCode::from_bytes(not_ok.as_bytes()).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_u16() {
|
||||
let status = StatusCode::from_u16(200u16).unwrap();
|
||||
assert_eq!(200u16, status);
|
||||
assert_eq!(status, 200u16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
for s in 100..1000 {
|
||||
let sstr = s.to_string();
|
||||
let status = StatusCode::from_bytes(sstr.as_bytes()).unwrap();
|
||||
assert_eq!(s, u16::from(status));
|
||||
assert_eq!(sstr, status.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_informational() {
|
||||
assert!(status_code(100).is_informational());
|
||||
assert!(status_code(199).is_informational());
|
||||
|
||||
assert!(!status_code(200).is_informational());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_success() {
|
||||
assert!(status_code(200).is_success());
|
||||
assert!(status_code(299).is_success());
|
||||
|
||||
assert!(!status_code(199).is_success());
|
||||
assert!(!status_code(300).is_success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_redirection() {
|
||||
assert!(status_code(300).is_redirection());
|
||||
assert!(status_code(399).is_redirection());
|
||||
|
||||
assert!(!status_code(299).is_redirection());
|
||||
assert!(!status_code(400).is_redirection());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_client_error() {
|
||||
assert!(status_code(400).is_client_error());
|
||||
assert!(status_code(499).is_client_error());
|
||||
|
||||
assert!(!status_code(399).is_client_error());
|
||||
assert!(!status_code(500).is_client_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_server_error() {
|
||||
assert!(status_code(500).is_server_error());
|
||||
assert!(status_code(599).is_server_error());
|
||||
|
||||
assert!(!status_code(499).is_server_error());
|
||||
assert!(!status_code(600).is_server_error());
|
||||
}
|
||||
|
||||
/// Helper method for readability
|
||||
fn status_code(status_code: u16) -> StatusCode {
|
||||
StatusCode::from_u16(status_code).unwrap()
|
||||
}
|
||||
Reference in New Issue
Block a user