http-1.2.0/.cargo_vcs_info.json0000644000000001360000000000100120130ustar { "git": { "sha1": "a9124455213238a888a25607eb7ca368ecf0e712" }, "path_in_vcs": "" }http-1.2.0/.github/workflows/ci.yml000064400000000000000000000057721046102023000153310ustar 00000000000000name: CI on: pull_request: push: branches: - master env: RUST_BACKTRACE: 1 jobs: ci-pass: name: CI is green runs-on: ubuntu-latest needs: - style - test - msrv - wasm - minimal-versions - miri - semver steps: - run: exit 0 style: name: Check Style runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: components: rustfmt - run: cargo fmt --all --check test: name: Test ${{ matrix.rust }} #needs: [style] strategy: matrix: rust: - stable - beta - nightly include: - rust: nightly benches: true runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Rust (${{ matrix.rust }}) uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: Test run: cargo test - name: Test all benches if: matrix.benches run: cargo test --benches ${{ matrix.features }} msrv: name: Check MSRV runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Get MSRV from package metadata id: metadata run: | cargo metadata --no-deps --format-version 1 | jq -r '"msrv=" + (.packages[] | select(.name == "http")).rust_version' >> $GITHUB_OUTPUT - name: Install Rust (${{ steps.metadata.outputs.msrv }}) uses: dtolnay/rust-toolchain@master with: toolchain: ${{ steps.metadata.outputs.msrv }} - name: Test run: cargo check -p http wasm: name: WASM #needs: [style] runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: targets: wasm32-unknown-unknown - name: Check run: cargo check --target wasm32-unknown-unknown minimal-versions: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/rust-toolchain@stable - uses: taiki-e/install-action@cargo-hack - uses: taiki-e/install-action@cargo-minimal-versions - run: cargo minimal-versions check miri: name: Miri runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@nightly with: components: miri - name: Test run: MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-ignore-leaks" cargo miri test semver: name: semver runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Check semver uses: obi1kenobi/cargo-semver-checks-action@v2 with: release-type: minor http-1.2.0/.gitignore000064400000000000000000000000221046102023000125650ustar 00000000000000target Cargo.lock http-1.2.0/CHANGELOG.md000064400000000000000000000214111046102023000124130ustar 00000000000000# 1.2.0 (December 3, 2024) * Add `StatusCode::TOO_EARLY` constant for 425 status. * Loosen `TryFrom` for `HeaderMap` to work with any state generic. * Change `Builder` methods to use `TryInto` instead of `TryFrom` arguments. * Make `StatusCode::as_u16` a `const` function. * Fix `Method` parsing to allow `#$%&'` characters. * Fix `HeaderName` parsing to reject `"` characters. * Fix off by 1 error in `Method::from_bytes` that could cause extra allocations. # 1.1.0 (March 4, 2024) * Add methods to allow trying to allocate in the `HeaderMap`, returning an error if oversize instead of panicking. * Add `Extensions::get_or_insert()` method. * Implement `From` for `uri::Builder`. * Fix `HeaderName::from_lowercase` that could allow NUL bytes in some cases. # 1.0.0 (November 15, 2023) - Implement `Clone` for `Request`, `Response`, and `Extensions`. This breaking change requires that all extensions now implement `Clone`. - Add a default-on `std` feature. Disabling it currently is not supported. - Fix MIRI warnings in `HeaderMap::iter()`. # 0.2.10 (November 10, 2023) * Fix parsing of `Authority` to handle square brackets in incorrect order. * Fix `HeaderMap::with_capacity()` to handle arithmetic overflow. # 0.2.9 (February 17, 2023) * Add `HeaderName` constants for `cache-status` and `cdn-cache-control`. * Implement `Hash` for `PathAndQuery`. * Re-export `HeaderName` at crate root. # 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` and `From` 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>` and `TryFrom` 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` instead of `Option`. * 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` 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` 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>` (#227) * Implement `Iterator::size_hint` for most iterators in `header` (#226) # 0.1.7 (June 22, 2018) * Add `From 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` 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` for `StatusCode` (#153). * Implement `HttpTryFrom<&Uri>` for `Uri` (#165). * Implement `FromStr` for `Method` (#167). * Implement `HttpTryFrom` 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 http-1.2.0/Cargo.lock0000644000000143650000000000100077770ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "bytes" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "env_logger" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ "log", "regex", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "http" version = "1.2.0" dependencies = [ "bytes", "doc-comment", "fnv", "itoa", "quickcheck", "rand", "serde", "serde_json", ] [[package]] name = "itoa" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quickcheck" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" dependencies = [ "env_logger", "log", "rand", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "regex" version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "ryu" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "serde" version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "syn" version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" http-1.2.0/Cargo.toml0000644000000032130000000000100100100ustar # 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 = "1.2.0" authors = [ "Alex Crichton ", "Carl Lerche ", "Sean McArthur ", ] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false 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" [lib] name = "http" path = "src/lib.rs" [[test]] name = "header_map" path = "tests/header_map.rs" [[test]] name = "header_map_fuzz" path = "tests/header_map_fuzz.rs" [[test]] name = "status_code" path = "tests/status_code.rs" [dependencies.bytes] version = "1" [dependencies.fnv] version = "1.0.5" [dependencies.itoa] version = "1" [dev-dependencies.doc-comment] version = "0.3" [dev-dependencies.quickcheck] version = "1" [dev-dependencies.rand] version = "0.8.0" [dev-dependencies.serde] version = "1.0" [dev-dependencies.serde_json] version = "1.0" [features] default = ["std"] std = [] http-1.2.0/Cargo.toml.orig000064400000000000000000000016721046102023000135000ustar 00000000000000[package] name = "http" # When releasing to crates.io: # - Update html_root_url in lib.rs. # - Update CHANGELOG.md. # - Create git tag version = "1.2.0" readme = "README.md" documentation = "https://docs.rs/http" repository = "https://github.com/hyperium/http" license = "MIT OR Apache-2.0" authors = [ "Alex Crichton ", "Carl Lerche ", "Sean McArthur ", ] description = """ A set of types for representing HTTP requests and responses. """ keywords = ["http"] categories = ["web-programming"] edition = "2018" # When updating this value, don't forget to also adjust the GitHub Actions config. rust-version = "1.49.0" [workspace] members = [ ".", ] exclude = [ "fuzz", "benches" ] [features] default = ["std"] std = [] [dependencies] bytes = "1" fnv = "1.0.5" itoa = "1" [dev-dependencies] quickcheck = "1" rand = "0.8.0" serde = "1.0" serde_json = "1.0" doc-comment = "0.3" http-1.2.0/LICENSE-APACHE000064400000000000000000000251231046102023000125320ustar 00000000000000 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. http-1.2.0/LICENSE-MIT000064400000000000000000000020431046102023000122360ustar 00000000000000Copyright (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. http-1.2.0/README.md000064400000000000000000000034121046102023000120620ustar 00000000000000# HTTP A general purpose library of common HTTP types [![CI](https://github.com/hyperium/http/workflows/CI/badge.svg)](https://github.com/hyperium/http/actions?query=workflow%3ACI) [![Crates.io](https://img.shields.io/crates/v/http.svg)](https://crates.io/crates/http) [![Documentation](https://docs.rs/http/badge.svg)][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 = "1.0" ``` 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. http-1.2.0/src/byte_str.rs000064400000000000000000000042671046102023000136040ustar 00000000000000use 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 valid 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 /// behavior 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 } } } 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 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 for Bytes { fn from(src: ByteStr) -> Self { src.bytes } } http-1.2.0/src/convert.rs000064400000000000000000000012501046102023000134160ustar 00000000000000macro_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::>() .unwrap() .take() .unwrap(); // Run the $body in scope of the replaced val. $body } }}; } http-1.2.0/src/error.rs000064400000000000000000000101131046102023000130650ustar 00000000000000use std::error; use std::fmt; use std::result; use crate::header; use crate::header::MaxSizeReached; 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 = result::Result; enum ErrorKind { StatusCode(status::InvalidStatusCode), Method(method::InvalidMethod), Uri(uri::InvalidUri), UriParts(uri::InvalidUriParts), HeaderName(header::InvalidHeaderName), HeaderValue(header::InvalidHeaderValue), MaxSizeReached(MaxSizeReached), } 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(&self) -> bool { self.get_ref().is::() } /// 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, MaxSizeReached(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 for Error { fn from(err: MaxSizeReached) -> Error { Error { inner: ErrorKind::MaxSizeReached(err), } } } impl From for Error { fn from(err: status::InvalidStatusCode) -> Error { Error { inner: ErrorKind::StatusCode(err), } } } impl From for Error { fn from(err: method::InvalidMethod) -> Error { Error { inner: ErrorKind::Method(err), } } } impl From for Error { fn from(err: uri::InvalidUri) -> Error { Error { inner: ErrorKind::Uri(err), } } } impl From for Error { fn from(err: uri::InvalidUriParts) -> Error { Error { inner: ErrorKind::UriParts(err), } } } impl From for Error { fn from(err: header::InvalidHeaderName) -> Error { Error { inner: ErrorKind::HeaderName(err), } } } impl From for Error { fn from(err: header::InvalidHeaderValue) -> Error { Error { inner: ErrorKind::HeaderValue(err), } } } impl From 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::()); assert!(ie.is::()); ie.downcast_ref::().unwrap(); assert!(!err.is::()); assert!(err.is::()); } else { panic!("Bad status allowed!"); } } } http-1.2.0/src/extensions.rs000064400000000000000000000221041046102023000141360ustar 00000000000000use std::any::{Any, TypeId}; use std::collections::HashMap; use std::fmt; use std::hash::{BuildHasherDefault, Hasher}; type AnyMap = HashMap, BuildHasherDefault>; // 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(Clone, 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>, } 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 and replaced with the new one. /// /// # 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(&mut self, val: T) -> Option { self.map .get_or_insert_with(Box::default) .insert(TypeId::of::(), Box::new(val)) .and_then(|boxed| boxed.into_any().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::().is_none()); /// ext.insert(5i32); /// /// assert_eq!(ext.get::(), Some(&5i32)); /// ``` pub fn get(&self) -> Option<&T> { self.map .as_ref() .and_then(|map| map.get(&TypeId::of::())) .and_then(|boxed| (**boxed).as_any().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::().unwrap().push_str(" World"); /// /// assert_eq!(ext.get::().unwrap(), "Hello World"); /// ``` pub fn get_mut(&mut self) -> Option<&mut T> { self.map .as_mut() .and_then(|map| map.get_mut(&TypeId::of::())) .and_then(|boxed| (**boxed).as_any_mut().downcast_mut()) } /// Get a mutable reference to a type, inserting `value` if not already present on this /// `Extensions`. /// /// # Example /// /// ``` /// # use http::Extensions; /// let mut ext = Extensions::new(); /// *ext.get_or_insert(1i32) += 2; /// /// assert_eq!(*ext.get::().unwrap(), 3); /// ``` pub fn get_or_insert(&mut self, value: T) -> &mut T { self.get_or_insert_with(|| value) } /// Get a mutable reference to a type, inserting the value created by `f` if not already present /// on this `Extensions`. /// /// # Example /// /// ``` /// # use http::Extensions; /// let mut ext = Extensions::new(); /// *ext.get_or_insert_with(|| 1i32) += 2; /// /// assert_eq!(*ext.get::().unwrap(), 3); /// ``` pub fn get_or_insert_with T>( &mut self, f: F, ) -> &mut T { let out = self .map .get_or_insert_with(Box::default) .entry(TypeId::of::()) .or_insert_with(|| Box::new(f())); (**out).as_any_mut().downcast_mut().unwrap() } /// Get a mutable reference to a type, inserting the type's default value if not already present /// on this `Extensions`. /// /// # Example /// /// ``` /// # use http::Extensions; /// let mut ext = Extensions::new(); /// *ext.get_or_insert_default::() += 2; /// /// assert_eq!(*ext.get::().unwrap(), 2); /// ``` pub fn get_or_insert_default(&mut self) -> &mut T { self.get_or_insert_with(T::default) } /// 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::(), Some(5i32)); /// assert!(ext.get::().is_none()); /// ``` pub fn remove(&mut self) -> Option { self.map .as_mut() .and_then(|map| map.remove(&TypeId::of::())) .and_then(|boxed| boxed.into_any().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::().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 number 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::(), Some(&4u8)); /// assert_eq!(ext_a.get::(), 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() } } trait AnyClone: Any { fn clone_box(&self) -> Box; fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; fn into_any(self: Box) -> Box; } impl AnyClone for T { fn clone_box(&self) -> Box { Box::new(self.clone()) } fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } fn into_any(self: Box) -> Box { self } } impl Clone for Box { fn clone(&self) -> Self { (**self).clone_box() } } #[test] fn test_extensions() { #[derive(Clone, 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)); let ext2 = extensions.clone(); assert_eq!(extensions.remove::(), Some(5i32)); assert!(extensions.get::().is_none()); // clone still has it assert_eq!(ext2.get(), Some(&5i32)); assert_eq!(ext2.get(), Some(&MyType(10))); assert_eq!(extensions.get::(), None); assert_eq!(extensions.get(), Some(&MyType(10))); } http-1.2.0/src/header/map.rs000064400000000000000000003414451046102023000137600ustar 00000000000000use std::collections::hash_map::RandomState; use std::collections::HashMap; use std::convert::TryFrom; use std::hash::{BuildHasher, Hash, Hasher}; use std::iter::{FromIterator, FusedIterator}; use std::marker::PhantomData; use std::{fmt, mem, ops, ptr, vec}; use crate::Error; use super::name::{HdrName, HeaderName, InvalidHeaderName}; use super::HeaderValue; pub use self::as_header_name::AsHeaderName; pub use self::into_header_name::IntoHeaderName; /// A set of HTTP headers /// /// `HeaderMap` is a multimap of [`HeaderName`] to values. /// /// [`HeaderName`]: struct.HeaderName.html /// /// # Examples /// /// Basic usage /// /// ``` /// # use http::HeaderMap; /// # use http::header::{CONTENT_LENGTH, HOST, LOCATION}; /// let mut headers = HeaderMap::new(); /// /// headers.insert(HOST, "example.com".parse().unwrap()); /// headers.insert(CONTENT_LENGTH, "123".parse().unwrap()); /// /// assert!(headers.contains_key(HOST)); /// assert!(!headers.contains_key(LOCATION)); /// /// assert_eq!(headers[HOST], "example.com"); /// /// headers.remove(HOST); /// /// assert!(!headers.contains_key(HOST)); /// ``` #[derive(Clone)] pub struct HeaderMap { // Used to mask values to get an index mask: Size, indices: Box<[Pos]>, entries: Vec>, extra_values: Vec>, danger: Danger, } // # Implementation notes // // Below, you will find a fairly large amount of code. Most of this is to // provide the necessary functions to efficiently manipulate the header // multimap. The core hashing table is based on robin hood hashing [1]. While // this is the same hashing algorithm used as part of Rust's `HashMap` in // stdlib, many implementation details are different. The two primary reasons // for this divergence are that `HeaderMap` is a multimap and the structure has // been optimized to take advantage of the characteristics of HTTP headers. // // ## Structure Layout // // Most of the data contained by `HeaderMap` is *not* stored in the hash table. // Instead, pairs of header name and *first* associated header value are stored // in the `entries` vector. If the header name has more than one associated // header value, then additional values are stored in `extra_values`. The actual // hash table (`indices`) only maps hash codes to indices in `entries`. This // means that, when an eviction happens, the actual header name and value stay // put and only a tiny amount of memory has to be copied. // // Extra values associated with a header name are tracked using a linked list. // Links are formed with offsets into `extra_values` and not pointers. // // [1]: https://en.wikipedia.org/wiki/Hash_table#Robin_Hood_hashing /// `HeaderMap` entry iterator. /// /// Yields `(&HeaderName, &value)` tuples. The same header name may be yielded /// more than once if it has more than one associated value. #[derive(Debug)] pub struct Iter<'a, T> { map: &'a HeaderMap, entry: usize, cursor: Option, } /// `HeaderMap` mutable entry iterator /// /// Yields `(&HeaderName, &mut value)` tuples. The same header name may be /// yielded more than once if it has more than one associated value. #[derive(Debug)] pub struct IterMut<'a, T> { map: *mut HeaderMap, entry: usize, cursor: Option, lt: PhantomData<&'a mut HeaderMap>, } /// An owning iterator over the entries of a `HeaderMap`. /// /// This struct is created by the `into_iter` method on `HeaderMap`. #[derive(Debug)] pub struct IntoIter { // If None, pull from `entries` next: Option, entries: vec::IntoIter>, extra_values: Vec>, } /// An iterator over `HeaderMap` keys. /// /// Each header name is yielded only once, even if it has more than one /// associated value. #[derive(Debug)] pub struct Keys<'a, T> { inner: ::std::slice::Iter<'a, Bucket>, } /// `HeaderMap` value iterator. /// /// Each value contained in the `HeaderMap` will be yielded. #[derive(Debug)] pub struct Values<'a, T> { inner: Iter<'a, T>, } /// `HeaderMap` mutable value iterator #[derive(Debug)] pub struct ValuesMut<'a, T> { inner: IterMut<'a, T>, } /// A drain iterator for `HeaderMap`. #[derive(Debug)] pub struct Drain<'a, T> { idx: usize, len: usize, entries: *mut [Bucket], // If None, pull from `entries` next: Option, extra_values: *mut Vec>, lt: PhantomData<&'a mut HeaderMap>, } /// A view to all values stored in a single entry. /// /// This struct is returned by `HeaderMap::get_all`. #[derive(Debug)] pub struct GetAll<'a, T> { map: &'a HeaderMap, index: Option, } /// A view into a single location in a `HeaderMap`, which may be vacant or occupied. #[derive(Debug)] pub enum Entry<'a, T: 'a> { /// An occupied entry Occupied(OccupiedEntry<'a, T>), /// A vacant entry Vacant(VacantEntry<'a, T>), } /// A view into a single empty location in a `HeaderMap`. /// /// This struct is returned as part of the `Entry` enum. #[derive(Debug)] pub struct VacantEntry<'a, T> { map: &'a mut HeaderMap, key: HeaderName, hash: HashValue, probe: usize, danger: bool, } /// A view into a single occupied location in a `HeaderMap`. /// /// This struct is returned as part of the `Entry` enum. #[derive(Debug)] pub struct OccupiedEntry<'a, T> { map: &'a mut HeaderMap, probe: usize, index: usize, } /// An iterator of all values associated with a single header name. #[derive(Debug)] pub struct ValueIter<'a, T> { map: &'a HeaderMap, index: usize, front: Option, back: Option, } /// A mutable iterator of all values associated with a single header name. #[derive(Debug)] pub struct ValueIterMut<'a, T> { map: *mut HeaderMap, index: usize, front: Option, back: Option, lt: PhantomData<&'a mut HeaderMap>, } /// An drain iterator of all values associated with a single header name. #[derive(Debug)] pub struct ValueDrain<'a, T> { first: Option, next: Option<::std::vec::IntoIter>, lt: PhantomData<&'a mut HeaderMap>, } /// Error returned when max capacity of `HeaderMap` is exceeded pub struct MaxSizeReached { _priv: (), } /// Tracks the value iterator state #[derive(Debug, Copy, Clone, Eq, PartialEq)] enum Cursor { Head, Values(usize), } /// Type used for representing the size of a HeaderMap value. /// /// 32,768 is more than enough entries for a single header map. Setting this /// limit enables using `u16` to represent all offsets, which takes 2 bytes /// instead of 8 on 64 bit processors. /// /// Setting this limit is especially beneficial for `indices`, making it more /// cache friendly. More hash codes can fit in a cache line. /// /// You may notice that `u16` may represent more than 32,768 values. This is /// true, but 32,768 should be plenty and it allows us to reserve the top bit /// for future usage. type Size = u16; /// This limit falls out from above. const MAX_SIZE: usize = 1 << 15; /// An entry in the hash table. This represents the full hash code for an entry /// as well as the position of the entry in the `entries` vector. #[derive(Copy, Clone)] struct Pos { // Index in the `entries` vec index: Size, // Full hash value for the entry. hash: HashValue, } /// Hash values are limited to u16 as well. While `fast_hash` and `Hasher` /// return `usize` hash codes, limiting the effective hash code to the lower 16 /// bits is fine since we know that the `indices` vector will never grow beyond /// that size. #[derive(Debug, Copy, Clone, Eq, PartialEq)] struct HashValue(u16); /// Stores the data associated with a `HeaderMap` entry. Only the first value is /// included in this struct. If a header name has more than one associated /// value, all extra values are stored in the `extra_values` vector. A doubly /// linked list of entries is maintained. The doubly linked list is used so that /// removing a value is constant time. This also has the nice property of /// enabling double ended iteration. #[derive(Debug, Clone)] struct Bucket { hash: HashValue, key: HeaderName, value: T, links: Option, } /// The head and tail of the value linked list. #[derive(Debug, Copy, Clone)] struct Links { next: usize, tail: usize, } /// Access to the `links` value in a slice of buckets. /// /// It's important that no other field is accessed, since it may have been /// freed in a `Drain` iterator. #[derive(Debug)] struct RawLinks(*mut [Bucket]); /// Node in doubly-linked list of header value entries #[derive(Debug, Clone)] struct ExtraValue { value: T, prev: Link, next: Link, } /// A header value node is either linked to another node in the `extra_values` /// list or it points to an entry in `entries`. The entry in `entries` is the /// start of the list and holds the associated header name. #[derive(Debug, Copy, Clone, Eq, PartialEq)] enum Link { Entry(usize), Extra(usize), } /// Tracks the header map danger level! This relates to the adaptive hashing /// algorithm. A HeaderMap starts in the "green" state, when a large number of /// collisions are detected, it transitions to the yellow state. At this point, /// the header map will either grow and switch back to the green state OR it /// will transition to the red state. /// /// When in the red state, a safe hashing algorithm is used and all values in /// the header map have to be rehashed. #[derive(Clone)] enum Danger { Green, Yellow, Red(RandomState), } // Constants related to detecting DOS attacks. // // Displacement is the number of entries that get shifted when inserting a new // value. Forward shift is how far the entry gets stored from the ideal // position. // // The current constant values were picked from another implementation. It could // be that there are different values better suited to the header map case. const DISPLACEMENT_THRESHOLD: usize = 128; const FORWARD_SHIFT_THRESHOLD: usize = 512; // The default strategy for handling the yellow danger state is to increase the // header map capacity in order to (hopefully) reduce the number of collisions. // If growing the hash map would cause the load factor to drop bellow this // threshold, then instead of growing, the headermap is switched to the red // danger state and safe hashing is used instead. const LOAD_FACTOR_THRESHOLD: f32 = 0.2; // Macro used to iterate the hash table starting at a given point, looping when // the end is hit. macro_rules! probe_loop { ($label:tt: $probe_var: ident < $len: expr, $body: expr) => { debug_assert!($len > 0); $label: loop { if $probe_var < $len { $body $probe_var += 1; } else { $probe_var = 0; } } }; ($probe_var: ident < $len: expr, $body: expr) => { debug_assert!($len > 0); loop { if $probe_var < $len { $body $probe_var += 1; } else { $probe_var = 0; } } }; } // First part of the robinhood algorithm. Given a key, find the slot in which it // will be inserted. This is done by starting at the "ideal" spot. Then scanning // until the destination slot is found. A destination slot is either the next // empty slot or the next slot that is occupied by an entry that has a lower // displacement (displacement is the distance from the ideal spot). // // This is implemented as a macro instead of a function that takes a closure in // order to guarantee that it is "inlined". There is no way to annotate closures // to guarantee inlining. macro_rules! insert_phase_one { ($map:ident, $key:expr, $probe:ident, $pos:ident, $hash:ident, $danger:ident, $vacant:expr, $occupied:expr, $robinhood:expr) => {{ let $hash = hash_elem_using(&$map.danger, &$key); let mut $probe = desired_pos($map.mask, $hash); let mut dist = 0; let ret; // Start at the ideal position, checking all slots probe_loop!('probe: $probe < $map.indices.len(), { if let Some(($pos, entry_hash)) = $map.indices[$probe].resolve() { // The slot is already occupied, but check if it has a lower // displacement. let their_dist = probe_distance($map.mask, entry_hash, $probe); if their_dist < dist { // The new key's distance is larger, so claim this spot and // displace the current entry. // // Check if this insertion is above the danger threshold. let $danger = dist >= FORWARD_SHIFT_THRESHOLD && !$map.danger.is_red(); ret = $robinhood; break 'probe; } else if entry_hash == $hash && $map.entries[$pos].key == $key { // There already is an entry with the same key. ret = $occupied; break 'probe; } } else { // The entry is vacant, use it for this key. let $danger = dist >= FORWARD_SHIFT_THRESHOLD && !$map.danger.is_red(); ret = $vacant; break 'probe; } dist += 1; }); ret }} } // ===== impl HeaderMap ===== impl HeaderMap { /// Create an empty `HeaderMap`. /// /// The map will be created without any capacity. This function will not /// allocate. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// let map = HeaderMap::new(); /// /// assert!(map.is_empty()); /// assert_eq!(0, map.capacity()); /// ``` pub fn new() -> Self { HeaderMap::try_with_capacity(0).unwrap() } } impl HeaderMap { /// Create an empty `HeaderMap` with the specified capacity. /// /// The returned map will allocate internal storage in order to hold about /// `capacity` elements without reallocating. However, this is a "best /// effort" as there are usage patterns that could cause additional /// allocations before `capacity` headers are stored in the map. /// /// More capacity than requested may be allocated. /// /// # Panics /// /// This method panics if capacity exceeds max `HeaderMap` capacity. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// let map: HeaderMap = HeaderMap::with_capacity(10); /// /// assert!(map.is_empty()); /// assert_eq!(12, map.capacity()); /// ``` pub fn with_capacity(capacity: usize) -> HeaderMap { Self::try_with_capacity(capacity).expect("size overflows MAX_SIZE") } /// Create an empty `HeaderMap` with the specified capacity. /// /// The returned map will allocate internal storage in order to hold about /// `capacity` elements without reallocating. However, this is a "best /// effort" as there are usage patterns that could cause additional /// allocations before `capacity` headers are stored in the map. /// /// More capacity than requested may be allocated. /// /// # Errors /// /// This function may return an error if `HeaderMap` exceeds max capacity /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// let map: HeaderMap = HeaderMap::try_with_capacity(10).unwrap(); /// /// assert!(map.is_empty()); /// assert_eq!(12, map.capacity()); /// ``` pub fn try_with_capacity(capacity: usize) -> Result, MaxSizeReached> { if capacity == 0 { Ok(HeaderMap { mask: 0, indices: Box::new([]), // as a ZST, this doesn't actually allocate anything entries: Vec::new(), extra_values: Vec::new(), danger: Danger::Green, }) } else { let raw_cap = match to_raw_capacity(capacity).checked_next_power_of_two() { Some(c) => c, None => return Err(MaxSizeReached { _priv: () }), }; if raw_cap > MAX_SIZE { return Err(MaxSizeReached { _priv: () }); } debug_assert!(raw_cap > 0); Ok(HeaderMap { mask: (raw_cap - 1) as Size, indices: vec![Pos::none(); raw_cap].into_boxed_slice(), entries: Vec::with_capacity(usable_capacity(raw_cap)), extra_values: Vec::new(), danger: Danger::Green, }) } } /// Returns the number of headers stored in the map. /// /// This number represents the total number of **values** stored in the map. /// This number can be greater than or equal to the number of **keys** /// stored given that a single key may have more than one associated value. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::{ACCEPT, HOST}; /// let mut map = HeaderMap::new(); /// /// assert_eq!(0, map.len()); /// /// map.insert(ACCEPT, "text/plain".parse().unwrap()); /// map.insert(HOST, "localhost".parse().unwrap()); /// /// assert_eq!(2, map.len()); /// /// map.append(ACCEPT, "text/html".parse().unwrap()); /// /// assert_eq!(3, map.len()); /// ``` pub fn len(&self) -> usize { self.entries.len() + self.extra_values.len() } /// Returns the number of keys stored in the map. /// /// This number will be less than or equal to `len()` as each key may have /// more than one associated value. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::{ACCEPT, HOST}; /// let mut map = HeaderMap::new(); /// /// assert_eq!(0, map.keys_len()); /// /// map.insert(ACCEPT, "text/plain".parse().unwrap()); /// map.insert(HOST, "localhost".parse().unwrap()); /// /// assert_eq!(2, map.keys_len()); /// /// map.insert(ACCEPT, "text/html".parse().unwrap()); /// /// assert_eq!(2, map.keys_len()); /// ``` pub fn keys_len(&self) -> usize { self.entries.len() } /// Returns true if the map contains no elements. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// /// assert!(map.is_empty()); /// /// map.insert(HOST, "hello.world".parse().unwrap()); /// /// assert!(!map.is_empty()); /// ``` pub fn is_empty(&self) -> bool { self.entries.len() == 0 } /// Clears the map, removing all key-value pairs. Keeps the allocated memory /// for reuse. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "hello.world".parse().unwrap()); /// /// map.clear(); /// assert!(map.is_empty()); /// assert!(map.capacity() > 0); /// ``` pub fn clear(&mut self) { self.entries.clear(); self.extra_values.clear(); self.danger = Danger::Green; for e in self.indices.iter_mut() { *e = Pos::none(); } } /// Returns the number of headers the map can hold without reallocating. /// /// This number is an approximation as certain usage patterns could cause /// additional allocations before the returned capacity is filled. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// /// assert_eq!(0, map.capacity()); /// /// map.insert(HOST, "hello.world".parse().unwrap()); /// assert_eq!(6, map.capacity()); /// ``` pub fn capacity(&self) -> usize { usable_capacity(self.indices.len()) } /// Reserves capacity for at least `additional` more headers to be inserted /// into the `HeaderMap`. /// /// The header map may reserve more space to avoid frequent reallocations. /// Like with `with_capacity`, this will be a "best effort" to avoid /// allocations until `additional` more headers are inserted. Certain usage /// patterns could cause additional allocations before the number is /// reached. /// /// # Panics /// /// Panics if the new allocation size overflows `HeaderMap` `MAX_SIZE`. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// map.reserve(10); /// # map.insert(HOST, "bar".parse().unwrap()); /// ``` pub fn reserve(&mut self, additional: usize) { self.try_reserve(additional) .expect("size overflows MAX_SIZE") } /// Reserves capacity for at least `additional` more headers to be inserted /// into the `HeaderMap`. /// /// The header map may reserve more space to avoid frequent reallocations. /// Like with `with_capacity`, this will be a "best effort" to avoid /// allocations until `additional` more headers are inserted. Certain usage /// patterns could cause additional allocations before the number is /// reached. /// /// # Errors /// /// This method differs from `reserve` by returning an error instead of /// panicking if the value is too large. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// map.try_reserve(10).unwrap(); /// # map.try_insert(HOST, "bar".parse().unwrap()).unwrap(); /// ``` pub fn try_reserve(&mut self, additional: usize) -> Result<(), MaxSizeReached> { // TODO: This can't overflow if done properly... since the max # of // elements is u16::MAX. let cap = self .entries .len() .checked_add(additional) .ok_or_else(MaxSizeReached::new)?; if cap > self.indices.len() { let cap = cap .checked_next_power_of_two() .ok_or_else(MaxSizeReached::new)?; if cap > MAX_SIZE { return Err(MaxSizeReached::new()); } if self.entries.is_empty() { self.mask = cap as Size - 1; self.indices = vec![Pos::none(); cap].into_boxed_slice(); self.entries = Vec::with_capacity(usable_capacity(cap)); } else { self.try_grow(cap)?; } } Ok(()) } /// Returns a reference to the value associated with the key. /// /// If there are multiple values associated with the key, then the first one /// is returned. Use `get_all` to get all values associated with a given /// key. Returns `None` if there are no values associated with the key. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// assert!(map.get("host").is_none()); /// /// map.insert(HOST, "hello".parse().unwrap()); /// assert_eq!(map.get(HOST).unwrap(), &"hello"); /// assert_eq!(map.get("host").unwrap(), &"hello"); /// /// map.append(HOST, "world".parse().unwrap()); /// assert_eq!(map.get("host").unwrap(), &"hello"); /// ``` pub fn get(&self, key: K) -> Option<&T> where K: AsHeaderName, { self.get2(&key) } fn get2(&self, key: &K) -> Option<&T> where K: AsHeaderName, { match key.find(self) { Some((_, found)) => { let entry = &self.entries[found]; Some(&entry.value) } None => None, } } /// Returns a mutable reference to the value associated with the key. /// /// If there are multiple values associated with the key, then the first one /// is returned. Use `entry` to get all values associated with a given /// key. Returns `None` if there are no values associated with the key. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::default(); /// map.insert(HOST, "hello".to_string()); /// map.get_mut("host").unwrap().push_str("-world"); /// /// assert_eq!(map.get(HOST).unwrap(), &"hello-world"); /// ``` pub fn get_mut(&mut self, key: K) -> Option<&mut T> where K: AsHeaderName, { match key.find(self) { Some((_, found)) => { let entry = &mut self.entries[found]; Some(&mut entry.value) } None => None, } } /// Returns a view of all values associated with a key. /// /// The returned view does not incur any allocations and allows iterating /// the values associated with the key. See [`GetAll`] for more details. /// Returns `None` if there are no values associated with the key. /// /// [`GetAll`]: struct.GetAll.html /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// /// map.insert(HOST, "hello".parse().unwrap()); /// map.append(HOST, "goodbye".parse().unwrap()); /// /// let view = map.get_all("host"); /// /// let mut iter = view.iter(); /// assert_eq!(&"hello", iter.next().unwrap()); /// assert_eq!(&"goodbye", iter.next().unwrap()); /// assert!(iter.next().is_none()); /// ``` pub fn get_all(&self, key: K) -> GetAll<'_, T> where K: AsHeaderName, { GetAll { map: self, index: key.find(self).map(|(_, i)| i), } } /// Returns true if the map contains a value for the specified key. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// assert!(!map.contains_key(HOST)); /// /// map.insert(HOST, "world".parse().unwrap()); /// assert!(map.contains_key("host")); /// ``` pub fn contains_key(&self, key: K) -> bool where K: AsHeaderName, { key.find(self).is_some() } /// An iterator visiting all key-value pairs. /// /// The iteration order is arbitrary, but consistent across platforms for /// the same crate version. Each key will be yielded once per associated /// value. So, if a key has 3 associated values, it will be yielded 3 times. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::{CONTENT_LENGTH, HOST}; /// let mut map = HeaderMap::new(); /// /// map.insert(HOST, "hello".parse().unwrap()); /// map.append(HOST, "goodbye".parse().unwrap()); /// map.insert(CONTENT_LENGTH, "123".parse().unwrap()); /// /// for (key, value) in map.iter() { /// println!("{:?}: {:?}", key, value); /// } /// ``` pub fn iter(&self) -> Iter<'_, T> { Iter { map: self, entry: 0, cursor: self.entries.first().map(|_| Cursor::Head), } } /// An iterator visiting all key-value pairs, with mutable value references. /// /// The iterator order is arbitrary, but consistent across platforms for the /// same crate version. Each key will be yielded once per associated value, /// so if a key has 3 associated values, it will be yielded 3 times. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::{CONTENT_LENGTH, HOST}; /// let mut map = HeaderMap::default(); /// /// map.insert(HOST, "hello".to_string()); /// map.append(HOST, "goodbye".to_string()); /// map.insert(CONTENT_LENGTH, "123".to_string()); /// /// for (key, value) in map.iter_mut() { /// value.push_str("-boop"); /// } /// ``` pub fn iter_mut(&mut self) -> IterMut<'_, T> { IterMut { map: self as *mut _, entry: 0, cursor: self.entries.first().map(|_| Cursor::Head), lt: PhantomData, } } /// An iterator visiting all keys. /// /// The iteration order is arbitrary, but consistent across platforms for /// the same crate version. Each key will be yielded only once even if it /// has multiple associated values. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::{CONTENT_LENGTH, HOST}; /// let mut map = HeaderMap::new(); /// /// map.insert(HOST, "hello".parse().unwrap()); /// map.append(HOST, "goodbye".parse().unwrap()); /// map.insert(CONTENT_LENGTH, "123".parse().unwrap()); /// /// for key in map.keys() { /// println!("{:?}", key); /// } /// ``` pub fn keys(&self) -> Keys<'_, T> { Keys { inner: self.entries.iter(), } } /// An iterator visiting all values. /// /// The iteration order is arbitrary, but consistent across platforms for /// the same crate version. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::{CONTENT_LENGTH, HOST}; /// let mut map = HeaderMap::new(); /// /// map.insert(HOST, "hello".parse().unwrap()); /// map.append(HOST, "goodbye".parse().unwrap()); /// map.insert(CONTENT_LENGTH, "123".parse().unwrap()); /// /// for value in map.values() { /// println!("{:?}", value); /// } /// ``` pub fn values(&self) -> Values<'_, T> { Values { inner: self.iter() } } /// An iterator visiting all values mutably. /// /// The iteration order is arbitrary, but consistent across platforms for /// the same crate version. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::{CONTENT_LENGTH, HOST}; /// let mut map = HeaderMap::default(); /// /// map.insert(HOST, "hello".to_string()); /// map.append(HOST, "goodbye".to_string()); /// map.insert(CONTENT_LENGTH, "123".to_string()); /// /// for value in map.values_mut() { /// value.push_str("-boop"); /// } /// ``` pub fn values_mut(&mut self) -> ValuesMut<'_, T> { ValuesMut { inner: self.iter_mut(), } } /// Clears the map, returning all entries as an iterator. /// /// The internal memory is kept for reuse. /// /// For each yielded item that has `None` provided for the `HeaderName`, /// then the associated header name is the same as that of the previously /// yielded item. The first yielded item will have `HeaderName` set. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::{CONTENT_LENGTH, HOST}; /// let mut map = HeaderMap::new(); /// /// map.insert(HOST, "hello".parse().unwrap()); /// map.append(HOST, "goodbye".parse().unwrap()); /// map.insert(CONTENT_LENGTH, "123".parse().unwrap()); /// /// let mut drain = map.drain(); /// /// /// assert_eq!(drain.next(), Some((Some(HOST), "hello".parse().unwrap()))); /// assert_eq!(drain.next(), Some((None, "goodbye".parse().unwrap()))); /// /// assert_eq!(drain.next(), Some((Some(CONTENT_LENGTH), "123".parse().unwrap()))); /// /// assert_eq!(drain.next(), None); /// ``` pub fn drain(&mut self) -> Drain<'_, T> { for i in self.indices.iter_mut() { *i = Pos::none(); } // Memory safety // // When the Drain is first created, it shortens the length of // the source vector to make sure no uninitialized or moved-from // elements are accessible at all if the Drain's destructor never // gets to run. let entries = &mut self.entries[..] as *mut _; let extra_values = &mut self.extra_values as *mut _; let len = self.entries.len(); unsafe { self.entries.set_len(0); } Drain { idx: 0, len, entries, extra_values, next: None, lt: PhantomData, } } fn value_iter(&self, idx: Option) -> ValueIter<'_, T> { use self::Cursor::*; if let Some(idx) = idx { let back = { let entry = &self.entries[idx]; entry.links.map(|l| Values(l.tail)).unwrap_or(Head) }; ValueIter { map: self, index: idx, front: Some(Head), back: Some(back), } } else { ValueIter { map: self, index: usize::MAX, front: None, back: None, } } } fn value_iter_mut(&mut self, idx: usize) -> ValueIterMut<'_, T> { use self::Cursor::*; let back = { let entry = &self.entries[idx]; entry.links.map(|l| Values(l.tail)).unwrap_or(Head) }; ValueIterMut { map: self as *mut _, index: idx, front: Some(Head), back: Some(back), lt: PhantomData, } } /// Gets the given key's corresponding entry in the map for in-place /// manipulation. /// /// # Panics /// /// This method panics if capacity exceeds max `HeaderMap` capacity /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// let mut map: HeaderMap = HeaderMap::default(); /// /// let headers = &[ /// "content-length", /// "x-hello", /// "Content-Length", /// "x-world", /// ]; /// /// for &header in headers { /// let counter = map.entry(header).or_insert(0); /// *counter += 1; /// } /// /// assert_eq!(map["content-length"], 2); /// assert_eq!(map["x-hello"], 1); /// ``` pub fn entry(&mut self, key: K) -> Entry<'_, T> where K: IntoHeaderName, { key.try_entry(self).expect("size overflows MAX_SIZE") } /// Gets the given key's corresponding entry in the map for in-place /// manipulation. /// /// # Errors /// /// This method differs from `entry` by allowing types that may not be /// valid `HeaderName`s to passed as the key (such as `String`). If they /// do not parse as a valid `HeaderName`, this returns an /// `InvalidHeaderName` error. /// /// If reserving space goes over the maximum, this will also return an /// error. However, to prevent breaking changes to the return type, the /// error will still say `InvalidHeaderName`, unlike other `try_*` methods /// which return a `MaxSizeReached` error. pub fn try_entry(&mut self, key: K) -> Result, InvalidHeaderName> where K: AsHeaderName, { key.try_entry(self).map_err(|err| match err { as_header_name::TryEntryError::InvalidHeaderName(e) => e, as_header_name::TryEntryError::MaxSizeReached(_e) => { // Unfortunately, we cannot change the return type of this // method, so the max size reached error needs to be converted // into an InvalidHeaderName. Yay. InvalidHeaderName::new() } }) } fn try_entry2(&mut self, key: K) -> Result, MaxSizeReached> where K: Hash + Into, HeaderName: PartialEq, { // Ensure that there is space in the map self.try_reserve_one()?; Ok(insert_phase_one!( self, key, probe, pos, hash, danger, Entry::Vacant(VacantEntry { map: self, hash, key: key.into(), probe, danger, }), Entry::Occupied(OccupiedEntry { map: self, index: pos, probe, }), Entry::Vacant(VacantEntry { map: self, hash, key: key.into(), probe, danger, }) )) } /// Inserts a key-value pair into the map. /// /// If the map did not previously have this key present, then `None` is /// returned. /// /// If the map did have this key present, the new value is associated with /// the key and all previous values are removed. **Note** that only a single /// one of the previous values is returned. If there are multiple values /// that have been previously associated with the key, then the first one is /// returned. See `insert_mult` on `OccupiedEntry` for an API that returns /// all values. /// /// The key is not updated, though; this matters for types that can be `==` /// without being identical. /// /// # Panics /// /// This method panics if capacity exceeds max `HeaderMap` capacity /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// assert!(map.insert(HOST, "world".parse().unwrap()).is_none()); /// assert!(!map.is_empty()); /// /// let mut prev = map.insert(HOST, "earth".parse().unwrap()).unwrap(); /// assert_eq!("world", prev); /// ``` pub fn insert(&mut self, key: K, val: T) -> Option where K: IntoHeaderName, { self.try_insert(key, val).expect("size overflows MAX_SIZE") } /// Inserts a key-value pair into the map. /// /// If the map did not previously have this key present, then `None` is /// returned. /// /// If the map did have this key present, the new value is associated with /// the key and all previous values are removed. **Note** that only a single /// one of the previous values is returned. If there are multiple values /// that have been previously associated with the key, then the first one is /// returned. See `insert_mult` on `OccupiedEntry` for an API that returns /// all values. /// /// The key is not updated, though; this matters for types that can be `==` /// without being identical. /// /// # Errors /// /// This function may return an error if `HeaderMap` exceeds max capacity /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// assert!(map.try_insert(HOST, "world".parse().unwrap()).unwrap().is_none()); /// assert!(!map.is_empty()); /// /// let mut prev = map.try_insert(HOST, "earth".parse().unwrap()).unwrap().unwrap(); /// assert_eq!("world", prev); /// ``` pub fn try_insert(&mut self, key: K, val: T) -> Result, MaxSizeReached> where K: IntoHeaderName, { key.try_insert(self, val) } #[inline] fn try_insert2(&mut self, key: K, value: T) -> Result, MaxSizeReached> where K: Hash + Into, HeaderName: PartialEq, { self.try_reserve_one()?; Ok(insert_phase_one!( self, key, probe, pos, hash, danger, // Vacant { let _ = danger; // Make lint happy let index = self.entries.len(); self.try_insert_entry(hash, key.into(), value)?; self.indices[probe] = Pos::new(index, hash); None }, // Occupied Some(self.insert_occupied(pos, value)), // Robinhood { self.try_insert_phase_two(key.into(), value, hash, probe, danger)?; None } )) } /// Set an occupied bucket to the given value #[inline] fn insert_occupied(&mut self, index: usize, value: T) -> T { if let Some(links) = self.entries[index].links { self.remove_all_extra_values(links.next); } let entry = &mut self.entries[index]; mem::replace(&mut entry.value, value) } fn insert_occupied_mult(&mut self, index: usize, value: T) -> ValueDrain<'_, T> { let old; let links; { let entry = &mut self.entries[index]; old = mem::replace(&mut entry.value, value); links = entry.links.take(); } let raw_links = self.raw_links(); let extra_values = &mut self.extra_values; let next = links.map(|l| drain_all_extra_values(raw_links, extra_values, l.next).into_iter()); ValueDrain { first: Some(old), next, lt: PhantomData, } } /// Inserts a key-value pair into the map. /// /// If the map did not previously have this key present, then `false` is /// returned. /// /// If the map did have this key present, the new value is pushed to the end /// of the list of values currently associated with the key. The key is not /// updated, though; this matters for types that can be `==` without being /// identical. /// /// # Panics /// /// This method panics if capacity exceeds max `HeaderMap` capacity /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// assert!(map.insert(HOST, "world".parse().unwrap()).is_none()); /// assert!(!map.is_empty()); /// /// map.append(HOST, "earth".parse().unwrap()); /// /// let values = map.get_all("host"); /// let mut i = values.iter(); /// assert_eq!("world", *i.next().unwrap()); /// assert_eq!("earth", *i.next().unwrap()); /// ``` pub fn append(&mut self, key: K, value: T) -> bool where K: IntoHeaderName, { self.try_append(key, value) .expect("size overflows MAX_SIZE") } /// Inserts a key-value pair into the map. /// /// If the map did not previously have this key present, then `false` is /// returned. /// /// If the map did have this key present, the new value is pushed to the end /// of the list of values currently associated with the key. The key is not /// updated, though; this matters for types that can be `==` without being /// identical. /// /// # Errors /// /// This function may return an error if `HeaderMap` exceeds max capacity /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// assert!(map.try_insert(HOST, "world".parse().unwrap()).unwrap().is_none()); /// assert!(!map.is_empty()); /// /// map.try_append(HOST, "earth".parse().unwrap()).unwrap(); /// /// let values = map.get_all("host"); /// let mut i = values.iter(); /// assert_eq!("world", *i.next().unwrap()); /// assert_eq!("earth", *i.next().unwrap()); /// ``` pub fn try_append(&mut self, key: K, value: T) -> Result where K: IntoHeaderName, { key.try_append(self, value) } #[inline] fn try_append2(&mut self, key: K, value: T) -> Result where K: Hash + Into, HeaderName: PartialEq, { self.try_reserve_one()?; Ok(insert_phase_one!( self, key, probe, pos, hash, danger, // Vacant { let _ = danger; let index = self.entries.len(); self.try_insert_entry(hash, key.into(), value)?; self.indices[probe] = Pos::new(index, hash); false }, // Occupied { append_value(pos, &mut self.entries[pos], &mut self.extra_values, value); true }, // Robinhood { self.try_insert_phase_two(key.into(), value, hash, probe, danger)?; false } )) } #[inline] fn find(&self, key: &K) -> Option<(usize, usize)> where K: Hash + Into + ?Sized, HeaderName: PartialEq, { if self.entries.is_empty() { return None; } let hash = hash_elem_using(&self.danger, key); let mask = self.mask; let mut probe = desired_pos(mask, hash); let mut dist = 0; probe_loop!(probe < self.indices.len(), { if let Some((i, entry_hash)) = self.indices[probe].resolve() { if dist > probe_distance(mask, entry_hash, probe) { // give up when probe distance is too long return None; } else if entry_hash == hash && self.entries[i].key == *key { return Some((probe, i)); } } else { return None; } dist += 1; }); } /// phase 2 is post-insert where we forward-shift `Pos` in the indices. #[inline] fn try_insert_phase_two( &mut self, key: HeaderName, value: T, hash: HashValue, probe: usize, danger: bool, ) -> Result { // Push the value and get the index let index = self.entries.len(); self.try_insert_entry(hash, key, value)?; let num_displaced = do_insert_phase_two(&mut self.indices, probe, Pos::new(index, hash)); if danger || num_displaced >= DISPLACEMENT_THRESHOLD { // Increase danger level self.danger.set_yellow(); } Ok(index) } /// Removes a key from the map, returning the value associated with the key. /// /// Returns `None` if the map does not contain the key. If there are /// multiple values associated with the key, then the first one is returned. /// See `remove_entry_mult` on `OccupiedEntry` for an API that yields all /// values. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "hello.world".parse().unwrap()); /// /// let prev = map.remove(HOST).unwrap(); /// assert_eq!("hello.world", prev); /// /// assert!(map.remove(HOST).is_none()); /// ``` pub fn remove(&mut self, key: K) -> Option where K: AsHeaderName, { match key.find(self) { Some((probe, idx)) => { if let Some(links) = self.entries[idx].links { self.remove_all_extra_values(links.next); } let entry = self.remove_found(probe, idx); Some(entry.value) } None => None, } } /// Remove an entry from the map. /// /// Warning: To avoid inconsistent state, extra values _must_ be removed /// for the `found` index (via `remove_all_extra_values` or similar) /// _before_ this method is called. #[inline] fn remove_found(&mut self, probe: usize, found: usize) -> Bucket { // index `probe` and entry `found` is to be removed // use swap_remove, but then we need to update the index that points // to the other entry that has to move self.indices[probe] = Pos::none(); let entry = self.entries.swap_remove(found); // correct index that points to the entry that had to swap places if let Some(entry) = self.entries.get(found) { // was not last element // examine new element in `found` and find it in indices let mut probe = desired_pos(self.mask, entry.hash); probe_loop!(probe < self.indices.len(), { if let Some((i, _)) = self.indices[probe].resolve() { if i >= self.entries.len() { // found it self.indices[probe] = Pos::new(found, entry.hash); break; } } }); // Update links if let Some(links) = entry.links { self.extra_values[links.next].prev = Link::Entry(found); self.extra_values[links.tail].next = Link::Entry(found); } } // backward shift deletion in self.indices // after probe, shift all non-ideally placed indices backward if !self.entries.is_empty() { let mut last_probe = probe; let mut probe = probe + 1; probe_loop!(probe < self.indices.len(), { if let Some((_, entry_hash)) = self.indices[probe].resolve() { if probe_distance(self.mask, entry_hash, probe) > 0 { self.indices[last_probe] = self.indices[probe]; self.indices[probe] = Pos::none(); } else { break; } } else { break; } last_probe = probe; }); } entry } /// Removes the `ExtraValue` at the given index. #[inline] fn remove_extra_value(&mut self, idx: usize) -> ExtraValue { let raw_links = self.raw_links(); remove_extra_value(raw_links, &mut self.extra_values, idx) } fn remove_all_extra_values(&mut self, mut head: usize) { loop { let extra = self.remove_extra_value(head); if let Link::Extra(idx) = extra.next { head = idx; } else { break; } } } #[inline] fn try_insert_entry( &mut self, hash: HashValue, key: HeaderName, value: T, ) -> Result<(), MaxSizeReached> { if self.entries.len() >= MAX_SIZE { return Err(MaxSizeReached::new()); } self.entries.push(Bucket { hash, key, value, links: None, }); Ok(()) } fn rebuild(&mut self) { // Loop over all entries and re-insert them into the map 'outer: for (index, entry) in self.entries.iter_mut().enumerate() { let hash = hash_elem_using(&self.danger, &entry.key); let mut probe = desired_pos(self.mask, hash); let mut dist = 0; // Update the entry's hash code entry.hash = hash; probe_loop!(probe < self.indices.len(), { if let Some((_, entry_hash)) = self.indices[probe].resolve() { // if existing element probed less than us, swap let their_dist = probe_distance(self.mask, entry_hash, probe); if their_dist < dist { // Robinhood break; } } else { // Vacant slot self.indices[probe] = Pos::new(index, hash); continue 'outer; } dist += 1; }); do_insert_phase_two(&mut self.indices, probe, Pos::new(index, hash)); } } fn reinsert_entry_in_order(&mut self, pos: Pos) { if let Some((_, entry_hash)) = pos.resolve() { // Find first empty bucket and insert there let mut probe = desired_pos(self.mask, entry_hash); probe_loop!(probe < self.indices.len(), { if self.indices[probe].resolve().is_none() { // empty bucket, insert here self.indices[probe] = pos; return; } }); } } fn try_reserve_one(&mut self) -> Result<(), MaxSizeReached> { let len = self.entries.len(); if self.danger.is_yellow() { let load_factor = self.entries.len() as f32 / self.indices.len() as f32; if load_factor >= LOAD_FACTOR_THRESHOLD { // Transition back to green danger level self.danger.set_green(); // Double the capacity let new_cap = self.indices.len() * 2; // Grow the capacity self.try_grow(new_cap)?; } else { self.danger.set_red(); // Rebuild hash table for index in self.indices.iter_mut() { *index = Pos::none(); } self.rebuild(); } } else if len == self.capacity() { if len == 0 { let new_raw_cap = 8; self.mask = 8 - 1; self.indices = vec![Pos::none(); new_raw_cap].into_boxed_slice(); self.entries = Vec::with_capacity(usable_capacity(new_raw_cap)); } else { let raw_cap = self.indices.len(); self.try_grow(raw_cap << 1)?; } } Ok(()) } #[inline] fn try_grow(&mut self, new_raw_cap: usize) -> Result<(), MaxSizeReached> { if new_raw_cap > MAX_SIZE { return Err(MaxSizeReached::new()); } // find first ideally placed element -- start of cluster let mut first_ideal = 0; for (i, pos) in self.indices.iter().enumerate() { if let Some((_, entry_hash)) = pos.resolve() { if 0 == probe_distance(self.mask, entry_hash, i) { first_ideal = i; break; } } } // visit the entries in an order where we can simply reinsert them // into self.indices without any bucket stealing. let old_indices = mem::replace( &mut self.indices, vec![Pos::none(); new_raw_cap].into_boxed_slice(), ); self.mask = new_raw_cap.wrapping_sub(1) as Size; for &pos in &old_indices[first_ideal..] { self.reinsert_entry_in_order(pos); } for &pos in &old_indices[..first_ideal] { self.reinsert_entry_in_order(pos); } // Reserve additional entry slots let more = self.capacity() - self.entries.len(); self.entries.reserve_exact(more); Ok(()) } #[inline] fn raw_links(&mut self) -> RawLinks { RawLinks(&mut self.entries[..] as *mut _) } } /// Removes the `ExtraValue` at the given index. #[inline] fn remove_extra_value( mut raw_links: RawLinks, extra_values: &mut Vec>, idx: usize, ) -> ExtraValue { let prev; let next; { debug_assert!(extra_values.len() > idx); let extra = &extra_values[idx]; prev = extra.prev; next = extra.next; } // First unlink the extra value match (prev, next) { (Link::Entry(prev), Link::Entry(next)) => { debug_assert_eq!(prev, next); raw_links[prev] = None; } (Link::Entry(prev), Link::Extra(next)) => { debug_assert!(raw_links[prev].is_some()); raw_links[prev].as_mut().unwrap().next = next; debug_assert!(extra_values.len() > next); extra_values[next].prev = Link::Entry(prev); } (Link::Extra(prev), Link::Entry(next)) => { debug_assert!(raw_links[next].is_some()); raw_links[next].as_mut().unwrap().tail = prev; debug_assert!(extra_values.len() > prev); extra_values[prev].next = Link::Entry(next); } (Link::Extra(prev), Link::Extra(next)) => { debug_assert!(extra_values.len() > next); debug_assert!(extra_values.len() > prev); extra_values[prev].next = Link::Extra(next); extra_values[next].prev = Link::Extra(prev); } } // Remove the extra value let mut extra = extra_values.swap_remove(idx); // This is the index of the value that was moved (possibly `extra`) let old_idx = extra_values.len(); // Update the links if extra.prev == Link::Extra(old_idx) { extra.prev = Link::Extra(idx); } if extra.next == Link::Extra(old_idx) { extra.next = Link::Extra(idx); } // Check if another entry was displaced. If it was, then the links // need to be fixed. if idx != old_idx { let next; let prev; { debug_assert!(extra_values.len() > idx); let moved = &extra_values[idx]; next = moved.next; prev = moved.prev; } // An entry was moved, we have to the links match prev { Link::Entry(entry_idx) => { // It is critical that we do not attempt to read the // header name or value as that memory may have been // "released" already. debug_assert!(raw_links[entry_idx].is_some()); let links = raw_links[entry_idx].as_mut().unwrap(); links.next = idx; } Link::Extra(extra_idx) => { debug_assert!(extra_values.len() > extra_idx); extra_values[extra_idx].next = Link::Extra(idx); } } match next { Link::Entry(entry_idx) => { debug_assert!(raw_links[entry_idx].is_some()); let links = raw_links[entry_idx].as_mut().unwrap(); links.tail = idx; } Link::Extra(extra_idx) => { debug_assert!(extra_values.len() > extra_idx); extra_values[extra_idx].prev = Link::Extra(idx); } } } debug_assert!({ for v in &*extra_values { assert!(v.next != Link::Extra(old_idx)); assert!(v.prev != Link::Extra(old_idx)); } true }); extra } fn drain_all_extra_values( raw_links: RawLinks, extra_values: &mut Vec>, mut head: usize, ) -> Vec { let mut vec = Vec::new(); loop { let extra = remove_extra_value(raw_links, extra_values, head); vec.push(extra.value); if let Link::Extra(idx) = extra.next { head = idx; } else { break; } } vec } impl<'a, T> IntoIterator for &'a HeaderMap { type Item = (&'a HeaderName, &'a T); type IntoIter = Iter<'a, T>; fn into_iter(self) -> Iter<'a, T> { self.iter() } } impl<'a, T> IntoIterator for &'a mut HeaderMap { type Item = (&'a HeaderName, &'a mut T); type IntoIter = IterMut<'a, T>; fn into_iter(self) -> IterMut<'a, T> { self.iter_mut() } } impl IntoIterator for HeaderMap { type Item = (Option, T); type IntoIter = IntoIter; /// Creates a consuming iterator, that is, one that moves keys and values /// out of the map in arbitrary order. The map cannot be used after calling /// this. /// /// For each yielded item that has `None` provided for the `HeaderName`, /// then the associated header name is the same as that of the previously /// yielded item. The first yielded item will have `HeaderName` set. /// /// # Examples /// /// Basic usage. /// /// ``` /// # use http::header; /// # use http::header::*; /// let mut map = HeaderMap::new(); /// map.insert(header::CONTENT_LENGTH, "123".parse().unwrap()); /// map.insert(header::CONTENT_TYPE, "json".parse().unwrap()); /// /// let mut iter = map.into_iter(); /// assert_eq!(iter.next(), Some((Some(header::CONTENT_LENGTH), "123".parse().unwrap()))); /// assert_eq!(iter.next(), Some((Some(header::CONTENT_TYPE), "json".parse().unwrap()))); /// assert!(iter.next().is_none()); /// ``` /// /// Multiple values per key. /// /// ``` /// # use http::header; /// # use http::header::*; /// let mut map = HeaderMap::new(); /// /// map.append(header::CONTENT_LENGTH, "123".parse().unwrap()); /// map.append(header::CONTENT_LENGTH, "456".parse().unwrap()); /// /// 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 mut iter = map.into_iter(); /// /// assert_eq!(iter.next(), Some((Some(header::CONTENT_LENGTH), "123".parse().unwrap()))); /// assert_eq!(iter.next(), Some((None, "456".parse().unwrap()))); /// /// assert_eq!(iter.next(), Some((Some(header::CONTENT_TYPE), "json".parse().unwrap()))); /// assert_eq!(iter.next(), Some((None, "html".parse().unwrap()))); /// assert_eq!(iter.next(), Some((None, "xml".parse().unwrap()))); /// assert!(iter.next().is_none()); /// ``` fn into_iter(self) -> IntoIter { IntoIter { next: None, entries: self.entries.into_iter(), extra_values: self.extra_values, } } } impl FromIterator<(HeaderName, T)> for HeaderMap { fn from_iter(iter: I) -> Self where I: IntoIterator, { let mut map = HeaderMap::default(); map.extend(iter); map } } /// Try to convert a `HashMap` into a `HeaderMap`. /// /// # Examples /// /// ``` /// use std::collections::HashMap; /// use std::convert::TryInto; /// use http::HeaderMap; /// /// let mut map = HashMap::new(); /// map.insert("X-Custom-Header".to_string(), "my value".to_string()); /// /// let headers: HeaderMap = (&map).try_into().expect("valid headers"); /// assert_eq!(headers["X-Custom-Header"], "my value"); /// ``` impl<'a, K, V, S, T> TryFrom<&'a HashMap> for HeaderMap where K: Eq + Hash, HeaderName: TryFrom<&'a K>, >::Error: Into, T: TryFrom<&'a V>, T::Error: Into, { type Error = Error; fn try_from(c: &'a HashMap) -> Result { c.iter() .map(|(k, v)| -> crate::Result<(HeaderName, T)> { let name = TryFrom::try_from(k).map_err(Into::into)?; let value = TryFrom::try_from(v).map_err(Into::into)?; Ok((name, value)) }) .collect() } } impl Extend<(Option, T)> for HeaderMap { /// Extend a `HeaderMap` with the contents of another `HeaderMap`. /// /// This function expects the yielded items to follow the same structure as /// `IntoIter`. /// /// # Panics /// /// This panics if the first yielded item does not have a `HeaderName`. /// /// # Examples /// /// ``` /// # use http::header::*; /// let mut map = HeaderMap::new(); /// /// map.insert(ACCEPT, "text/plain".parse().unwrap()); /// map.insert(HOST, "hello.world".parse().unwrap()); /// /// let mut extra = HeaderMap::new(); /// /// extra.insert(HOST, "foo.bar".parse().unwrap()); /// extra.insert(COOKIE, "hello".parse().unwrap()); /// extra.append(COOKIE, "world".parse().unwrap()); /// /// map.extend(extra); /// /// assert_eq!(map["host"], "foo.bar"); /// assert_eq!(map["accept"], "text/plain"); /// assert_eq!(map["cookie"], "hello"); /// /// let v = map.get_all("host"); /// assert_eq!(1, v.iter().count()); /// /// let v = map.get_all("cookie"); /// assert_eq!(2, v.iter().count()); /// ``` fn extend, T)>>(&mut self, iter: I) { let mut iter = iter.into_iter(); // The structure of this is a bit weird, but it is mostly to make the // borrow checker happy. let (mut key, mut val) = match iter.next() { Some((Some(key), val)) => (key, val), Some((None, _)) => panic!("expected a header name, but got None"), None => return, }; 'outer: loop { let mut entry = match self.try_entry2(key).expect("size overflows MAX_SIZE") { Entry::Occupied(mut e) => { // Replace all previous values while maintaining a handle to // the entry. e.insert(val); e } Entry::Vacant(e) => e.insert_entry(val), }; // As long as `HeaderName` is none, keep inserting the value into // the current entry loop { match iter.next() { Some((Some(k), v)) => { key = k; val = v; continue 'outer; } Some((None, v)) => { entry.append(v); } None => { return; } } } } } } impl Extend<(HeaderName, T)> for HeaderMap { fn extend>(&mut self, iter: I) { // Keys may be already present or show multiple times in the iterator. // Reserve the entire hint lower bound if the map is empty. // Otherwise reserve half the hint (rounded up), so the map // will only resize twice in the worst case. let iter = iter.into_iter(); let reserve = if self.is_empty() { iter.size_hint().0 } else { (iter.size_hint().0 + 1) / 2 }; self.reserve(reserve); for (k, v) in iter { self.append(k, v); } } } impl PartialEq for HeaderMap { fn eq(&self, other: &HeaderMap) -> bool { if self.len() != other.len() { return false; } self.keys() .all(|key| self.get_all(key) == other.get_all(key)) } } impl Eq for HeaderMap {} impl fmt::Debug for HeaderMap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_map().entries(self.iter()).finish() } } impl Default for HeaderMap { fn default() -> Self { HeaderMap::try_with_capacity(0).expect("zero capacity should never fail") } } impl ops::Index for HeaderMap where K: AsHeaderName, { type Output = T; /// # Panics /// Using the index operator will cause a panic if the header you're querying isn't set. #[inline] fn index(&self, index: K) -> &T { match self.get2(&index) { Some(val) => val, None => panic!("no entry found for key {:?}", index.as_str()), } } } /// phase 2 is post-insert where we forward-shift `Pos` in the indices. /// /// returns the number of displaced elements #[inline] fn do_insert_phase_two(indices: &mut [Pos], mut probe: usize, mut old_pos: Pos) -> usize { let mut num_displaced = 0; probe_loop!(probe < indices.len(), { let pos = &mut indices[probe]; if pos.is_none() { *pos = old_pos; break; } else { num_displaced += 1; old_pos = mem::replace(pos, old_pos); } }); num_displaced } #[inline] fn append_value( entry_idx: usize, entry: &mut Bucket, extra: &mut Vec>, value: T, ) { match entry.links { Some(links) => { let idx = extra.len(); extra.push(ExtraValue { value, prev: Link::Extra(links.tail), next: Link::Entry(entry_idx), }); extra[links.tail].next = Link::Extra(idx); entry.links = Some(Links { tail: idx, ..links }); } None => { let idx = extra.len(); extra.push(ExtraValue { value, prev: Link::Entry(entry_idx), next: Link::Entry(entry_idx), }); entry.links = Some(Links { next: idx, tail: idx, }); } } } // ===== impl Iter ===== impl<'a, T> Iterator for Iter<'a, T> { type Item = (&'a HeaderName, &'a T); fn next(&mut self) -> Option { use self::Cursor::*; if self.cursor.is_none() { if (self.entry + 1) >= self.map.entries.len() { return None; } self.entry += 1; self.cursor = Some(Cursor::Head); } let entry = &self.map.entries[self.entry]; match self.cursor.unwrap() { Head => { self.cursor = entry.links.map(|l| Values(l.next)); Some((&entry.key, &entry.value)) } Values(idx) => { let extra = &self.map.extra_values[idx]; match extra.next { Link::Entry(_) => self.cursor = None, Link::Extra(i) => self.cursor = Some(Values(i)), } Some((&entry.key, &extra.value)) } } } fn size_hint(&self) -> (usize, Option) { let map = self.map; debug_assert!(map.entries.len() >= self.entry); let lower = map.entries.len() - self.entry; // We could pessimistically guess at the upper bound, saying // that its lower + map.extra_values.len(). That could be // way over though, such as if we're near the end, and have // already gone through several extra values... (lower, None) } } impl<'a, T> FusedIterator for Iter<'a, T> {} unsafe impl<'a, T: Sync> Sync for Iter<'a, T> {} unsafe impl<'a, T: Sync> Send for Iter<'a, T> {} // ===== impl IterMut ===== impl<'a, T> IterMut<'a, T> { fn next_unsafe(&mut self) -> Option<(&'a HeaderName, *mut T)> { use self::Cursor::*; if self.cursor.is_none() { if (self.entry + 1) >= unsafe { &*self.map }.entries.len() { return None; } self.entry += 1; self.cursor = Some(Cursor::Head); } let entry = unsafe { &mut (*self.map).entries[self.entry] }; match self.cursor.unwrap() { Head => { self.cursor = entry.links.map(|l| Values(l.next)); Some((&entry.key, &mut entry.value as *mut _)) } Values(idx) => { let extra = unsafe { &mut (*self.map).extra_values[idx] }; match extra.next { Link::Entry(_) => self.cursor = None, Link::Extra(i) => self.cursor = Some(Values(i)), } Some((&entry.key, &mut extra.value as *mut _)) } } } } impl<'a, T> Iterator for IterMut<'a, T> { type Item = (&'a HeaderName, &'a mut T); fn next(&mut self) -> Option { self.next_unsafe() .map(|(key, ptr)| (key, unsafe { &mut *ptr })) } fn size_hint(&self) -> (usize, Option) { let map = unsafe { &*self.map }; debug_assert!(map.entries.len() >= self.entry); let lower = map.entries.len() - self.entry; // We could pessimistically guess at the upper bound, saying // that its lower + map.extra_values.len(). That could be // way over though, such as if we're near the end, and have // already gone through several extra values... (lower, None) } } impl<'a, T> FusedIterator for IterMut<'a, T> {} unsafe impl<'a, T: Sync> Sync for IterMut<'a, T> {} unsafe impl<'a, T: Send> Send for IterMut<'a, T> {} // ===== impl Keys ===== impl<'a, T> Iterator for Keys<'a, T> { type Item = &'a HeaderName; fn next(&mut self) -> Option { self.inner.next().map(|b| &b.key) } fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } fn nth(&mut self, n: usize) -> Option { self.inner.nth(n).map(|b| &b.key) } fn count(self) -> usize { self.inner.count() } fn last(self) -> Option { self.inner.last().map(|b| &b.key) } } impl<'a, T> ExactSizeIterator for Keys<'a, T> {} impl<'a, T> FusedIterator for Keys<'a, T> {} // ===== impl Values ==== impl<'a, T> Iterator for Values<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { self.inner.next().map(|(_, v)| v) } fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl<'a, T> FusedIterator for Values<'a, T> {} // ===== impl ValuesMut ==== impl<'a, T> Iterator for ValuesMut<'a, T> { type Item = &'a mut T; fn next(&mut self) -> Option { self.inner.next().map(|(_, v)| v) } fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl<'a, T> FusedIterator for ValuesMut<'a, T> {} // ===== impl Drain ===== impl<'a, T> Iterator for Drain<'a, T> { type Item = (Option, T); fn next(&mut self) -> Option { if let Some(next) = self.next { // Remove the extra value let raw_links = RawLinks(self.entries); let extra = unsafe { remove_extra_value(raw_links, &mut *self.extra_values, next) }; match extra.next { Link::Extra(idx) => self.next = Some(idx), Link::Entry(_) => self.next = None, } return Some((None, extra.value)); } let idx = self.idx; if idx == self.len { return None; } self.idx += 1; unsafe { let entry = &(*self.entries)[idx]; // Read the header name let key = ptr::read(&entry.key as *const _); let value = ptr::read(&entry.value as *const _); self.next = entry.links.map(|l| l.next); Some((Some(key), value)) } } fn size_hint(&self) -> (usize, Option) { // At least this many names... It's unknown if the user wants // to count the extra_values on top. // // For instance, extending a new `HeaderMap` wouldn't need to // reserve the upper-bound in `entries`, only the lower-bound. let lower = self.len - self.idx; let upper = unsafe { (*self.extra_values).len() } + lower; (lower, Some(upper)) } } impl<'a, T> FusedIterator for Drain<'a, T> {} impl<'a, T> Drop for Drain<'a, T> { fn drop(&mut self) { for _ in self {} } } unsafe impl<'a, T: Sync> Sync for Drain<'a, T> {} unsafe impl<'a, T: Send> Send for Drain<'a, T> {} // ===== impl Entry ===== impl<'a, T> Entry<'a, T> { /// Ensures a value is in the entry by inserting the default if empty. /// /// Returns a mutable reference to the **first** value in the entry. /// /// # Panics /// /// This method panics if capacity exceeds max `HeaderMap` capacity /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// let mut map: HeaderMap = HeaderMap::default(); /// /// let headers = &[ /// "content-length", /// "x-hello", /// "Content-Length", /// "x-world", /// ]; /// /// for &header in headers { /// let counter = map.entry(header) /// .or_insert(0); /// *counter += 1; /// } /// /// assert_eq!(map["content-length"], 2); /// assert_eq!(map["x-hello"], 1); /// ``` pub fn or_insert(self, default: T) -> &'a mut T { self.or_try_insert(default) .expect("size overflows MAX_SIZE") } /// Ensures a value is in the entry by inserting the default if empty. /// /// Returns a mutable reference to the **first** value in the entry. /// /// # Errors /// /// This function may return an error if `HeaderMap` exceeds max capacity /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// let mut map: HeaderMap = HeaderMap::default(); /// /// let headers = &[ /// "content-length", /// "x-hello", /// "Content-Length", /// "x-world", /// ]; /// /// for &header in headers { /// let counter = map.entry(header) /// .or_try_insert(0) /// .unwrap(); /// *counter += 1; /// } /// /// assert_eq!(map["content-length"], 2); /// assert_eq!(map["x-hello"], 1); /// ``` pub fn or_try_insert(self, default: T) -> Result<&'a mut T, MaxSizeReached> { use self::Entry::*; match self { Occupied(e) => Ok(e.into_mut()), Vacant(e) => e.try_insert(default), } } /// Ensures a value is in the entry by inserting the result of the default /// function if empty. /// /// The default function is not called if the entry exists in the map. /// Returns a mutable reference to the **first** value in the entry. /// /// # Examples /// /// Basic usage. /// /// ``` /// # use http::HeaderMap; /// let mut map = HeaderMap::new(); /// /// let res = map.entry("x-hello") /// .or_insert_with(|| "world".parse().unwrap()); /// /// assert_eq!(res, "world"); /// ``` /// /// The default function is not called if the entry exists in the map. /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// map.try_insert(HOST, "world".parse().unwrap()).unwrap(); /// /// let res = map.try_entry("host") /// .unwrap() /// .or_try_insert_with(|| unreachable!()) /// .unwrap(); /// /// /// assert_eq!(res, "world"); /// ``` pub fn or_insert_with T>(self, default: F) -> &'a mut T { self.or_try_insert_with(default) .expect("size overflows MAX_SIZE") } /// Ensures a value is in the entry by inserting the result of the default /// function if empty. /// /// The default function is not called if the entry exists in the map. /// Returns a mutable reference to the **first** value in the entry. /// /// # Examples /// /// Basic usage. /// /// ``` /// # use http::HeaderMap; /// let mut map = HeaderMap::new(); /// /// let res = map.entry("x-hello") /// .or_insert_with(|| "world".parse().unwrap()); /// /// assert_eq!(res, "world"); /// ``` /// /// The default function is not called if the entry exists in the map. /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// map.try_insert(HOST, "world".parse().unwrap()).unwrap(); /// /// let res = map.try_entry("host") /// .unwrap() /// .or_try_insert_with(|| unreachable!()) /// .unwrap(); /// /// /// assert_eq!(res, "world"); /// ``` pub fn or_try_insert_with T>( self, default: F, ) -> Result<&'a mut T, MaxSizeReached> { use self::Entry::*; match self { Occupied(e) => Ok(e.into_mut()), Vacant(e) => e.try_insert(default()), } } /// Returns a reference to the entry's key /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// let mut map = HeaderMap::new(); /// /// assert_eq!(map.entry("x-hello").key(), "x-hello"); /// ``` pub fn key(&self) -> &HeaderName { use self::Entry::*; match *self { Vacant(ref e) => e.key(), Occupied(ref e) => e.key(), } } } // ===== impl VacantEntry ===== impl<'a, T> VacantEntry<'a, T> { /// Returns a reference to the entry's key /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// let mut map = HeaderMap::new(); /// /// assert_eq!(map.entry("x-hello").key().as_str(), "x-hello"); /// ``` pub fn key(&self) -> &HeaderName { &self.key } /// Take ownership of the key /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry}; /// let mut map = HeaderMap::new(); /// /// if let Entry::Vacant(v) = map.entry("x-hello") { /// assert_eq!(v.into_key().as_str(), "x-hello"); /// } /// ``` pub fn into_key(self) -> HeaderName { self.key } /// Insert the value into the entry. /// /// The value will be associated with this entry's key. A mutable reference /// to the inserted value will be returned. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry}; /// let mut map = HeaderMap::new(); /// /// if let Entry::Vacant(v) = map.entry("x-hello") { /// v.insert("world".parse().unwrap()); /// } /// /// assert_eq!(map["x-hello"], "world"); /// ``` pub fn insert(self, value: T) -> &'a mut T { self.try_insert(value).expect("size overflows MAX_SIZE") } /// Insert the value into the entry. /// /// The value will be associated with this entry's key. A mutable reference /// to the inserted value will be returned. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry}; /// let mut map = HeaderMap::new(); /// /// if let Entry::Vacant(v) = map.entry("x-hello") { /// v.insert("world".parse().unwrap()); /// } /// /// assert_eq!(map["x-hello"], "world"); /// ``` pub fn try_insert(self, value: T) -> Result<&'a mut T, MaxSizeReached> { // Ensure that there is space in the map let index = self.map .try_insert_phase_two(self.key, value, self.hash, self.probe, self.danger)?; Ok(&mut self.map.entries[index].value) } /// Insert the value into the entry. /// /// The value will be associated with this entry's key. The new /// `OccupiedEntry` is returned, allowing for further manipulation. /// /// # Examples /// /// ``` /// # use http::header::*; /// let mut map = HeaderMap::new(); /// /// if let Entry::Vacant(v) = map.try_entry("x-hello").unwrap() { /// let mut e = v.try_insert_entry("world".parse().unwrap()).unwrap(); /// e.insert("world2".parse().unwrap()); /// } /// /// assert_eq!(map["x-hello"], "world2"); /// ``` pub fn insert_entry(self, value: T) -> OccupiedEntry<'a, T> { self.try_insert_entry(value) .expect("size overflows MAX_SIZE") } /// Insert the value into the entry. /// /// The value will be associated with this entry's key. The new /// `OccupiedEntry` is returned, allowing for further manipulation. /// /// # Examples /// /// ``` /// # use http::header::*; /// let mut map = HeaderMap::new(); /// /// if let Entry::Vacant(v) = map.try_entry("x-hello").unwrap() { /// let mut e = v.try_insert_entry("world".parse().unwrap()).unwrap(); /// e.insert("world2".parse().unwrap()); /// } /// /// assert_eq!(map["x-hello"], "world2"); /// ``` pub fn try_insert_entry(self, value: T) -> Result, MaxSizeReached> { // Ensure that there is space in the map let index = self.map .try_insert_phase_two(self.key, value, self.hash, self.probe, self.danger)?; Ok(OccupiedEntry { map: self.map, index, probe: self.probe, }) } } // ===== impl GetAll ===== impl<'a, T: 'a> GetAll<'a, T> { /// Returns an iterator visiting all values associated with the entry. /// /// Values are iterated in insertion order. /// /// # Examples /// /// ``` /// # use http::HeaderMap; /// # use http::header::HOST; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "hello.world".parse().unwrap()); /// map.append(HOST, "hello.earth".parse().unwrap()); /// /// let values = map.get_all("host"); /// let mut iter = values.iter(); /// assert_eq!(&"hello.world", iter.next().unwrap()); /// assert_eq!(&"hello.earth", iter.next().unwrap()); /// assert!(iter.next().is_none()); /// ``` pub fn iter(&self) -> ValueIter<'a, T> { // This creates a new GetAll struct so that the lifetime // isn't bound to &self. GetAll { map: self.map, index: self.index, } .into_iter() } } impl<'a, T: PartialEq> PartialEq for GetAll<'a, T> { fn eq(&self, other: &Self) -> bool { self.iter().eq(other.iter()) } } impl<'a, T> IntoIterator for GetAll<'a, T> { type Item = &'a T; type IntoIter = ValueIter<'a, T>; fn into_iter(self) -> ValueIter<'a, T> { self.map.value_iter(self.index) } } impl<'a, 'b: 'a, T> IntoIterator for &'b GetAll<'a, T> { type Item = &'a T; type IntoIter = ValueIter<'a, T>; fn into_iter(self) -> ValueIter<'a, T> { self.map.value_iter(self.index) } } // ===== impl ValueIter ===== impl<'a, T: 'a> Iterator for ValueIter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { use self::Cursor::*; match self.front { Some(Head) => { let entry = &self.map.entries[self.index]; if self.back == Some(Head) { self.front = None; self.back = None; } else { // Update the iterator state match entry.links { Some(links) => { self.front = Some(Values(links.next)); } None => unreachable!(), } } Some(&entry.value) } Some(Values(idx)) => { let extra = &self.map.extra_values[idx]; if self.front == self.back { self.front = None; self.back = None; } else { match extra.next { Link::Entry(_) => self.front = None, Link::Extra(i) => self.front = Some(Values(i)), } } Some(&extra.value) } None => None, } } fn size_hint(&self) -> (usize, Option) { match (self.front, self.back) { // Exactly 1 value... (Some(Cursor::Head), Some(Cursor::Head)) => (1, Some(1)), // At least 1... (Some(_), _) => (1, None), // No more values... (None, _) => (0, Some(0)), } } } impl<'a, T: 'a> DoubleEndedIterator for ValueIter<'a, T> { fn next_back(&mut self) -> Option { use self::Cursor::*; match self.back { Some(Head) => { self.front = None; self.back = None; Some(&self.map.entries[self.index].value) } Some(Values(idx)) => { let extra = &self.map.extra_values[idx]; if self.front == self.back { self.front = None; self.back = None; } else { match extra.prev { Link::Entry(_) => self.back = Some(Head), Link::Extra(idx) => self.back = Some(Values(idx)), } } Some(&extra.value) } None => None, } } } impl<'a, T> FusedIterator for ValueIter<'a, T> {} // ===== impl ValueIterMut ===== impl<'a, T: 'a> Iterator for ValueIterMut<'a, T> { type Item = &'a mut T; fn next(&mut self) -> Option { use self::Cursor::*; let entry = unsafe { &mut (*self.map).entries[self.index] }; match self.front { Some(Head) => { if self.back == Some(Head) { self.front = None; self.back = None; } else { // Update the iterator state match entry.links { Some(links) => { self.front = Some(Values(links.next)); } None => unreachable!(), } } Some(&mut entry.value) } Some(Values(idx)) => { let extra = unsafe { &mut (*self.map).extra_values[idx] }; if self.front == self.back { self.front = None; self.back = None; } else { match extra.next { Link::Entry(_) => self.front = None, Link::Extra(i) => self.front = Some(Values(i)), } } Some(&mut extra.value) } None => None, } } } impl<'a, T: 'a> DoubleEndedIterator for ValueIterMut<'a, T> { fn next_back(&mut self) -> Option { use self::Cursor::*; let entry = unsafe { &mut (*self.map).entries[self.index] }; match self.back { Some(Head) => { self.front = None; self.back = None; Some(&mut entry.value) } Some(Values(idx)) => { let extra = unsafe { &mut (*self.map).extra_values[idx] }; if self.front == self.back { self.front = None; self.back = None; } else { match extra.prev { Link::Entry(_) => self.back = Some(Head), Link::Extra(idx) => self.back = Some(Values(idx)), } } Some(&mut extra.value) } None => None, } } } impl<'a, T> FusedIterator for ValueIterMut<'a, T> {} unsafe impl<'a, T: Sync> Sync for ValueIterMut<'a, T> {} unsafe impl<'a, T: Send> Send for ValueIterMut<'a, T> {} // ===== impl IntoIter ===== impl Iterator for IntoIter { type Item = (Option, T); fn next(&mut self) -> Option { if let Some(next) = self.next { self.next = match self.extra_values[next].next { Link::Entry(_) => None, Link::Extra(v) => Some(v), }; let value = unsafe { ptr::read(&self.extra_values[next].value) }; return Some((None, value)); } if let Some(bucket) = self.entries.next() { self.next = bucket.links.map(|l| l.next); let name = Some(bucket.key); let value = bucket.value; return Some((name, value)); } None } fn size_hint(&self) -> (usize, Option) { let (lower, _) = self.entries.size_hint(); // There could be more than just the entries upper, as there // could be items in the `extra_values`. We could guess, saying // `upper + extra_values.len()`, but that could overestimate by a lot. (lower, None) } } impl FusedIterator for IntoIter {} impl Drop for IntoIter { fn drop(&mut self) { // Ensure the iterator is consumed for _ in self.by_ref() {} // All the values have already been yielded out. unsafe { self.extra_values.set_len(0); } } } // ===== impl OccupiedEntry ===== impl<'a, T> OccupiedEntry<'a, T> { /// Returns a reference to the entry's key. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "world".parse().unwrap()); /// /// if let Entry::Occupied(e) = map.entry("host") { /// assert_eq!("host", e.key()); /// } /// ``` pub fn key(&self) -> &HeaderName { &self.map.entries[self.index].key } /// Get a reference to the first value in the entry. /// /// Values are stored in insertion order. /// /// # Panics /// /// `get` panics if there are no values associated with the entry. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "hello.world".parse().unwrap()); /// /// if let Entry::Occupied(mut e) = map.entry("host") { /// assert_eq!(e.get(), &"hello.world"); /// /// e.append("hello.earth".parse().unwrap()); /// /// assert_eq!(e.get(), &"hello.world"); /// } /// ``` pub fn get(&self) -> &T { &self.map.entries[self.index].value } /// Get a mutable reference to the first value in the entry. /// /// Values are stored in insertion order. /// /// # Panics /// /// `get_mut` panics if there are no values associated with the entry. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::default(); /// map.insert(HOST, "hello.world".to_string()); /// /// if let Entry::Occupied(mut e) = map.entry("host") { /// e.get_mut().push_str("-2"); /// assert_eq!(e.get(), &"hello.world-2"); /// } /// ``` pub fn get_mut(&mut self) -> &mut T { &mut self.map.entries[self.index].value } /// Converts the `OccupiedEntry` into a mutable reference to the **first** /// value. /// /// The lifetime of the returned reference is bound to the original map. /// /// # Panics /// /// `into_mut` panics if there are no values associated with the entry. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::default(); /// map.insert(HOST, "hello.world".to_string()); /// map.append(HOST, "hello.earth".to_string()); /// /// if let Entry::Occupied(e) = map.entry("host") { /// e.into_mut().push_str("-2"); /// } /// /// assert_eq!("hello.world-2", map["host"]); /// ``` pub fn into_mut(self) -> &'a mut T { &mut self.map.entries[self.index].value } /// Sets the value of the entry. /// /// All previous values associated with the entry are removed and the first /// one is returned. See `insert_mult` for an API that returns all values. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "hello.world".parse().unwrap()); /// /// if let Entry::Occupied(mut e) = map.entry("host") { /// let mut prev = e.insert("earth".parse().unwrap()); /// assert_eq!("hello.world", prev); /// } /// /// assert_eq!("earth", map["host"]); /// ``` pub fn insert(&mut self, value: T) -> T { self.map.insert_occupied(self.index, value) } /// Sets the value of the entry. /// /// This function does the same as `insert` except it returns an iterator /// that yields all values previously associated with the key. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "world".parse().unwrap()); /// map.append(HOST, "world2".parse().unwrap()); /// /// if let Entry::Occupied(mut e) = map.entry("host") { /// let mut prev = e.insert_mult("earth".parse().unwrap()); /// assert_eq!("world", prev.next().unwrap()); /// assert_eq!("world2", prev.next().unwrap()); /// assert!(prev.next().is_none()); /// } /// /// assert_eq!("earth", map["host"]); /// ``` pub fn insert_mult(&mut self, value: T) -> ValueDrain<'_, T> { self.map.insert_occupied_mult(self.index, value) } /// Insert the value into the entry. /// /// The new value is appended to the end of the entry's value list. All /// previous values associated with the entry are retained. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "world".parse().unwrap()); /// /// if let Entry::Occupied(mut e) = map.entry("host") { /// e.append("earth".parse().unwrap()); /// } /// /// let values = map.get_all("host"); /// let mut i = values.iter(); /// assert_eq!("world", *i.next().unwrap()); /// assert_eq!("earth", *i.next().unwrap()); /// ``` pub fn append(&mut self, value: T) { let idx = self.index; let entry = &mut self.map.entries[idx]; append_value(idx, entry, &mut self.map.extra_values, value); } /// Remove the entry from the map. /// /// All values associated with the entry are removed and the first one is /// returned. See `remove_entry_mult` for an API that returns all values. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "world".parse().unwrap()); /// /// if let Entry::Occupied(e) = map.entry("host") { /// let mut prev = e.remove(); /// assert_eq!("world", prev); /// } /// /// assert!(!map.contains_key("host")); /// ``` pub fn remove(self) -> T { self.remove_entry().1 } /// Remove the entry from the map. /// /// The key and all values associated with the entry are removed and the /// first one is returned. See `remove_entry_mult` for an API that returns /// all values. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "world".parse().unwrap()); /// /// if let Entry::Occupied(e) = map.entry("host") { /// let (key, mut prev) = e.remove_entry(); /// assert_eq!("host", key.as_str()); /// assert_eq!("world", prev); /// } /// /// assert!(!map.contains_key("host")); /// ``` pub fn remove_entry(self) -> (HeaderName, T) { if let Some(links) = self.map.entries[self.index].links { self.map.remove_all_extra_values(links.next); } let entry = self.map.remove_found(self.probe, self.index); (entry.key, entry.value) } /// Remove the entry from the map. /// /// The key and all values associated with the entry are removed and /// returned. pub fn remove_entry_mult(self) -> (HeaderName, ValueDrain<'a, T>) { let raw_links = self.map.raw_links(); let extra_values = &mut self.map.extra_values; let next = self.map.entries[self.index] .links .map(|l| drain_all_extra_values(raw_links, extra_values, l.next).into_iter()); let entry = self.map.remove_found(self.probe, self.index); let drain = ValueDrain { first: Some(entry.value), next, lt: PhantomData, }; (entry.key, drain) } /// Returns an iterator visiting all values associated with the entry. /// /// Values are iterated in insertion order. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::new(); /// map.insert(HOST, "world".parse().unwrap()); /// map.append(HOST, "earth".parse().unwrap()); /// /// if let Entry::Occupied(e) = map.entry("host") { /// let mut iter = e.iter(); /// assert_eq!(&"world", iter.next().unwrap()); /// assert_eq!(&"earth", iter.next().unwrap()); /// assert!(iter.next().is_none()); /// } /// ``` pub fn iter(&self) -> ValueIter<'_, T> { self.map.value_iter(Some(self.index)) } /// Returns an iterator mutably visiting all values associated with the /// entry. /// /// Values are iterated in insertion order. /// /// # Examples /// /// ``` /// # use http::header::{HeaderMap, Entry, HOST}; /// let mut map = HeaderMap::default(); /// map.insert(HOST, "world".to_string()); /// map.append(HOST, "earth".to_string()); /// /// if let Entry::Occupied(mut e) = map.entry("host") { /// for e in e.iter_mut() { /// e.push_str("-boop"); /// } /// } /// /// let mut values = map.get_all("host"); /// let mut i = values.iter(); /// assert_eq!(&"world-boop", i.next().unwrap()); /// assert_eq!(&"earth-boop", i.next().unwrap()); /// ``` pub fn iter_mut(&mut self) -> ValueIterMut<'_, T> { self.map.value_iter_mut(self.index) } } impl<'a, T> IntoIterator for OccupiedEntry<'a, T> { type Item = &'a mut T; type IntoIter = ValueIterMut<'a, T>; fn into_iter(self) -> ValueIterMut<'a, T> { self.map.value_iter_mut(self.index) } } impl<'a, 'b: 'a, T> IntoIterator for &'b OccupiedEntry<'a, T> { type Item = &'a T; type IntoIter = ValueIter<'a, T>; fn into_iter(self) -> ValueIter<'a, T> { self.iter() } } impl<'a, 'b: 'a, T> IntoIterator for &'b mut OccupiedEntry<'a, T> { type Item = &'a mut T; type IntoIter = ValueIterMut<'a, T>; fn into_iter(self) -> ValueIterMut<'a, T> { self.iter_mut() } } // ===== impl ValueDrain ===== impl<'a, T> Iterator for ValueDrain<'a, T> { type Item = T; fn next(&mut self) -> Option { if self.first.is_some() { self.first.take() } else if let Some(ref mut extras) = self.next { extras.next() } else { None } } fn size_hint(&self) -> (usize, Option) { match (&self.first, &self.next) { // Exactly 1 (&Some(_), &None) => (1, Some(1)), // 1 + extras (&Some(_), Some(extras)) => { let (l, u) = extras.size_hint(); (l + 1, u.map(|u| u + 1)) } // Extras only (&None, Some(extras)) => extras.size_hint(), // No more (&None, &None) => (0, Some(0)), } } } impl<'a, T> FusedIterator for ValueDrain<'a, T> {} impl<'a, T> Drop for ValueDrain<'a, T> { fn drop(&mut self) { for _ in self.by_ref() {} } } unsafe impl<'a, T: Sync> Sync for ValueDrain<'a, T> {} unsafe impl<'a, T: Send> Send for ValueDrain<'a, T> {} // ===== impl RawLinks ===== impl Clone for RawLinks { fn clone(&self) -> RawLinks { *self } } impl Copy for RawLinks {} impl ops::Index for RawLinks { type Output = Option; fn index(&self, idx: usize) -> &Self::Output { unsafe { &(*self.0)[idx].links } } } impl ops::IndexMut for RawLinks { fn index_mut(&mut self, idx: usize) -> &mut Self::Output { unsafe { &mut (*self.0)[idx].links } } } // ===== impl Pos ===== impl Pos { #[inline] fn new(index: usize, hash: HashValue) -> Self { debug_assert!(index < MAX_SIZE); Pos { index: index as Size, hash, } } #[inline] fn none() -> Self { Pos { index: !0, hash: HashValue(0), } } #[inline] fn is_some(&self) -> bool { !self.is_none() } #[inline] fn is_none(&self) -> bool { self.index == !0 } #[inline] fn resolve(&self) -> Option<(usize, HashValue)> { if self.is_some() { Some((self.index as usize, self.hash)) } else { None } } } impl Danger { fn is_red(&self) -> bool { matches!(*self, Danger::Red(_)) } fn set_red(&mut self) { debug_assert!(self.is_yellow()); *self = Danger::Red(RandomState::new()); } fn is_yellow(&self) -> bool { matches!(*self, Danger::Yellow) } fn set_yellow(&mut self) { if let Danger::Green = *self { *self = Danger::Yellow; } } fn set_green(&mut self) { debug_assert!(self.is_yellow()); *self = Danger::Green; } } // ===== impl MaxSizeReached ===== impl MaxSizeReached { fn new() -> Self { MaxSizeReached { _priv: () } } } impl fmt::Debug for MaxSizeReached { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("MaxSizeReached") // skip _priv noise .finish() } } impl fmt::Display for MaxSizeReached { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("max size reached") } } impl std::error::Error for MaxSizeReached {} // ===== impl Utils ===== #[inline] fn usable_capacity(cap: usize) -> usize { cap - cap / 4 } #[inline] fn to_raw_capacity(n: usize) -> usize { match n.checked_add(n / 3) { Some(n) => n, None => panic!( "requested capacity {} too large: overflow while converting to raw capacity", n ), } } #[inline] fn desired_pos(mask: Size, hash: HashValue) -> usize { (hash.0 & mask) as usize } /// The number of steps that `current` is forward of the desired position for hash #[inline] fn probe_distance(mask: Size, hash: HashValue, current: usize) -> usize { current.wrapping_sub(desired_pos(mask, hash)) & mask as usize } fn hash_elem_using(danger: &Danger, k: &K) -> HashValue where K: Hash + ?Sized, { use fnv::FnvHasher; const MASK: u64 = (MAX_SIZE as u64) - 1; let hash = match *danger { // Safe hash Danger::Red(ref hasher) => { let mut h = hasher.build_hasher(); k.hash(&mut h); h.finish() } // Fast hash _ => { let mut h = FnvHasher::default(); k.hash(&mut h); h.finish() } }; HashValue((hash & MASK) as u16) } /* * * ===== impl IntoHeaderName / AsHeaderName ===== * */ mod into_header_name { use super::{Entry, HdrName, HeaderMap, HeaderName, MaxSizeReached}; /// A marker trait used to identify values that can be used as insert keys /// to a `HeaderMap`. pub trait IntoHeaderName: Sealed {} // All methods are on this pub(super) trait, instead of `IntoHeaderName`, // so that they aren't publicly exposed to the world. // // Being on the `IntoHeaderName` trait would mean users could call // `"host".insert(&mut map, "localhost")`. // // Ultimately, this allows us to adjust the signatures of these methods // without breaking any external crate. pub trait Sealed { #[doc(hidden)] fn try_insert(self, map: &mut HeaderMap, val: T) -> Result, MaxSizeReached>; #[doc(hidden)] fn try_append(self, map: &mut HeaderMap, val: T) -> Result; #[doc(hidden)] fn try_entry(self, map: &mut HeaderMap) -> Result, MaxSizeReached>; } // ==== impls ==== impl Sealed for HeaderName { #[inline] fn try_insert( self, map: &mut HeaderMap, val: T, ) -> Result, MaxSizeReached> { map.try_insert2(self, val) } #[inline] fn try_append(self, map: &mut HeaderMap, val: T) -> Result { map.try_append2(self, val) } #[inline] fn try_entry(self, map: &mut HeaderMap) -> Result, MaxSizeReached> { map.try_entry2(self) } } impl IntoHeaderName for HeaderName {} impl<'a> Sealed for &'a HeaderName { #[inline] fn try_insert( self, map: &mut HeaderMap, val: T, ) -> Result, MaxSizeReached> { map.try_insert2(self, val) } #[inline] fn try_append(self, map: &mut HeaderMap, val: T) -> Result { map.try_append2(self, val) } #[inline] fn try_entry(self, map: &mut HeaderMap) -> Result, MaxSizeReached> { map.try_entry2(self) } } impl<'a> IntoHeaderName for &'a HeaderName {} impl Sealed for &'static str { #[inline] fn try_insert( self, map: &mut HeaderMap, val: T, ) -> Result, MaxSizeReached> { HdrName::from_static(self, move |hdr| map.try_insert2(hdr, val)) } #[inline] fn try_append(self, map: &mut HeaderMap, val: T) -> Result { HdrName::from_static(self, move |hdr| map.try_append2(hdr, val)) } #[inline] fn try_entry(self, map: &mut HeaderMap) -> Result, MaxSizeReached> { HdrName::from_static(self, move |hdr| map.try_entry2(hdr)) } } impl IntoHeaderName for &'static str {} } mod as_header_name { use super::{Entry, HdrName, HeaderMap, HeaderName, InvalidHeaderName, MaxSizeReached}; /// A marker trait used to identify values that can be used as search keys /// to a `HeaderMap`. pub trait AsHeaderName: Sealed {} // Debug not currently needed, save on compiling it #[allow(missing_debug_implementations)] pub enum TryEntryError { InvalidHeaderName(InvalidHeaderName), MaxSizeReached(MaxSizeReached), } impl From for TryEntryError { fn from(e: InvalidHeaderName) -> TryEntryError { TryEntryError::InvalidHeaderName(e) } } impl From for TryEntryError { fn from(e: MaxSizeReached) -> TryEntryError { TryEntryError::MaxSizeReached(e) } } // All methods are on this pub(super) trait, instead of `AsHeaderName`, // so that they aren't publicly exposed to the world. // // Being on the `AsHeaderName` trait would mean users could call // `"host".find(&map)`. // // Ultimately, this allows us to adjust the signatures of these methods // without breaking any external crate. pub trait Sealed { #[doc(hidden)] fn try_entry(self, map: &mut HeaderMap) -> Result, TryEntryError>; #[doc(hidden)] fn find(&self, map: &HeaderMap) -> Option<(usize, usize)>; #[doc(hidden)] fn as_str(&self) -> &str; } // ==== impls ==== impl Sealed for HeaderName { #[inline] fn try_entry(self, map: &mut HeaderMap) -> Result, TryEntryError> { Ok(map.try_entry2(self)?) } #[inline] fn find(&self, map: &HeaderMap) -> Option<(usize, usize)> { map.find(self) } fn as_str(&self) -> &str { ::as_str(self) } } impl AsHeaderName for HeaderName {} impl<'a> Sealed for &'a HeaderName { #[inline] fn try_entry(self, map: &mut HeaderMap) -> Result, TryEntryError> { Ok(map.try_entry2(self)?) } #[inline] fn find(&self, map: &HeaderMap) -> Option<(usize, usize)> { map.find(*self) } fn as_str(&self) -> &str { ::as_str(self) } } impl<'a> AsHeaderName for &'a HeaderName {} impl<'a> Sealed for &'a str { #[inline] fn try_entry(self, map: &mut HeaderMap) -> Result, TryEntryError> { Ok(HdrName::from_bytes(self.as_bytes(), move |hdr| { map.try_entry2(hdr) })??) } #[inline] fn find(&self, map: &HeaderMap) -> Option<(usize, usize)> { HdrName::from_bytes(self.as_bytes(), move |hdr| map.find(&hdr)).unwrap_or(None) } fn as_str(&self) -> &str { self } } impl<'a> AsHeaderName for &'a str {} impl Sealed for String { #[inline] fn try_entry(self, map: &mut HeaderMap) -> Result, TryEntryError> { self.as_str().try_entry(map) } #[inline] fn find(&self, map: &HeaderMap) -> Option<(usize, usize)> { Sealed::find(&self.as_str(), map) } fn as_str(&self) -> &str { self } } impl AsHeaderName for String {} impl<'a> Sealed for &'a String { #[inline] fn try_entry(self, map: &mut HeaderMap) -> Result, TryEntryError> { self.as_str().try_entry(map) } #[inline] fn find(&self, map: &HeaderMap) -> Option<(usize, usize)> { Sealed::find(*self, map) } fn as_str(&self) -> &str { self } } impl<'a> AsHeaderName for &'a String {} } #[test] fn test_bounds() { fn check_bounds() {} check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); check_bounds::>(); } #[test] fn skip_duplicates_during_key_iteration() { let mut map = HeaderMap::new(); map.try_append("a", HeaderValue::from_static("a")).unwrap(); map.try_append("a", HeaderValue::from_static("b")).unwrap(); assert_eq!(map.keys().count(), map.keys_len()); } http-1.2.0/src/header/mod.rs000064400000000000000000000133071046102023000137530ustar 00000000000000//! 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, MaxSizeReached, OccupiedEntry, VacantEntry, ValueDrain, ValueIter, ValueIterMut, Values, ValuesMut, }; pub use self::name::{HeaderName, InvalidHeaderName}; pub use self::value::{HeaderValue, InvalidHeaderValue, ToStrError}; // Use header name constants #[rustfmt::skip] 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, CACHE_STATUS, CDN_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; http-1.2.0/src/header/name.rs000064400000000000000000002313601046102023000141150ustar 00000000000000use crate::byte_str::ByteStr; use bytes::{Bytes, BytesMut}; use std::borrow::Borrow; use std::convert::TryFrom; use std::error::Error; use std::fmt; use std::hash::{Hash, Hasher}; use std::mem::MaybeUninit; use std::str::FromStr; /// Represents an HTTP header field name /// /// Header field names identify the header. Header sets may include multiple /// headers with the same name. The HTTP specification defines a number of /// standard headers, but HTTP messages may include non-standard header names as /// well as long as they adhere to the specification. /// /// `HeaderName` is used as the [`HeaderMap`] key. Constants are available for /// all standard header names in the [`header`] module. /// /// # Representation /// /// `HeaderName` represents standard header names using an `enum`, as such they /// will not require an allocation for storage. All custom header names are /// lower cased upon conversion to a `HeaderName` value. This avoids the /// overhead of dynamically doing lower case conversion during the hash code /// computation and the comparison operation. /// /// [`HeaderMap`]: struct.HeaderMap.html /// [`header`]: index.html #[derive(Clone, Eq, PartialEq, Hash)] pub struct HeaderName { inner: Repr, } // Almost a full `HeaderName` #[derive(Debug, Hash)] pub struct HdrName<'a> { inner: Repr>, } #[derive(Debug, Clone, Eq, PartialEq, Hash)] enum Repr { Standard(StandardHeader), Custom(T), } // Used to hijack the Hash impl #[derive(Debug, Clone, Eq, PartialEq)] struct Custom(ByteStr); #[derive(Debug, Clone)] // Invariant: If lower then buf is valid UTF-8. struct MaybeLower<'a> { buf: &'a [u8], lower: bool, } /// A possible error when converting a `HeaderName` from another type. pub struct InvalidHeaderName { _priv: (), } macro_rules! standard_headers { ( $( $(#[$docs:meta])* ($konst:ident, $upcase:ident, $name_bytes:literal); )+ ) => { #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] enum StandardHeader { $( $konst, )+ } $( $(#[$docs])* pub const $upcase: HeaderName = HeaderName { inner: Repr::Standard(StandardHeader::$konst), }; )+ impl StandardHeader { #[inline] fn as_str(&self) -> &'static str { match *self { // Safety: test_parse_standard_headers ensures these &[u8]s are &str-safe. $( StandardHeader::$konst => unsafe { std::str::from_utf8_unchecked( $name_bytes ) }, )+ } } const fn from_bytes(name_bytes: &[u8]) -> Option { match name_bytes { $( $name_bytes => Some(StandardHeader::$konst), )+ _ => None, } } } #[cfg(test)] const TEST_HEADERS: &'static [(StandardHeader, &'static [u8])] = &[ $( (StandardHeader::$konst, $name_bytes), )+ ]; #[test] fn test_parse_standard_headers() { for &(std, name_bytes) in TEST_HEADERS { // Test lower case assert_eq!(HeaderName::from_bytes(name_bytes).unwrap(), HeaderName::from(std)); // Test upper case let upper = std::str::from_utf8(name_bytes).expect("byte string constants are all utf-8").to_uppercase(); assert_eq!(HeaderName::from_bytes(upper.as_bytes()).unwrap(), HeaderName::from(std)); } } #[test] fn test_standard_headers_into_bytes() { for &(std, name_bytes) in TEST_HEADERS { let name = std::str::from_utf8(name_bytes).unwrap(); let std = HeaderName::from(std); // Test lower case let bytes: Bytes = HeaderName::from_bytes(name_bytes).unwrap().inner.into(); assert_eq!(bytes, name); assert_eq!(HeaderName::from_bytes(name_bytes).unwrap(), std); // Test upper case let upper = name.to_uppercase(); let bytes: Bytes = HeaderName::from_bytes(upper.as_bytes()).unwrap().inner.into(); assert_eq!(bytes, name_bytes); assert_eq!(HeaderName::from_bytes(upper.as_bytes()).unwrap(), std); } } } } // Generate constants for all standard HTTP headers. This includes a static hash // code for the "fast hash" path. The hash code for static headers *do not* have // to match the text representation of those headers. This is because header // strings are always converted to the static values (when they match) before // being hashed. This means that it is impossible to compare the static hash // code of CONTENT_LENGTH with "content-length". standard_headers! { /// Advertises which content types the client is able to understand. /// /// The Accept request HTTP header advertises which content types, expressed /// as MIME types, the client is able to understand. Using content /// negotiation, the server then selects one of the proposals, uses it and /// informs the client of its choice with the Content-Type response header. /// Browsers set adequate values for this header depending of the context /// where the request is done: when fetching a CSS stylesheet a different /// value is set for the request than when fetching an image, video or a /// script. (Accept, ACCEPT, b"accept"); /// Advertises which character set the client is able to understand. /// /// The Accept-Charset request HTTP header advertises which character set /// the client is able to understand. Using content negotiation, the server /// then selects one of the proposals, uses it and informs the client of its /// choice within the Content-Type response header. Browsers usually don't /// set this header as the default value for each content type is usually /// correct and transmitting it would allow easier fingerprinting. /// /// If the server cannot serve any matching character set, it can /// theoretically send back a 406 (Not Acceptable) error code. But, for a /// better user experience, this is rarely done and the more common way is /// to ignore the Accept-Charset header in this case. (AcceptCharset, ACCEPT_CHARSET, b"accept-charset"); /// Advertises which content encoding the client is able to understand. /// /// The Accept-Encoding request HTTP header advertises which content /// encoding, usually a compression algorithm, the client is able to /// understand. Using content negotiation, the server selects one of the /// proposals, uses it and informs the client of its choice with the /// Content-Encoding response header. /// /// Even if both the client and the server supports the same compression /// algorithms, the server may choose not to compress the body of a /// response, if the identity value is also acceptable. Two common cases /// lead to this: /// /// * The data to be sent is already compressed and a second compression /// won't lead to smaller data to be transmitted. This may the case with /// some image formats; /// /// * The server is overloaded and cannot afford the computational overhead /// induced by the compression requirement. Typically, Microsoft recommends /// not to compress if a server use more than 80 % of its computational /// power. /// /// As long as the identity value, meaning no encryption, is not explicitly /// forbidden, by an identity;q=0 or a *;q=0 without another explicitly set /// value for identity, the server must never send back a 406 Not Acceptable /// error. (AcceptEncoding, ACCEPT_ENCODING, b"accept-encoding"); /// Advertises which languages the client is able to understand. /// /// The Accept-Language request HTTP header advertises which languages the /// client is able to understand, and which locale variant is preferred. /// Using content negotiation, the server then selects one of the proposals, /// uses it and informs the client of its choice with the Content-Language /// response header. Browsers set adequate values for this header according /// their user interface language and even if a user can change it, this /// happens rarely (and is frown upon as it leads to fingerprinting). /// /// This header is a hint to be used when the server has no way of /// determining the language via another way, like a specific URL, that is /// controlled by an explicit user decision. It is recommended that the /// server never overrides an explicit decision. The content of the /// Accept-Language is often out of the control of the user (like when /// traveling and using an Internet Cafe in a different country); the user /// may also want to visit a page in another language than the locale of /// their user interface. /// /// If the server cannot serve any matching language, it can theoretically /// send back a 406 (Not Acceptable) error code. But, for a better user /// experience, this is rarely done and more common way is to ignore the /// Accept-Language header in this case. (AcceptLanguage, ACCEPT_LANGUAGE, b"accept-language"); /// Marker used by the server to advertise partial request support. /// /// The Accept-Ranges response HTTP header is a marker used by the server to /// advertise its support of partial requests. The value of this field /// indicates the unit that can be used to define a range. /// /// In presence of an Accept-Ranges header, the browser may try to resume an /// interrupted download, rather than to start it from the start again. (AcceptRanges, ACCEPT_RANGES, b"accept-ranges"); /// Preflight response indicating if the response to the request can be /// exposed to the page. /// /// The Access-Control-Allow-Credentials response header indicates whether /// or not the response to the request can be exposed to the page. It can be /// exposed when the true value is returned; it can't in other cases. /// /// Credentials are cookies, authorization headers or TLS client /// certificates. /// /// When used as part of a response to a preflight request, this indicates /// whether or not the actual request can be made using credentials. Note /// that simple GET requests are not preflighted, and so if a request is /// made for a resource with credentials, if this header is not returned /// with the resource, the response is ignored by the browser and not /// returned to web content. /// /// The Access-Control-Allow-Credentials header works in conjunction with /// the XMLHttpRequest.withCredentials property or with the credentials /// option in the Request() constructor of the Fetch API. Credentials must /// be set on both sides (the Access-Control-Allow-Credentials header and in /// the XHR or Fetch request) in order for the CORS request with credentials /// to succeed. (AccessControlAllowCredentials, ACCESS_CONTROL_ALLOW_CREDENTIALS, b"access-control-allow-credentials"); /// Preflight response indicating permitted HTTP headers. /// /// The Access-Control-Allow-Headers response header is used in response to /// a preflight request to indicate which HTTP headers will be available via /// Access-Control-Expose-Headers when making the actual request. /// /// The simple headers, Accept, Accept-Language, Content-Language, /// Content-Type (but only with a MIME type of its parsed value (ignoring /// parameters) of either application/x-www-form-urlencoded, /// multipart/form-data, or text/plain), are always available and don't need /// to be listed by this header. /// /// This header is required if the request has an /// Access-Control-Request-Headers header. (AccessControlAllowHeaders, ACCESS_CONTROL_ALLOW_HEADERS, b"access-control-allow-headers"); /// Preflight header response indicating permitted access methods. /// /// The Access-Control-Allow-Methods response header specifies the method or /// methods allowed when accessing the resource in response to a preflight /// request. (AccessControlAllowMethods, ACCESS_CONTROL_ALLOW_METHODS, b"access-control-allow-methods"); /// Indicates whether the response can be shared with resources with the /// given origin. (AccessControlAllowOrigin, ACCESS_CONTROL_ALLOW_ORIGIN, b"access-control-allow-origin"); /// Indicates which headers can be exposed as part of the response by /// listing their names. (AccessControlExposeHeaders, ACCESS_CONTROL_EXPOSE_HEADERS, b"access-control-expose-headers"); /// Indicates how long the results of a preflight request can be cached. (AccessControlMaxAge, ACCESS_CONTROL_MAX_AGE, b"access-control-max-age"); /// Informs the server which HTTP headers will be used when an actual /// request is made. (AccessControlRequestHeaders, ACCESS_CONTROL_REQUEST_HEADERS, b"access-control-request-headers"); /// Informs the server know which HTTP method will be used when the actual /// request is made. (AccessControlRequestMethod, ACCESS_CONTROL_REQUEST_METHOD, b"access-control-request-method"); /// Indicates the time in seconds the object has been in a proxy cache. /// /// The Age header is usually close to zero. If it is Age: 0, it was /// probably just fetched from the origin server; otherwise It is usually /// calculated as a difference between the proxy's current date and the Date /// general header included in the HTTP response. (Age, AGE, b"age"); /// Lists the set of methods support by a resource. /// /// This header must be sent if the server responds with a 405 Method Not /// Allowed status code to indicate which request methods can be used. An /// empty Allow header indicates that the resource allows no request /// methods, which might occur temporarily for a given resource, for /// example. (Allow, ALLOW, b"allow"); /// Advertises the availability of alternate services to clients. (AltSvc, ALT_SVC, b"alt-svc"); /// Contains the credentials to authenticate a user agent with a server. /// /// Usually this header is included after the server has responded with a /// 401 Unauthorized status and the WWW-Authenticate header. (Authorization, AUTHORIZATION, b"authorization"); /// Specifies directives for caching mechanisms in both requests and /// responses. /// /// Caching directives are unidirectional, meaning that a given directive in /// a request is not implying that the same directive is to be given in the /// response. (CacheControl, CACHE_CONTROL, b"cache-control"); /// Indicates how caches have handled a response and its corresponding request. /// /// See [RFC 9211](https://www.rfc-editor.org/rfc/rfc9211.html). (CacheStatus, CACHE_STATUS, b"cache-status"); /// Specifies directives that allow origin servers to control the behavior of CDN caches /// interposed between them and clients separately from other caches that might handle the /// response. /// /// See [RFC 9213](https://www.rfc-editor.org/rfc/rfc9213.html). (CdnCacheControl, CDN_CACHE_CONTROL, b"cdn-cache-control"); /// Controls whether or not the network connection stays open after the /// current transaction finishes. /// /// If the value sent is keep-alive, the connection is persistent and not /// closed, allowing for subsequent requests to the same server to be done. /// /// Except for the standard hop-by-hop headers (Keep-Alive, /// Transfer-Encoding, TE, Connection, Trailer, Upgrade, Proxy-Authorization /// and Proxy-Authenticate), any hop-by-hop headers used by the message must /// be listed in the Connection header, so that the first proxy knows he has /// to consume them and not to forward them further. Standard hop-by-hop /// headers can be listed too (it is often the case of Keep-Alive, but this /// is not mandatory. (Connection, CONNECTION, b"connection"); /// Indicates if the content is expected to be displayed inline. /// /// In a regular HTTP response, the Content-Disposition response header is a /// header indicating if the content is expected to be displayed inline in /// the browser, that is, as a Web page or as part of a Web page, or as an /// attachment, that is downloaded and saved locally. /// /// In a multipart/form-data body, the HTTP Content-Disposition general /// header is a header that can be used on the subpart of a multipart body /// to give information about the field it applies to. The subpart is /// delimited by the boundary defined in the Content-Type header. Used on /// the body itself, Content-Disposition has no effect. /// /// The Content-Disposition header is defined in the larger context of MIME /// messages for e-mail, but only a subset of the possible parameters apply /// to HTTP forms and POST requests. Only the value form-data, as well as /// the optional directive name and filename, can be used in the HTTP /// context. (ContentDisposition, CONTENT_DISPOSITION, b"content-disposition"); /// Used to compress the media-type. /// /// When present, its value indicates what additional content encoding has /// been applied to the entity-body. It lets the client know, how to decode /// in order to obtain the media-type referenced by the Content-Type header. /// /// It is recommended to compress data as much as possible and therefore to /// use this field, but some types of resources, like jpeg images, are /// already compressed. Sometimes using additional compression doesn't /// reduce payload size and can even make the payload longer. (ContentEncoding, CONTENT_ENCODING, b"content-encoding"); /// Used to describe the languages intended for the audience. /// /// This header allows a user to differentiate according to the users' own /// preferred language. For example, if "Content-Language: de-DE" is set, it /// says that the document is intended for German language speakers /// (however, it doesn't indicate the document is written in German. For /// example, it might be written in English as part of a language course for /// German speakers). /// /// If no Content-Language is specified, the default is that the content is /// intended for all language audiences. Multiple language tags are also /// possible, as well as applying the Content-Language header to various /// media types and not only to textual documents. (ContentLanguage, CONTENT_LANGUAGE, b"content-language"); /// Indicates the size of the entity-body. /// /// The header value must be a decimal indicating the number of octets sent /// to the recipient. (ContentLength, CONTENT_LENGTH, b"content-length"); /// Indicates an alternate location for the returned data. /// /// The principal use case is to indicate the URL of the resource /// transmitted as the result of content negotiation. /// /// Location and Content-Location are different: Location indicates the /// target of a redirection (or the URL of a newly created document), while /// Content-Location indicates the direct URL to use to access the resource, /// without the need of further content negotiation. Location is a header /// associated with the response, while Content-Location is associated with /// the entity returned. (ContentLocation, CONTENT_LOCATION, b"content-location"); /// Indicates where in a full body message a partial message belongs. (ContentRange, CONTENT_RANGE, b"content-range"); /// Allows controlling resources the user agent is allowed to load for a /// given page. /// /// With a few exceptions, policies mostly involve specifying server origins /// and script endpoints. This helps guard against cross-site scripting /// attacks (XSS). (ContentSecurityPolicy, CONTENT_SECURITY_POLICY, b"content-security-policy"); /// Allows experimenting with policies by monitoring their effects. /// /// The HTTP Content-Security-Policy-Report-Only response header allows web /// developers to experiment with policies by monitoring (but not enforcing) /// their effects. These violation reports consist of JSON documents sent /// via an HTTP POST request to the specified URI. (ContentSecurityPolicyReportOnly, CONTENT_SECURITY_POLICY_REPORT_ONLY, b"content-security-policy-report-only"); /// Used to indicate the media type of the resource. /// /// In responses, a Content-Type header tells the client what the content /// type of the returned content actually is. Browsers will do MIME sniffing /// in some cases and will not necessarily follow the value of this header; /// to prevent this behavior, the header X-Content-Type-Options can be set /// to nosniff. /// /// In requests, (such as POST or PUT), the client tells the server what /// type of data is actually sent. (ContentType, CONTENT_TYPE, b"content-type"); /// Contains stored HTTP cookies previously sent by the server with the /// Set-Cookie header. /// /// The Cookie header might be omitted entirely, if the privacy setting of /// the browser are set to block them, for example. (Cookie, COOKIE, b"cookie"); /// Indicates the client's tracking preference. /// /// This header lets users indicate whether they would prefer privacy rather /// than personalized content. (Dnt, DNT, b"dnt"); /// Contains the date and time at which the message was originated. (Date, DATE, b"date"); /// Identifier for a specific version of a resource. /// /// This header allows caches to be more efficient, and saves bandwidth, as /// a web server does not need to send a full response if the content has /// not changed. On the other side, if the content has changed, etags are /// useful to help prevent simultaneous updates of a resource from /// overwriting each other ("mid-air collisions"). /// /// If the resource at a given URL changes, a new Etag value must be /// generated. Etags are therefore similar to fingerprints and might also be /// used for tracking purposes by some servers. A comparison of them allows /// to quickly determine whether two representations of a resource are the /// same, but they might also be set to persist indefinitely by a tracking /// server. (Etag, ETAG, b"etag"); /// Indicates expectations that need to be fulfilled by the server in order /// to properly handle the request. /// /// The only expectation defined in the specification is Expect: /// 100-continue, to which the server shall respond with: /// /// * 100 if the information contained in the header is sufficient to cause /// an immediate success, /// /// * 417 (Expectation Failed) if it cannot meet the expectation; or any /// other 4xx status otherwise. /// /// For example, the server may reject a request if its Content-Length is /// too large. /// /// No common browsers send the Expect header, but some other clients such /// as cURL do so by default. (Expect, EXPECT, b"expect"); /// Contains the date/time after which the response is considered stale. /// /// Invalid dates, like the value 0, represent a date in the past and mean /// that the resource is already expired. /// /// If there is a Cache-Control header with the "max-age" or "s-max-age" /// directive in the response, the Expires header is ignored. (Expires, EXPIRES, b"expires"); /// Contains information from the client-facing side of proxy servers that /// is altered or lost when a proxy is involved in the path of the request. /// /// The alternative and de-facto standard versions of this header are the /// X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Proto headers. /// /// This header is used for debugging, statistics, and generating /// location-dependent content and by design it exposes privacy sensitive /// information, such as the IP address of the client. Therefore the user's /// privacy must be kept in mind when deploying this header. (Forwarded, FORWARDED, b"forwarded"); /// Contains an Internet email address for a human user who controls the /// requesting user agent. /// /// If you are running a robotic user agent (e.g. a crawler), the From /// header should be sent, so you can be contacted if problems occur on /// servers, such as if the robot is sending excessive, unwanted, or invalid /// requests. (From, FROM, b"from"); /// Specifies the domain name of the server and (optionally) the TCP port /// number on which the server is listening. /// /// If no port is given, the default port for the service requested (e.g., /// "80" for an HTTP URL) is implied. /// /// A Host header field must be sent in all HTTP/1.1 request messages. A 400 /// (Bad Request) status code will be sent to any HTTP/1.1 request message /// that lacks a Host header field or contains more than one. (Host, HOST, b"host"); /// Makes a request conditional based on the E-Tag. /// /// For GET and HEAD methods, the server will send back the requested /// resource only if it matches one of the listed ETags. For PUT and other /// non-safe methods, it will only upload the resource in this case. /// /// The comparison with the stored ETag uses the strong comparison /// algorithm, meaning two files are considered identical byte to byte only. /// This is weakened when the W/ prefix is used in front of the ETag. /// /// There are two common use cases: /// /// * For GET and HEAD methods, used in combination with an Range header, it /// can guarantee that the new ranges requested comes from the same resource /// than the previous one. If it doesn't match, then a 416 (Range Not /// Satisfiable) response is returned. /// /// * For other methods, and in particular for PUT, If-Match can be used to /// prevent the lost update problem. It can check if the modification of a /// resource that the user wants to upload will not override another change /// that has been done since the original resource was fetched. If the /// request cannot be fulfilled, the 412 (Precondition Failed) response is /// returned. (IfMatch, IF_MATCH, b"if-match"); /// Makes a request conditional based on the modification date. /// /// The If-Modified-Since request HTTP header makes the request conditional: /// the server will send back the requested resource, with a 200 status, /// only if it has been last modified after the given date. If the request /// has not been modified since, the response will be a 304 without any /// body; the Last-Modified header will contain the date of last /// modification. Unlike If-Unmodified-Since, If-Modified-Since can only be /// used with a GET or HEAD. /// /// When used in combination with If-None-Match, it is ignored, unless the /// server doesn't support If-None-Match. /// /// The most common use case is to update a cached entity that has no /// associated ETag. (IfModifiedSince, IF_MODIFIED_SINCE, b"if-modified-since"); /// Makes a request conditional based on the E-Tag. /// /// The If-None-Match HTTP request header makes the request conditional. For /// GET and HEAD methods, the server will send back the requested resource, /// with a 200 status, only if it doesn't have an ETag matching the given /// ones. For other methods, the request will be processed only if the /// eventually existing resource's ETag doesn't match any of the values /// listed. /// /// When the condition fails for GET and HEAD methods, then the server must /// return HTTP status code 304 (Not Modified). For methods that apply /// server-side changes, the status code 412 (Precondition Failed) is used. /// Note that the server generating a 304 response MUST generate any of the /// following header fields that would have been sent in a 200 (OK) response /// to the same request: Cache-Control, Content-Location, Date, ETag, /// Expires, and Vary. /// /// The comparison with the stored ETag uses the weak comparison algorithm, /// meaning two files are considered identical not only if they are /// identical byte to byte, but if the content is equivalent. For example, /// two pages that would differ only by the date of generation in the footer /// would be considered as identical. /// /// When used in combination with If-Modified-Since, it has precedence (if /// the server supports it). /// /// There are two common use cases: /// /// * For `GET` and `HEAD` methods, to update a cached entity that has an associated ETag. /// * For other methods, and in particular for `PUT`, `If-None-Match` used with /// the `*` value can be used to save a file not known to exist, /// guaranteeing that another upload didn't happen before, losing the data /// of the previous put; this problems is the variation of the lost update /// problem. (IfNoneMatch, IF_NONE_MATCH, b"if-none-match"); /// Makes a request conditional based on range. /// /// The If-Range HTTP request header makes a range request conditional: if /// the condition is fulfilled, the range request will be issued and the /// server sends back a 206 Partial Content answer with the appropriate /// body. If the condition is not fulfilled, the full resource is sent back, /// with a 200 OK status. /// /// This header can be used either with a Last-Modified validator, or with /// an ETag, but not with both. /// /// The most common use case is to resume a download, to guarantee that the /// stored resource has not been modified since the last fragment has been /// received. (IfRange, IF_RANGE, b"if-range"); /// Makes the request conditional based on the last modification date. /// /// The If-Unmodified-Since request HTTP header makes the request /// conditional: the server will send back the requested resource, or accept /// it in the case of a POST or another non-safe method, only if it has not /// been last modified after the given date. If the request has been /// modified after the given date, the response will be a 412 (Precondition /// Failed) error. /// /// There are two common use cases: /// /// * In conjunction non-safe methods, like POST, it can be used to /// implement an optimistic concurrency control, like done by some wikis: /// editions are rejected if the stored document has been modified since the /// original has been retrieved. /// /// * In conjunction with a range request with a If-Range header, it can be /// used to ensure that the new fragment requested comes from an unmodified /// document. (IfUnmodifiedSince, IF_UNMODIFIED_SINCE, b"if-unmodified-since"); /// The Last-Modified header contains the date and time when the origin believes /// the resource was last modified. /// /// The value is a valid Date/Time string defined in [RFC9910](https://datatracker.ietf.org/doc/html/rfc9110#section-5.6.7) (LastModified, LAST_MODIFIED, b"last-modified"); /// Allows the server to point an interested client to another resource /// containing metadata about the requested resource. (Link, LINK, b"link"); /// Indicates the URL to redirect a page to. /// /// The Location response header indicates the URL to redirect a page to. It /// only provides a meaning when served with a 3xx status response. /// /// The HTTP method used to make the new request to fetch the page pointed /// to by Location depends of the original method and of the kind of /// redirection: /// /// * If 303 (See Also) responses always lead to the use of a GET method, /// 307 (Temporary Redirect) and 308 (Permanent Redirect) don't change the /// method used in the original request; /// /// * 301 (Permanent Redirect) and 302 (Found) doesn't change the method /// most of the time, though older user-agents may (so you basically don't /// know). /// /// All responses with one of these status codes send a Location header. /// /// Beside redirect response, messages with 201 (Created) status also /// include the Location header. It indicates the URL to the newly created /// resource. /// /// Location and Content-Location are different: Location indicates the /// target of a redirection (or the URL of a newly created resource), while /// Content-Location indicates the direct URL to use to access the resource /// when content negotiation happened, without the need of further content /// negotiation. Location is a header associated with the response, while /// Content-Location is associated with the entity returned. (Location, LOCATION, b"location"); /// Indicates the max number of intermediaries the request should be sent /// through. (MaxForwards, MAX_FORWARDS, b"max-forwards"); /// Indicates where a fetch originates from. /// /// It doesn't include any path information, but only the server name. It is /// sent with CORS requests, as well as with POST requests. It is similar to /// the Referer header, but, unlike this header, it doesn't disclose the /// whole path. (Origin, ORIGIN, b"origin"); /// HTTP/1.0 header usually used for backwards compatibility. /// /// The Pragma HTTP/1.0 general header is an implementation-specific header /// that may have various effects along the request-response chain. It is /// used for backwards compatibility with HTTP/1.0 caches where the /// Cache-Control HTTP/1.1 header is not yet present. (Pragma, PRAGMA, b"pragma"); /// Defines the authentication method that should be used to gain access to /// a proxy. /// /// Unlike `www-authenticate`, the `proxy-authenticate` header field applies /// only to the next outbound client on the response chain. This is because /// only the client that chose a given proxy is likely to have the /// credentials necessary for authentication. However, when multiple proxies /// are used within the same administrative domain, such as office and /// regional caching proxies within a large corporate network, it is common /// for credentials to be generated by the user agent and passed through the /// hierarchy until consumed. Hence, in such a configuration, it will appear /// as if Proxy-Authenticate is being forwarded because each proxy will send /// the same challenge set. /// /// The `proxy-authenticate` header is sent along with a `407 Proxy /// Authentication Required`. (ProxyAuthenticate, PROXY_AUTHENTICATE, b"proxy-authenticate"); /// Contains the credentials to authenticate a user agent to a proxy server. /// /// This header is usually included after the server has responded with a /// 407 Proxy Authentication Required status and the Proxy-Authenticate /// header. (ProxyAuthorization, PROXY_AUTHORIZATION, b"proxy-authorization"); /// Associates a specific cryptographic public key with a certain server. /// /// This decreases the risk of MITM attacks with forged certificates. If one /// or several keys are pinned and none of them are used by the server, the /// browser will not accept the response as legitimate, and will not display /// it. (PublicKeyPins, PUBLIC_KEY_PINS, b"public-key-pins"); /// Sends reports of pinning violation to the report-uri specified in the /// header. /// /// Unlike `Public-Key-Pins`, this header still allows browsers to connect /// to the server if the pinning is violated. (PublicKeyPinsReportOnly, PUBLIC_KEY_PINS_REPORT_ONLY, b"public-key-pins-report-only"); /// Indicates the part of a document that the server should return. /// /// Several parts can be requested with one Range header at once, and the /// server may send back these ranges in a multipart document. If the server /// sends back ranges, it uses the 206 Partial Content for the response. If /// the ranges are invalid, the server returns the 416 Range Not Satisfiable /// error. The server can also ignore the Range header and return the whole /// document with a 200 status code. (Range, RANGE, b"range"); /// Contains the address of the previous web page from which a link to the /// currently requested page was followed. /// /// The Referer header allows servers to identify where people are visiting /// them from and may use that data for analytics, logging, or optimized /// caching, for example. (Referer, REFERER, b"referer"); /// Governs which referrer information should be included with requests /// made. (ReferrerPolicy, REFERRER_POLICY, b"referrer-policy"); /// Informs the web browser that the current page or frame should be /// refreshed. (Refresh, REFRESH, b"refresh"); /// The Retry-After response HTTP header indicates how long the user agent /// should wait before making a follow-up request. There are two main cases /// this header is used: /// /// * When sent with a 503 (Service Unavailable) response, it indicates how /// long the service is expected to be unavailable. /// /// * When sent with a redirect response, such as 301 (Moved Permanently), /// it indicates the minimum time that the user agent is asked to wait /// before issuing the redirected request. (RetryAfter, RETRY_AFTER, b"retry-after"); /// The |Sec-WebSocket-Accept| header field is used in the WebSocket /// opening handshake. It is sent from the server to the client to /// confirm that the server is willing to initiate the WebSocket /// connection. (SecWebSocketAccept, SEC_WEBSOCKET_ACCEPT, b"sec-websocket-accept"); /// The |Sec-WebSocket-Extensions| header field is used in the WebSocket /// opening handshake. It is initially sent from the client to the /// server, and then subsequently sent from the server to the client, to /// agree on a set of protocol-level extensions to use for the duration /// of the connection. (SecWebSocketExtensions, SEC_WEBSOCKET_EXTENSIONS, b"sec-websocket-extensions"); /// The |Sec-WebSocket-Key| header field is used in the WebSocket opening /// handshake. It is sent from the client to the server to provide part /// of the information used by the server to prove that it received a /// valid WebSocket opening handshake. This helps ensure that the server /// does not accept connections from non-WebSocket clients (e.g., HTTP /// clients) that are being abused to send data to unsuspecting WebSocket /// servers. (SecWebSocketKey, SEC_WEBSOCKET_KEY, b"sec-websocket-key"); /// The |Sec-WebSocket-Protocol| header field is used in the WebSocket /// opening handshake. It is sent from the client to the server and back /// from the server to the client to confirm the subprotocol of the /// connection. This enables scripts to both select a subprotocol and be /// sure that the server agreed to serve that subprotocol. (SecWebSocketProtocol, SEC_WEBSOCKET_PROTOCOL, b"sec-websocket-protocol"); /// The |Sec-WebSocket-Version| header field is used in the WebSocket /// opening handshake. It is sent from the client to the server to /// indicate the protocol version of the connection. This enables /// servers to correctly interpret the opening handshake and subsequent /// data being sent from the data, and close the connection if the server /// cannot interpret that data in a safe manner. (SecWebSocketVersion, SEC_WEBSOCKET_VERSION, b"sec-websocket-version"); /// Contains information about the software used by the origin server to /// handle the request. /// /// Overly long and detailed Server values should be avoided as they /// potentially reveal internal implementation details that might make it /// (slightly) easier for attackers to find and exploit known security /// holes. (Server, SERVER, b"server"); /// Used to send cookies from the server to the user agent. (SetCookie, SET_COOKIE, b"set-cookie"); /// Tells the client to communicate with HTTPS instead of using HTTP. (StrictTransportSecurity, STRICT_TRANSPORT_SECURITY, b"strict-transport-security"); /// Informs the server of transfer encodings willing to be accepted as part /// of the response. /// /// See also the Transfer-Encoding response header for more details on /// transfer encodings. Note that chunked is always acceptable for HTTP/1.1 /// recipients and you that don't have to specify "chunked" using the TE /// header. However, it is useful for setting if the client is accepting /// trailer fields in a chunked transfer coding using the "trailers" value. (Te, TE, b"te"); /// Allows the sender to include additional fields at the end of chunked /// messages. (Trailer, TRAILER, b"trailer"); /// Specifies the form of encoding used to safely transfer the entity to the /// client. /// /// `transfer-encoding` is a hop-by-hop header, that is applying to a /// message between two nodes, not to a resource itself. Each segment of a /// multi-node connection can use different `transfer-encoding` values. If /// you want to compress data over the whole connection, use the end-to-end /// header `content-encoding` header instead. /// /// When present on a response to a `HEAD` request that has no body, it /// indicates the value that would have applied to the corresponding `GET` /// message. (TransferEncoding, TRANSFER_ENCODING, b"transfer-encoding"); /// Contains a string that allows identifying the requesting client's /// software. (UserAgent, USER_AGENT, b"user-agent"); /// Used as part of the exchange to upgrade the protocol. (Upgrade, UPGRADE, b"upgrade"); /// Sends a signal to the server expressing the client’s preference for an /// encrypted and authenticated response. (UpgradeInsecureRequests, UPGRADE_INSECURE_REQUESTS, b"upgrade-insecure-requests"); /// Determines how to match future requests with cached responses. /// /// The `vary` HTTP response header determines how to match future request /// headers to decide whether a cached response can be used rather than /// requesting a fresh one from the origin server. It is used by the server /// to indicate which headers it used when selecting a representation of a /// resource in a content negotiation algorithm. /// /// The `vary` header should be set on a 304 Not Modified response exactly /// like it would have been set on an equivalent 200 OK response. (Vary, VARY, b"vary"); /// Added by proxies to track routing. /// /// The `via` general header is added by proxies, both forward and reverse /// proxies, and can appear in the request headers and the response headers. /// It is used for tracking message forwards, avoiding request loops, and /// identifying the protocol capabilities of senders along the /// request/response chain. (Via, VIA, b"via"); /// General HTTP header contains information about possible problems with /// the status of the message. /// /// More than one `warning` header may appear in a response. Warning header /// fields can in general be applied to any message, however some warn-codes /// are specific to caches and can only be applied to response messages. (Warning, WARNING, b"warning"); /// Defines the authentication method that should be used to gain access to /// a resource. (WwwAuthenticate, WWW_AUTHENTICATE, b"www-authenticate"); /// Marker used by the server to indicate that the MIME types advertised in /// the `content-type` headers should not be changed and be followed. /// /// This allows to opt-out of MIME type sniffing, or, in other words, it is /// a way to say that the webmasters knew what they were doing. /// /// This header was introduced by Microsoft in IE 8 as a way for webmasters /// to block content sniffing that was happening and could transform /// non-executable MIME types into executable MIME types. Since then, other /// browsers have introduced it, even if their MIME sniffing algorithms were /// less aggressive. /// /// Site security testers usually expect this header to be set. (XContentTypeOptions, X_CONTENT_TYPE_OPTIONS, b"x-content-type-options"); /// Controls DNS prefetching. /// /// The `x-dns-prefetch-control` HTTP response header controls DNS /// prefetching, a feature by which browsers proactively perform domain name /// resolution on both links that the user may choose to follow as well as /// URLs for items referenced by the document, including images, CSS, /// JavaScript, and so forth. /// /// This prefetching is performed in the background, so that the DNS is /// likely to have been resolved by the time the referenced items are /// needed. This reduces latency when the user clicks a link. (XDnsPrefetchControl, X_DNS_PREFETCH_CONTROL, b"x-dns-prefetch-control"); /// Indicates whether or not a browser should be allowed to render a page in /// a frame. /// /// Sites can use this to avoid clickjacking attacks, by ensuring that their /// content is not embedded into other sites. /// /// The added security is only provided if the user accessing the document /// is using a browser supporting `x-frame-options`. (XFrameOptions, X_FRAME_OPTIONS, b"x-frame-options"); /// Stop pages from loading when an XSS attack is detected. /// /// The HTTP X-XSS-Protection response header is a feature of Internet /// Explorer, Chrome and Safari that stops pages from loading when they /// detect reflected cross-site scripting (XSS) attacks. Although these /// protections are largely unnecessary in modern browsers when sites /// implement a strong Content-Security-Policy that disables the use of /// inline JavaScript ('unsafe-inline'), they can still provide protections /// for users of older web browsers that don't yet support CSP. (XXssProtection, X_XSS_PROTECTION, b"x-xss-protection"); } /// Valid header name characters /// /// ```not_rust /// field-name = token /// separators = "(" | ")" | "<" | ">" | "@" /// | "," | ";" | ":" | "\" | <"> /// | "/" | "[" | "]" | "?" | "=" /// | "{" | "}" | SP | HT /// token = 1*tchar /// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" /// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" /// / DIGIT / ALPHA /// ; any VCHAR, except delimiters /// ``` // HEADER_CHARS maps every byte that is 128 or larger to 0 so everything that is // mapped by HEADER_CHARS, maps to a valid single-byte UTF-8 codepoint. #[rustfmt::skip] const HEADER_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, b'!', 0, b'#', b'$', b'%', b'&', b'\'', // 3x 0, 0, b'*', 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', 0, 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, 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', 0, b'|', 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 ]; /// Valid header name characters for HTTP/2.0 and HTTP/3.0 // HEADER_CHARS_H2 maps every byte that is 128 or larger to 0 so everything that is // mapped by HEADER_CHARS_H2, maps to a valid single-byte UTF-8 codepoint. #[rustfmt::skip] const HEADER_CHARS_H2: [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, b'!', b'"', b'#', b'$', b'%', b'&', b'\'', // 3x 0, 0, b'*', 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', 0, 0, // 5x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x 0, 0, 0, 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', 0, b'|', 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 ]; fn parse_hdr<'a>( data: &'a [u8], b: &'a mut [MaybeUninit; SCRATCH_BUF_SIZE], table: &[u8; 256], ) -> Result, InvalidHeaderName> { match data.len() { 0 => Err(InvalidHeaderName::new()), len @ 1..=SCRATCH_BUF_SIZE => { // Read from data into the buffer - transforming using `table` as we go data.iter() .zip(b.iter_mut()) .for_each(|(index, out)| *out = MaybeUninit::new(table[*index as usize])); // Safety: len bytes of b were just initialized. let name: &'a [u8] = unsafe { slice_assume_init(&b[0..len]) }; match StandardHeader::from_bytes(name) { Some(sh) => Ok(sh.into()), None => { if name.contains(&0) { Err(InvalidHeaderName::new()) } else { Ok(HdrName::custom(name, true)) } } } } SCRATCH_BUF_OVERFLOW..=super::MAX_HEADER_NAME_LEN => Ok(HdrName::custom(data, false)), _ => Err(InvalidHeaderName::new()), } } impl<'a> From for HdrName<'a> { fn from(hdr: StandardHeader) -> HdrName<'a> { HdrName { inner: Repr::Standard(hdr), } } } impl HeaderName { /// Converts a slice of bytes to an HTTP header name. /// /// This function normalizes the input. pub fn from_bytes(src: &[u8]) -> Result { let mut buf = uninit_u8_array(); // Precondition: HEADER_CHARS is a valid table for parse_hdr(). match parse_hdr(src, &mut buf, &HEADER_CHARS)?.inner { Repr::Standard(std) => Ok(std.into()), Repr::Custom(MaybeLower { buf, lower: true }) => { let buf = Bytes::copy_from_slice(buf); // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; Ok(Custom(val).into()) } Repr::Custom(MaybeLower { buf, lower: false }) => { use bytes::BufMut; let mut dst = BytesMut::with_capacity(buf.len()); for b in buf.iter() { // HEADER_CHARS maps all bytes to valid single-byte UTF-8 let b = HEADER_CHARS[*b as usize]; if b == 0 { return Err(InvalidHeaderName::new()); } dst.put_u8(b); } // Safety: the loop above maps all bytes in buf to valid single byte // UTF-8 before copying them into dst. This means that dst (and hence // dst.freeze()) is valid UTF-8. let val = unsafe { ByteStr::from_utf8_unchecked(dst.freeze()) }; Ok(Custom(val).into()) } } } /// Converts a slice of bytes to an HTTP header name. /// /// This function expects the input to only contain lowercase characters. /// This is useful when decoding HTTP/2.0 or HTTP/3.0 headers. Both /// require that all headers be represented in lower case. /// /// # Examples /// /// ``` /// # use http::header::*; /// /// // Parsing a lower case header /// let hdr = HeaderName::from_lowercase(b"content-length").unwrap(); /// assert_eq!(CONTENT_LENGTH, hdr); /// /// // Parsing a header that contains uppercase characters /// assert!(HeaderName::from_lowercase(b"Content-Length").is_err()); /// ``` pub fn from_lowercase(src: &[u8]) -> Result { let mut buf = uninit_u8_array(); // Precondition: HEADER_CHARS_H2 is a valid table for parse_hdr() match parse_hdr(src, &mut buf, &HEADER_CHARS_H2)?.inner { Repr::Standard(std) => Ok(std.into()), Repr::Custom(MaybeLower { buf, lower: true }) => { let buf = Bytes::copy_from_slice(buf); // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; Ok(Custom(val).into()) } Repr::Custom(MaybeLower { buf, lower: false }) => { for &b in buf.iter() { // HEADER_CHARS_H2 maps all bytes that are not valid single-byte // UTF-8 to 0 so this check returns an error for invalid UTF-8. if HEADER_CHARS_H2[b as usize] == 0 { return Err(InvalidHeaderName::new()); } } let buf = Bytes::copy_from_slice(buf); // Safety: the loop above checks that each byte of buf (either // version) is valid UTF-8. let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; Ok(Custom(val).into()) } } } /// Converts a static string to a HTTP header name. /// /// This function requires the static string to only contain lowercase /// characters, numerals and symbols, as per the HTTP/2.0 specification /// and header names internal representation within this library. /// /// # Panics /// /// This function panics when the static string is a invalid header. /// /// 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/name.rs:1241:13 /// | /// 1241 | ([] as [u8; 0])[0]; // Invalid header name /// | ^^^^^^^^^^^^^^^^^^ /// | | /// | index out of bounds: the length is 0 but the index is 0 /// | inside `http::HeaderName::from_static` at http/src/header/name.rs:1241:13 /// | inside `INVALID_NAME` at src/main.rs:3:34 /// | /// ::: src/main.rs:3:1 /// | /// 3 | const INVALID_NAME: HeaderName = HeaderName::from_static("Capitalized"); /// | ------------------------------------------------------------------------ /// ``` /// /// # Examples /// /// ``` /// # use http::header::*; /// // Parsing a standard header /// let hdr = HeaderName::from_static("content-length"); /// assert_eq!(CONTENT_LENGTH, hdr); /// /// // Parsing a custom header /// let CUSTOM_HEADER: &'static str = "custom-header"; /// /// let a = HeaderName::from_lowercase(b"custom-header").unwrap(); /// let b = HeaderName::from_static(CUSTOM_HEADER); /// assert_eq!(a, b); /// ``` /// /// ```should_panic /// # use http::header::*; /// # /// // Parsing a header that contains invalid symbols(s): /// HeaderName::from_static("content{}{}length"); // This line panics! /// /// // Parsing a header that contains invalid uppercase characters. /// let a = HeaderName::from_static("foobar"); /// let b = HeaderName::from_static("FOOBAR"); // This line panics! /// ``` #[allow(unconditional_panic)] // required for the panic circumvention pub const fn from_static(src: &'static str) -> HeaderName { let name_bytes = src.as_bytes(); if let Some(standard) = StandardHeader::from_bytes(name_bytes) { return HeaderName { inner: Repr::Standard(standard), }; } if name_bytes.is_empty() || name_bytes.len() > super::MAX_HEADER_NAME_LEN || { let mut i = 0; loop { if i >= name_bytes.len() { break false; } else if HEADER_CHARS_H2[name_bytes[i] as usize] == 0 { break true; } i += 1; } } { // TODO: When msrv is bumped to larger than 1.57, this should be // replaced with `panic!` macro. // https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html#panic-in-const-contexts // // See the panics section of this method's document for details. #[allow(clippy::no_effect, clippy::out_of_bounds_indexing)] ([] as [u8; 0])[0]; // Invalid header name } HeaderName { inner: Repr::Custom(Custom(ByteStr::from_static(src))), } } /// Returns a `str` representation of the header. /// /// The returned string will always be lower case. #[inline] pub fn as_str(&self) -> &str { match self.inner { Repr::Standard(v) => v.as_str(), Repr::Custom(ref v) => &v.0, } } pub(super) fn into_bytes(self) -> Bytes { self.inner.into() } } impl FromStr for HeaderName { type Err = InvalidHeaderName; fn from_str(s: &str) -> Result { HeaderName::from_bytes(s.as_bytes()).map_err(|_| InvalidHeaderName { _priv: () }) } } impl AsRef for HeaderName { fn as_ref(&self) -> &str { self.as_str() } } impl AsRef<[u8]> for HeaderName { fn as_ref(&self) -> &[u8] { self.as_str().as_bytes() } } impl Borrow for HeaderName { fn borrow(&self) -> &str { self.as_str() } } impl fmt::Debug for HeaderName { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.as_str(), fmt) } } impl fmt::Display for HeaderName { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self.as_str(), fmt) } } impl InvalidHeaderName { pub(super) fn new() -> InvalidHeaderName { InvalidHeaderName { _priv: () } } } impl<'a> From<&'a HeaderName> for HeaderName { fn from(src: &'a HeaderName) -> HeaderName { src.clone() } } #[doc(hidden)] impl From> for Bytes where T: Into, { fn from(repr: Repr) -> Bytes { match repr { Repr::Standard(header) => Bytes::from_static(header.as_str().as_bytes()), Repr::Custom(header) => header.into(), } } } impl From for Bytes { #[inline] fn from(Custom(inner): Custom) -> Bytes { Bytes::from(inner) } } impl<'a> TryFrom<&'a str> for HeaderName { type Error = InvalidHeaderName; #[inline] fn try_from(s: &'a str) -> Result { Self::from_bytes(s.as_bytes()) } } impl<'a> TryFrom<&'a String> for HeaderName { type Error = InvalidHeaderName; #[inline] fn try_from(s: &'a String) -> Result { Self::from_bytes(s.as_bytes()) } } impl<'a> TryFrom<&'a [u8]> for HeaderName { type Error = InvalidHeaderName; #[inline] fn try_from(s: &'a [u8]) -> Result { Self::from_bytes(s) } } impl TryFrom for HeaderName { type Error = InvalidHeaderName; #[inline] fn try_from(s: String) -> Result { Self::from_bytes(s.as_bytes()) } } impl TryFrom> for HeaderName { type Error = InvalidHeaderName; #[inline] fn try_from(vec: Vec) -> Result { Self::from_bytes(&vec) } } #[doc(hidden)] impl From for HeaderName { fn from(src: StandardHeader) -> HeaderName { HeaderName { inner: Repr::Standard(src), } } } #[doc(hidden)] impl From for HeaderName { fn from(src: Custom) -> HeaderName { HeaderName { inner: Repr::Custom(src), } } } impl<'a> PartialEq<&'a HeaderName> for HeaderName { #[inline] fn eq(&self, other: &&'a HeaderName) -> bool { *self == **other } } impl<'a> PartialEq for &'a HeaderName { #[inline] fn eq(&self, other: &HeaderName) -> bool { *other == *self } } impl PartialEq for HeaderName { /// Performs a case-insensitive comparison of the string against the header /// name /// /// # Examples /// /// ``` /// use http::header::CONTENT_LENGTH; /// /// assert_eq!(CONTENT_LENGTH, "content-length"); /// assert_eq!(CONTENT_LENGTH, "Content-Length"); /// assert_ne!(CONTENT_LENGTH, "content length"); /// ``` #[inline] fn eq(&self, other: &str) -> bool { eq_ignore_ascii_case(self.as_ref(), other.as_bytes()) } } impl PartialEq for str { /// Performs a case-insensitive comparison of the string against the header /// name /// /// # Examples /// /// ``` /// use http::header::CONTENT_LENGTH; /// /// assert_eq!(CONTENT_LENGTH, "content-length"); /// assert_eq!(CONTENT_LENGTH, "Content-Length"); /// assert_ne!(CONTENT_LENGTH, "content length"); /// ``` #[inline] fn eq(&self, other: &HeaderName) -> bool { *other == *self } } impl<'a> PartialEq<&'a str> for HeaderName { /// Performs a case-insensitive comparison of the string against the header /// name #[inline] fn eq(&self, other: &&'a str) -> bool { *self == **other } } impl<'a> PartialEq for &'a str { /// Performs a case-insensitive comparison of the string against the header /// name #[inline] fn eq(&self, other: &HeaderName) -> bool { *other == *self } } impl fmt::Debug for InvalidHeaderName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("InvalidHeaderName") // skip _priv noise .finish() } } impl fmt::Display for InvalidHeaderName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("invalid HTTP header name") } } impl Error for InvalidHeaderName {} // ===== HdrName ===== impl<'a> HdrName<'a> { // Precondition: if lower then buf is valid UTF-8 fn custom(buf: &'a [u8], lower: bool) -> HdrName<'a> { HdrName { // Invariant (on MaybeLower): follows from the precondition inner: Repr::Custom(MaybeLower { buf, lower }), } } pub fn from_bytes(hdr: &[u8], f: F) -> Result where F: FnOnce(HdrName<'_>) -> U, { let mut buf = uninit_u8_array(); // Precondition: HEADER_CHARS is a valid table for parse_hdr(). let hdr = parse_hdr(hdr, &mut buf, &HEADER_CHARS)?; Ok(f(hdr)) } pub fn from_static(hdr: &'static str, f: F) -> U where F: FnOnce(HdrName<'_>) -> U, { let mut buf = uninit_u8_array(); let hdr = // Precondition: HEADER_CHARS is a valid table for parse_hdr(). parse_hdr(hdr.as_bytes(), &mut buf, &HEADER_CHARS).expect("static str is invalid name"); f(hdr) } } #[doc(hidden)] impl<'a> From> for HeaderName { fn from(src: HdrName<'a>) -> HeaderName { match src.inner { Repr::Standard(s) => HeaderName { inner: Repr::Standard(s), }, Repr::Custom(maybe_lower) => { if maybe_lower.lower { let buf = Bytes::copy_from_slice(maybe_lower.buf); // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. let byte_str = unsafe { ByteStr::from_utf8_unchecked(buf) }; HeaderName { inner: Repr::Custom(Custom(byte_str)), } } else { use bytes::BufMut; let mut dst = BytesMut::with_capacity(maybe_lower.buf.len()); for b in maybe_lower.buf.iter() { // HEADER_CHARS maps each byte to a valid single-byte UTF-8 // codepoint. dst.put_u8(HEADER_CHARS[*b as usize]); } // Safety: the loop above maps each byte of maybe_lower.buf to a // valid single-byte UTF-8 codepoint before copying it into dst. // dst (and hence dst.freeze()) is thus valid UTF-8. let buf = unsafe { ByteStr::from_utf8_unchecked(dst.freeze()) }; HeaderName { inner: Repr::Custom(Custom(buf)), } } } } } } #[doc(hidden)] impl<'a> PartialEq> for HeaderName { #[inline] fn eq(&self, other: &HdrName<'a>) -> bool { match self.inner { Repr::Standard(a) => match other.inner { Repr::Standard(b) => a == b, _ => false, }, Repr::Custom(Custom(ref a)) => match other.inner { Repr::Custom(ref b) => { if b.lower { a.as_bytes() == b.buf } else { eq_ignore_ascii_case(a.as_bytes(), b.buf) } } _ => false, }, } } } // ===== Custom ===== impl Hash for Custom { #[inline] fn hash(&self, hasher: &mut H) { hasher.write(self.0.as_bytes()) } } // ===== MaybeLower ===== impl<'a> Hash for MaybeLower<'a> { #[inline] fn hash(&self, hasher: &mut H) { if self.lower { hasher.write(self.buf); } else { for &b in self.buf { hasher.write(&[HEADER_CHARS[b as usize]]); } } } } // Assumes that the left hand side is already lower case #[inline] fn eq_ignore_ascii_case(lower: &[u8], s: &[u8]) -> bool { if lower.len() != s.len() { return false; } lower .iter() .zip(s) .all(|(a, b)| *a == HEADER_CHARS[*b as usize]) } // Utility functions for MaybeUninit<>. These are drawn from unstable API's on // MaybeUninit<> itself. const SCRATCH_BUF_SIZE: usize = 64; const SCRATCH_BUF_OVERFLOW: usize = SCRATCH_BUF_SIZE + 1; fn uninit_u8_array() -> [MaybeUninit; SCRATCH_BUF_SIZE] { let arr = MaybeUninit::<[MaybeUninit; SCRATCH_BUF_SIZE]>::uninit(); // Safety: assume_init() is claiming that an array of MaybeUninit<> // has been initialized, but MaybeUninit<>'s do not require initialization. unsafe { arr.assume_init() } } // Assuming all the elements are initialized, get a slice of them. // // Safety: All elements of `slice` must be initialized to prevent // undefined behavior. unsafe fn slice_assume_init(slice: &[MaybeUninit]) -> &[T] { &*(slice as *const [MaybeUninit] as *const [T]) } #[cfg(test)] mod tests { use self::StandardHeader::Vary; use super::*; #[test] fn test_bounds() { fn check_bounds() {} check_bounds::(); } #[test] fn test_parse_invalid_headers() { for i in 0..128 { let hdr = vec![1u8; i]; assert!( HeaderName::from_bytes(&hdr).is_err(), "{} invalid header chars did not fail", i ); } } const ONE_TOO_LONG: &[u8] = &[b'a'; super::super::MAX_HEADER_NAME_LEN + 1]; #[test] fn test_invalid_name_lengths() { assert!( HeaderName::from_bytes(&[]).is_err(), "zero-length header name is an error", ); let long = &ONE_TOO_LONG[0..super::super::MAX_HEADER_NAME_LEN]; let long_str = std::str::from_utf8(long).unwrap(); assert_eq!(HeaderName::from_static(long_str), long_str); // shouldn't panic! assert!( HeaderName::from_bytes(long).is_ok(), "max header name length is ok", ); assert!( HeaderName::from_bytes(ONE_TOO_LONG).is_err(), "longer than max header name length is an error", ); } #[test] #[should_panic] fn test_static_invalid_name_lengths() { // Safety: ONE_TOO_LONG contains only the UTF-8 safe, single-byte codepoint b'a'. let _ = HeaderName::from_static(unsafe { std::str::from_utf8_unchecked(ONE_TOO_LONG) }); } #[test] fn test_from_hdr_name() { use self::StandardHeader::Vary; let name = HeaderName::from(HdrName { inner: Repr::Standard(Vary), }); assert_eq!(name.inner, Repr::Standard(Vary)); let name = HeaderName::from(HdrName { inner: Repr::Custom(MaybeLower { buf: b"hello-world", lower: true, }), }); assert_eq!( name.inner, Repr::Custom(Custom(ByteStr::from_static("hello-world"))) ); let name = HeaderName::from(HdrName { inner: Repr::Custom(MaybeLower { buf: b"Hello-World", lower: false, }), }); assert_eq!( name.inner, Repr::Custom(Custom(ByteStr::from_static("hello-world"))) ); } #[test] fn test_eq_hdr_name() { use self::StandardHeader::Vary; let a = HeaderName { inner: Repr::Standard(Vary), }; let b = HdrName { inner: Repr::Standard(Vary), }; assert_eq!(a, b); let a = HeaderName { inner: Repr::Custom(Custom(ByteStr::from_static("vaary"))), }; assert_ne!(a, b); let b = HdrName { inner: Repr::Custom(MaybeLower { buf: b"vaary", lower: true, }), }; assert_eq!(a, b); let b = HdrName { inner: Repr::Custom(MaybeLower { buf: b"vaary", lower: false, }), }; assert_eq!(a, b); let b = HdrName { inner: Repr::Custom(MaybeLower { buf: b"VAARY", lower: false, }), }; assert_eq!(a, b); let a = HeaderName { inner: Repr::Standard(Vary), }; assert_ne!(a, b); } #[test] fn test_from_static_std() { let a = HeaderName { inner: Repr::Standard(Vary), }; let b = HeaderName::from_static("vary"); assert_eq!(a, b); let b = HeaderName::from_static("vaary"); assert_ne!(a, b); } #[test] #[should_panic] fn test_from_static_std_uppercase() { HeaderName::from_static("Vary"); } #[test] #[should_panic] fn test_from_static_std_symbol() { HeaderName::from_static("vary{}"); } // MaybeLower { lower: true } #[test] fn test_from_static_custom_short() { let a = HeaderName { inner: Repr::Custom(Custom(ByteStr::from_static("customheader"))), }; let b = HeaderName::from_static("customheader"); assert_eq!(a, b); } #[test] #[should_panic] fn test_from_static_custom_short_uppercase() { HeaderName::from_static("custom header"); } #[test] #[should_panic] fn test_from_static_custom_short_symbol() { HeaderName::from_static("CustomHeader"); } // MaybeLower { lower: false } #[test] fn test_from_static_custom_long() { let a = HeaderName { inner: Repr::Custom(Custom(ByteStr::from_static( "longer-than-63--thisheaderislongerthansixtythreecharactersandthushandleddifferent", ))), }; let b = HeaderName::from_static( "longer-than-63--thisheaderislongerthansixtythreecharactersandthushandleddifferent", ); assert_eq!(a, b); } #[test] #[should_panic] fn test_from_static_custom_long_uppercase() { HeaderName::from_static( "Longer-Than-63--ThisHeaderIsLongerThanSixtyThreeCharactersAndThusHandledDifferent", ); } #[test] #[should_panic] fn test_from_static_custom_long_symbol() { HeaderName::from_static( "longer-than-63--thisheader{}{}{}{}islongerthansixtythreecharactersandthushandleddifferent" ); } #[test] fn test_from_static_custom_single_char() { let a = HeaderName { inner: Repr::Custom(Custom(ByteStr::from_static("a"))), }; let b = HeaderName::from_static("a"); assert_eq!(a, b); } #[test] #[should_panic] fn test_from_static_empty() { HeaderName::from_static(""); } #[test] fn test_all_tokens() { HeaderName::from_static("!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyz"); } #[test] fn test_from_lowercase() { HeaderName::from_lowercase(&[0; 10]).unwrap_err(); HeaderName::from_lowercase(&[b'A'; 10]).unwrap_err(); HeaderName::from_lowercase(&[0x1; 10]).unwrap_err(); HeaderName::from_lowercase(&[0xFF; 10]).unwrap_err(); //HeaderName::from_lowercase(&[0; 100]).unwrap_err(); HeaderName::from_lowercase(&[b'A'; 100]).unwrap_err(); HeaderName::from_lowercase(&[0x1; 100]).unwrap_err(); HeaderName::from_lowercase(&[0xFF; 100]).unwrap_err(); } } http-1.2.0/src/header/value.rs000064400000000000000000000547011046102023000143130ustar 00000000000000use bytes::{Bytes, BytesMut}; use std::convert::TryFrom; use std::error::Error; use std::fmt::Write; use std::hash::{Hash, Hasher}; 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)] 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]) { // TODO: When msrv is bumped to larger than 1.57, this should be // replaced with `panic!` macro. // https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html#panic-in-const-contexts // // See the panics section of this method's document for details. #[allow(clippy::no_effect, clippy::out_of_bounds_indexing)] ([] 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] #[allow(clippy::should_implement_trait)] pub fn from_str(src: &str) -> Result { 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::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(src: T) -> Result 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. /// /// ## Panics /// In a debug build this will panic if `src` is not valid UTF-8. /// /// ## Safety /// `src` must contain valid UTF-8. In a release build it is undefined /// behaviour to call this with `src` that is not valid UTF-8. pub unsafe fn from_maybe_shared_unchecked(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::try_from_generic(src, std::convert::identity) } fn try_from_generic, F: FnOnce(T) -> Bytes>( src: T, into: F, ) -> Result { 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 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::() - 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::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 { t.parse() } } impl<'a> TryFrom<&'a String> for HeaderValue { type Error = InvalidHeaderValue; #[inline] fn try_from(s: &'a String) -> Result { Self::from_bytes(s.as_bytes()) } } impl<'a> TryFrom<&'a [u8]> for HeaderValue { type Error = InvalidHeaderValue; #[inline] fn try_from(t: &'a [u8]) -> Result { HeaderValue::from_bytes(t) } } impl TryFrom for HeaderValue { type Error = InvalidHeaderValue; #[inline] fn try_from(t: String) -> Result { HeaderValue::from_shared(t.into()) } } impl TryFrom> for HeaderValue { type Error = InvalidHeaderValue; #[inline] fn try_from(vec: Vec) -> Result { 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 Hash for HeaderValue { fn hash(&self, state: &mut H) { self.inner.hash(state); } } 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 { Some(self.cmp(other)) } } impl Ord for HeaderValue { #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { self.inner.cmp(&other.inner) } } impl PartialEq 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 for HeaderValue { #[inline] fn partial_cmp(&self, other: &str) -> Option { (*self.inner).partial_cmp(other.as_bytes()) } } impl PartialOrd<[u8]> for HeaderValue { #[inline] fn partial_cmp(&self, other: &[u8]) -> Option { (*self.inner).partial_cmp(other) } } impl PartialEq for str { #[inline] fn eq(&self, other: &HeaderValue) -> bool { *other == *self } } impl PartialEq for [u8] { #[inline] fn eq(&self, other: &HeaderValue) -> bool { *other == *self } } impl PartialOrd for str { #[inline] fn partial_cmp(&self, other: &HeaderValue) -> Option { self.as_bytes().partial_cmp(other.as_bytes()) } } impl PartialOrd for [u8] { #[inline] fn partial_cmp(&self, other: &HeaderValue) -> Option { self.partial_cmp(other.as_bytes()) } } impl PartialEq for HeaderValue { #[inline] fn eq(&self, other: &String) -> bool { *self == other[..] } } impl PartialOrd for HeaderValue { #[inline] fn partial_cmp(&self, other: &String) -> Option { self.inner.partial_cmp(other.as_bytes()) } } impl PartialEq for String { #[inline] fn eq(&self, other: &HeaderValue) -> bool { *other == *self } } impl PartialOrd for String { #[inline] fn partial_cmp(&self, other: &HeaderValue) -> Option { self.as_bytes().partial_cmp(other.as_bytes()) } } impl<'a> PartialEq for &'a HeaderValue { #[inline] fn eq(&self, other: &HeaderValue) -> bool { **self == *other } } impl<'a> PartialOrd for &'a HeaderValue { #[inline] fn partial_cmp(&self, other: &HeaderValue) -> Option { (**self).partial_cmp(other) } } impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue where HeaderValue: PartialEq, { #[inline] fn eq(&self, other: &&'a T) -> bool { *self == **other } } impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue where HeaderValue: PartialOrd, { #[inline] fn partial_cmp(&self, other: &&'a T) -> Option { self.partial_cmp(*other) } } impl<'a> PartialEq for &'a str { #[inline] fn eq(&self, other: &HeaderValue) -> bool { *other == *self } } impl<'a> PartialOrd for &'a str { #[inline] fn partial_cmp(&self, other: &HeaderValue) -> Option { 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)); } http-1.2.0/src/lib.rs000064400000000000000000000163211046102023000125110ustar 00000000000000//! 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. //! //! ## 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> { //! match req.uri().path() { //! "/" => index(req), //! "/foo" => foo(req), //! "/bar" => bar(req), //! _ => not_found(req), //! } //! } //! # fn index(_req: Request<()>) -> http::Result> { panic!() } //! # fn foo(_req: Request<()>) -> http::Result> { panic!() } //! # fn bar(_req: Request<()>) -> http::Result> { panic!() } //! # fn not_found(_req: Request<()>) -> http::Result> { panic!() } //! ``` //! //! On a [`Request`] you'll also find accessors like [`method`][Request::method] to return a //! [`Method`] and [`headers`][Request::method] 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(response: &mut Response) { //! 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>`, `Response`, //! `Request, Error = _>>`, or even //! `Response` 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::().unwrap(); //! assert_eq!(name, header::ACCEPT); //! ``` //! //! Header values can be created from string literals through the [`from_static`][header::HeaderValue::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::().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`][uri::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::().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_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] compile_error!("`std` feature currently required, support for `no_std` may be added later"); #[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, HeaderName, 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; #[cfg(test)] mod tests { use super::*; fn assert_send_sync() {} #[test] fn request_satisfies_send_sync() { assert_send_sync::>(); } #[test] fn response_satisfies_send_sync() { assert_send_sync::>(); } } http-1.2.0/src/method.rs000064400000000000000000000360231046102023000132240ustar 00000000000000//! 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::extension::{AllocatedExtension, InlineExtension}; use self::Inner::*; use std::convert::TryFrom; use std::error::Error; use std::str::FromStr; 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 { 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 { 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 { matches!(self.0, Get | Head | Options | Trace) } /// 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 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 for &'a Method { #[inline] fn eq(&self, other: &Method) -> bool { *self == other } } impl PartialEq for Method { #[inline] fn eq(&self, other: &str) -> bool { self.as_ref() == other } } impl PartialEq 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 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 { Method::from_bytes(t) } } impl<'a> TryFrom<&'a str> for Method { type Error = InvalidMethod; #[inline] fn try_from(t: &'a str) -> Result { TryFrom::try_from(t.as_bytes()) } } impl FromStr for Method { type Err = InvalidMethod; #[inline] fn from_str(t: &str) -> Result { 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 { 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 { let mut data: Vec = 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 RFC 9110 HTTP Semantics, section 9.1, the HTTP method is case-sensitive and can // contain the following characters: // // ``` // method = token // token = 1*tchar // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / // "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA // ``` // // https://datatracker.ietf.org/doc/html/rfc9110#section-9.1 // // 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. #[rustfmt::skip] 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'#', b'$', b'%', b'&', b'\'', // 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_extension_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); let longest_inline_method = [b'A'; InlineExtension::MAX]; assert_eq!( Method::from_bytes(&longest_inline_method).unwrap(), Method(ExtensionInline( InlineExtension::new(&longest_inline_method).unwrap() )) ); let shortest_allocated_method = [b'A'; InlineExtension::MAX + 1]; assert_eq!( Method::from_bytes(&shortest_allocated_method).unwrap(), Method(ExtensionAllocated( AllocatedExtension::new(&shortest_allocated_method).unwrap() )) ); } #[test] fn test_extension_method_chars() { const VALID_METHOD_CHARS: &str = "!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; for c in VALID_METHOD_CHARS.chars() { let c = c.to_string(); assert_eq!( Method::from_str(&c).unwrap(), c.as_str(), "testing {c} is a valid method character" ); } } } http-1.2.0/src/request.rs000064400000000000000000000672631046102023000134460ustar 00000000000000//! HTTP request types. //! //! This module contains structs related to HTTP requests, notably the //! `Request` type itself as well as a builder to create requests. Typically //! you'll import the `http::Request` type rather than reaching into this //! module itself. //! //! # Examples //! //! Creating a `Request` to send //! //! ```no_run //! use http::{Request, Response}; //! //! let mut request = Request::builder() //! .uri("https://www.rust-lang.org/") //! .header("User-Agent", "my-awesome-agent/1.0"); //! //! if needs_awesome_header() { //! request = request.header("Awesome", "yes"); //! } //! //! let response = send(request.body(()).unwrap()); //! //! # fn needs_awesome_header() -> bool { //! # true //! # } //! # //! fn send(req: Request<()>) -> Response<()> { //! // ... //! # panic!() //! } //! ``` //! //! Inspecting a request to see what was sent. //! //! ``` //! use http::{Request, Response, StatusCode}; //! //! fn respond_to(req: Request<()>) -> http::Result> { //! if req.uri() != "/awesome-url" { //! return Response::builder() //! .status(StatusCode::NOT_FOUND) //! .body(()) //! } //! //! let has_awesome_header = req.headers().contains_key("Awesome"); //! let body = req.body(); //! //! // ... //! # panic!() //! } //! ``` use std::any::Any; use std::convert::TryInto; use std::fmt; use crate::header::{HeaderMap, HeaderName, HeaderValue}; use crate::method::Method; use crate::version::Version; use crate::{Extensions, Result, Uri}; /// Represents an HTTP request. /// /// An HTTP request 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`, a `Stream` of byte chunks, or a /// value that has been deserialized. /// /// # Examples /// /// Creating a `Request` to send /// /// ```no_run /// use http::{Request, Response}; /// /// let mut request = Request::builder() /// .uri("https://www.rust-lang.org/") /// .header("User-Agent", "my-awesome-agent/1.0"); /// /// if needs_awesome_header() { /// request = request.header("Awesome", "yes"); /// } /// /// let response = send(request.body(()).unwrap()); /// /// # fn needs_awesome_header() -> bool { /// # true /// # } /// # /// fn send(req: Request<()>) -> Response<()> { /// // ... /// # panic!() /// } /// ``` /// /// Inspecting a request to see what was sent. /// /// ``` /// use http::{Request, Response, StatusCode}; /// /// fn respond_to(req: Request<()>) -> http::Result> { /// if req.uri() != "/awesome-url" { /// return Response::builder() /// .status(StatusCode::NOT_FOUND) /// .body(()) /// } /// /// let has_awesome_header = req.headers().contains_key("Awesome"); /// let body = req.body(); /// /// // ... /// # panic!() /// } /// ``` /// /// Deserialize a request of bytes via json: /// /// ``` /// # extern crate serde; /// # extern crate serde_json; /// # extern crate http; /// use http::Request; /// use serde::de; /// /// fn deserialize(req: Request>) -> serde_json::Result> /// where for<'de> T: de::Deserialize<'de>, /// { /// let (parts, body) = req.into_parts(); /// let body = serde_json::from_slice(&body)?; /// Ok(Request::from_parts(parts, body)) /// } /// # /// # fn main() {} /// ``` /// /// Or alternatively, serialize the body of a request to json /// /// ``` /// # extern crate serde; /// # extern crate serde_json; /// # extern crate http; /// use http::Request; /// use serde::ser; /// /// fn serialize(req: Request) -> serde_json::Result>> /// where T: ser::Serialize, /// { /// let (parts, body) = req.into_parts(); /// let body = serde_json::to_vec(&body)?; /// Ok(Request::from_parts(parts, body)) /// } /// # /// # fn main() {} /// ``` #[derive(Clone)] pub struct Request { head: Parts, body: T, } /// Component parts of an HTTP `Request` /// /// The HTTP request head consists of a method, uri, version, and a set of /// header fields. #[derive(Clone)] pub struct Parts { /// The request's method pub method: Method, /// The request's URI pub uri: Uri, /// The request's version pub version: Version, /// The request's headers pub headers: HeaderMap, /// The request's extensions pub extensions: Extensions, _priv: (), } /// An HTTP request builder /// /// This type can be used to construct an instance or `Request` /// through a builder-like pattern. #[derive(Debug)] pub struct Builder { inner: Result, } impl Request<()> { /// Creates a new builder-style object to manufacture a `Request` /// /// This method returns an instance of `Builder` which can be used to /// create a `Request`. /// /// # Examples /// /// ``` /// # use http::*; /// let request = Request::builder() /// .method("GET") /// .uri("https://www.rust-lang.org/") /// .header("X-Custom-Foo", "Bar") /// .body(()) /// .unwrap(); /// ``` #[inline] pub fn builder() -> Builder { Builder::new() } /// Creates a new `Builder` initialized with a GET method and the given URI. /// /// This method returns an instance of `Builder` which can be used to /// create a `Request`. /// /// # Example /// /// ``` /// # use http::*; /// /// let request = Request::get("https://www.rust-lang.org/") /// .body(()) /// .unwrap(); /// ``` pub fn get(uri: T) -> Builder where T: TryInto, >::Error: Into, { Builder::new().method(Method::GET).uri(uri) } /// Creates a new `Builder` initialized with a PUT method and the given URI. /// /// This method returns an instance of `Builder` which can be used to /// create a `Request`. /// /// # Example /// /// ``` /// # use http::*; /// /// let request = Request::put("https://www.rust-lang.org/") /// .body(()) /// .unwrap(); /// ``` pub fn put(uri: T) -> Builder where T: TryInto, >::Error: Into, { Builder::new().method(Method::PUT).uri(uri) } /// Creates a new `Builder` initialized with a POST method and the given URI. /// /// This method returns an instance of `Builder` which can be used to /// create a `Request`. /// /// # Example /// /// ``` /// # use http::*; /// /// let request = Request::post("https://www.rust-lang.org/") /// .body(()) /// .unwrap(); /// ``` pub fn post(uri: T) -> Builder where T: TryInto, >::Error: Into, { Builder::new().method(Method::POST).uri(uri) } /// Creates a new `Builder` initialized with a DELETE method and the given URI. /// /// This method returns an instance of `Builder` which can be used to /// create a `Request`. /// /// # Example /// /// ``` /// # use http::*; /// /// let request = Request::delete("https://www.rust-lang.org/") /// .body(()) /// .unwrap(); /// ``` pub fn delete(uri: T) -> Builder where T: TryInto, >::Error: Into, { Builder::new().method(Method::DELETE).uri(uri) } /// Creates a new `Builder` initialized with an OPTIONS method and the given URI. /// /// This method returns an instance of `Builder` which can be used to /// create a `Request`. /// /// # Example /// /// ``` /// # use http::*; /// /// let request = Request::options("https://www.rust-lang.org/") /// .body(()) /// .unwrap(); /// # assert_eq!(*request.method(), Method::OPTIONS); /// ``` pub fn options(uri: T) -> Builder where T: TryInto, >::Error: Into, { Builder::new().method(Method::OPTIONS).uri(uri) } /// Creates a new `Builder` initialized with a HEAD method and the given URI. /// /// This method returns an instance of `Builder` which can be used to /// create a `Request`. /// /// # Example /// /// ``` /// # use http::*; /// /// let request = Request::head("https://www.rust-lang.org/") /// .body(()) /// .unwrap(); /// ``` pub fn head(uri: T) -> Builder where T: TryInto, >::Error: Into, { Builder::new().method(Method::HEAD).uri(uri) } /// Creates a new `Builder` initialized with a CONNECT method and the given URI. /// /// This method returns an instance of `Builder` which can be used to /// create a `Request`. /// /// # Example /// /// ``` /// # use http::*; /// /// let request = Request::connect("https://www.rust-lang.org/") /// .body(()) /// .unwrap(); /// ``` pub fn connect(uri: T) -> Builder where T: TryInto, >::Error: Into, { Builder::new().method(Method::CONNECT).uri(uri) } /// Creates a new `Builder` initialized with a PATCH method and the given URI. /// /// This method returns an instance of `Builder` which can be used to /// create a `Request`. /// /// # Example /// /// ``` /// # use http::*; /// /// let request = Request::patch("https://www.rust-lang.org/") /// .body(()) /// .unwrap(); /// ``` pub fn patch(uri: T) -> Builder where T: TryInto, >::Error: Into, { Builder::new().method(Method::PATCH).uri(uri) } /// Creates a new `Builder` initialized with a TRACE method and the given URI. /// /// This method returns an instance of `Builder` which can be used to /// create a `Request`. /// /// # Example /// /// ``` /// # use http::*; /// /// let request = Request::trace("https://www.rust-lang.org/") /// .body(()) /// .unwrap(); /// ``` pub fn trace(uri: T) -> Builder where T: TryInto, >::Error: Into, { Builder::new().method(Method::TRACE).uri(uri) } } impl Request { /// Creates a new blank `Request` with the body /// /// The component parts of this request will be set to their default, e.g. /// the GET method, no headers, etc. /// /// # Examples /// /// ``` /// # use http::*; /// let request = Request::new("hello world"); /// /// assert_eq!(*request.method(), Method::GET); /// assert_eq!(*request.body(), "hello world"); /// ``` #[inline] pub fn new(body: T) -> Request { Request { head: Parts::new(), body, } } /// Creates a new `Request` with the given components parts and body. /// /// # Examples /// /// ``` /// # use http::*; /// let request = Request::new("hello world"); /// let (mut parts, body) = request.into_parts(); /// parts.method = Method::POST; /// /// let request = Request::from_parts(parts, body); /// ``` #[inline] pub fn from_parts(parts: Parts, body: T) -> Request { Request { head: parts, body } } /// Returns a reference to the associated HTTP method. /// /// # Examples /// /// ``` /// # use http::*; /// let request: Request<()> = Request::default(); /// assert_eq!(*request.method(), Method::GET); /// ``` #[inline] pub fn method(&self) -> &Method { &self.head.method } /// Returns a mutable reference to the associated HTTP method. /// /// # Examples /// /// ``` /// # use http::*; /// let mut request: Request<()> = Request::default(); /// *request.method_mut() = Method::PUT; /// assert_eq!(*request.method(), Method::PUT); /// ``` #[inline] pub fn method_mut(&mut self) -> &mut Method { &mut self.head.method } /// Returns a reference to the associated URI. /// /// # Examples /// /// ``` /// # use http::*; /// let request: Request<()> = Request::default(); /// assert_eq!(*request.uri(), *"/"); /// ``` #[inline] pub fn uri(&self) -> &Uri { &self.head.uri } /// Returns a mutable reference to the associated URI. /// /// # Examples /// /// ``` /// # use http::*; /// let mut request: Request<()> = Request::default(); /// *request.uri_mut() = "/hello".parse().unwrap(); /// assert_eq!(*request.uri(), *"/hello"); /// ``` #[inline] pub fn uri_mut(&mut self) -> &mut Uri { &mut self.head.uri } /// Returns the associated version. /// /// # Examples /// /// ``` /// # use http::*; /// let request: Request<()> = Request::default(); /// assert_eq!(request.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 request: Request<()> = Request::default(); /// *request.version_mut() = Version::HTTP_2; /// assert_eq!(request.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 request: Request<()> = Request::default(); /// assert!(request.headers().is_empty()); /// ``` #[inline] pub fn headers(&self) -> &HeaderMap { &self.head.headers } /// Returns a mutable reference to the associated header field map. /// /// # Examples /// /// ``` /// # use http::*; /// # use http::header::*; /// let mut request: Request<()> = Request::default(); /// request.headers_mut().insert(HOST, HeaderValue::from_static("world")); /// assert!(!request.headers().is_empty()); /// ``` #[inline] pub fn headers_mut(&mut self) -> &mut HeaderMap { &mut self.head.headers } /// Returns a reference to the associated extensions. /// /// # Examples /// /// ``` /// # use http::*; /// let request: Request<()> = Request::default(); /// assert!(request.extensions().get::().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 request: Request<()> = Request::default(); /// request.extensions_mut().insert("hello"); /// assert_eq!(request.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 request: Request = Request::default(); /// assert!(request.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 request: Request = Request::default(); /// request.body_mut().push_str("hello world"); /// assert!(!request.body().is_empty()); /// ``` #[inline] pub fn body_mut(&mut self) -> &mut T { &mut self.body } /// Consumes the request, returning just the body. /// /// # Examples /// /// ``` /// # use http::Request; /// let request = Request::new(10); /// let body = request.into_body(); /// assert_eq!(body, 10); /// ``` #[inline] pub fn into_body(self) -> T { self.body } /// Consumes the request returning the head and body parts. /// /// # Examples /// /// ``` /// # use http::*; /// let request = Request::new(()); /// let (parts, body) = request.into_parts(); /// assert_eq!(parts.method, Method::GET); /// ``` #[inline] pub fn into_parts(self) -> (Parts, T) { (self.head, self.body) } /// Consumes the request returning a new request with body mapped to the /// return type of the passed in function. /// /// # Examples /// /// ``` /// # use http::*; /// let request = Request::builder().body("some string").unwrap(); /// let mapped_request: Request<&[u8]> = request.map(|b| { /// assert_eq!(b, "some string"); /// b.as_bytes() /// }); /// assert_eq!(mapped_request.body(), &"some string".as_bytes()); /// ``` #[inline] pub fn map(self, f: F) -> Request where F: FnOnce(T) -> U, { Request { body: f(self.body), head: self.head, } } } impl Default for Request { fn default() -> Request { Request::new(T::default()) } } impl fmt::Debug for Request { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Request") .field("method", self.method()) .field("uri", self.uri()) .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 { method: Method::default(), uri: Uri::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("method", &self.method) .field("uri", &self.uri) .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 a `Request`. /// /// # Examples /// /// ``` /// # use http::*; /// /// let req = request::Builder::new() /// .method("POST") /// .body(()) /// .unwrap(); /// ``` #[inline] pub fn new() -> Builder { Builder::default() } /// Set the HTTP method for this request. /// /// By default this is `GET`. /// /// # Examples /// /// ``` /// # use http::*; /// /// let req = Request::builder() /// .method("POST") /// .body(()) /// .unwrap(); /// ``` pub fn method(self, method: T) -> Builder where T: TryInto, >::Error: Into, { self.and_then(move |mut head| { let method = method.try_into().map_err(Into::into)?; head.method = method; Ok(head) }) } /// Get the HTTP Method for this request. /// /// By default this is `GET`. If builder has error, returns None. /// /// # Examples /// /// ``` /// # use http::*; /// /// let mut req = Request::builder(); /// assert_eq!(req.method_ref(),Some(&Method::GET)); /// /// req = req.method("POST"); /// assert_eq!(req.method_ref(),Some(&Method::POST)); /// ``` pub fn method_ref(&self) -> Option<&Method> { self.inner.as_ref().ok().map(|h| &h.method) } /// Set the URI for this request. /// /// By default this is `/`. /// /// # Examples /// /// ``` /// # use http::*; /// /// let req = Request::builder() /// .uri("https://www.rust-lang.org/") /// .body(()) /// .unwrap(); /// ``` pub fn uri(self, uri: T) -> Builder where T: TryInto, >::Error: Into, { self.and_then(move |mut head| { head.uri = uri.try_into().map_err(Into::into)?; Ok(head) }) } /// Get the URI for this request /// /// By default this is `/`. /// /// # Examples /// /// ``` /// # use http::*; /// /// let mut req = Request::builder(); /// assert_eq!(req.uri_ref().unwrap(), "/" ); /// /// req = req.uri("https://www.rust-lang.org/"); /// assert_eq!(req.uri_ref().unwrap(), "https://www.rust-lang.org/" ); /// ``` pub fn uri_ref(&self) -> Option<&Uri> { self.inner.as_ref().ok().map(|h| &h.uri) } /// Set the HTTP version for this request. /// /// By default this is HTTP/1.1 /// /// # Examples /// /// ``` /// # use http::*; /// /// let req = Request::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) }) } /// Get the HTTP version for this request /// /// By default this is HTTP/1.1. /// /// # Examples /// /// ``` /// # use http::*; /// /// let mut req = Request::builder(); /// assert_eq!(req.version_ref().unwrap(), &Version::HTTP_11 ); /// /// req = req.version(Version::HTTP_2); /// assert_eq!(req.version_ref().unwrap(), &Version::HTTP_2 ); /// ``` pub fn version_ref(&self) -> Option<&Version> { self.inner.as_ref().ok().map(|h| &h.version) } /// Appends a header to this request 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 req = Request::builder() /// .header("Accept", "text/html") /// .header("X-Custom-Foo", "bar") /// .body(()) /// .unwrap(); /// ``` pub fn header(self, key: K, value: V) -> Builder where K: TryInto, >::Error: Into, V: TryInto, >::Error: Into, { self.and_then(move |mut head| { let name = key.try_into().map_err(Into::into)?; let value = value.try_into().map_err(Into::into)?; head.headers.try_append(name, value)?; Ok(head) }) } /// Get header on this request builder. /// when builder has error returns None /// /// # Example /// /// ``` /// # use http::Request; /// let req = Request::builder() /// .header("Accept", "text/html") /// .header("X-Custom-Foo", "bar"); /// let headers = req.headers_ref().unwrap(); /// assert_eq!( headers["Accept"], "text/html" ); /// assert_eq!( headers["X-Custom-Foo"], "bar" ); /// ``` pub fn headers_ref(&self) -> Option<&HeaderMap> { self.inner.as_ref().ok().map(|h| &h.headers) } /// Get headers on this request builder. /// /// When builder has error returns None. /// /// # Example /// /// ``` /// # use http::{header::HeaderValue, Request}; /// let mut req = Request::builder(); /// { /// let headers = req.headers_mut().unwrap(); /// headers.insert("Accept", HeaderValue::from_static("text/html")); /// headers.insert("X-Custom-Foo", HeaderValue::from_static("bar")); /// } /// let headers = req.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> { self.inner.as_mut().ok().map(|h| &mut h.headers) } /// Adds an extension to this builder /// /// # Examples /// /// ``` /// # use http::*; /// /// let req = Request::builder() /// .extension("My Extension") /// .body(()) /// .unwrap(); /// /// assert_eq!(req.extensions().get::<&'static str>(), /// Some(&"My Extension")); /// ``` pub fn extension(self, extension: T) -> Builder where T: Clone + Any + Send + Sync + 'static, { self.and_then(move |mut head| { head.extensions.insert(extension); Ok(head) }) } /// Get a reference to the extensions for this request builder. /// /// If the builder has an error, this returns `None`. /// /// # Example /// /// ``` /// # use http::Request; /// let req = Request::builder().extension("My Extension").extension(5u32); /// let extensions = req.extensions_ref().unwrap(); /// assert_eq!(extensions.get::<&'static str>(), Some(&"My Extension")); /// assert_eq!(extensions.get::(), 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 request builder. /// /// If the builder has an error, this returns `None`. /// /// # Example /// /// ``` /// # use http::Request; /// let mut req = Request::builder().extension("My Extension"); /// let mut extensions = req.extensions_mut().unwrap(); /// assert_eq!(extensions.get::<&'static str>(), Some(&"My Extension")); /// extensions.insert(5u32); /// assert_eq!(extensions.get::(), 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 `Request`. /// /// # 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 request = Request::builder() /// .body(()) /// .unwrap(); /// ``` pub fn body(self, body: T) -> Result> { self.inner.map(move |head| Request { head, body }) } // private fn and_then(self, func: F) -> Self where F: FnOnce(Parts) -> Result, { 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 request = Request::builder().body("some string").unwrap(); let mapped_request = request.map(|s| { assert_eq!(s, "some string"); 123u32 }); assert_eq!(mapped_request.body(), &123u32); } } http-1.2.0/src/response.rs000064400000000000000000000511241046102023000136010ustar 00000000000000//! 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> { //! 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::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> { //! // ... //! # 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::TryInto; 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`, 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> { /// 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::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> { /// // ... /// # 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(res: Response>) -> serde_json::Result> /// 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(res: Response) -> serde_json::Result>> /// 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() {} /// ``` #[derive(Clone)] pub struct Response { 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. #[derive(Clone)] pub struct Parts { /// The response's status pub status: StatusCode, /// The response's version pub version: Version, /// The response's headers pub headers: HeaderMap, /// 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, } 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 Response { /// Creates a new blank `Response` with the body /// /// The component parts 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 { Response { head: Parts::new(), 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 { Response { head: parts, 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 { &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 { &mut self.head.headers } /// Returns a reference to the associated extensions. /// /// # Examples /// /// ``` /// # use http::*; /// let response: Response<()> = Response::default(); /// assert!(response.extensions().get::().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 = 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 = 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(self, f: F) -> Response where F: FnOnce(T) -> U, { Response { body: f(self.body), head: self.head, } } } impl Default for Response { #[inline] fn default() -> Response { Response::new(T::default()) } } impl fmt::Debug for Response { 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. /// /// By default this is `200`. /// /// # Examples /// /// ``` /// # use http::*; /// /// let response = Response::builder() /// .status(200) /// .body(()) /// .unwrap(); /// ``` pub fn status(self, status: T) -> Builder where T: TryInto, >::Error: Into, { self.and_then(move |mut head| { head.status = status.try_into().map_err(Into::into)?; Ok(head) }) } /// Set the HTTP version for this response. /// /// 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(self, key: K, value: V) -> Builder where K: TryInto, >::Error: Into, V: TryInto, >::Error: Into, { self.and_then(move |mut head| { let name = key.try_into().map_err(Into::into)?; let value = value.try_into().map_err(Into::into)?; head.headers.try_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> { 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> { 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(self, extension: T) -> Builder where T: Clone + 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::(), 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::(), 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(self, body: T) -> Result> { self.inner.map(move |head| Response { head, body }) } // private fn and_then(self, func: F) -> Self where F: FnOnce(Parts) -> Result, { 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); } } http-1.2.0/src/status.rs000064400000000000000000000543721046102023000132760ustar 00000000000000//! 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::error::Error; use std::fmt; use std::num::NonZeroU16; use std::str::FromStr; /// An HTTP status code (`status-code` in RFC 9110 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 { if !(100..1000).contains(&src) { 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 { 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` 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 const fn as_u16(&self) -> u16 { (*self).0.get() } /// 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("") ) } } impl Default for StatusCode { #[inline] fn default() -> StatusCode { StatusCode::OK } } impl PartialEq for StatusCode { #[inline] fn eq(&self, other: &u16) -> bool { self.as_u16() == *other } } impl PartialEq for u16 { #[inline] fn eq(&self, other: &StatusCode) -> bool { *self == other.as_u16() } } impl From 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::from_bytes(s.as_ref()) } } impl<'a> From<&'a StatusCode> for StatusCode { #[inline] fn from(t: &'a StatusCode) -> Self { t.to_owned() } } impl<'a> TryFrom<&'a [u8]> for StatusCode { type Error = InvalidStatusCode; #[inline] fn try_from(t: &'a [u8]) -> Result { StatusCode::from_bytes(t) } } impl<'a> TryFrom<&'a str> for StatusCode { type Error = InvalidStatusCode; #[inline] fn try_from(t: &'a str) -> Result { t.parse() } } impl TryFrom for StatusCode { type Error = InvalidStatusCode; #[inline] fn try_from(t: u16) -> Result { 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 /// [[RFC9110, Section 15.2.1](https://datatracker.ietf.org/doc/html/rfc9110#section-15.2.1)] (100, CONTINUE, "Continue"); /// 101 Switching Protocols /// [[RFC9110, Section 15.2.2](https://datatracker.ietf.org/doc/html/rfc9110#section-15.2.2)] (101, SWITCHING_PROTOCOLS, "Switching Protocols"); /// 102 Processing /// [[RFC2518, Section 10.1](https://datatracker.ietf.org/doc/html/rfc2518#section-10.1)] (102, PROCESSING, "Processing"); /// 200 OK /// [[RFC9110, Section 15.3.1](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.1)] (200, OK, "OK"); /// 201 Created /// [[RFC9110, Section 15.3.2](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.2)] (201, CREATED, "Created"); /// 202 Accepted /// [[RFC9110, Section 15.3.3](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.3)] (202, ACCEPTED, "Accepted"); /// 203 Non-Authoritative Information /// [[RFC9110, Section 15.3.4](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.4)] (203, NON_AUTHORITATIVE_INFORMATION, "Non Authoritative Information"); /// 204 No Content /// [[RFC9110, Section 15.3.5](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.5)] (204, NO_CONTENT, "No Content"); /// 205 Reset Content /// [[RFC9110, Section 15.3.6](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.6)] (205, RESET_CONTENT, "Reset Content"); /// 206 Partial Content /// [[RFC9110, Section 15.3.7](https://datatracker.ietf.org/doc/html/rfc9110#section-15.3.7)] (206, PARTIAL_CONTENT, "Partial Content"); /// 207 Multi-Status /// [[RFC4918, Section 11.1](https://datatracker.ietf.org/doc/html/rfc4918#section-11.1)] (207, MULTI_STATUS, "Multi-Status"); /// 208 Already Reported /// [[RFC5842, Section 7.1](https://datatracker.ietf.org/doc/html/rfc5842#section-7.1)] (208, ALREADY_REPORTED, "Already Reported"); /// 226 IM Used /// [[RFC3229, Section 10.4.1](https://datatracker.ietf.org/doc/html/rfc3229#section-10.4.1)] (226, IM_USED, "IM Used"); /// 300 Multiple Choices /// [[RFC9110, Section 15.4.1](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.1)] (300, MULTIPLE_CHOICES, "Multiple Choices"); /// 301 Moved Permanently /// [[RFC9110, Section 15.4.2](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.2)] (301, MOVED_PERMANENTLY, "Moved Permanently"); /// 302 Found /// [[RFC9110, Section 15.4.3](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.3)] (302, FOUND, "Found"); /// 303 See Other /// [[RFC9110, Section 15.4.4](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.4)] (303, SEE_OTHER, "See Other"); /// 304 Not Modified /// [[RFC9110, Section 15.4.5](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.5)] (304, NOT_MODIFIED, "Not Modified"); /// 305 Use Proxy /// [[RFC9110, Section 15.4.6](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.6)] (305, USE_PROXY, "Use Proxy"); /// 307 Temporary Redirect /// [[RFC9110, Section 15.4.7](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.7)] (307, TEMPORARY_REDIRECT, "Temporary Redirect"); /// 308 Permanent Redirect /// [[RFC9110, Section 15.4.8](https://datatracker.ietf.org/doc/html/rfc9110#section-15.4.8)] (308, PERMANENT_REDIRECT, "Permanent Redirect"); /// 400 Bad Request /// [[RFC9110, Section 15.5.1](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.1)] (400, BAD_REQUEST, "Bad Request"); /// 401 Unauthorized /// [[RFC9110, Section 15.5.2](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.2)] (401, UNAUTHORIZED, "Unauthorized"); /// 402 Payment Required /// [[RFC9110, Section 15.5.3](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.3)] (402, PAYMENT_REQUIRED, "Payment Required"); /// 403 Forbidden /// [[RFC9110, Section 15.5.4](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.4)] (403, FORBIDDEN, "Forbidden"); /// 404 Not Found /// [[RFC9110, Section 15.5.5](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.5)] (404, NOT_FOUND, "Not Found"); /// 405 Method Not Allowed /// [[RFC9110, Section 15.5.6](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.6)] (405, METHOD_NOT_ALLOWED, "Method Not Allowed"); /// 406 Not Acceptable /// [[RFC9110, Section 15.5.7](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.7)] (406, NOT_ACCEPTABLE, "Not Acceptable"); /// 407 Proxy Authentication Required /// [[RFC9110, Section 15.5.8](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.8)] (407, PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required"); /// 408 Request Timeout /// [[RFC9110, Section 15.5.9](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.9)] (408, REQUEST_TIMEOUT, "Request Timeout"); /// 409 Conflict /// [[RFC9110, Section 15.5.10](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.10)] (409, CONFLICT, "Conflict"); /// 410 Gone /// [[RFC9110, Section 15.5.11](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.11)] (410, GONE, "Gone"); /// 411 Length Required /// [[RFC9110, Section 15.5.12](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.12)] (411, LENGTH_REQUIRED, "Length Required"); /// 412 Precondition Failed /// [[RFC9110, Section 15.5.13](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.13)] (412, PRECONDITION_FAILED, "Precondition Failed"); /// 413 Payload Too Large /// [[RFC9110, Section 15.5.14](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.14)] (413, PAYLOAD_TOO_LARGE, "Payload Too Large"); /// 414 URI Too Long /// [[RFC9110, Section 15.5.15](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.15)] (414, URI_TOO_LONG, "URI Too Long"); /// 415 Unsupported Media Type /// [[RFC9110, Section 15.5.16](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.16)] (415, UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type"); /// 416 Range Not Satisfiable /// [[RFC9110, Section 15.5.17](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.17)] (416, RANGE_NOT_SATISFIABLE, "Range Not Satisfiable"); /// 417 Expectation Failed /// [[RFC9110, Section 15.5.18](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.18)] (417, EXPECTATION_FAILED, "Expectation Failed"); /// 418 I'm a teapot /// [curiously not registered by IANA but [RFC2324, Section 2.3.2](https://datatracker.ietf.org/doc/html/rfc2324#section-2.3.2)] (418, IM_A_TEAPOT, "I'm a teapot"); /// 421 Misdirected Request /// [[RFC9110, Section 15.5.20](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.20)] (421, MISDIRECTED_REQUEST, "Misdirected Request"); /// 422 Unprocessable Entity /// [[RFC9110, Section 15.5.21](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.21)] (422, UNPROCESSABLE_ENTITY, "Unprocessable Entity"); /// 423 Locked /// [[RFC4918, Section 11.3](https://datatracker.ietf.org/doc/html/rfc4918#section-11.3)] (423, LOCKED, "Locked"); /// 424 Failed Dependency /// [[RFC4918, Section 11.4](https://tools.ietf.org/html/rfc4918#section-11.4)] (424, FAILED_DEPENDENCY, "Failed Dependency"); /// 425 Too early /// [[RFC8470, Section 5.2](https://httpwg.org/specs/rfc8470.html#status)] (425, TOO_EARLY, "Too Early"); /// 426 Upgrade Required /// [[RFC9110, Section 15.5.22](https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.22)] (426, UPGRADE_REQUIRED, "Upgrade Required"); /// 428 Precondition Required /// [[RFC6585, Section 3](https://datatracker.ietf.org/doc/html/rfc6585#section-3)] (428, PRECONDITION_REQUIRED, "Precondition Required"); /// 429 Too Many Requests /// [[RFC6585, Section 4](https://datatracker.ietf.org/doc/html/rfc6585#section-4)] (429, TOO_MANY_REQUESTS, "Too Many Requests"); /// 431 Request Header Fields Too Large /// [[RFC6585, Section 5](https://datatracker.ietf.org/doc/html/rfc6585#section-5)] (431, REQUEST_HEADER_FIELDS_TOO_LARGE, "Request Header Fields Too Large"); /// 451 Unavailable For Legal Reasons /// [[RFC7725, Section 3](https://tools.ietf.org/html/rfc7725#section-3)] (451, UNAVAILABLE_FOR_LEGAL_REASONS, "Unavailable For Legal Reasons"); /// 500 Internal Server Error /// [[RFC9110, Section 15.6.1](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.1)] (500, INTERNAL_SERVER_ERROR, "Internal Server Error"); /// 501 Not Implemented /// [[RFC9110, Section 15.6.2](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.2)] (501, NOT_IMPLEMENTED, "Not Implemented"); /// 502 Bad Gateway /// [[RFC9110, Section 15.6.3](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.3)] (502, BAD_GATEWAY, "Bad Gateway"); /// 503 Service Unavailable /// [[RFC9110, Section 15.6.4](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.4)] (503, SERVICE_UNAVAILABLE, "Service Unavailable"); /// 504 Gateway Timeout /// [[RFC9110, Section 15.6.5](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.5)] (504, GATEWAY_TIMEOUT, "Gateway Timeout"); /// 505 HTTP Version Not Supported /// [[RFC9110, Section 15.6.6](https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.6)] (505, HTTP_VERSION_NOT_SUPPORTED, "HTTP Version Not Supported"); /// 506 Variant Also Negotiates /// [[RFC2295, Section 8.1](https://datatracker.ietf.org/doc/html/rfc2295#section-8.1)] (506, VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates"); /// 507 Insufficient Storage /// [[RFC4918, Section 11.5](https://datatracker.ietf.org/doc/html/rfc4918#section-11.5)] (507, INSUFFICIENT_STORAGE, "Insufficient Storage"); /// 508 Loop Detected /// [[RFC5842, Section 7.2](https://datatracker.ietf.org/doc/html/rfc5842#section-7.2)] (508, LOOP_DETECTED, "Loop Detected"); /// 510 Not Extended /// [[RFC2774, Section 7](https://datatracker.ietf.org/doc/html/rfc2774#section-7)] (510, NOT_EXTENDED, "Not Extended"); /// 511 Network Authentication Required /// [[RFC6585, Section 6](https://datatracker.ietf.org/doc/html/rfc6585#section-6)] (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: &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"; http-1.2.0/src/uri/authority.rs000064400000000000000000000516641046102023000146030ustar 00000000000000use 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 { // Precondition on create_authority: trivially satisfied by the // identity closure 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(src: T) -> Result 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 { let mut colon_cnt = 0u32; 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; const MAX_COLONS: u32 = 8; // e.g., [FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80 // 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':' => { if colon_cnt >= MAX_COLONS { return Err(ErrorKind::InvalidAuthority.into()); } 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 (!start_bracket) || 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 { 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> { 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 { self.port().map(|p| 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 for Authority impl AsRef 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 for Authority { fn eq(&self, other: &str) -> bool { self.data.eq_ignore_ascii_case(other) } } impl PartialEq for str { fn eq(&self, other: &Authority) -> bool { self.eq_ignore_ascii_case(other.as_str()) } } impl<'a> PartialEq 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 for Authority { fn eq(&self, other: &String) -> bool { self.data.eq_ignore_ascii_case(other.as_str()) } } impl PartialEq 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 { 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 for Authority { fn partial_cmp(&self, other: &str) -> Option { 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 for str { fn partial_cmp(&self, other: &Authority) -> Option { 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 for &'a str { fn partial_cmp(&self, other: &Authority) -> Option { 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 { 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 for Authority { fn partial_cmp(&self, other: &String) -> Option { 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 for String { fn partial_cmp(&self, other: &Authority) -> Option { 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(&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 { // 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, Bytes::copy_from_slice) } } impl<'a> TryFrom<&'a str> for Authority { type Error = InvalidUri; #[inline] fn try_from(s: &'a str) -> Result { TryFrom::try_from(s.as_bytes()) } } impl TryFrom> for Authority { type Error = InvalidUri; #[inline] fn try_from(vec: Vec) -> Result { Authority::from_shared(vec.into()) } } impl TryFrom for Authority { type Error = InvalidUri; #[inline] fn try_from(t: String) -> Result { Authority::from_shared(t.into()) } } impl FromStr for Authority { type Err = InvalidUri; fn from_str(s: &str) -> Result { 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 .rsplit('@') .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: B, f: F) -> Result 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 reject_obviously_invalid_ipv6_address() { let err = Authority::parse_non_empty(b"[0:1:2:3:4:5:6:7:8:9:10:11:12:13:14]").unwrap_err(); assert_eq!(err.0, ErrorKind::InvalidAuthority); } #[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); // reject tie-fighter let err = Authority::parse_non_empty(b"]o[").unwrap_err(); assert_eq!(err.0, ErrorKind::InvalidAuthority); } } http-1.2.0/src/uri/builder.rs000064400000000000000000000124771046102023000142000ustar 00000000000000use std::convert::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, } 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(self, scheme: T) -> Self where T: TryInto, >::Error: Into, { 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(self, auth: T) -> Self where T: TryInto, >::Error: Into, { 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(self, p_and_q: T) -> Self where T: TryInto, >::Error: Into, { 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 { let parts = self.parts?; Uri::from_parts(parts).map_err(Into::into) } // private fn map(self, func: F) -> Self where F: FnOnce(Parts) -> Result, { Builder { parts: self.parts.and_then(func), } } } impl Default for Builder { #[inline] fn default() -> Builder { Builder { parts: Ok(Parts::default()), } } } impl From for Builder { fn from(uri: Uri) -> Self { Self { parts: Ok(uri.into_parts()), } } } #[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())); } } #[test] fn build_from_uri() { let original_uri = Uri::default(); let uri = Builder::from(original_uri.clone()).build().unwrap(); assert_eq!(original_uri, uri); } } http-1.2.0/src/uri/mod.rs000064400000000000000000000751051046102023000133260ustar 00000000000000//! URI component of request and response lines //! //! This module primarily contains the `Uri` type which is a component of all //! HTTP requests and also reexports this type at the root of the crate. A URI //! is not always a "full URL" in the sense of something you'd type into a web //! browser, but HTTP requests may only have paths on servers but may have full //! schemes and hostnames on clients. //! //! # Examples //! //! ``` //! use http::Uri; //! //! let uri = "/foo/bar?baz".parse::().unwrap(); //! assert_eq!(uri.path(), "/foo/bar"); //! assert_eq!(uri.query(), Some("baz")); //! assert_eq!(uri.host(), None); //! //! let uri = "https://www.rust-lang.org/install.html".parse::().unwrap(); //! assert_eq!(uri.scheme_str(), Some("https")); //! assert_eq!(uri.host(), Some("www.rust-lang.org")); //! assert_eq!(uri.path(), "/install.html"); //! ``` use crate::byte_str::ByteStr; use std::convert::TryFrom; use bytes::Bytes; use std::error::Error; use std::fmt; use std::hash::{Hash, Hasher}; use std::str::{self, FromStr}; use self::scheme::Scheme2; pub use self::authority::Authority; pub use self::builder::Builder; pub use self::path::PathAndQuery; pub use self::port::Port; pub use self::scheme::Scheme; mod authority; mod builder; mod path; mod port; mod scheme; #[cfg(test)] mod tests; /// The URI component of a request. /// /// For HTTP 1, this is included as part of the request line. From Section 5.3, /// Request Target: /// /// > Once an inbound connection is obtained, the client sends an HTTP /// > request message (Section 3) with a request-target derived from the /// > target URI. There are four distinct formats for the request-target, /// > depending on both the method being requested and whether the request /// > is to a proxy. /// > /// > ```notrust /// > request-target = origin-form /// > / absolute-form /// > / authority-form /// > / asterisk-form /// > ``` /// /// The URI is structured as follows: /// /// ```notrust /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1 /// |-| |-------------------------------||--------| |-------------------| |-----| /// | | | | | /// scheme authority path query fragment /// ``` /// /// For HTTP 2.0, the URI is encoded using pseudoheaders. /// /// # Examples /// /// ``` /// use http::Uri; /// /// let uri = "/foo/bar?baz".parse::().unwrap(); /// assert_eq!(uri.path(), "/foo/bar"); /// assert_eq!(uri.query(), Some("baz")); /// assert_eq!(uri.host(), None); /// /// let uri = "https://www.rust-lang.org/install.html".parse::().unwrap(); /// assert_eq!(uri.scheme_str(), Some("https")); /// assert_eq!(uri.host(), Some("www.rust-lang.org")); /// assert_eq!(uri.path(), "/install.html"); /// ``` #[derive(Clone)] pub struct Uri { scheme: Scheme, authority: Authority, path_and_query: PathAndQuery, } /// The various parts of a URI. /// /// This struct is used to provide to and retrieve from a URI. #[derive(Debug, Default)] pub struct Parts { /// The scheme component of a URI pub scheme: Option, /// The authority component of a URI pub authority: Option, /// The origin-form component of a URI pub path_and_query: Option, /// Allow extending in the future _priv: (), } /// An error resulting from a failed attempt to construct a URI. #[derive(Debug)] pub struct InvalidUri(ErrorKind); /// An error resulting from a failed attempt to construct a URI. #[derive(Debug)] pub struct InvalidUriParts(InvalidUri); #[derive(Debug, Eq, PartialEq)] enum ErrorKind { InvalidUriChar, InvalidScheme, InvalidAuthority, InvalidPort, InvalidFormat, SchemeMissing, AuthorityMissing, PathAndQueryMissing, TooLong, Empty, SchemeTooLong, } // u16::MAX is reserved for None const MAX_LEN: usize = (u16::MAX - 1) as usize; // URI_CHARS is a table of valid characters in a URI. An entry in the table is // 0 for invalid characters. For valid characters the entry is itself (i.e. // the entry for 33 is b'!' because b'!' == 33u8). 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. #[rustfmt::skip] const URI_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, b'!', 0, b'#', b'$', 0, b'&', b'\'', // 3x b'(', b')', b'*', b'+', b',', b'-', b'.', b'/', b'0', b'1', // 4x b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', b';', // 5x 0, b'=', 0, b'?', b'@', 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'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 Uri { /// Creates a new builder-style object to manufacture a `Uri`. /// /// This method returns an instance of `Builder` which can be usd to /// create a `Uri`. /// /// # Examples /// /// ``` /// use http::Uri; /// /// let uri = Uri::builder() /// .scheme("https") /// .authority("hyper.rs") /// .path_and_query("/") /// .build() /// .unwrap(); /// ``` pub fn builder() -> Builder { Builder::new() } /// Attempt to convert a `Parts` into a `Uri`. /// /// # Examples /// /// Relative URI /// /// ``` /// # use http::uri::*; /// let mut parts = Parts::default(); /// parts.path_and_query = Some("/foo".parse().unwrap()); /// /// let uri = Uri::from_parts(parts).unwrap(); /// /// assert_eq!(uri.path(), "/foo"); /// /// assert!(uri.scheme().is_none()); /// assert!(uri.authority().is_none()); /// ``` /// /// Absolute URI /// /// ``` /// # use http::uri::*; /// let mut parts = Parts::default(); /// parts.scheme = Some("http".parse().unwrap()); /// parts.authority = Some("foo.com".parse().unwrap()); /// parts.path_and_query = Some("/foo".parse().unwrap()); /// /// let uri = Uri::from_parts(parts).unwrap(); /// /// assert_eq!(uri.scheme().unwrap().as_str(), "http"); /// assert_eq!(uri.authority().unwrap(), "foo.com"); /// assert_eq!(uri.path(), "/foo"); /// ``` pub fn from_parts(src: Parts) -> Result { if src.scheme.is_some() { if src.authority.is_none() { return Err(ErrorKind::AuthorityMissing.into()); } if src.path_and_query.is_none() { return Err(ErrorKind::PathAndQueryMissing.into()); } } else if src.authority.is_some() && src.path_and_query.is_some() { return Err(ErrorKind::SchemeMissing.into()); } let scheme = match src.scheme { Some(scheme) => scheme, None => Scheme { inner: Scheme2::None, }, }; let authority = match src.authority { Some(authority) => authority, None => Authority::empty(), }; let path_and_query = match src.path_and_query { Some(path_and_query) => path_and_query, None => PathAndQuery::empty(), }; Ok(Uri { scheme, authority, path_and_query, }) } /// Attempt to convert a `Bytes` buffer to a `Uri`. /// /// 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(src: T) -> Result where T: AsRef<[u8]> + 'static, { if_downcast_into!(T, Bytes, src, { return Uri::from_shared(src); }); Uri::try_from(src.as_ref()) } // Not public while `bytes` is unstable. fn from_shared(s: Bytes) -> Result { use self::ErrorKind::*; if s.len() > MAX_LEN { return Err(TooLong.into()); } match s.len() { 0 => { return Err(Empty.into()); } 1 => match s[0] { b'/' => { return Ok(Uri { scheme: Scheme::empty(), authority: Authority::empty(), path_and_query: PathAndQuery::slash(), }); } b'*' => { return Ok(Uri { scheme: Scheme::empty(), authority: Authority::empty(), path_and_query: PathAndQuery::star(), }); } _ => { let authority = Authority::from_shared(s)?; return Ok(Uri { scheme: Scheme::empty(), authority, path_and_query: PathAndQuery::empty(), }); } }, _ => {} } if s[0] == b'/' { return Ok(Uri { scheme: Scheme::empty(), authority: Authority::empty(), path_and_query: PathAndQuery::from_shared(s)?, }); } parse_full(s) } /// Convert a `Uri` 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 URI. /// /// # Examples /// /// ``` /// # use http::uri::Uri; /// let uri = Uri::from_static("http://example.com/foo"); /// /// assert_eq!(uri.host().unwrap(), "example.com"); /// assert_eq!(uri.path(), "/foo"); /// ``` pub fn from_static(src: &'static str) -> Self { let s = Bytes::from_static(src.as_bytes()); match Uri::from_shared(s) { Ok(uri) => uri, Err(e) => panic!("static str is not valid URI: {}", e), } } /// Convert a `Uri` into `Parts`. /// /// # Note /// /// This is just an inherent method providing the same functionality as /// `let parts: Parts = uri.into()` /// /// # Examples /// /// ``` /// # use http::uri::*; /// let uri: Uri = "/foo".parse().unwrap(); /// /// let parts = uri.into_parts(); /// /// assert_eq!(parts.path_and_query.unwrap(), "/foo"); /// /// assert!(parts.scheme.is_none()); /// assert!(parts.authority.is_none()); /// ``` #[inline] pub fn into_parts(self) -> Parts { self.into() } /// Returns the path & query components of the Uri #[inline] pub fn path_and_query(&self) -> Option<&PathAndQuery> { if !self.scheme.inner.is_none() || self.authority.data.is_empty() { Some(&self.path_and_query) } else { None } } /// Get the path of this `Uri`. /// /// Both relative and absolute URIs contain a path component, though it /// might be the empty string. 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 /// /// A relative URI /// /// ``` /// # use http::Uri; /// /// let uri: Uri = "/hello/world".parse().unwrap(); /// /// assert_eq!(uri.path(), "/hello/world"); /// ``` /// /// An absolute URI /// /// ``` /// # use http::Uri; /// let uri: Uri = "http://example.org/hello/world".parse().unwrap(); /// /// assert_eq!(uri.path(), "/hello/world"); /// ``` #[inline] pub fn path(&self) -> &str { if self.has_path() { self.path_and_query.path() } else { "" } } /// Get the scheme of this `Uri`. /// /// The URI scheme refers to a specification for assigning identifiers /// within that scheme. Only absolute URIs contain a scheme component, but /// not all absolute URIs will contain a scheme component. Although scheme /// names are case-insensitive, the canonical form is lowercase. /// /// ```notrust /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1 /// |-| /// | /// scheme /// ``` /// /// # Examples /// /// Absolute URI /// /// ``` /// use http::uri::{Scheme, Uri}; /// /// let uri: Uri = "http://example.org/hello/world".parse().unwrap(); /// /// assert_eq!(uri.scheme(), Some(&Scheme::HTTP)); /// ``` /// /// /// Relative URI /// /// ``` /// # use http::Uri; /// let uri: Uri = "/hello/world".parse().unwrap(); /// /// assert!(uri.scheme().is_none()); /// ``` #[inline] pub fn scheme(&self) -> Option<&Scheme> { if self.scheme.inner.is_none() { None } else { Some(&self.scheme) } } /// Get the scheme of this `Uri` as a `&str`. /// /// # Example /// /// ``` /// # use http::Uri; /// let uri: Uri = "http://example.org/hello/world".parse().unwrap(); /// /// assert_eq!(uri.scheme_str(), Some("http")); /// ``` #[inline] pub fn scheme_str(&self) -> Option<&str> { if self.scheme.inner.is_none() { None } else { Some(self.scheme.as_str()) } } /// Get the authority of this `Uri`. /// /// The authority is a hierarchical element for naming authority such that /// the remainder of the URI is delegated to that authority. For HTTP, the /// authority consists of the host and port. The host portion of the /// authority is **case-insensitive**. /// /// The authority also includes a `username:password` component, however /// the use of this is deprecated and should be avoided. /// /// ```notrust /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1 /// |-------------------------------| /// | /// authority /// ``` /// /// # Examples /// /// Absolute URI /// /// ``` /// # use http::Uri; /// let uri: Uri = "http://example.org:80/hello/world".parse().unwrap(); /// /// assert_eq!(uri.authority().map(|a| a.as_str()), Some("example.org:80")); /// ``` /// /// /// Relative URI /// /// ``` /// # use http::Uri; /// let uri: Uri = "/hello/world".parse().unwrap(); /// /// assert!(uri.authority().is_none()); /// ``` #[inline] pub fn authority(&self) -> Option<&Authority> { if self.authority.data.is_empty() { None } else { Some(&self.authority) } } /// Get the host of this `Uri`. /// /// 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 /// /// Absolute URI /// /// ``` /// # use http::Uri; /// let uri: Uri = "http://example.org:80/hello/world".parse().unwrap(); /// /// assert_eq!(uri.host(), Some("example.org")); /// ``` /// /// /// Relative URI /// /// ``` /// # use http::Uri; /// let uri: Uri = "/hello/world".parse().unwrap(); /// /// assert!(uri.host().is_none()); /// ``` #[inline] pub fn host(&self) -> Option<&str> { self.authority().map(|a| a.host()) } /// Get the port part of this `Uri`. /// /// 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 /// /// Absolute URI with port /// /// ``` /// # use http::Uri; /// let uri: Uri = "http://example.org:80/hello/world".parse().unwrap(); /// /// let port = uri.port().unwrap(); /// assert_eq!(port.as_u16(), 80); /// ``` /// /// Absolute URI without port /// /// ``` /// # use http::Uri; /// let uri: Uri = "http://example.org/hello/world".parse().unwrap(); /// /// assert!(uri.port().is_none()); /// ``` /// /// Relative URI /// /// ``` /// # use http::Uri; /// let uri: Uri = "/hello/world".parse().unwrap(); /// /// assert!(uri.port().is_none()); /// ``` pub fn port(&self) -> Option> { self.authority().and_then(|a| a.port()) } /// Get the port of this `Uri` as a `u16`. /// /// /// # Example /// /// ``` /// # use http::{Uri, uri::Port}; /// let uri: Uri = "http://example.org:80/hello/world".parse().unwrap(); /// /// assert_eq!(uri.port_u16(), Some(80)); /// ``` pub fn port_u16(&self) -> Option { self.port().map(|p| p.as_u16()) } /// Get the query string of this `Uri`, starting after the `?`. /// /// 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 /// /// Absolute URI /// /// ``` /// # use http::Uri; /// let uri: Uri = "http://example.org/hello/world?key=value".parse().unwrap(); /// /// assert_eq!(uri.query(), Some("key=value")); /// ``` /// /// Relative URI with a query string component /// /// ``` /// # use http::Uri; /// let uri: Uri = "/hello/world?key=value&foo=bar".parse().unwrap(); /// /// assert_eq!(uri.query(), Some("key=value&foo=bar")); /// ``` /// /// Relative URI without a query string component /// /// ``` /// # use http::Uri; /// let uri: Uri = "/hello/world".parse().unwrap(); /// /// assert!(uri.query().is_none()); /// ``` #[inline] pub fn query(&self) -> Option<&str> { self.path_and_query.query() } fn has_path(&self) -> bool { !self.path_and_query.data.is_empty() || !self.scheme.inner.is_none() } } impl<'a> TryFrom<&'a [u8]> for Uri { type Error = InvalidUri; #[inline] fn try_from(t: &'a [u8]) -> Result { Uri::from_shared(Bytes::copy_from_slice(t)) } } impl<'a> TryFrom<&'a str> for Uri { type Error = InvalidUri; #[inline] fn try_from(t: &'a str) -> Result { t.parse() } } impl<'a> TryFrom<&'a String> for Uri { type Error = InvalidUri; #[inline] fn try_from(t: &'a String) -> Result { t.parse() } } impl TryFrom for Uri { type Error = InvalidUri; #[inline] fn try_from(t: String) -> Result { Uri::from_shared(Bytes::from(t)) } } impl TryFrom> for Uri { type Error = InvalidUri; #[inline] fn try_from(vec: Vec) -> Result { Uri::from_shared(Bytes::from(vec)) } } impl TryFrom for Uri { type Error = InvalidUriParts; #[inline] fn try_from(src: Parts) -> Result { Uri::from_parts(src) } } impl<'a> TryFrom<&'a Uri> for Uri { type Error = crate::Error; #[inline] fn try_from(src: &'a Uri) -> Result { Ok(src.clone()) } } /// Convert an `Authority` into a `Uri`. impl From for Uri { fn from(authority: Authority) -> Self { Self { scheme: Scheme::empty(), authority, path_and_query: PathAndQuery::empty(), } } } /// Convert a `PathAndQuery` into a `Uri`. impl From for Uri { fn from(path_and_query: PathAndQuery) -> Self { Self { scheme: Scheme::empty(), authority: Authority::empty(), path_and_query, } } } /// Convert a `Uri` into `Parts` impl From for Parts { fn from(src: Uri) -> Self { let path_and_query = if src.has_path() { Some(src.path_and_query) } else { None }; let scheme = match src.scheme.inner { Scheme2::None => None, _ => Some(src.scheme), }; let authority = if src.authority.data.is_empty() { None } else { Some(src.authority) }; Parts { scheme, authority, path_and_query, _priv: (), } } } fn parse_full(mut s: Bytes) -> Result { // Parse the scheme let scheme = match Scheme2::parse(&s[..])? { Scheme2::None => Scheme2::None, Scheme2::Standard(p) => { // TODO: use truncate let _ = s.split_to(p.len() + 3); Scheme2::Standard(p) } Scheme2::Other(n) => { // Grab the protocol let mut scheme = s.split_to(n + 3); // Strip ://, TODO: truncate let _ = scheme.split_off(n); // Allocate the ByteStr let val = unsafe { ByteStr::from_utf8_unchecked(scheme) }; Scheme2::Other(Box::new(val)) } }; // Find the end of the authority. The scheme will already have been // extracted. let authority_end = Authority::parse(&s[..])?; if scheme.is_none() { if authority_end != s.len() { return Err(ErrorKind::InvalidFormat.into()); } let authority = Authority { data: unsafe { ByteStr::from_utf8_unchecked(s) }, }; return Ok(Uri { scheme: scheme.into(), authority, path_and_query: PathAndQuery::empty(), }); } // Authority is required when absolute if authority_end == 0 { return Err(ErrorKind::InvalidFormat.into()); } let authority = s.split_to(authority_end); let authority = Authority { data: unsafe { ByteStr::from_utf8_unchecked(authority) }, }; Ok(Uri { scheme: scheme.into(), authority, path_and_query: PathAndQuery::from_shared(s)?, }) } impl FromStr for Uri { type Err = InvalidUri; #[inline] fn from_str(s: &str) -> Result { Uri::try_from(s.as_bytes()) } } impl PartialEq for Uri { fn eq(&self, other: &Uri) -> bool { if self.scheme() != other.scheme() { return false; } if self.authority() != other.authority() { return false; } if self.path() != other.path() { return false; } if self.query() != other.query() { return false; } true } } impl PartialEq for Uri { fn eq(&self, other: &str) -> bool { let mut other = other.as_bytes(); let mut absolute = false; if let Some(scheme) = self.scheme() { let scheme = scheme.as_str().as_bytes(); absolute = true; if other.len() < scheme.len() + 3 { return false; } if !scheme.eq_ignore_ascii_case(&other[..scheme.len()]) { return false; } other = &other[scheme.len()..]; if &other[..3] != b"://" { return false; } other = &other[3..]; } if let Some(auth) = self.authority() { let len = auth.data.len(); absolute = true; if other.len() < len { return false; } if !auth.data.as_bytes().eq_ignore_ascii_case(&other[..len]) { return false; } other = &other[len..]; } let path = self.path(); if other.len() < path.len() || path.as_bytes() != &other[..path.len()] { if absolute && path == "/" { // PathAndQuery can be omitted, fall through } else { return false; } } else { other = &other[path.len()..]; } if let Some(query) = self.query() { if other.is_empty() { return query.is_empty(); } if other[0] != b'?' { return false; } other = &other[1..]; if other.len() < query.len() { return false; } if query.as_bytes() != &other[..query.len()] { return false; } other = &other[query.len()..]; } other.is_empty() || other[0] == b'#' } } impl PartialEq for str { fn eq(&self, uri: &Uri) -> bool { uri == self } } impl<'a> PartialEq<&'a str> for Uri { fn eq(&self, other: &&'a str) -> bool { self == *other } } impl<'a> PartialEq for &'a str { fn eq(&self, uri: &Uri) -> bool { uri == *self } } impl Eq for Uri {} /// Returns a `Uri` representing `/` impl Default for Uri { #[inline] fn default() -> Uri { Uri { scheme: Scheme::empty(), authority: Authority::empty(), path_and_query: PathAndQuery::slash(), } } } impl fmt::Display for Uri { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(scheme) = self.scheme() { write!(f, "{}://", scheme)?; } if let Some(authority) = self.authority() { write!(f, "{}", authority)?; } write!(f, "{}", self.path())?; if let Some(query) = self.query() { write!(f, "?{}", query)?; } Ok(()) } } impl fmt::Debug for Uri { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } } impl From for InvalidUri { fn from(src: ErrorKind) -> InvalidUri { InvalidUri(src) } } impl From for InvalidUriParts { fn from(src: ErrorKind) -> InvalidUriParts { InvalidUriParts(src.into()) } } impl InvalidUri { fn s(&self) -> &str { match self.0 { ErrorKind::InvalidUriChar => "invalid uri character", ErrorKind::InvalidScheme => "invalid scheme", ErrorKind::InvalidAuthority => "invalid authority", ErrorKind::InvalidPort => "invalid port", ErrorKind::InvalidFormat => "invalid format", ErrorKind::SchemeMissing => "scheme missing", ErrorKind::AuthorityMissing => "authority missing", ErrorKind::PathAndQueryMissing => "path missing", ErrorKind::TooLong => "uri too long", ErrorKind::Empty => "empty string", ErrorKind::SchemeTooLong => "scheme too long", } } } impl fmt::Display for InvalidUri { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.s().fmt(f) } } impl Error for InvalidUri {} impl fmt::Display for InvalidUriParts { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl Error for InvalidUriParts {} impl Hash for Uri { fn hash(&self, state: &mut H) where H: Hasher, { if !self.scheme.inner.is_none() { self.scheme.hash(state); state.write_u8(0xff); } if let Some(auth) = self.authority() { auth.hash(state); } Hash::hash_slice(self.path().as_bytes(), state); if let Some(query) = self.query() { b'?'.hash(state); Hash::hash_slice(query.as_bytes(), state); } } } http-1.2.0/src/uri/path.rs000064400000000000000000000402441046102023000134770ustar 00000000000000use std::convert::TryFrom; use std::str::FromStr; use std::{cmp, fmt, hash, 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 = u16::MAX; impl PathAndQuery { // Not public while `bytes` is unstable. pub(super) fn from_shared(mut src: Bytes) -> Result { 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. #[rustfmt::skip] 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. #[rustfmt::skip] 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 #[rustfmt::skip] 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, }) } /// 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(src: T) -> Result 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 { 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 { TryFrom::try_from(s.as_bytes()) } } impl TryFrom> for PathAndQuery { type Error = InvalidUri; #[inline] fn try_from(vec: Vec) -> Result { PathAndQuery::from_shared(vec.into()) } } impl TryFrom for PathAndQuery { type Error = InvalidUri; #[inline] fn try_from(s: String) -> Result { PathAndQuery::from_shared(s.into()) } } impl TryFrom<&String> for PathAndQuery { type Error = InvalidUri; #[inline] fn try_from(s: &String) -> Result { TryFrom::try_from(s.as_bytes()) } } impl FromStr for PathAndQuery { type Err = InvalidUri; #[inline] fn from_str(s: &str) -> Result { 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, "/") } } } impl hash::Hash for PathAndQuery { fn hash(&self, state: &mut H) { self.data.hash(state); } } // ===== PartialEq / PartialOrd ===== impl PartialEq for PathAndQuery { #[inline] fn eq(&self, other: &PathAndQuery) -> bool { self.data == other.data } } impl Eq for PathAndQuery {} impl PartialEq for PathAndQuery { #[inline] fn eq(&self, other: &str) -> bool { self.as_str() == other } } impl<'a> PartialEq 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 for str { #[inline] fn eq(&self, other: &PathAndQuery) -> bool { self == other.as_str() } } impl PartialEq for PathAndQuery { #[inline] fn eq(&self, other: &String) -> bool { self.as_str() == other.as_str() } } impl PartialEq 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 { self.as_str().partial_cmp(other.as_str()) } } impl PartialOrd for PathAndQuery { #[inline] fn partial_cmp(&self, other: &str) -> Option { self.as_str().partial_cmp(other) } } impl PartialOrd for str { #[inline] fn partial_cmp(&self, other: &PathAndQuery) -> Option { self.partial_cmp(other.as_str()) } } impl<'a> PartialOrd<&'a str> for PathAndQuery { #[inline] fn partial_cmp(&self, other: &&'a str) -> Option { self.as_str().partial_cmp(*other) } } impl<'a> PartialOrd for &'a str { #[inline] fn partial_cmp(&self, other: &PathAndQuery) -> Option { self.partial_cmp(&other.as_str()) } } impl PartialOrd for PathAndQuery { #[inline] fn partial_cmp(&self, other: &String) -> Option { self.as_str().partial_cmp(other.as_str()) } } impl PartialOrd for String { #[inline] fn partial_cmp(&self, other: &PathAndQuery) -> Option { 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)) } } http-1.2.0/src/uri/port.rs000064400000000000000000000063361046102023000135330ustar 00000000000000use std::fmt; use super::{ErrorKind, InvalidUri}; /// The port component of a URI. pub struct Port { port: u16, repr: T, } impl Port { /// 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 const fn as_u16(&self) -> u16 { self.port } } impl Port where T: AsRef, { /// Converts a `str` to a port number. /// /// The supplied `str` must be a valid u16. pub(crate) fn from_str(bytes: T) -> Result { bytes .as_ref() .parse::() .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 fmt::Debug for Port where T: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Port").field(&self.port).finish() } } impl fmt::Display for Port { 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 From> for u16 { fn from(port: Port) -> Self { port.as_u16() } } impl AsRef for Port where T: AsRef, { fn as_ref(&self) -> &str { self.as_str() } } impl PartialEq> for Port { fn eq(&self, other: &Port) -> bool { self.port == other.port } } impl PartialEq for Port { fn eq(&self, other: &u16) -> bool { self.port == *other } } impl PartialEq> for u16 { fn eq(&self, other: &Port) -> 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)); } } http-1.2.0/src/uri/scheme.rs000064400000000000000000000252131046102023000140060ustar 00000000000000use 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> { 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 { 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 { TryFrom::try_from(s.as_bytes()) } } impl FromStr for Scheme { type Err = InvalidUri; fn from_str(s: &str) -> Result { 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 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(a), Other(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 for Scheme { fn eq(&self, other: &str) -> bool { self.as_str().eq_ignore_ascii_case(other) } } /// Case-insensitive equality impl PartialEq for str { fn eq(&self, other: &Scheme) -> bool { other == self } } /// Case-insensitive hashing impl Hash for Scheme { fn hash(&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 Scheme2 { pub(super) fn is_none(&self) -> bool { matches!(*self, Scheme2::None) } } // 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. #[rustfmt::skip] 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 { // Postcondition: On all Ok() returns, s is valid UTF-8 fn parse_exact(s: &[u8]) -> Result, 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, 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)); } // Invalid 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 From for Scheme2 { fn from(src: Protocol) -> Self { Scheme2::Standard(src) } } #[doc(hidden)] impl From 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("Unexpectedly valid Scheme"); // Invalid UTF-8 Scheme::try_from([0xC0].as_ref()).expect_err("Unexpectedly valid Scheme"); } fn scheme(s: &str) -> Scheme { s.parse().expect(&format!("Invalid scheme: {}", s)) } } http-1.2.0/src/uri/tests.rs000064400000000000000000000256411046102023000137110ustar 00000000000000use 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.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.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); } http-1.2.0/src/version.rs000064400000000000000000000032261046102023000134300ustar 00000000000000//! 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!(), }) } } http-1.2.0/tests/header_map.rs000064400000000000000000000423261046102023000144070ustar 00000000000000use 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::::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::::with_capacity(24_576); } #[test] #[should_panic] fn with_capacity_overflow() { HeaderMap::::with_capacity(24_577); } #[test] #[should_panic] fn reserve_overflow() { // See https://github.com/hyperium/http/issues/352 let mut headers = HeaderMap::::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::().unwrap(), "world".parse().unwrap(), ); headers.insert( "zomg".parse::().unwrap(), "bar".parse().unwrap(), ); headers.append( "hello".parse::().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::::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::().unwrap(), "world".parse().unwrap(), ); headers.insert( "zomg".parse::().unwrap(), "foo".parse().unwrap(), ); headers.append( "hello".parse::().unwrap(), "world2".parse().unwrap(), ); headers.insert( "more".parse::().unwrap(), "words".parse().unwrap(), ); headers.append( "more".parse::().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::().unwrap(), "world".parse().unwrap(), ); assert_ne!(a, b); b.insert( "hello".parse::().unwrap(), "world".parse().unwrap(), ); assert_eq!(a, b); a.insert("foo".parse::().unwrap(), "bar".parse().unwrap()); a.append("foo".parse::().unwrap(), "baz".parse().unwrap()); assert_ne!(a, b); b.insert("foo".parse::().unwrap(), "bar".parse().unwrap()); assert_ne!(a, b); b.append("foo".parse::().unwrap(), "baz".parse().unwrap()); assert_eq!(a, b); a.append("a".parse::().unwrap(), "a".parse().unwrap()); a.append("a".parse::().unwrap(), "b".parse().unwrap()); b.append("a".parse::().unwrap(), "b".parse().unwrap()); b.append("a".parse::().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::>(); assert_eq!(&vals, &[&"json", &"html", &"xml"]); } fn custom_std(n: usize) -> Vec { (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, CACHE_STATUS, CDN_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(headers: &mut HeaderMap, key: K) -> Vec 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(headers: &mut HeaderMap, key: K) -> Option where K: IntoHeaderName, { match headers.entry(key) { Entry::Occupied(e) => Some(e.remove_entry().1), Entry::Vacant(_) => None, } } #[test] fn ensure_miri_sharedreadonly_not_violated() { let mut headers = HeaderMap::new(); headers.insert( HeaderName::from_static("chunky-trailer"), HeaderValue::from_static("header data"), ); let _foo = &headers.iter().next(); } http-1.2.0/tests/header_map_fuzz.rs000064400000000000000000000230441046102023000154610ustar 00000000000000use 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; #[cfg(not(miri))] #[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)] #[allow(dead_code)] struct Fuzz { // The magic seed that makes the test case reproducible seed: [u8; 32], // Actions to perform steps: Vec, // 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, // Old value }, Append { name: HeaderName, val: HeaderValue, ret: bool, }, Remove { name: HeaderName, // Name to remove val: Option, // Value to get }, } // An alternate implementation of HeaderMap backed by HashMap #[derive(Debug, Clone, Default)] struct AltMap { map: HashMap>, } 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, 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(_: &mut Gen) -> Self { Self::new(rand::thread_rng().gen()) } } impl AltMap { fn gen_step(&mut self, weight: &Weight, rng: &mut StdRng) -> Step { let action = self.gen_action(weight, rng); Step { 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, val, 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, 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, val, 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 { 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 { let old = self.map.insert(name, vec![val]); old.and_then(|v| v.into_iter().next()) } fn remove(&mut self, name: &HeaderName) -> Option { self.map.remove(name).and_then(|v| v.into_iter().next()) } fn assert_identical(&self, other: &HeaderMap) { 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) { 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::>(); 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::CACHE_STATUS, header::CDN_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() } http-1.2.0/tests/status_code.rs000064400000000000000000000037621046102023000146400ustar 00000000000000use 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() }