chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

1
vendor/h2/.cargo-checksum.json vendored Normal file

File diff suppressed because one or more lines are too long

6
vendor/h2/.cargo_vcs_info.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "2aeb81fdb5f1d96ee035266186f94fa2927616c7"
},
"path_in_vcs": ""
}

1
vendor/h2/.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
github: seanmonstar

115
vendor/h2/.github/workflows/CI.yml vendored Normal file
View File

@@ -0,0 +1,115 @@
name: CI
on:
pull_request:
push:
branches:
- master
env:
RUST_BACKTRACE: 1
jobs:
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
needs: [style]
runs-on: ubuntu-latest
env:
RUSTFLAGS: -Dwarnings
strategy:
matrix:
rust:
- nightly
- beta
- stable
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust (${{ matrix.rust }})
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Install libssl-dev
run: sudo apt-get update && sudo apt-get install libssl-dev
- name: Build without unstable flag
run: cargo build
- name: Check with unstable flag
run: cargo check --features unstable
- name: Run lib tests and doc tests
run: cargo test
- name: Run integration tests
run: cargo test -p h2-tests
- name: Run h2spec
run: ./ci/h2spec.sh
if: matrix.rust == 'stable'
#clippy_check:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - name: Run Clippy
# run: cargo clippy --all-targets --all-features
msrv:
name: Check MSRV
needs: [style]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get MSRV from package metadata
id: msrv
run: grep rust-version Cargo.toml | cut -d '"' -f2 | sed 's/^/version=/' >> $GITHUB_OUTPUT
- name: Install Rust (${{ steps.metadata.outputs.msrv }})
id: msrv-toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ steps.msrv.outputs.version }}
- name: Pin some dependencies for MSRV
run: |
cargo update --package tokio-util --precise 0.7.11
cargo update --package tokio --precise 1.38.1
cargo update --package indexmap --precise 2.11.3
cargo update --package hashbrown --precise 0.15.0
cargo update --package once_cell --precise 1.20.3
cargo update --package tracing --precise 0.1.41
cargo update --package tracing-subscriber --precise 0.3.19
cargo update --package tracing-core --precise 0.1.33
cargo update --package itoa --precise 1.0.15
- run: cargo check -p h2
minimal-versions:
runs-on: ubuntu-latest
needs: [style]
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 --ignore-private check

388
vendor/h2/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,388 @@
# 0.4.13 (January 5, 2026)
* Add support for 1xx informational responses on client and server side.
* Fix auto-releasing of padding bytes of DATA frames for flow control windows.
* Fix to stop assigning capacity to pending streams which can't use it yet.
* Fix tracing to not grab the parent for the connection span.
# 0.4.12 (August 5, 2025)
* Fix default limits on max stored reset streams and duration to more reasonable values.
# 0.4.11 (June 30, 2025)
* Fix client to not return an error when a clean shutdown otherwise doesn't get a TLS close_notify, which some servers don't bother sending.
# 0.4.10 (May 5, 2025)
* Fix `is_end_stream()` to return true only when ended cleanly, not when errored.
# 0.4.9 (April 14, 2025)
* Add `sever::Connection::has_streams()` method to check for active streams.
# 0.4.8 (February 18, 2025)
* Fix handling implicit stream resets at the more correct time.
* Fix window size decrements of send-closed streams.
* Fix reclaiming of reserved capacity when streams are closed.
* Fix to no longer call `poll_flush` after `poll_shutdown`.
* Fix busy loop in task when poll_shutdown returns pending.
# 0.4.7 (November 19, 2024)
* Fix treating HEADERS frames with a non-zero content-length but END_STREAM flag as malformed.
* Fix notifying the stream task when automatically reset on receipt of a stream error.
# 0.4.6 (August 19, 2024)
* Add `current_max_send_streams()` and `current_max_recv_streams()` to `client::SendRequest`.
* Fix sending a PROTOCOL_ERROR instead of REFUSED_STREAM when receiving oversized headers.
* Fix notifying a PushPromise task properly.
* Fix notifying a stream task when reset.
# 0.4.5 (May 17, 2024)
* Fix race condition that sometimes hung connections during shutdown.
* Fix pseudo header construction for CONNECT and OPTIONS requests.
# 0.4.4 (April 3, 2024)
* Limit number of CONTINUATION frames for misbehaving connections.
# 0.4.3 (March 15, 2024)
* Fix flow control limits to not apply until receiving SETTINGS ack.
* Fix not returning an error if IO ended without `close_notify`.
* Improve performance of decoding many headers.
# 0.4.2 (January 17th, 2024)
* Limit error resets for misbehaving connections.
* Fix selecting MAX_CONCURRENT_STREAMS value if no value is advertised initially.
# 0.4.1 (January 8, 2024)
* Fix assigning connection capacity which could starve streams in some instances.
# 0.4.0 (November 15, 2023)
* Update to `http` 1.0.
* Remove deprecated `Server::poll_close()`.
# 0.3.22 (November 15, 2023)
* Add `header_table_size(usize)` option to client and server builders.
* Improve throughput when vectored IO is not available.
* Update indexmap to 2.
# 0.3.21 (August 21, 2023)
* Fix opening of new streams over peer's max concurrent limit.
* Fix `RecvStream` to return data even if it has received a `CANCEL` stream error.
* Update MSRV to 1.63.
# 0.3.20 (June 26, 2023)
* Fix panic if a server received a request with a `:status` pseudo header in the 1xx range.
* Fix panic if a reset stream had pending push promises that were more than allowed.
* Fix potential flow control overflow by subtraction, instead returning a connection error.
# 0.3.19 (May 12, 2023)
* Fix counting reset streams when triggered by a GOAWAY.
* Send `too_many_resets` in opaque debug data of GOAWAY when too many resets received.
# 0.3.18 (April 17, 2023)
* Fix panic because of opposite check in `is_remote_local()`.
# 0.3.17 (April 13, 2023)
* Add `Error::is_library()` method to check if the originated inside `h2`.
* Add `max_pending_accept_reset_streams(usize)` option to client and server
builders.
* Fix theoretical memory growth when receiving too many HEADERS and then
RST_STREAM frames faster than an application can accept them off the queue.
(CVE-2023-26964)
# 0.3.16 (February 27, 2023)
* Set `Protocol` extension on requests when received Extended CONNECT requests.
* Remove `B: Unpin + 'static` bound requiremented of bufs
* Fix releasing of frames when stream is finished, reducing memory usage.
* Fix panic when trying to send data and connection window is available, but stream window is not.
* Fix spurious wakeups when stream capacity is not available.
# 0.3.15 (October 21, 2022)
* Remove `B: Buf` bound on `SendStream`'s parameter
* add accessor for `StreamId` u32
# 0.3.14 (August 16, 2022)
* Add `Error::is_reset` function.
* Bump MSRV to Rust 1.56.
* Return `RST_STREAM(NO_ERROR)` when the server early responds.
# 0.3.13 (March 31, 2022)
* Update private internal `tokio-util` dependency.
# 0.3.12 (March 9, 2022)
* Avoid time operations that can panic (#599)
* Bump MSRV to Rust 1.49 (#606)
* Fix header decoding error when a header name is contained at a continuation
header boundary (#589)
* Remove I/O type names from handshake `tracing` spans (#608)
# 0.3.11 (January 26, 2022)
* Make `SendStream::poll_capacity` never return `Ok(Some(0))` (#596)
* Fix panic when receiving already reset push promise (#597)
# 0.3.10 (January 6, 2022)
* Add `Error::is_go_away()` and `Error::is_remote()` methods.
* Fix panic if receiving malformed PUSH_PROMISE with stream ID of 0.
# 0.3.9 (December 9, 2021)
* Fix hang related to new `max_send_buffer_size`.
# 0.3.8 (December 8, 2021)
* Add "extended CONNECT support". Adds `h2::ext::Protocol`, which is used for request and response extensions to connect new protocols over an HTTP/2 stream.
* Add `max_send_buffer_size` options to client and server builders, and a default of ~400MB. This acts like a high-water mark for the `poll_capacity()` method.
* Fix panic if receiving malformed HEADERS with stream ID of 0.
# 0.3.7 (October 22, 2021)
* Fix panic if server sends a malformed frame on a stream client was about to open.
* Fix server to treat `:status` in a request as a stream error instead of connection error.
# 0.3.6 (September 30, 2021)
* Fix regression of `h2::Error` that were created via `From<h2::Reason>` not returning their reason code in `Error::reason()`.
# 0.3.5 (September 29, 2021)
* Fix sending of very large headers. Previously when a single header was too big to fit in a single `HEADERS` frame, an error was returned. Now it is broken up and sent correctly.
* Fix buffered data field to be a bigger integer size.
* Refactor error format to include what initiated the error (remote, local, or user), if it was a stream or connection-level error, and any received debug data.
# 0.3.4 (August 20, 2021)
* Fix panic when encoding header size update over a certain size.
* Fix `SendRequest` to wake up connection when dropped.
* Fix potential hang if `RecvStream` is placed in the request or response `extensions`.
* Stop calling `Instant::now` if zero reset streams are configured.
# 0.3.3 (April 29, 2021)
* Fix client being able to make `CONNECT` requests without a `:path`.
* Expose `RecvStream::poll_data`.
* Fix some docs.
# 0.3.2 (March 24, 2021)
* Fix incorrect handling of received 1xx responses on the client when the request body is still streaming.
# 0.3.1 (February 26, 2021)
* Add `Connection::max_concurrent_recv_streams()` getter.
* Add `Connection::max_concurrent_send_streams()` getter.
* Fix client to ignore receipt of 1xx headers frames.
* Fix incorrect calculation of pseudo header lengths when determining if a received header is too big.
* Reduce monomorphized code size of internal code.
# 0.3.0 (December 23, 2020)
* Update to Tokio v1 and Bytes v1.
* Disable `tracing`'s `log` feature. (It can still be enabled by a user in their own `Cargo.toml`.)
# 0.2.7 (October 22, 2020)
* Fix stream ref count when sending a push promise
* Fix receiving empty DATA frames in response to a HEAD request
* Fix handling of client disabling SERVER_PUSH
# 0.2.6 (July 13, 2020)
* Integrate `tracing` directly where `log` was used. (For 0.2.x, `log`s are still emitted by default.)
# 0.2.5 (May 6, 2020)
* Fix rare debug assert failure in store shutdown.
# 0.2.4 (March 30, 2020)
* Fix when receiving `SETTINGS_HEADER_TABLE_SIZE` setting.
# 0.2.3 (March 25, 2020)
* Fix server being able to accept `CONNECT` requests without `:scheme` or `:path`.
* Fix receiving a GOAWAY frame from updating the recv max ID, it should only update max send ID.
# 0.2.2 (March 3, 2020)
* Reduce size of `FlowControl` and `RecvStream`.
# 0.2.1 (December 6, 2019)
* Relax `Unpin` bounds on the send `Buf` generic.
# 0.2.0 (December 3, 2019)
* Add `server::Connection::set_initial_window_size` and `client::Connection::set_initial_window_size` which can adjust the `INITIAL_WINDOW_SIZE` setting on an existing connection (#421).
* Update to `http` v0.2.
* Update to `tokio` v0.2.
* Change `unstable-stream` feature to `stream`.
* Change `ReserveCapacity` to `FlowControl` (#423).
* Remove `From<io::Error>` for `Error`.
# 0.2.0-alpha.3 (October 1, 2019)
* Update to futures `0.3.0-alpha.19`.
* Update to tokio `0.2.0-alpha.6`.
# 0.2.0-alpha.2 (September 20, 2019)
* Add server support for `PUSH_PROMISE`s (#327).
* Update to tokio `0.2.0-alpha.5`.
* Change `stream` feature to `unstable-stream`.
# 0.2.0-alpha.1 (August 30, 2019)
* Update from `futures` 0.1 to `std::future::Future`.
* Update `AsyncRead`/`AsyncWrite` to `tokio-io` 0.2 alpha.
* Change `Stream` implementations to be optional, default disabled. Specific async and poll functions are now inherent, and `Stream` can be re-enabled with the `stream` cargo feature.
# 0.1.25 (June 28, 2019)
* Fix to send a `RST_STREAM` instead of `GOAWAY` if receiving a frame on a previously closed stream.
* Fix receiving trailers without an end-stream flag to be a stream error instead of connection error.
# 0.1.24 (June 17, 2019)
* Fix server wrongly rejecting requests that don't have an `:authority` header (#372).
# 0.1.23 (June 4, 2019)
* Fix leaking of received DATA frames if the `RecvStream` is never polled (#368).
# 0.1.22 (June 3, 2019)
* Fix rare panic when remote sends `RST_STREAM` or `GOAWAY` for a stream pending window capacity (#364).
# 0.1.21 (May 30, 2019)
* Fix write loop when a header didn't fit in write buffer.
# 0.1.20 (May 16, 2019)
* Fix lifetime conflict for older compilers.
# 0.1.19 (May 15, 2019)
* Fix rare crash if `CONTINUATION` frame resumed in the middle of headers with the same name.
* Fix HPACK encoder using an old evicted index for repeated header names.
# 0.1.18 (April 9, 2019)
* Fix `server::Connection::abrupt_shutdown` to no longer return the same error the user sent (#352).
# 0.1.17 (March 12, 2019)
* Add user PING support (#346).
* Fix notifying a `RecvStream` task if locally sending a reset.
* Fix connections "hanging" when all handles are dropped but some streams had been reset.
# 0.1.16 (January 24, 2019)
* Log header values when malformed (#342).
# 0.1.15 (January 12, 2019)
* Fix race condition bug related to shutting down the client (#338).
# 0.1.14 (December 5, 2018)
* Fix closed streams to always return window capacity to the connection (#334).
* Fix locking when `Debug` printing an `OpaqueStreamRef` (#333).
* Fix inverted split for DATA frame padding (#330).
* Reduce `Debug` noise for `Frame` (#329).
# 0.1.13 (October 16, 2018)
* Add client support for Push Promises (#314).
* Expose `io::Error` from `h2::Error` (#311)
* Misc bug fixes (#304, #309, #319, #313, #320).
# 0.1.12 (August 8, 2018)
* Fix initial send window size (#301).
* Fix panic when calling `reserve_capacity` after connection has been closed (#302).
* Fix handling of incoming `SETTINGS_INITIAL_WINDOW_SIZE`. (#299)
# 0.1.11 (July 31, 2018)
* Add `stream_id` accessors to public API types (#292).
* Fix potential panic when dropping clients (#295).
* Fix busy loop when shutting down server (#296).
# 0.1.10 (June 15, 2018)
* Fix potential panic in `SendRequest::poll_ready()` (#281).
* Fix infinite loop on reset connection during prefix (#285).
# 0.1.9 (May 31, 2018)
* Add `poll_reset` to `SendResponse` and `SendStream` (#279).
# 0.1.8 (May 23, 2018)
* Fix client bug when max streams is reached. (#277)
# 0.1.7 (May 14, 2018)
* Misc bug fixes (#266, #273, #261, #275).
# 0.1.6 (April 24, 2018)
* Misc bug fixes related to stream management (#258, #260, #262).
# 0.1.5 (April 6, 2018)
* Fix the `last_stream_id` sent during graceful GOAWAY (#254).
# 0.1.4 (April 5, 2018)
* Add `initial_connection_window_size` to client and server `Builder`s (#249).
* Add `graceful_shutdown` and `abrupt_shutdown` to `server::Connection`,
deprecating `close_connection` (#250).
# 0.1.3 (March 28, 2018)
* Allow configuring max streams before the peer's settings frame is
received (#242).
* Fix HPACK decoding bug with regards to large literals (#244).
* Fix state transition bug triggered by receiving a RST_STREAM frame (#247).
# 0.1.2 (March 13, 2018)
* Fix another bug relating to resetting connections and reaching
max concurrency (#238).
# 0.1.1 (March 8, 2018)
* When streams are dropped, close the connection (#222).
* Notify send tasks on connection error (#231).
* Fix bug relating to resetting connections and reaching max concurrency (#235).
* Normalize HTTP request path to satisfy HTTP/2.0 specification (#228).
* Update internal dependencies.
# 0.1.0 (Jan 12, 2018)
* Initial release

84
vendor/h2/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,84 @@
# Contributing to _h2_ #
:balloon: Thanks for your help improving the project!
## Getting Help ##
If you have a question about the h2 library or have encountered problems using it, you may
[file an issue][issue] or ask a question on the [Tokio Discord][discord].
## Submitting a Pull Request ##
Do you have an improvement?
1. Submit an [issue][issue] describing your proposed change.
2. We will try to respond to your issue promptly.
3. Fork this repo, develop and test your code changes. See the project's [README](README.md) for further information about working in this repository.
4. Submit a pull request against this repo's `master` branch.
5. Your branch may be merged once all configured checks pass, including:
- Code review has been completed.
- The branch has passed tests in CI.
## Committing ##
When initially submitting a pull request, we prefer a single squashed commit. It
is preferable to split up contributions into multiple pull requests if the
changes are unrelated. All pull requests are squashed when merged, but
squashing yourself gives you better control over the commit message.
After the pull request is submitted, all changes should be done in separate
commits. This makes reviewing the evolution of the pull request easier. We will
squash all the changes into a single commit when we merge the pull request.
### Commit messages ###
Finalized commit messages should be in the following format:
```
Subject
Problem
Solution
Validation
```
#### Subject ####
- one line, <= 50 characters
- describe what is done; not the result
- use the active voice
- capitalize first word and proper nouns
- do not end in a period — this is a title/subject
- reference the github issue by number
##### Examples #####
```
bad: server disconnects should cause dst client disconnects.
good: Propagate disconnects from source to destination
```
```
bad: support tls servers
good: Introduce support for server-side TLS (#347)
```
#### Problem ####
Explain the context and why you're making that change. What is the problem
you're trying to solve? In some cases there is not a problem and this can be
thought of as being the motivation for your change.
#### Solution ####
Describe the modifications you've made.
#### Validation ####
Describe the testing you've done to validate your change. Performance-related
changes should include before- and after- benchmark results.
[issue]: https://github.com/hyperium/h2/issues/new
[discord]: https://discord.gg/tokio

788
vendor/h2/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,788 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "aws-lc-rs"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b5ce75405893cd713f9ab8e297d8e438f624dde7d706108285f7e17a25a180f"
dependencies = [
"aws-lc-sys",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "179c3777a8b5e70e90ea426114ffc565b2c1a9f82f6c4a0c5a34aa6ef5e781b6"
dependencies = [
"cc",
"cmake",
"dunce",
"fs_extra",
]
[[package]]
name = "bytes"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]]
name = "cc"
version = "1.2.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cmake"
version = "0.1.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
dependencies = [
"cc",
]
[[package]]
name = "dunce"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "env_logger"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
dependencies = [
"log",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "find-msvc-tools"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fs_extra"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "h2"
version = "0.4.13"
dependencies = [
"atomic-waker",
"bytes",
"env_logger",
"fnv",
"futures-core",
"futures-sink",
"hex",
"http",
"indexmap",
"quickcheck",
"rand",
"serde",
"serde_json",
"slab",
"tokio",
"tokio-rustls",
"tokio-util",
"tracing",
"walkdir",
"webpki-roots",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "http"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
dependencies = [
"bytes",
"itoa",
]
[[package]]
name = "indexmap"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jobserver"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom 0.3.4",
"libc",
]
[[package]]
name = "libc"
version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "mio"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
dependencies = [
"libc",
"wasi",
"windows-sys 0.61.2",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quickcheck"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
dependencies = [
"rand",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[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 0.2.16",
]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.16",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rustls"
version = "0.23.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
dependencies = [
"aws-lc-rs",
"log",
"once_cell",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "slab"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "socket2"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
dependencies = [
"libc",
"windows-sys 0.60.2",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
dependencies = [
"bytes",
"libc",
"mio",
"pin-project-lite",
"socket2",
"tokio-macros",
"windows-sys 0.61.2",
]
[[package]]
name = "tokio-macros"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-rustls"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
dependencies = [
"rustls",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tracing"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
dependencies = [
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
dependencies = [
"once_cell",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "webpki-roots"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1",
"windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1",
"windows_x86_64_msvc 0.53.1",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "zerocopy"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"

159
vendor/h2/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,159 @@
# 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 = "2021"
rust-version = "1.63"
name = "h2"
version = "0.4.13"
authors = [
"Carl Lerche <me@carllerche.com>",
"Sean McArthur <sean@seanmonstar.com>",
]
build = false
exclude = [
"fixtures/**",
"ci/**",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "An HTTP/2 client and server"
documentation = "https://docs.rs/h2"
readme = "README.md"
keywords = [
"http",
"async",
"non-blocking",
]
categories = [
"asynchronous",
"web-programming",
"network-programming",
]
license = "MIT"
repository = "https://github.com/hyperium/h2"
[package.metadata.docs.rs]
features = ["stream"]
[features]
stream = []
unstable = []
[lib]
name = "h2"
path = "src/lib.rs"
[[example]]
name = "akamai"
path = "examples/akamai.rs"
[[example]]
name = "client"
path = "examples/client.rs"
[[example]]
name = "server"
path = "examples/server.rs"
[[bench]]
name = "main"
path = "benches/main.rs"
harness = false
[dependencies.atomic-waker]
version = "1.0.0"
[dependencies.bytes]
version = "1"
[dependencies.fnv]
version = "1.0.5"
[dependencies.futures-core]
version = "0.3"
default-features = false
[dependencies.futures-sink]
version = "0.3"
default-features = false
[dependencies.http]
version = "1"
[dependencies.indexmap]
version = "2"
features = ["std"]
[dependencies.slab]
version = "0.4.2"
[dependencies.tokio]
version = "1"
features = ["io-util"]
[dependencies.tokio-util]
version = "0.7.1"
features = [
"codec",
"io",
]
[dependencies.tracing]
version = "0.1.35"
features = ["std"]
default-features = false
[dev-dependencies.env_logger]
version = "0.10"
default-features = false
[dev-dependencies.hex]
version = "0.4.3"
[dev-dependencies.quickcheck]
version = "1.0.3"
default-features = false
[dev-dependencies.rand]
version = "0.8.4"
[dev-dependencies.serde]
version = "1.0.0"
[dev-dependencies.serde_json]
version = "1.0.0"
[dev-dependencies.tokio]
version = "1"
features = [
"rt-multi-thread",
"macros",
"sync",
"net",
]
[dev-dependencies.tokio-rustls]
version = "0.26"
[dev-dependencies.walkdir]
version = "2.3.2"
[dev-dependencies.webpki-roots]
version = "1"
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(fuzzing)"]

25
vendor/h2/LICENSE vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2017 h2 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.

58
vendor/h2/README.md vendored Normal file
View File

@@ -0,0 +1,58 @@
# H2
A Tokio aware, HTTP/2 client & server implementation for Rust.
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Crates.io](https://img.shields.io/crates/v/h2.svg)](https://crates.io/crates/h2)
[![Documentation](https://docs.rs/h2/badge.svg)][dox]
More information about this crate can be found in the [crate documentation][dox].
[dox]: https://docs.rs/h2
## Features
* Client and server HTTP/2 implementation.
* Implements the full HTTP/2 specification.
* Passes [h2spec](https://github.com/summerwind/h2spec).
* Focus on performance and correctness.
* Built on [Tokio](https://tokio.rs).
## Non goals
This crate is intended to only be an implementation of the HTTP/2
specification. It does not handle:
* Managing TCP connections
* HTTP 1.0 upgrade
* TLS
* Any feature not described by the HTTP/2 specification.
This crate is now used by [hyper](https://github.com/hyperium/hyper), which will provide all of these features.
## Usage
To use `h2`, first add this to your `Cargo.toml`:
```toml
[dependencies]
h2 = "0.4"
```
Next, add this to your crate:
```rust
extern crate h2;
use h2::server::Connection;
fn main() {
// ...
}
```
## FAQ
**Is this an embedded Java SQL database engine?**
[No](https://www.h2database.com).

148
vendor/h2/benches/main.rs vendored Normal file
View File

@@ -0,0 +1,148 @@
use bytes::Bytes;
use h2::{
client,
server::{self, SendResponse},
RecvStream,
};
use http::Request;
use std::{
error::Error,
time::{Duration, Instant},
};
use tokio::net::{TcpListener, TcpStream};
const NUM_REQUESTS_TO_SEND: usize = 100_000;
// The actual server.
async fn server(addr: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
let listener = TcpListener::bind(addr).await?;
loop {
if let Ok((socket, _peer_addr)) = listener.accept().await {
tokio::spawn(async move {
if let Err(e) = serve(socket).await {
println!(" -> err={:?}", e);
}
});
}
}
}
async fn serve(socket: TcpStream) -> Result<(), Box<dyn Error + Send + Sync>> {
let mut connection = server::handshake(socket).await?;
while let Some(result) = connection.accept().await {
let (request, respond) = result?;
tokio::spawn(async move {
if let Err(e) = handle_request(request, respond).await {
println!("error while handling request: {}", e);
}
});
}
Ok(())
}
async fn handle_request(
mut request: Request<RecvStream>,
mut respond: SendResponse<Bytes>,
) -> Result<(), Box<dyn Error + Send + Sync>> {
let body = request.body_mut();
while let Some(data) = body.data().await {
let data = data?;
let _ = body.flow_control().release_capacity(data.len());
}
let response = http::Response::new(());
let mut send = respond.send_response(response, false)?;
send.send_data(Bytes::from_static(b"pong"), true)?;
Ok(())
}
// The benchmark
async fn send_requests(addr: &str) -> Result<(), Box<dyn Error>> {
let tcp = loop {
let Ok(tcp) = TcpStream::connect(addr).await else {
continue;
};
break tcp;
};
let (client, h2) = client::handshake(tcp).await?;
// Spawn a task to run the conn...
tokio::spawn(async move {
if let Err(e) = h2.await {
println!("GOT ERR={:?}", e);
}
});
let mut handles = Vec::with_capacity(NUM_REQUESTS_TO_SEND);
for _i in 0..NUM_REQUESTS_TO_SEND {
let mut client = client.clone();
let task = tokio::spawn(async move {
let request = Request::builder().body(()).unwrap();
let instant = Instant::now();
let (response, _) = client.send_request(request, true).unwrap();
let response = response.await.unwrap();
let mut body = response.into_body();
while let Some(_chunk) = body.data().await {}
instant.elapsed()
});
handles.push(task);
}
let instant = Instant::now();
let mut result = Vec::with_capacity(NUM_REQUESTS_TO_SEND);
for handle in handles {
result.push(handle.await.unwrap());
}
let mut sum = Duration::new(0, 0);
for r in result.iter() {
sum = sum.checked_add(*r).unwrap();
}
println!("Overall: {}ms.", instant.elapsed().as_millis());
println!("Fastest: {}ms", result.iter().min().unwrap().as_millis());
println!("Slowest: {}ms", result.iter().max().unwrap().as_millis());
println!(
"Avg : {}ms",
sum.div_f64(NUM_REQUESTS_TO_SEND as f64).as_millis()
);
Ok(())
}
fn main() {
let _ = env_logger::try_init();
let addr = "127.0.0.1:5928";
println!("H2 running in current-thread runtime at {addr}:");
std::thread::spawn(|| {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(server(addr)).unwrap();
});
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(send_requests(addr)).unwrap();
let addr = "127.0.0.1:5929";
println!("H2 running in multi-thread runtime at {addr}:");
std::thread::spawn(|| {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.enable_all()
.build()
.unwrap();
rt.block_on(server(addr)).unwrap();
});
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(send_requests(addr)).unwrap();
}

72
vendor/h2/examples/akamai.rs vendored Normal file
View File

@@ -0,0 +1,72 @@
use h2::client;
use http::{Method, Request};
use tokio::net::TcpStream;
use tokio_rustls::TlsConnector;
use tokio_rustls::rustls::{pki_types::ServerName, RootCertStore};
use std::error::Error;
use std::net::ToSocketAddrs;
const ALPN_H2: &str = "h2";
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn Error>> {
let _ = env_logger::try_init();
let tls_client_config = std::sync::Arc::new({
let root_store = RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let mut c = tokio_rustls::rustls::ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
c.alpn_protocols.push(ALPN_H2.as_bytes().to_owned());
c
});
// Sync DNS resolution.
let addr = "http2.akamai.com:443"
.to_socket_addrs()
.unwrap()
.next()
.unwrap();
println!("ADDR: {:?}", addr);
let tcp = TcpStream::connect(&addr).await?;
let dns_name = ServerName::try_from("http2.akamai.com").unwrap();
let connector = TlsConnector::from(tls_client_config);
let res = connector.connect(dns_name, tcp).await;
let tls = res.unwrap();
{
let (_, session) = tls.get_ref();
let negotiated_protocol = session.alpn_protocol();
assert_eq!(Some(ALPN_H2.as_bytes()), negotiated_protocol);
}
println!("Starting client handshake");
let (mut client, h2) = client::handshake(tls).await?;
println!("building request");
let request = Request::builder()
.method(Method::GET)
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
println!("sending request");
let (response, other) = client.send_request(request, true).unwrap();
tokio::spawn(async move {
if let Err(e) = h2.await {
println!("GOT ERR={:?}", e);
}
});
println!("waiting on response : {:?}", other);
let (_, mut body) = response.await?.into_parts();
println!("processing body");
while let Some(chunk) = body.data().await {
println!("RX: {:?}", chunk?);
}
Ok(())
}

52
vendor/h2/examples/client.rs vendored Normal file
View File

@@ -0,0 +1,52 @@
use h2::client;
use http::{HeaderMap, Request};
use std::error::Error;
use tokio::net::TcpStream;
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn Error>> {
let _ = env_logger::try_init();
let tcp = TcpStream::connect("127.0.0.1:5928").await?;
let (mut client, h2) = client::handshake(tcp).await?;
println!("sending request");
let request = Request::builder()
.uri("https://http2.akamai.com/")
.body(())
.unwrap();
let mut trailers = HeaderMap::new();
trailers.insert("zomg", "hello".parse().unwrap());
let (response, mut stream) = client.send_request(request, false).unwrap();
// send trailers
stream.send_trailers(trailers).unwrap();
// Spawn a task to run the conn...
tokio::spawn(async move {
if let Err(e) = h2.await {
println!("GOT ERR={:?}", e);
}
});
let response = response.await?;
println!("GOT RESPONSE: {:?}", response);
// Get the body
let mut body = response.into_body();
while let Some(chunk) = body.data().await {
println!("GOT CHUNK = {:?}", chunk?);
}
if let Some(trailers) = body.trailers().await? {
println!("GOT TRAILERS: {:?}", trailers);
}
Ok(())
}

65
vendor/h2/examples/server.rs vendored Normal file
View File

@@ -0,0 +1,65 @@
use std::error::Error;
use bytes::Bytes;
use h2::server::{self, SendResponse};
use h2::RecvStream;
use http::Request;
use tokio::net::{TcpListener, TcpStream};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let _ = env_logger::try_init();
let listener = TcpListener::bind("127.0.0.1:5928").await?;
println!("listening on {:?}", listener.local_addr());
loop {
if let Ok((socket, _peer_addr)) = listener.accept().await {
tokio::spawn(async move {
if let Err(e) = serve(socket).await {
println!(" -> err={:?}", e);
}
});
}
}
}
async fn serve(socket: TcpStream) -> Result<(), Box<dyn Error + Send + Sync>> {
let mut connection = server::handshake(socket).await?;
println!("H2 connection bound");
while let Some(result) = connection.accept().await {
let (request, respond) = result?;
tokio::spawn(async move {
if let Err(e) = handle_request(request, respond).await {
println!("error while handling request: {}", e);
}
});
}
println!("~~~~~~~~~~~ H2 connection CLOSE !!!!!! ~~~~~~~~~~~");
Ok(())
}
async fn handle_request(
mut request: Request<RecvStream>,
mut respond: SendResponse<Bytes>,
) -> Result<(), Box<dyn Error + Send + Sync>> {
println!("GOT request: {:?}", request);
let body = request.body_mut();
while let Some(data) = body.data().await {
let data = data?;
println!("<<<< recv {:?}", data);
let _ = body.flow_control().release_capacity(data.len());
}
let response = http::Response::new(());
let mut send = respond.send_response(response, false)?;
println!(">>>> send");
send.send_data(Bytes::from_static(b"hello "), false)?;
send.send_data(Bytes::from_static(b"world\n"), true)?;
Ok(())
}

1709
vendor/h2/src/client.rs vendored Normal file

File diff suppressed because it is too large Load Diff

106
vendor/h2/src/codec/error.rs vendored Normal file
View File

@@ -0,0 +1,106 @@
use crate::proto::Error;
use std::{error, fmt, io};
/// Errors caused by sending a message
#[derive(Debug)]
pub enum SendError {
Connection(Error),
User(UserError),
}
/// Errors caused by users of the library
#[derive(Debug)]
pub enum UserError {
/// The stream ID is no longer accepting frames.
InactiveStreamId,
/// The stream is not currently expecting a frame of this type.
UnexpectedFrameType,
/// The payload size is too big
PayloadTooBig,
/// The application attempted to initiate too many streams to remote.
Rejected,
/// The released capacity is larger than claimed capacity.
ReleaseCapacityTooBig,
/// The stream ID space is overflowed.
///
/// A new connection is needed.
OverflowedStreamId,
/// Illegal headers, such as connection-specific headers.
MalformedHeaders,
/// Request submitted with relative URI.
MissingUriSchemeAndAuthority,
/// Calls `SendResponse::poll_reset` after having called `send_response`.
PollResetAfterSendResponse,
/// Calls `PingPong::send_ping` before receiving a pong.
SendPingWhilePending,
/// Tries to update local SETTINGS while ACK has not been received.
SendSettingsWhilePending,
/// Tries to send push promise to peer who has disabled server push
PeerDisabledServerPush,
/// Invalid status code for informational response (must be 1xx)
InvalidInformationalStatusCode,
}
// ===== impl SendError =====
impl error::Error for SendError {}
impl fmt::Display for SendError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Connection(ref e) => e.fmt(fmt),
Self::User(ref e) => e.fmt(fmt),
}
}
}
impl From<io::Error> for SendError {
fn from(src: io::Error) -> Self {
Self::Connection(src.into())
}
}
impl From<UserError> for SendError {
fn from(src: UserError) -> Self {
SendError::User(src)
}
}
// ===== impl UserError =====
impl error::Error for UserError {}
impl fmt::Display for UserError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use self::UserError::*;
fmt.write_str(match *self {
InactiveStreamId => "inactive stream",
UnexpectedFrameType => "unexpected frame type",
PayloadTooBig => "payload too big",
Rejected => "rejected",
ReleaseCapacityTooBig => "release capacity too big",
OverflowedStreamId => "stream ID overflowed",
MalformedHeaders => "malformed headers",
MissingUriSchemeAndAuthority => "request URI missing scheme and authority",
PollResetAfterSendResponse => "poll_reset after send_response is illegal",
SendPingWhilePending => "send_ping before received previous pong",
SendSettingsWhilePending => "sending SETTINGS before received previous ACK",
PeerDisabledServerPush => "sending PUSH_PROMISE to peer who disabled server push",
InvalidInformationalStatusCode => "invalid informational status code",
})
}
}

465
vendor/h2/src/codec/framed_read.rs vendored Normal file
View File

@@ -0,0 +1,465 @@
use crate::frame::{self, Frame, Kind, Reason};
use crate::frame::{
DEFAULT_MAX_FRAME_SIZE, DEFAULT_SETTINGS_HEADER_TABLE_SIZE, MAX_MAX_FRAME_SIZE,
};
use crate::proto::Error;
use crate::hpack;
use futures_core::Stream;
use bytes::{Buf, BytesMut};
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::AsyncRead;
use tokio_util::codec::FramedRead as InnerFramedRead;
use tokio_util::codec::{LengthDelimitedCodec, LengthDelimitedCodecError};
// 16 MB "sane default" taken from golang http2
const DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE: usize = 16 << 20;
#[derive(Debug)]
pub struct FramedRead<T> {
inner: InnerFramedRead<T, LengthDelimitedCodec>,
// hpack decoder state
hpack: hpack::Decoder,
max_header_list_size: usize,
max_continuation_frames: usize,
partial: Option<Partial>,
}
/// Partially loaded headers frame
#[derive(Debug)]
struct Partial {
/// Empty frame
frame: Continuable,
/// Partial header payload
buf: BytesMut,
continuation_frames_count: usize,
}
#[derive(Debug)]
enum Continuable {
Headers(frame::Headers),
PushPromise(frame::PushPromise),
}
impl<T> FramedRead<T> {
pub fn new(inner: InnerFramedRead<T, LengthDelimitedCodec>) -> FramedRead<T> {
let max_header_list_size = DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE;
let max_continuation_frames =
calc_max_continuation_frames(max_header_list_size, inner.decoder().max_frame_length());
FramedRead {
inner,
hpack: hpack::Decoder::new(DEFAULT_SETTINGS_HEADER_TABLE_SIZE),
max_header_list_size,
max_continuation_frames,
partial: None,
}
}
pub fn get_ref(&self) -> &T {
self.inner.get_ref()
}
pub fn get_mut(&mut self) -> &mut T {
self.inner.get_mut()
}
/// Returns the current max frame size setting
#[inline]
pub fn max_frame_size(&self) -> usize {
self.inner.decoder().max_frame_length()
}
/// Updates the max frame size setting.
///
/// Must be within 16,384 and 16,777,215.
#[inline]
pub fn set_max_frame_size(&mut self, val: usize) {
assert!(DEFAULT_MAX_FRAME_SIZE as usize <= val && val <= MAX_MAX_FRAME_SIZE as usize);
self.inner.decoder_mut().set_max_frame_length(val);
// Update max CONTINUATION frames too, since its based on this
self.max_continuation_frames = calc_max_continuation_frames(self.max_header_list_size, val);
}
/// Update the max header list size setting.
#[inline]
pub fn set_max_header_list_size(&mut self, val: usize) {
self.max_header_list_size = val;
// Update max CONTINUATION frames too, since its based on this
self.max_continuation_frames = calc_max_continuation_frames(val, self.max_frame_size());
}
/// Update the header table size setting.
#[inline]
pub fn set_header_table_size(&mut self, val: usize) {
self.hpack.queue_size_update(val);
}
}
fn calc_max_continuation_frames(header_max: usize, frame_max: usize) -> usize {
// At least this many frames needed to use max header list size
let min_frames_for_list = (header_max / frame_max).max(1);
// Some padding for imperfectly packed frames
// 25% without floats
let padding = min_frames_for_list >> 2;
min_frames_for_list.saturating_add(padding).max(5)
}
/// Decodes a frame.
///
/// This method is intentionally de-generified and outlined because it is very large.
fn decode_frame(
hpack: &mut hpack::Decoder,
max_header_list_size: usize,
max_continuation_frames: usize,
partial_inout: &mut Option<Partial>,
mut bytes: BytesMut,
) -> Result<Option<Frame>, Error> {
let span = tracing::trace_span!("FramedRead::decode_frame", offset = bytes.len());
let _e = span.enter();
tracing::trace!("decoding frame from {}B", bytes.len());
// Parse the head
let head = frame::Head::parse(&bytes);
if partial_inout.is_some() && head.kind() != Kind::Continuation {
proto_err!(conn: "expected CONTINUATION, got {:?}", head.kind());
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
let kind = head.kind();
tracing::trace!(frame.kind = ?kind);
macro_rules! header_block {
($frame:ident, $head:ident, $bytes:ident) => ({
// Drop the frame header
$bytes.advance(frame::HEADER_LEN);
// Parse the header frame w/o parsing the payload
let (mut frame, mut payload) = match frame::$frame::load($head, $bytes) {
Ok(res) => res,
Err(frame::Error::InvalidDependencyId) => {
proto_err!(stream: "invalid HEADERS dependency ID");
// A stream cannot depend on itself. An endpoint MUST
// treat this as a stream error (Section 5.4.2) of type
// `PROTOCOL_ERROR`.
return Err(Error::library_reset($head.stream_id(), Reason::PROTOCOL_ERROR));
},
Err(e) => {
proto_err!(conn: "failed to load frame; err={:?}", e);
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
};
let is_end_headers = frame.is_end_headers();
// Load the HPACK encoded headers
match frame.load_hpack(&mut payload, max_header_list_size, hpack) {
Ok(_) => {},
Err(frame::Error::Hpack(hpack::DecoderError::NeedMore(_))) if !is_end_headers => {},
Err(frame::Error::MalformedMessage) => {
let id = $head.stream_id();
proto_err!(stream: "malformed header block; stream={:?}", id);
return Err(Error::library_reset(id, Reason::PROTOCOL_ERROR));
},
Err(e) => {
proto_err!(conn: "failed HPACK decoding; err={:?}", e);
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
}
if is_end_headers {
frame.into()
} else {
tracing::trace!("loaded partial header block");
// Defer returning the frame
*partial_inout = Some(Partial {
frame: Continuable::$frame(frame),
buf: payload,
continuation_frames_count: 0,
});
return Ok(None);
}
});
}
let frame = match kind {
Kind::Settings => {
let res = frame::Settings::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|e| {
proto_err!(conn: "failed to load SETTINGS frame; err={:?}", e);
Error::library_go_away(Reason::PROTOCOL_ERROR)
})?
.into()
}
Kind::Ping => {
let res = frame::Ping::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|e| {
proto_err!(conn: "failed to load PING frame; err={:?}", e);
Error::library_go_away(Reason::PROTOCOL_ERROR)
})?
.into()
}
Kind::WindowUpdate => {
let res = frame::WindowUpdate::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|e| {
proto_err!(conn: "failed to load WINDOW_UPDATE frame; err={:?}", e);
Error::library_go_away(Reason::PROTOCOL_ERROR)
})?
.into()
}
Kind::Data => {
bytes.advance(frame::HEADER_LEN);
let res = frame::Data::load(head, bytes.freeze());
// TODO: Should this always be connection level? Probably not...
res.map_err(|e| {
proto_err!(conn: "failed to load DATA frame; err={:?}", e);
Error::library_go_away(Reason::PROTOCOL_ERROR)
})?
.into()
}
Kind::Headers => header_block!(Headers, head, bytes),
Kind::Reset => {
let res = frame::Reset::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|e| {
proto_err!(conn: "failed to load RESET frame; err={:?}", e);
Error::library_go_away(Reason::PROTOCOL_ERROR)
})?
.into()
}
Kind::GoAway => {
let res = frame::GoAway::load(&bytes[frame::HEADER_LEN..]);
res.map_err(|e| {
proto_err!(conn: "failed to load GO_AWAY frame; err={:?}", e);
Error::library_go_away(Reason::PROTOCOL_ERROR)
})?
.into()
}
Kind::PushPromise => header_block!(PushPromise, head, bytes),
Kind::Priority => {
if head.stream_id() == 0 {
// Invalid stream identifier
proto_err!(conn: "invalid stream ID 0");
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
match frame::Priority::load(head, &bytes[frame::HEADER_LEN..]) {
Ok(frame) => frame.into(),
Err(frame::Error::InvalidDependencyId) => {
// A stream cannot depend on itself. An endpoint MUST
// treat this as a stream error (Section 5.4.2) of type
// `PROTOCOL_ERROR`.
let id = head.stream_id();
proto_err!(stream: "PRIORITY invalid dependency ID; stream={:?}", id);
return Err(Error::library_reset(id, Reason::PROTOCOL_ERROR));
}
Err(e) => {
proto_err!(conn: "failed to load PRIORITY frame; err={:?};", e);
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
}
}
Kind::Continuation => {
let is_end_headers = (head.flag() & 0x4) == 0x4;
let mut partial = match partial_inout.take() {
Some(partial) => partial,
None => {
proto_err!(conn: "received unexpected CONTINUATION frame");
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
};
// The stream identifiers must match
if partial.frame.stream_id() != head.stream_id() {
proto_err!(conn: "CONTINUATION frame stream ID does not match previous frame stream ID");
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
// Check for CONTINUATION flood
if is_end_headers {
partial.continuation_frames_count = 0;
} else {
let cnt = partial.continuation_frames_count + 1;
if cnt > max_continuation_frames {
tracing::debug!("too_many_continuations, max = {}", max_continuation_frames);
return Err(Error::library_go_away_data(
Reason::ENHANCE_YOUR_CALM,
"too_many_continuations",
));
} else {
partial.continuation_frames_count = cnt;
}
}
// Extend the buf
if partial.buf.is_empty() {
partial.buf = bytes.split_off(frame::HEADER_LEN);
} else {
if partial.frame.is_over_size() {
// If there was left over bytes previously, they may be
// needed to continue decoding, even though we will
// be ignoring this frame. This is done to keep the HPACK
// decoder state up-to-date.
//
// Still, we need to be careful, because if a malicious
// attacker were to try to send a gigantic string, such
// that it fits over multiple header blocks, we could
// grow memory uncontrollably again, and that'd be a shame.
//
// Instead, we use a simple heuristic to determine if
// we should continue to ignore decoding, or to tell
// the attacker to go away.
if partial.buf.len() + bytes.len() > max_header_list_size {
proto_err!(conn: "CONTINUATION frame header block size over ignorable limit");
return Err(Error::library_go_away(Reason::COMPRESSION_ERROR));
}
}
partial.buf.extend_from_slice(&bytes[frame::HEADER_LEN..]);
}
match partial
.frame
.load_hpack(&mut partial.buf, max_header_list_size, hpack)
{
Ok(_) => {}
Err(frame::Error::Hpack(hpack::DecoderError::NeedMore(_))) if !is_end_headers => {}
Err(frame::Error::MalformedMessage) => {
let id = head.stream_id();
proto_err!(stream: "malformed CONTINUATION frame; stream={:?}", id);
return Err(Error::library_reset(id, Reason::PROTOCOL_ERROR));
}
Err(e) => {
proto_err!(conn: "failed HPACK decoding; err={:?}", e);
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
}
if is_end_headers {
partial.frame.into()
} else {
*partial_inout = Some(partial);
return Ok(None);
}
}
Kind::Unknown => {
// Unknown frames are ignored
return Ok(None);
}
};
Ok(Some(frame))
}
impl<T> Stream for FramedRead<T>
where
T: AsyncRead + Unpin,
{
type Item = Result<Frame, Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let span = tracing::trace_span!("FramedRead::poll_next");
let _e = span.enter();
loop {
tracing::trace!("poll");
let bytes = match ready!(Pin::new(&mut self.inner).poll_next(cx)) {
Some(Ok(bytes)) => bytes,
Some(Err(e)) => return Poll::Ready(Some(Err(map_err(e)))),
None => return Poll::Ready(None),
};
tracing::trace!(read.bytes = bytes.len());
let Self {
ref mut hpack,
max_header_list_size,
ref mut partial,
max_continuation_frames,
..
} = *self;
if let Some(frame) = decode_frame(
hpack,
max_header_list_size,
max_continuation_frames,
partial,
bytes,
)? {
tracing::debug!(?frame, "received");
return Poll::Ready(Some(Ok(frame)));
}
}
}
}
fn map_err(err: io::Error) -> Error {
if let io::ErrorKind::InvalidData = err.kind() {
if let Some(custom) = err.get_ref() {
if custom.is::<LengthDelimitedCodecError>() {
return Error::library_go_away(Reason::FRAME_SIZE_ERROR);
}
}
}
err.into()
}
// ===== impl Continuable =====
impl Continuable {
fn stream_id(&self) -> frame::StreamId {
match *self {
Continuable::Headers(ref h) => h.stream_id(),
Continuable::PushPromise(ref p) => p.stream_id(),
}
}
fn is_over_size(&self) -> bool {
match *self {
Continuable::Headers(ref h) => h.is_over_size(),
Continuable::PushPromise(ref p) => p.is_over_size(),
}
}
fn load_hpack(
&mut self,
src: &mut BytesMut,
max_header_list_size: usize,
decoder: &mut hpack::Decoder,
) -> Result<(), frame::Error> {
match *self {
Continuable::Headers(ref mut h) => h.load_hpack(src, max_header_list_size, decoder),
Continuable::PushPromise(ref mut p) => p.load_hpack(src, max_header_list_size, decoder),
}
}
}
impl<T> From<Continuable> for Frame<T> {
fn from(cont: Continuable) -> Self {
match cont {
Continuable::Headers(mut headers) => {
headers.set_end_headers();
headers.into()
}
Continuable::PushPromise(mut push) => {
push.set_end_headers();
push.into()
}
}
}
}

367
vendor/h2/src/codec/framed_write.rs vendored Normal file
View File

@@ -0,0 +1,367 @@
use crate::codec::UserError;
use crate::codec::UserError::*;
use crate::frame::{self, Frame, FrameSize};
use crate::hpack;
use bytes::{Buf, BufMut, BytesMut};
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio_util::io::poll_write_buf;
use std::io::{self, Cursor};
// A macro to get around a method needing to borrow &mut self
macro_rules! limited_write_buf {
($self:expr) => {{
let limit = $self.max_frame_size() + frame::HEADER_LEN;
$self.buf.get_mut().limit(limit)
}};
}
#[derive(Debug)]
pub struct FramedWrite<T, B> {
/// Upstream `AsyncWrite`
inner: T,
final_flush_done: bool,
encoder: Encoder<B>,
}
#[derive(Debug)]
struct Encoder<B> {
/// HPACK encoder
hpack: hpack::Encoder,
/// Write buffer
///
/// TODO: Should this be a ring buffer?
buf: Cursor<BytesMut>,
/// Next frame to encode
next: Option<Next<B>>,
/// Last data frame
last_data_frame: Option<frame::Data<B>>,
/// Max frame size, this is specified by the peer
max_frame_size: FrameSize,
/// Chain payloads bigger than this.
chain_threshold: usize,
/// Min buffer required to attempt to write a frame
min_buffer_capacity: usize,
}
#[derive(Debug)]
enum Next<B> {
Data(frame::Data<B>),
Continuation(frame::Continuation),
}
/// Initialize the connection with this amount of write buffer.
///
/// The minimum MAX_FRAME_SIZE is 16kb, so always be able to send a HEADERS
/// frame that big.
const DEFAULT_BUFFER_CAPACITY: usize = 16 * 1_024;
/// Chain payloads bigger than this when vectored I/O is enabled. The remote
/// will never advertise a max frame size less than this (well, the spec says
/// the max frame size can't be less than 16kb, so not even close).
const CHAIN_THRESHOLD: usize = 256;
/// Chain payloads bigger than this when vectored I/O is **not** enabled.
/// A larger value in this scenario will reduce the number of small and
/// fragmented data being sent, and hereby improve the throughput.
const CHAIN_THRESHOLD_WITHOUT_VECTORED_IO: usize = 1024;
// TODO: Make generic
impl<T, B> FramedWrite<T, B>
where
T: AsyncWrite + Unpin,
B: Buf,
{
pub fn new(inner: T) -> FramedWrite<T, B> {
let chain_threshold = if inner.is_write_vectored() {
CHAIN_THRESHOLD
} else {
CHAIN_THRESHOLD_WITHOUT_VECTORED_IO
};
FramedWrite {
inner,
final_flush_done: false,
encoder: Encoder {
hpack: hpack::Encoder::default(),
buf: Cursor::new(BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY)),
next: None,
last_data_frame: None,
max_frame_size: frame::DEFAULT_MAX_FRAME_SIZE,
chain_threshold,
min_buffer_capacity: chain_threshold + frame::HEADER_LEN,
},
}
}
/// Returns `Ready` when `send` is able to accept a frame
///
/// Calling this function may result in the current contents of the buffer
/// to be flushed to `T`.
pub fn poll_ready(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
if !self.encoder.has_capacity() {
// Try flushing
ready!(self.flush(cx))?;
if !self.encoder.has_capacity() {
return Poll::Pending;
}
}
Poll::Ready(Ok(()))
}
/// Buffer a frame.
///
/// `poll_ready` must be called first to ensure that a frame may be
/// accepted.
pub fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError> {
self.encoder.buffer(item)
}
/// Flush buffered data to the wire
pub fn flush(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
let span = tracing::trace_span!("FramedWrite::flush");
let _e = span.enter();
loop {
while !self.encoder.is_empty() {
match self.encoder.next {
Some(Next::Data(ref mut frame)) => {
tracing::trace!(queued_data_frame = true);
let mut buf = (&mut self.encoder.buf).chain(frame.payload_mut());
ready!(poll_write_buf(Pin::new(&mut self.inner), cx, &mut buf))?
}
_ => {
tracing::trace!(queued_data_frame = false);
ready!(poll_write_buf(
Pin::new(&mut self.inner),
cx,
&mut self.encoder.buf
))?
}
};
}
match self.encoder.unset_frame() {
ControlFlow::Continue => (),
ControlFlow::Break => break,
}
}
tracing::trace!("flushing buffer");
// Flush the upstream
ready!(Pin::new(&mut self.inner).poll_flush(cx))?;
Poll::Ready(Ok(()))
}
/// Close the codec
pub fn shutdown(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
if !self.final_flush_done {
ready!(self.flush(cx))?;
self.final_flush_done = true;
}
Pin::new(&mut self.inner).poll_shutdown(cx)
}
}
#[must_use]
enum ControlFlow {
Continue,
Break,
}
impl<B> Encoder<B>
where
B: Buf,
{
fn unset_frame(&mut self) -> ControlFlow {
// Clear internal buffer
self.buf.set_position(0);
self.buf.get_mut().clear();
// The data frame has been written, so unset it
match self.next.take() {
Some(Next::Data(frame)) => {
self.last_data_frame = Some(frame);
debug_assert!(self.is_empty());
ControlFlow::Break
}
Some(Next::Continuation(frame)) => {
// Buffer the continuation frame, then try to write again
let mut buf = limited_write_buf!(self);
if let Some(continuation) = frame.encode(&mut buf) {
self.next = Some(Next::Continuation(continuation));
}
ControlFlow::Continue
}
None => ControlFlow::Break,
}
}
fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError> {
// Ensure that we have enough capacity to accept the write.
assert!(self.has_capacity());
let span = tracing::trace_span!("FramedWrite::buffer", frame = ?item);
let _e = span.enter();
tracing::debug!(frame = ?item, "send");
match item {
Frame::Data(mut v) => {
// Ensure that the payload is not greater than the max frame.
let len = v.payload().remaining();
if len > self.max_frame_size() {
return Err(PayloadTooBig);
}
if len >= self.chain_threshold {
let head = v.head();
// Encode the frame head to the buffer
head.encode(len, self.buf.get_mut());
if self.buf.get_ref().remaining() < self.chain_threshold {
let extra_bytes = self.chain_threshold - self.buf.remaining();
self.buf.get_mut().put(v.payload_mut().take(extra_bytes));
}
// Save the data frame
self.next = Some(Next::Data(v));
} else {
v.encode_chunk(self.buf.get_mut());
// The chunk has been fully encoded, so there is no need to
// keep it around
assert_eq!(v.payload().remaining(), 0, "chunk not fully encoded");
// Save off the last frame...
self.last_data_frame = Some(v);
}
}
Frame::Headers(v) => {
let mut buf = limited_write_buf!(self);
if let Some(continuation) = v.encode(&mut self.hpack, &mut buf) {
self.next = Some(Next::Continuation(continuation));
}
}
Frame::PushPromise(v) => {
let mut buf = limited_write_buf!(self);
if let Some(continuation) = v.encode(&mut self.hpack, &mut buf) {
self.next = Some(Next::Continuation(continuation));
}
}
Frame::Settings(v) => {
v.encode(self.buf.get_mut());
tracing::trace!(rem = self.buf.remaining(), "encoded settings");
}
Frame::GoAway(v) => {
v.encode(self.buf.get_mut());
tracing::trace!(rem = self.buf.remaining(), "encoded go_away");
}
Frame::Ping(v) => {
v.encode(self.buf.get_mut());
tracing::trace!(rem = self.buf.remaining(), "encoded ping");
}
Frame::WindowUpdate(v) => {
v.encode(self.buf.get_mut());
tracing::trace!(rem = self.buf.remaining(), "encoded window_update");
}
Frame::Priority(_) => {
/*
v.encode(self.buf.get_mut());
tracing::trace!("encoded priority; rem={:?}", self.buf.remaining());
*/
unimplemented!();
}
Frame::Reset(v) => {
v.encode(self.buf.get_mut());
tracing::trace!(rem = self.buf.remaining(), "encoded reset");
}
}
Ok(())
}
fn has_capacity(&self) -> bool {
self.next.is_none()
&& (self.buf.get_ref().capacity() - self.buf.get_ref().len()
>= self.min_buffer_capacity)
}
fn is_empty(&self) -> bool {
match self.next {
Some(Next::Data(ref frame)) => !frame.payload().has_remaining(),
_ => !self.buf.has_remaining(),
}
}
}
impl<B> Encoder<B> {
fn max_frame_size(&self) -> usize {
self.max_frame_size as usize
}
}
impl<T, B> FramedWrite<T, B> {
/// Returns the max frame size that can be sent
pub fn max_frame_size(&self) -> usize {
self.encoder.max_frame_size()
}
/// Set the peer's max frame size.
pub fn set_max_frame_size(&mut self, val: usize) {
assert!(val <= frame::MAX_MAX_FRAME_SIZE as usize);
self.encoder.max_frame_size = val as FrameSize;
}
/// Set the peer's header table size.
pub fn set_header_table_size(&mut self, val: usize) {
self.encoder.hpack.update_max_size(val);
}
/// Retrieve the last data frame that has been sent
pub fn take_last_data_frame(&mut self) -> Option<frame::Data<B>> {
self.encoder.last_data_frame.take()
}
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<T: AsyncRead + Unpin, B> AsyncRead for FramedWrite<T, B> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_read(cx, buf)
}
}
// We never project the Pin to `B`.
impl<T: Unpin, B> Unpin for FramedWrite<T, B> {}
#[cfg(feature = "unstable")]
mod unstable {
use super::*;
impl<T, B> FramedWrite<T, B> {
pub fn get_ref(&self) -> &T {
&self.inner
}
}
}

206
vendor/h2/src/codec/mod.rs vendored Normal file
View File

@@ -0,0 +1,206 @@
mod error;
mod framed_read;
mod framed_write;
pub use self::error::{SendError, UserError};
use self::framed_read::FramedRead;
use self::framed_write::FramedWrite;
use crate::frame::{self, Data, Frame};
use crate::proto::Error;
use bytes::Buf;
use futures_core::Stream;
use futures_sink::Sink;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_util::codec::length_delimited;
use std::io;
#[derive(Debug)]
pub struct Codec<T, B> {
inner: FramedRead<FramedWrite<T, B>>,
}
impl<T, B> Codec<T, B>
where
T: AsyncRead + AsyncWrite + Unpin,
B: Buf,
{
/// Returns a new `Codec` with the default max frame size
#[inline]
pub fn new(io: T) -> Self {
Self::with_max_recv_frame_size(io, frame::DEFAULT_MAX_FRAME_SIZE as usize)
}
/// Returns a new `Codec` with the given maximum frame size
pub fn with_max_recv_frame_size(io: T, max_frame_size: usize) -> Self {
// Wrap with writer
let framed_write = FramedWrite::new(io);
// Delimit the frames
let delimited = length_delimited::Builder::new()
.big_endian()
.length_field_length(3)
.length_adjustment(9)
.num_skip(0) // Don't skip the header
.new_read(framed_write);
let mut inner = FramedRead::new(delimited);
// Use FramedRead's method since it checks the value is within range.
inner.set_max_frame_size(max_frame_size);
Codec { inner }
}
}
impl<T, B> Codec<T, B> {
/// Updates the max received frame size.
///
/// The change takes effect the next time a frame is decoded. In other
/// words, if a frame is currently in process of being decoded with a frame
/// size greater than `val` but less than the max frame size in effect
/// before calling this function, then the frame will be allowed.
#[inline]
pub fn set_max_recv_frame_size(&mut self, val: usize) {
self.inner.set_max_frame_size(val)
}
/// Returns the current max received frame size setting.
///
/// This is the largest size this codec will accept from the wire. Larger
/// frames will be rejected.
#[cfg(feature = "unstable")]
#[inline]
pub fn max_recv_frame_size(&self) -> usize {
self.inner.max_frame_size()
}
/// Returns the max frame size that can be sent to the peer.
pub fn max_send_frame_size(&self) -> usize {
self.inner.get_ref().max_frame_size()
}
/// Set the peer's max frame size.
pub fn set_max_send_frame_size(&mut self, val: usize) {
self.framed_write().set_max_frame_size(val)
}
/// Set the peer's header table size size.
pub fn set_send_header_table_size(&mut self, val: usize) {
self.framed_write().set_header_table_size(val)
}
/// Set the decoder header table size size.
pub fn set_recv_header_table_size(&mut self, val: usize) {
self.inner.set_header_table_size(val)
}
/// Set the max header list size that can be received.
pub fn set_max_recv_header_list_size(&mut self, val: usize) {
self.inner.set_max_header_list_size(val);
}
/// Get a reference to the inner stream.
#[cfg(feature = "unstable")]
pub fn get_ref(&self) -> &T {
self.inner.get_ref().get_ref()
}
/// Get a mutable reference to the inner stream.
pub fn get_mut(&mut self) -> &mut T {
self.inner.get_mut().get_mut()
}
/// Takes the data payload value that was fully written to the socket
pub(crate) fn take_last_data_frame(&mut self) -> Option<Data<B>> {
self.framed_write().take_last_data_frame()
}
fn framed_write(&mut self) -> &mut FramedWrite<T, B> {
self.inner.get_mut()
}
}
impl<T, B> Codec<T, B>
where
T: AsyncWrite + Unpin,
B: Buf,
{
/// Returns `Ready` when the codec can buffer a frame
pub fn poll_ready(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
self.framed_write().poll_ready(cx)
}
/// Buffer a frame.
///
/// `poll_ready` must be called first to ensure that a frame may be
/// accepted.
///
/// TODO: Rename this to avoid conflicts with Sink::buffer
pub fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError> {
self.framed_write().buffer(item)
}
/// Flush buffered data to the wire
pub fn flush(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
self.framed_write().flush(cx)
}
/// Shutdown the send half
pub fn shutdown(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
self.framed_write().shutdown(cx)
}
}
impl<T, B> Stream for Codec<T, B>
where
T: AsyncRead + Unpin,
{
type Item = Result<Frame, Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Pin::new(&mut self.inner).poll_next(cx)
}
}
impl<T, B> Sink<Frame<B>> for Codec<T, B>
where
T: AsyncWrite + Unpin,
B: Buf,
{
type Error = SendError;
fn start_send(mut self: Pin<&mut Self>, item: Frame<B>) -> Result<(), Self::Error> {
Codec::buffer(&mut self, item)?;
Ok(())
}
/// Returns `Ready` when the codec can buffer a frame
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.framed_write().poll_ready(cx).map_err(Into::into)
}
/// Flush buffered data to the wire
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.framed_write().flush(cx).map_err(Into::into)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
ready!(self.shutdown(cx))?;
Poll::Ready(Ok(()))
}
}
// TODO: remove (or improve) this
impl<T> From<T> for Codec<T, bytes::Bytes>
where
T: AsyncRead + AsyncWrite + Unpin,
{
fn from(src: T) -> Self {
Self::new(src)
}
}

211
vendor/h2/src/error.rs vendored Normal file
View File

@@ -0,0 +1,211 @@
use crate::codec::{SendError, UserError};
use crate::frame::StreamId;
use crate::proto::{self, Initiator};
use bytes::Bytes;
use std::{error, fmt, io};
pub use crate::frame::Reason;
/// Represents HTTP/2 operation errors.
///
/// `Error` covers error cases raised by protocol errors caused by the
/// peer, I/O (transport) errors, and errors caused by the user of the library.
///
/// If the error was caused by the remote peer, then it will contain a
/// [`Reason`] which can be obtained with the [`reason`] function.
///
/// [`Reason`]: struct.Reason.html
/// [`reason`]: #method.reason
#[derive(Debug)]
pub struct Error {
kind: Kind,
}
#[derive(Debug)]
enum Kind {
/// A RST_STREAM frame was received or sent.
#[allow(dead_code)]
Reset(StreamId, Reason, Initiator),
/// A GO_AWAY frame was received or sent.
GoAway(Bytes, Reason, Initiator),
/// The user created an error from a bare Reason.
Reason(Reason),
/// An error resulting from an invalid action taken by the user of this
/// library.
User(UserError),
/// An `io::Error` occurred while trying to read or write.
Io(io::Error),
}
// ===== impl Error =====
impl Error {
/// If the error was caused by the remote peer, the error reason.
///
/// This is either an error received by the peer or caused by an invalid
/// action taken by the peer (i.e. a protocol error).
pub fn reason(&self) -> Option<Reason> {
match self.kind {
Kind::Reset(_, reason, _) | Kind::GoAway(_, reason, _) | Kind::Reason(reason) => {
Some(reason)
}
_ => None,
}
}
/// Returns true if the error is an io::Error
pub fn is_io(&self) -> bool {
matches!(self.kind, Kind::Io(..))
}
/// Returns the error if the error is an io::Error
pub fn get_io(&self) -> Option<&io::Error> {
match self.kind {
Kind::Io(ref e) => Some(e),
_ => None,
}
}
/// Returns the error if the error is an io::Error
pub fn into_io(self) -> Option<io::Error> {
match self.kind {
Kind::Io(e) => Some(e),
_ => None,
}
}
pub(crate) fn from_io(err: io::Error) -> Self {
Error {
kind: Kind::Io(err),
}
}
/// Returns true if the error is from a `GOAWAY`.
pub fn is_go_away(&self) -> bool {
matches!(self.kind, Kind::GoAway(..))
}
/// Returns true if the error is from a `RST_STREAM`.
pub fn is_reset(&self) -> bool {
matches!(self.kind, Kind::Reset(..))
}
/// Returns true if the error was received in a frame from the remote.
///
/// Such as from a received `RST_STREAM` or `GOAWAY` frame.
pub fn is_remote(&self) -> bool {
matches!(
self.kind,
Kind::GoAway(_, _, Initiator::Remote) | Kind::Reset(_, _, Initiator::Remote)
)
}
/// Returns true if the error was created by `h2`.
///
/// Such as noticing some protocol error and sending a GOAWAY or RST_STREAM.
pub fn is_library(&self) -> bool {
matches!(
self.kind,
Kind::GoAway(_, _, Initiator::Library) | Kind::Reset(_, _, Initiator::Library)
)
}
}
impl From<proto::Error> for Error {
fn from(src: proto::Error) -> Error {
use crate::proto::Error::*;
Error {
kind: match src {
Reset(stream_id, reason, initiator) => Kind::Reset(stream_id, reason, initiator),
GoAway(debug_data, reason, initiator) => {
Kind::GoAway(debug_data, reason, initiator)
}
Io(kind, inner) => {
Kind::Io(inner.map_or_else(|| kind.into(), |inner| io::Error::new(kind, inner)))
}
},
}
}
}
impl From<Reason> for Error {
fn from(src: Reason) -> Error {
Error {
kind: Kind::Reason(src),
}
}
}
impl From<SendError> for Error {
fn from(src: SendError) -> Error {
match src {
SendError::User(e) => e.into(),
SendError::Connection(e) => e.into(),
}
}
}
impl From<UserError> for Error {
fn from(src: UserError) -> Error {
Error {
kind: Kind::User(src),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let debug_data = match self.kind {
Kind::Reset(_, reason, Initiator::User) => {
return write!(fmt, "stream error sent by user: {}", reason)
}
Kind::Reset(_, reason, Initiator::Library) => {
return write!(fmt, "stream error detected: {}", reason)
}
Kind::Reset(_, reason, Initiator::Remote) => {
return write!(fmt, "stream error received: {}", reason)
}
Kind::GoAway(ref debug_data, reason, Initiator::User) => {
write!(fmt, "connection error sent by user: {}", reason)?;
debug_data
}
Kind::GoAway(ref debug_data, reason, Initiator::Library) => {
write!(fmt, "connection error detected: {}", reason)?;
debug_data
}
Kind::GoAway(ref debug_data, reason, Initiator::Remote) => {
write!(fmt, "connection error received: {}", reason)?;
debug_data
}
Kind::Reason(reason) => return write!(fmt, "protocol error: {}", reason),
Kind::User(ref e) => return write!(fmt, "user error: {}", e),
Kind::Io(ref e) => return e.fmt(fmt),
};
if !debug_data.is_empty() {
write!(fmt, " ({:?})", debug_data)?;
}
Ok(())
}
}
impl error::Error for Error {}
#[cfg(test)]
mod tests {
use super::Error;
use crate::Reason;
#[test]
fn error_from_reason() {
let err = Error::from(Reason::HTTP_1_1_REQUIRED);
assert_eq!(err.reason(), Some(Reason::HTTP_1_1_REQUIRED));
}
}

55
vendor/h2/src/ext.rs vendored Normal file
View File

@@ -0,0 +1,55 @@
//! Extensions specific to the HTTP/2 protocol.
use crate::hpack::BytesStr;
use bytes::Bytes;
use std::fmt;
/// Represents the `:protocol` pseudo-header used by
/// the [Extended CONNECT Protocol].
///
/// [Extended CONNECT Protocol]: https://datatracker.ietf.org/doc/html/rfc8441#section-4
#[derive(Clone, Eq, PartialEq)]
pub struct Protocol {
value: BytesStr,
}
impl Protocol {
/// Converts a static string to a protocol name.
pub const fn from_static(value: &'static str) -> Self {
Self {
value: BytesStr::from_static(value),
}
}
/// Returns a str representation of the header.
pub fn as_str(&self) -> &str {
self.value.as_str()
}
pub(crate) fn try_from(bytes: Bytes) -> Result<Self, std::str::Utf8Error> {
Ok(Self {
value: BytesStr::try_from(bytes)?,
})
}
}
impl<'a> From<&'a str> for Protocol {
fn from(value: &'a str) -> Self {
Self {
value: BytesStr::from(value),
}
}
}
impl AsRef<[u8]> for Protocol {
fn as_ref(&self) -> &[u8] {
self.value.as_ref()
}
}
impl fmt::Debug for Protocol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.value.fmt(f)
}
}

241
vendor/h2/src/frame/data.rs vendored Normal file
View File

@@ -0,0 +1,241 @@
use crate::frame::{util, Error, Frame, Head, Kind, StreamId};
use bytes::{Buf, BufMut, Bytes};
use std::fmt;
/// Data frame
///
/// Data frames convey arbitrary, variable-length sequences of octets associated
/// with a stream. One or more DATA frames are used, for instance, to carry HTTP
/// request or response payloads.
#[derive(Eq, PartialEq)]
pub struct Data<T = Bytes> {
stream_id: StreamId,
data: T,
flags: DataFlags,
pad_len: Option<u8>,
}
#[derive(Copy, Clone, Default, Eq, PartialEq)]
struct DataFlags(u8);
const END_STREAM: u8 = 0x1;
const PADDED: u8 = 0x8;
const ALL: u8 = END_STREAM | PADDED;
impl<T> Data<T> {
/// Creates a new DATA frame.
pub fn new(stream_id: StreamId, payload: T) -> Self {
assert!(!stream_id.is_zero());
Data {
stream_id,
data: payload,
flags: DataFlags::default(),
pad_len: None,
}
}
/// Returns the stream identifier that this frame is associated with.
///
/// This cannot be a zero stream identifier.
pub fn stream_id(&self) -> StreamId {
self.stream_id
}
/// Gets the value of the `END_STREAM` flag for this frame.
///
/// If true, this frame is the last that the endpoint will send for the
/// identified stream.
///
/// Setting this flag causes the stream to enter one of the "half-closed"
/// states or the "closed" state (Section 5.1).
pub fn is_end_stream(&self) -> bool {
self.flags.is_end_stream()
}
/// Sets the value for the `END_STREAM` flag on this frame.
pub fn set_end_stream(&mut self, val: bool) {
if val {
self.flags.set_end_stream();
} else {
self.flags.unset_end_stream();
}
}
/// Returns whether the `PADDED` flag is set on this frame.
#[cfg(feature = "unstable")]
pub fn is_padded(&self) -> bool {
self.flags.is_padded()
}
/// Sets the value for the `PADDED` flag on this frame.
#[cfg(feature = "unstable")]
pub fn set_padded(&mut self) {
self.flags.set_padded();
}
/// Returns a reference to this frame's payload.
///
/// This does **not** include any padding that might have been originally
/// included.
pub fn payload(&self) -> &T {
&self.data
}
/// Returns a mutable reference to this frame's payload.
///
/// This does **not** include any padding that might have been originally
/// included.
pub fn payload_mut(&mut self) -> &mut T {
&mut self.data
}
/// Consumes `self` and returns the frame's payload.
///
/// This does **not** include any padding that might have been originally
/// included.
pub fn into_payload(self) -> T {
self.data
}
pub(crate) fn head(&self) -> Head {
Head::new(Kind::Data, self.flags.into(), self.stream_id)
}
pub(crate) fn map<F, U>(self, f: F) -> Data<U>
where
F: FnOnce(T) -> U,
{
Data {
stream_id: self.stream_id,
data: f(self.data),
flags: self.flags,
pad_len: self.pad_len,
}
}
}
impl Data<Bytes> {
pub(crate) fn load(head: Head, mut payload: Bytes) -> Result<Self, Error> {
let flags = DataFlags::load(head.flag());
// The stream identifier must not be zero
if head.stream_id().is_zero() {
return Err(Error::InvalidStreamId);
}
let pad_len = if flags.is_padded() {
let len = util::strip_padding(&mut payload)?;
Some(len)
} else {
None
};
Ok(Data {
stream_id: head.stream_id(),
data: payload,
flags,
pad_len,
})
}
pub(crate) fn flow_controlled_len(&self) -> usize {
if let Some(pad_len) = self.pad_len {
// if PADDED, pad length field counts too (the + 1)
self.data.len() + usize::from(pad_len) + 1
} else {
self.data.len()
}
}
/// If this frame is PADDED, it returns the pad len + 1 (length field).
pub(crate) fn padded_len(&self) -> Option<u8> {
self.pad_len.map(|n| n + 1)
}
}
impl<T: Buf> Data<T> {
/// Encode the data frame into the `dst` buffer.
///
/// # Panics
///
/// Panics if `dst` cannot contain the data frame.
pub(crate) fn encode_chunk<U: BufMut>(&mut self, dst: &mut U) {
let len = self.data.remaining();
assert!(dst.remaining_mut() >= len);
self.head().encode(len, dst);
dst.put(&mut self.data);
}
}
impl<T> From<Data<T>> for Frame<T> {
fn from(src: Data<T>) -> Self {
Frame::Data(src)
}
}
impl<T> fmt::Debug for Data<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut f = fmt.debug_struct("Data");
f.field("stream_id", &self.stream_id);
if !self.flags.is_empty() {
f.field("flags", &self.flags);
}
if let Some(ref pad_len) = self.pad_len {
f.field("pad_len", pad_len);
}
// `data` bytes purposefully excluded
f.finish()
}
}
// ===== impl DataFlags =====
impl DataFlags {
fn load(bits: u8) -> DataFlags {
DataFlags(bits & ALL)
}
fn is_empty(&self) -> bool {
self.0 == 0
}
fn is_end_stream(&self) -> bool {
self.0 & END_STREAM == END_STREAM
}
fn set_end_stream(&mut self) {
self.0 |= END_STREAM
}
fn unset_end_stream(&mut self) {
self.0 &= !END_STREAM
}
fn is_padded(&self) -> bool {
self.0 & PADDED == PADDED
}
#[cfg(feature = "unstable")]
fn set_padded(&mut self) {
self.0 |= PADDED
}
}
impl From<DataFlags> for u8 {
fn from(src: DataFlags) -> u8 {
src.0
}
}
impl fmt::Debug for DataFlags {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
util::debug_flags(fmt, self.0)
.flag_if(self.is_end_stream(), "END_STREAM")
.flag_if(self.is_padded(), "PADDED")
.finish()
}
}

87
vendor/h2/src/frame/go_away.rs vendored Normal file
View File

@@ -0,0 +1,87 @@
use std::fmt;
use bytes::{BufMut, Bytes};
use crate::frame::{self, Error, Head, Kind, Reason, StreamId};
#[derive(Clone, Eq, PartialEq)]
pub struct GoAway {
last_stream_id: StreamId,
error_code: Reason,
debug_data: Bytes,
}
impl GoAway {
pub fn new(last_stream_id: StreamId, reason: Reason) -> Self {
GoAway {
last_stream_id,
error_code: reason,
debug_data: Bytes::new(),
}
}
pub fn with_debug_data(last_stream_id: StreamId, reason: Reason, debug_data: Bytes) -> Self {
Self {
last_stream_id,
error_code: reason,
debug_data,
}
}
pub fn last_stream_id(&self) -> StreamId {
self.last_stream_id
}
pub fn reason(&self) -> Reason {
self.error_code
}
pub fn debug_data(&self) -> &Bytes {
&self.debug_data
}
pub fn load(payload: &[u8]) -> Result<GoAway, Error> {
if payload.len() < 8 {
return Err(Error::BadFrameSize);
}
let (last_stream_id, _) = StreamId::parse(&payload[..4]);
let error_code = unpack_octets_4!(payload, 4, u32);
let debug_data = Bytes::copy_from_slice(&payload[8..]);
Ok(GoAway {
last_stream_id,
error_code: error_code.into(),
debug_data,
})
}
pub fn encode<B: BufMut>(&self, dst: &mut B) {
tracing::trace!("encoding GO_AWAY; code={:?}", self.error_code);
let head = Head::new(Kind::GoAway, 0, StreamId::zero());
head.encode(8 + self.debug_data.len(), dst);
dst.put_u32(self.last_stream_id.into());
dst.put_u32(self.error_code.into());
dst.put(self.debug_data.slice(..));
}
}
impl<B> From<GoAway> for frame::Frame<B> {
fn from(src: GoAway) -> Self {
frame::Frame::GoAway(src)
}
}
impl fmt::Debug for GoAway {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut builder = f.debug_struct("GoAway");
builder.field("error_code", &self.error_code);
builder.field("last_stream_id", &self.last_stream_id);
if !self.debug_data.is_empty() {
builder.field("debug_data", &self.debug_data);
}
builder.finish()
}
}

94
vendor/h2/src/frame/head.rs vendored Normal file
View File

@@ -0,0 +1,94 @@
use super::StreamId;
use bytes::BufMut;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Head {
kind: Kind,
flag: u8,
stream_id: StreamId,
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Kind {
Data = 0,
Headers = 1,
Priority = 2,
Reset = 3,
Settings = 4,
PushPromise = 5,
Ping = 6,
GoAway = 7,
WindowUpdate = 8,
Continuation = 9,
Unknown,
}
// ===== impl Head =====
impl Head {
pub fn new(kind: Kind, flag: u8, stream_id: StreamId) -> Head {
Head {
kind,
flag,
stream_id,
}
}
/// Parse an HTTP/2 frame header
pub fn parse(header: &[u8]) -> Head {
let (stream_id, _) = StreamId::parse(&header[5..]);
Head {
kind: Kind::new(header[3]),
flag: header[4],
stream_id,
}
}
pub fn stream_id(&self) -> StreamId {
self.stream_id
}
pub fn kind(&self) -> Kind {
self.kind
}
pub fn flag(&self) -> u8 {
self.flag
}
pub fn encode_len(&self) -> usize {
super::HEADER_LEN
}
pub fn encode<T: BufMut>(&self, payload_len: usize, dst: &mut T) {
debug_assert!(self.encode_len() <= dst.remaining_mut());
dst.put_uint(payload_len as u64, 3);
dst.put_u8(self.kind as u8);
dst.put_u8(self.flag);
dst.put_u32(self.stream_id.into());
}
}
// ===== impl Kind =====
impl Kind {
pub fn new(byte: u8) -> Kind {
match byte {
0 => Kind::Data,
1 => Kind::Headers,
2 => Kind::Priority,
3 => Kind::Reset,
4 => Kind::Settings,
5 => Kind::PushPromise,
6 => Kind::Ping,
7 => Kind::GoAway,
8 => Kind::WindowUpdate,
9 => Kind::Continuation,
_ => Kind::Unknown,
}
}
}

1216
vendor/h2/src/frame/headers.rs vendored Normal file

File diff suppressed because it is too large Load Diff

171
vendor/h2/src/frame/mod.rs vendored Normal file
View File

@@ -0,0 +1,171 @@
use crate::hpack;
use bytes::Bytes;
use std::fmt;
/// A helper macro that unpacks a sequence of 4 bytes found in the buffer with
/// the given identifier, starting at the given offset, into the given integer
/// type. Obviously, the integer type should be able to support at least 4
/// bytes.
///
/// # Examples
///
/// ```ignore
/// # // We ignore this doctest because the macro is not exported.
/// let buf: [u8; 4] = [0, 0, 0, 1];
/// assert_eq!(1u32, unpack_octets_4!(buf, 0, u32));
/// ```
macro_rules! unpack_octets_4 {
// TODO: Get rid of this macro
($buf:expr, $offset:expr, $tip:ty) => {
(($buf[$offset + 0] as $tip) << 24)
| (($buf[$offset + 1] as $tip) << 16)
| (($buf[$offset + 2] as $tip) << 8)
| (($buf[$offset + 3] as $tip) << 0)
};
}
#[cfg(test)]
mod tests {
#[test]
fn test_unpack_octets_4() {
let buf: [u8; 4] = [0, 0, 0, 1];
assert_eq!(1u32, unpack_octets_4!(buf, 0, u32));
}
}
mod data;
mod go_away;
mod head;
mod headers;
mod ping;
mod priority;
mod reason;
mod reset;
mod settings;
mod stream_id;
mod util;
mod window_update;
pub use self::data::Data;
pub use self::go_away::GoAway;
pub use self::head::{Head, Kind};
pub use self::headers::{
parse_u64, Continuation, Headers, Pseudo, PushPromise, PushPromiseHeaderError,
};
pub use self::ping::Ping;
pub use self::priority::{Priority, StreamDependency};
pub use self::reason::Reason;
pub use self::reset::Reset;
pub use self::settings::Settings;
pub use self::stream_id::{StreamId, StreamIdOverflow};
pub use self::window_update::WindowUpdate;
#[cfg(feature = "unstable")]
pub use crate::hpack::BytesStr;
// Re-export some constants
pub use self::settings::{
DEFAULT_INITIAL_WINDOW_SIZE, DEFAULT_MAX_FRAME_SIZE, DEFAULT_SETTINGS_HEADER_TABLE_SIZE,
MAX_MAX_FRAME_SIZE,
};
pub type FrameSize = u32;
pub const HEADER_LEN: usize = 9;
#[derive(Eq, PartialEq)]
pub enum Frame<T = Bytes> {
Data(Data<T>),
Headers(Headers),
Priority(Priority),
PushPromise(PushPromise),
Settings(Settings),
Ping(Ping),
GoAway(GoAway),
WindowUpdate(WindowUpdate),
Reset(Reset),
}
impl<T> Frame<T> {
pub fn map<F, U>(self, f: F) -> Frame<U>
where
F: FnOnce(T) -> U,
{
use self::Frame::*;
match self {
Data(frame) => frame.map(f).into(),
Headers(frame) => frame.into(),
Priority(frame) => frame.into(),
PushPromise(frame) => frame.into(),
Settings(frame) => frame.into(),
Ping(frame) => frame.into(),
GoAway(frame) => frame.into(),
WindowUpdate(frame) => frame.into(),
Reset(frame) => frame.into(),
}
}
}
impl<T> fmt::Debug for Frame<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use self::Frame::*;
match *self {
Data(ref frame) => fmt::Debug::fmt(frame, fmt),
Headers(ref frame) => fmt::Debug::fmt(frame, fmt),
Priority(ref frame) => fmt::Debug::fmt(frame, fmt),
PushPromise(ref frame) => fmt::Debug::fmt(frame, fmt),
Settings(ref frame) => fmt::Debug::fmt(frame, fmt),
Ping(ref frame) => fmt::Debug::fmt(frame, fmt),
GoAway(ref frame) => fmt::Debug::fmt(frame, fmt),
WindowUpdate(ref frame) => fmt::Debug::fmt(frame, fmt),
Reset(ref frame) => fmt::Debug::fmt(frame, fmt),
}
}
}
/// Errors that can occur during parsing an HTTP/2 frame.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
/// A length value other than 8 was set on a PING message.
BadFrameSize,
/// The padding length was larger than the frame-header-specified
/// length of the payload.
TooMuchPadding,
/// An invalid setting value was provided
InvalidSettingValue,
/// An invalid window update value
InvalidWindowUpdateValue,
/// The payload length specified by the frame header was not the
/// value necessary for the specific frame type.
InvalidPayloadLength,
/// Received a payload with an ACK settings frame
InvalidPayloadAckSettings,
/// An invalid stream identifier was provided.
///
/// This is returned if a SETTINGS or PING frame is received with a stream
/// identifier other than zero.
InvalidStreamId,
/// A request or response is malformed.
MalformedMessage,
/// An invalid stream dependency ID was provided
///
/// This is returned if a HEADERS or PRIORITY frame is received with an
/// invalid stream identifier.
InvalidDependencyId,
/// Failed to perform HPACK decoding
Hpack(hpack::DecoderError),
}

102
vendor/h2/src/frame/ping.rs vendored Normal file
View File

@@ -0,0 +1,102 @@
use crate::frame::{Error, Frame, Head, Kind, StreamId};
use bytes::BufMut;
const ACK_FLAG: u8 = 0x1;
pub type Payload = [u8; 8];
#[derive(Debug, Eq, PartialEq)]
pub struct Ping {
ack: bool,
payload: Payload,
}
// This was just 8 randomly generated bytes. We use something besides just
// zeroes to distinguish this specific PING from any other.
const SHUTDOWN_PAYLOAD: Payload = [0x0b, 0x7b, 0xa2, 0xf0, 0x8b, 0x9b, 0xfe, 0x54];
const USER_PAYLOAD: Payload = [0x3b, 0x7c, 0xdb, 0x7a, 0x0b, 0x87, 0x16, 0xb4];
impl Ping {
#[cfg(feature = "unstable")]
pub const SHUTDOWN: Payload = SHUTDOWN_PAYLOAD;
#[cfg(not(feature = "unstable"))]
pub(crate) const SHUTDOWN: Payload = SHUTDOWN_PAYLOAD;
#[cfg(feature = "unstable")]
pub const USER: Payload = USER_PAYLOAD;
#[cfg(not(feature = "unstable"))]
pub(crate) const USER: Payload = USER_PAYLOAD;
pub fn new(payload: Payload) -> Ping {
Ping {
ack: false,
payload,
}
}
pub fn pong(payload: Payload) -> Ping {
Ping { ack: true, payload }
}
pub fn is_ack(&self) -> bool {
self.ack
}
pub fn payload(&self) -> &Payload {
&self.payload
}
pub fn into_payload(self) -> Payload {
self.payload
}
/// Builds a `Ping` frame from a raw frame.
pub fn load(head: Head, bytes: &[u8]) -> Result<Ping, Error> {
debug_assert_eq!(head.kind(), crate::frame::Kind::Ping);
// PING frames are not associated with any individual stream. If a PING
// frame is received with a stream identifier field value other than
// 0x0, the recipient MUST respond with a connection error
// (Section 5.4.1) of type PROTOCOL_ERROR.
if !head.stream_id().is_zero() {
return Err(Error::InvalidStreamId);
}
// In addition to the frame header, PING frames MUST contain 8 octets of opaque
// data in the payload.
if bytes.len() != 8 {
return Err(Error::BadFrameSize);
}
let mut payload = [0; 8];
payload.copy_from_slice(bytes);
// The PING frame defines the following flags:
//
// ACK (0x1): When set, bit 0 indicates that this PING frame is a PING
// response. An endpoint MUST set this flag in PING responses. An
// endpoint MUST NOT respond to PING frames containing this flag.
let ack = head.flag() & ACK_FLAG != 0;
Ok(Ping { ack, payload })
}
pub fn encode<B: BufMut>(&self, dst: &mut B) {
let sz = self.payload.len();
tracing::trace!("encoding PING; ack={} len={}", self.ack, sz);
let flags = if self.ack { ACK_FLAG } else { 0 };
let head = Head::new(Kind::Ping, flags, StreamId::zero());
head.encode(sz, dst);
dst.put_slice(&self.payload);
}
}
impl<T> From<Ping> for Frame<T> {
fn from(src: Ping) -> Frame<T> {
Frame::Ping(src)
}
}

72
vendor/h2/src/frame/priority.rs vendored Normal file
View File

@@ -0,0 +1,72 @@
use crate::frame::*;
#[derive(Debug, Eq, PartialEq)]
pub struct Priority {
stream_id: StreamId,
dependency: StreamDependency,
}
#[derive(Debug, Eq, PartialEq)]
pub struct StreamDependency {
/// The ID of the stream dependency target
dependency_id: StreamId,
/// The weight for the stream. The value exposed (and set) here is always in
/// the range [0, 255], instead of [1, 256] (as defined in section 5.3.2.)
/// so that the value fits into a `u8`.
weight: u8,
/// True if the stream dependency is exclusive.
is_exclusive: bool,
}
impl Priority {
pub fn load(head: Head, payload: &[u8]) -> Result<Self, Error> {
let dependency = StreamDependency::load(payload)?;
if dependency.dependency_id() == head.stream_id() {
return Err(Error::InvalidDependencyId);
}
Ok(Priority {
stream_id: head.stream_id(),
dependency,
})
}
}
impl<B> From<Priority> for Frame<B> {
fn from(src: Priority) -> Self {
Frame::Priority(src)
}
}
// ===== impl StreamDependency =====
impl StreamDependency {
pub fn new(dependency_id: StreamId, weight: u8, is_exclusive: bool) -> Self {
StreamDependency {
dependency_id,
weight,
is_exclusive,
}
}
pub fn load(src: &[u8]) -> Result<Self, Error> {
if src.len() != 5 {
return Err(Error::InvalidPayloadLength);
}
// Parse the stream ID and exclusive flag
let (dependency_id, is_exclusive) = StreamId::parse(&src[..4]);
// Read the weight
let weight = src[4];
Ok(StreamDependency::new(dependency_id, weight, is_exclusive))
}
pub fn dependency_id(&self) -> StreamId {
self.dependency_id
}
}

134
vendor/h2/src/frame/reason.rs vendored Normal file
View File

@@ -0,0 +1,134 @@
use std::fmt;
/// HTTP/2 error codes.
///
/// Error codes are used in `RST_STREAM` and `GOAWAY` frames to convey the
/// reasons for the stream or connection error. For example,
/// [`SendStream::send_reset`] takes a `Reason` argument. Also, the `Error` type
/// may contain a `Reason`.
///
/// Error codes share a common code space. Some error codes apply only to
/// streams, others apply only to connections, and others may apply to either.
/// See [RFC 7540] for more information.
///
/// See [Error Codes in the spec][spec].
///
/// [spec]: http://httpwg.org/specs/rfc7540.html#ErrorCodes
/// [`SendStream::send_reset`]: struct.SendStream.html#method.send_reset
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Reason(u32);
impl Reason {
/// The associated condition is not a result of an error.
///
/// For example, a GOAWAY might include this code to indicate graceful
/// shutdown of a connection.
pub const NO_ERROR: Reason = Reason(0);
/// The endpoint detected an unspecific protocol error.
///
/// This error is for use when a more specific error code is not available.
pub const PROTOCOL_ERROR: Reason = Reason(1);
/// The endpoint encountered an unexpected internal error.
pub const INTERNAL_ERROR: Reason = Reason(2);
/// The endpoint detected that its peer violated the flow-control protocol.
pub const FLOW_CONTROL_ERROR: Reason = Reason(3);
/// The endpoint sent a SETTINGS frame but did not receive a response in
/// a timely manner.
pub const SETTINGS_TIMEOUT: Reason = Reason(4);
/// The endpoint received a frame after a stream was half-closed.
pub const STREAM_CLOSED: Reason = Reason(5);
/// The endpoint received a frame with an invalid size.
pub const FRAME_SIZE_ERROR: Reason = Reason(6);
/// The endpoint refused the stream prior to performing any application
/// processing.
pub const REFUSED_STREAM: Reason = Reason(7);
/// Used by the endpoint to indicate that the stream is no longer needed.
pub const CANCEL: Reason = Reason(8);
/// The endpoint is unable to maintain the header compression context for
/// the connection.
pub const COMPRESSION_ERROR: Reason = Reason(9);
/// The connection established in response to a CONNECT request was reset
/// or abnormally closed.
pub const CONNECT_ERROR: Reason = Reason(10);
/// The endpoint detected that its peer is exhibiting a behavior that might
/// be generating excessive load.
pub const ENHANCE_YOUR_CALM: Reason = Reason(11);
/// The underlying transport has properties that do not meet minimum
/// security requirements.
pub const INADEQUATE_SECURITY: Reason = Reason(12);
/// The endpoint requires that HTTP/1.1 be used instead of HTTP/2.
pub const HTTP_1_1_REQUIRED: Reason = Reason(13);
/// Get a string description of the error code.
pub fn description(&self) -> &str {
match self.0 {
0 => "not a result of an error",
1 => "unspecific protocol error detected",
2 => "unexpected internal error encountered",
3 => "flow-control protocol violated",
4 => "settings ACK not received in timely manner",
5 => "received frame when stream half-closed",
6 => "frame with invalid size",
7 => "refused stream before processing any application logic",
8 => "stream no longer needed",
9 => "unable to maintain the header compression context",
10 => {
"connection established in response to a CONNECT request was reset or abnormally \
closed"
}
11 => "detected excessive load generating behavior",
12 => "security properties do not meet minimum requirements",
13 => "endpoint requires HTTP/1.1",
_ => "unknown reason",
}
}
}
impl From<u32> for Reason {
fn from(src: u32) -> Reason {
Reason(src)
}
}
impl From<Reason> for u32 {
fn from(src: Reason) -> u32 {
src.0
}
}
impl fmt::Debug for Reason {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match self.0 {
0 => "NO_ERROR",
1 => "PROTOCOL_ERROR",
2 => "INTERNAL_ERROR",
3 => "FLOW_CONTROL_ERROR",
4 => "SETTINGS_TIMEOUT",
5 => "STREAM_CLOSED",
6 => "FRAME_SIZE_ERROR",
7 => "REFUSED_STREAM",
8 => "CANCEL",
9 => "COMPRESSION_ERROR",
10 => "CONNECT_ERROR",
11 => "ENHANCE_YOUR_CALM",
12 => "INADEQUATE_SECURITY",
13 => "HTTP_1_1_REQUIRED",
other => return f.debug_tuple("Reason").field(&Hex(other)).finish(),
};
f.write_str(name)
}
}
struct Hex(u32);
impl fmt::Debug for Hex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::LowerHex::fmt(&self.0, f)
}
}
impl fmt::Display for Reason {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", self.description())
}
}

56
vendor/h2/src/frame/reset.rs vendored Normal file
View File

@@ -0,0 +1,56 @@
use crate::frame::{self, Error, Head, Kind, Reason, StreamId};
use bytes::BufMut;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Reset {
stream_id: StreamId,
error_code: Reason,
}
impl Reset {
pub fn new(stream_id: StreamId, error: Reason) -> Reset {
Reset {
stream_id,
error_code: error,
}
}
pub fn stream_id(&self) -> StreamId {
self.stream_id
}
pub fn reason(&self) -> Reason {
self.error_code
}
pub fn load(head: Head, payload: &[u8]) -> Result<Reset, Error> {
if payload.len() != 4 {
return Err(Error::InvalidPayloadLength);
}
let error_code = unpack_octets_4!(payload, 0, u32);
Ok(Reset {
stream_id: head.stream_id(),
error_code: error_code.into(),
})
}
pub fn encode<B: BufMut>(&self, dst: &mut B) {
tracing::trace!(
"encoding RESET; id={:?} code={:?}",
self.stream_id,
self.error_code
);
let head = Head::new(Kind::Reset, 0, self.stream_id);
head.encode(4, dst);
dst.put_u32(self.error_code.into());
}
}
impl<B> From<Reset> for frame::Frame<B> {
fn from(src: Reset) -> Self {
frame::Frame::Reset(src)
}
}

389
vendor/h2/src/frame/settings.rs vendored Normal file
View File

@@ -0,0 +1,389 @@
use std::fmt;
use crate::frame::{util, Error, Frame, FrameSize, Head, Kind, StreamId};
use bytes::{BufMut, BytesMut};
#[derive(Clone, Default, Eq, PartialEq)]
pub struct Settings {
flags: SettingsFlags,
// Fields
header_table_size: Option<u32>,
enable_push: Option<u32>,
max_concurrent_streams: Option<u32>,
initial_window_size: Option<u32>,
max_frame_size: Option<u32>,
max_header_list_size: Option<u32>,
enable_connect_protocol: Option<u32>,
}
/// An enum that lists all valid settings that can be sent in a SETTINGS
/// frame.
///
/// Each setting has a value that is a 32 bit unsigned integer (6.5.1.).
#[derive(Debug)]
pub enum Setting {
HeaderTableSize(u32),
EnablePush(u32),
MaxConcurrentStreams(u32),
InitialWindowSize(u32),
MaxFrameSize(u32),
MaxHeaderListSize(u32),
EnableConnectProtocol(u32),
}
#[derive(Copy, Clone, Eq, PartialEq, Default)]
pub struct SettingsFlags(u8);
const ACK: u8 = 0x1;
const ALL: u8 = ACK;
/// The default value of SETTINGS_HEADER_TABLE_SIZE
pub const DEFAULT_SETTINGS_HEADER_TABLE_SIZE: usize = 4_096;
/// The default value of SETTINGS_INITIAL_WINDOW_SIZE
pub const DEFAULT_INITIAL_WINDOW_SIZE: u32 = 65_535;
/// The default value of MAX_FRAME_SIZE
pub const DEFAULT_MAX_FRAME_SIZE: FrameSize = 16_384;
/// INITIAL_WINDOW_SIZE upper bound
pub const MAX_INITIAL_WINDOW_SIZE: usize = (1 << 31) - 1;
/// MAX_FRAME_SIZE upper bound
pub const MAX_MAX_FRAME_SIZE: FrameSize = (1 << 24) - 1;
// ===== impl Settings =====
impl Settings {
pub fn ack() -> Settings {
Settings {
flags: SettingsFlags::ack(),
..Settings::default()
}
}
pub fn is_ack(&self) -> bool {
self.flags.is_ack()
}
pub fn initial_window_size(&self) -> Option<u32> {
self.initial_window_size
}
pub fn set_initial_window_size(&mut self, size: Option<u32>) {
self.initial_window_size = size;
}
pub fn max_concurrent_streams(&self) -> Option<u32> {
self.max_concurrent_streams
}
pub fn set_max_concurrent_streams(&mut self, max: Option<u32>) {
self.max_concurrent_streams = max;
}
pub fn max_frame_size(&self) -> Option<u32> {
self.max_frame_size
}
pub fn set_max_frame_size(&mut self, size: Option<u32>) {
if let Some(val) = size {
assert!(DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE);
}
self.max_frame_size = size;
}
pub fn max_header_list_size(&self) -> Option<u32> {
self.max_header_list_size
}
pub fn set_max_header_list_size(&mut self, size: Option<u32>) {
self.max_header_list_size = size;
}
pub fn is_push_enabled(&self) -> Option<bool> {
self.enable_push.map(|val| val != 0)
}
pub fn set_enable_push(&mut self, enable: bool) {
self.enable_push = Some(enable as u32);
}
pub fn is_extended_connect_protocol_enabled(&self) -> Option<bool> {
self.enable_connect_protocol.map(|val| val != 0)
}
pub fn set_enable_connect_protocol(&mut self, val: Option<u32>) {
self.enable_connect_protocol = val;
}
pub fn header_table_size(&self) -> Option<u32> {
self.header_table_size
}
pub fn set_header_table_size(&mut self, size: Option<u32>) {
self.header_table_size = size;
}
pub fn load(head: Head, payload: &[u8]) -> Result<Settings, Error> {
use self::Setting::*;
debug_assert_eq!(head.kind(), crate::frame::Kind::Settings);
if !head.stream_id().is_zero() {
return Err(Error::InvalidStreamId);
}
// Load the flag
let flag = SettingsFlags::load(head.flag());
if flag.is_ack() {
// Ensure that the payload is empty
if !payload.is_empty() {
return Err(Error::InvalidPayloadLength);
}
// Return the ACK frame
return Ok(Settings::ack());
}
// Ensure the payload length is correct, each setting is 6 bytes long.
if payload.len() % 6 != 0 {
tracing::debug!("invalid settings payload length; len={:?}", payload.len());
return Err(Error::InvalidPayloadAckSettings);
}
let mut settings = Settings::default();
debug_assert!(!settings.flags.is_ack());
for raw in payload.chunks(6) {
match Setting::load(raw) {
Some(HeaderTableSize(val)) => {
settings.header_table_size = Some(val);
}
Some(EnablePush(val)) => match val {
0 | 1 => {
settings.enable_push = Some(val);
}
_ => {
return Err(Error::InvalidSettingValue);
}
},
Some(MaxConcurrentStreams(val)) => {
settings.max_concurrent_streams = Some(val);
}
Some(InitialWindowSize(val)) => {
if val as usize > MAX_INITIAL_WINDOW_SIZE {
return Err(Error::InvalidSettingValue);
} else {
settings.initial_window_size = Some(val);
}
}
Some(MaxFrameSize(val)) => {
if DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE {
settings.max_frame_size = Some(val);
} else {
return Err(Error::InvalidSettingValue);
}
}
Some(MaxHeaderListSize(val)) => {
settings.max_header_list_size = Some(val);
}
Some(EnableConnectProtocol(val)) => match val {
0 | 1 => {
settings.enable_connect_protocol = Some(val);
}
_ => {
return Err(Error::InvalidSettingValue);
}
},
None => {}
}
}
Ok(settings)
}
fn payload_len(&self) -> usize {
let mut len = 0;
self.for_each(|_| len += 6);
len
}
pub fn encode(&self, dst: &mut BytesMut) {
// Create & encode an appropriate frame head
let head = Head::new(Kind::Settings, self.flags.into(), StreamId::zero());
let payload_len = self.payload_len();
tracing::trace!("encoding SETTINGS; len={}", payload_len);
head.encode(payload_len, dst);
// Encode the settings
self.for_each(|setting| {
tracing::trace!("encoding setting; val={:?}", setting);
setting.encode(dst)
});
}
fn for_each<F: FnMut(Setting)>(&self, mut f: F) {
use self::Setting::*;
if let Some(v) = self.header_table_size {
f(HeaderTableSize(v));
}
if let Some(v) = self.enable_push {
f(EnablePush(v));
}
if let Some(v) = self.max_concurrent_streams {
f(MaxConcurrentStreams(v));
}
if let Some(v) = self.initial_window_size {
f(InitialWindowSize(v));
}
if let Some(v) = self.max_frame_size {
f(MaxFrameSize(v));
}
if let Some(v) = self.max_header_list_size {
f(MaxHeaderListSize(v));
}
if let Some(v) = self.enable_connect_protocol {
f(EnableConnectProtocol(v));
}
}
}
impl<T> From<Settings> for Frame<T> {
fn from(src: Settings) -> Frame<T> {
Frame::Settings(src)
}
}
impl fmt::Debug for Settings {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut builder = f.debug_struct("Settings");
builder.field("flags", &self.flags);
self.for_each(|setting| match setting {
Setting::EnablePush(v) => {
builder.field("enable_push", &v);
}
Setting::HeaderTableSize(v) => {
builder.field("header_table_size", &v);
}
Setting::InitialWindowSize(v) => {
builder.field("initial_window_size", &v);
}
Setting::MaxConcurrentStreams(v) => {
builder.field("max_concurrent_streams", &v);
}
Setting::MaxFrameSize(v) => {
builder.field("max_frame_size", &v);
}
Setting::MaxHeaderListSize(v) => {
builder.field("max_header_list_size", &v);
}
Setting::EnableConnectProtocol(v) => {
builder.field("enable_connect_protocol", &v);
}
});
builder.finish()
}
}
// ===== impl Setting =====
impl Setting {
/// Creates a new `Setting` with the correct variant corresponding to the
/// given setting id, based on the settings IDs defined in section
/// 6.5.2.
pub fn from_id(id: u16, val: u32) -> Option<Setting> {
use self::Setting::*;
match id {
1 => Some(HeaderTableSize(val)),
2 => Some(EnablePush(val)),
3 => Some(MaxConcurrentStreams(val)),
4 => Some(InitialWindowSize(val)),
5 => Some(MaxFrameSize(val)),
6 => Some(MaxHeaderListSize(val)),
8 => Some(EnableConnectProtocol(val)),
_ => None,
}
}
/// Creates a new `Setting` by parsing the given buffer of 6 bytes, which
/// contains the raw byte representation of the setting, according to the
/// "SETTINGS format" defined in section 6.5.1.
///
/// The `raw` parameter should have length at least 6 bytes, since the
/// length of the raw setting is exactly 6 bytes.
///
/// # Panics
///
/// If given a buffer shorter than 6 bytes, the function will panic.
fn load(raw: &[u8]) -> Option<Setting> {
let id: u16 = (u16::from(raw[0]) << 8) | u16::from(raw[1]);
let val: u32 = unpack_octets_4!(raw, 2, u32);
Setting::from_id(id, val)
}
fn encode(&self, dst: &mut BytesMut) {
use self::Setting::*;
let (kind, val) = match *self {
HeaderTableSize(v) => (1, v),
EnablePush(v) => (2, v),
MaxConcurrentStreams(v) => (3, v),
InitialWindowSize(v) => (4, v),
MaxFrameSize(v) => (5, v),
MaxHeaderListSize(v) => (6, v),
EnableConnectProtocol(v) => (8, v),
};
dst.put_u16(kind);
dst.put_u32(val);
}
}
// ===== impl SettingsFlags =====
impl SettingsFlags {
pub fn empty() -> SettingsFlags {
SettingsFlags(0)
}
pub fn load(bits: u8) -> SettingsFlags {
SettingsFlags(bits & ALL)
}
pub fn ack() -> SettingsFlags {
SettingsFlags(ACK)
}
pub fn is_ack(&self) -> bool {
self.0 & ACK == ACK
}
}
impl From<SettingsFlags> for u8 {
fn from(src: SettingsFlags) -> u8 {
src.0
}
}
impl fmt::Debug for SettingsFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
util::debug_flags(f, self.0)
.flag_if(self.is_ack(), "ACK")
.finish()
}
}

94
vendor/h2/src/frame/stream_id.rs vendored Normal file
View File

@@ -0,0 +1,94 @@
/// A stream identifier, as described in [Section 5.1.1] of RFC 7540.
///
/// Streams are identified with an unsigned 31-bit integer. Streams
/// initiated by a client MUST use odd-numbered stream identifiers; those
/// initiated by the server MUST use even-numbered stream identifiers. A
/// stream identifier of zero (0x0) is used for connection control
/// messages; the stream identifier of zero cannot be used to establish a
/// new stream.
///
/// [Section 5.1.1]: https://tools.ietf.org/html/rfc7540#section-5.1.1
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct StreamId(u32);
#[derive(Debug, Copy, Clone)]
pub struct StreamIdOverflow;
const STREAM_ID_MASK: u32 = 1 << 31;
impl StreamId {
/// Stream ID 0.
pub const ZERO: StreamId = StreamId(0);
/// The maximum allowed stream ID.
pub const MAX: StreamId = StreamId(u32::MAX >> 1);
/// Parse the stream ID
#[inline]
pub fn parse(buf: &[u8]) -> (StreamId, bool) {
let mut ubuf = [0; 4];
ubuf.copy_from_slice(&buf[0..4]);
let unpacked = u32::from_be_bytes(ubuf);
let flag = unpacked & STREAM_ID_MASK == STREAM_ID_MASK;
// Now clear the most significant bit, as that is reserved and MUST be
// ignored when received.
(StreamId(unpacked & !STREAM_ID_MASK), flag)
}
/// Returns true if this stream ID corresponds to a stream that
/// was initiated by the client.
pub fn is_client_initiated(&self) -> bool {
let id = self.0;
id != 0 && id % 2 == 1
}
/// Returns true if this stream ID corresponds to a stream that
/// was initiated by the server.
pub fn is_server_initiated(&self) -> bool {
let id = self.0;
id != 0 && id % 2 == 0
}
/// Return a new `StreamId` for stream 0.
#[inline]
pub fn zero() -> StreamId {
StreamId::ZERO
}
/// Returns true if this stream ID is zero.
pub fn is_zero(&self) -> bool {
self.0 == 0
}
/// Returns the next stream ID initiated by the same peer as this stream
/// ID, or an error if incrementing this stream ID would overflow the
/// maximum.
pub fn next_id(&self) -> Result<StreamId, StreamIdOverflow> {
let next = self.0 + 2;
if next > StreamId::MAX.0 {
Err(StreamIdOverflow)
} else {
Ok(StreamId(next))
}
}
}
impl From<u32> for StreamId {
fn from(src: u32) -> Self {
assert_eq!(src & STREAM_ID_MASK, 0, "invalid stream ID -- MSB is set");
StreamId(src)
}
}
impl From<StreamId> for u32 {
fn from(src: StreamId) -> Self {
src.0
}
}
impl PartialEq<u32> for StreamId {
fn eq(&self, other: &u32) -> bool {
self.0 == *other
}
}

79
vendor/h2/src/frame/util.rs vendored Normal file
View File

@@ -0,0 +1,79 @@
use std::fmt;
use super::Error;
use bytes::{Buf, Bytes};
/// Strip padding from the given payload.
///
/// It is assumed that the frame had the padded flag set. This means that the
/// first byte is the length of the padding with that many
/// 0 bytes expected to follow the actual payload.
///
/// # Returns
///
/// A slice of the given payload where the actual one is found and the length
/// of the padding.
///
/// If the padded payload is invalid (e.g. the length of the padding is equal
/// to the total length), returns `None`.
pub fn strip_padding(payload: &mut Bytes) -> Result<u8, Error> {
let payload_len = payload.len();
if payload_len == 0 {
// If this is the case, the frame is invalid as no padding length can be
// extracted, even though the frame should be padded.
return Err(Error::TooMuchPadding);
}
let pad_len = payload[0] as usize;
if pad_len >= payload_len {
// This is invalid: the padding length MUST be less than the
// total frame size.
return Err(Error::TooMuchPadding);
}
payload.advance(1);
payload.truncate(payload_len - pad_len - 1);
Ok(pad_len as u8)
}
pub(super) fn debug_flags<'a, 'f: 'a>(
fmt: &'a mut fmt::Formatter<'f>,
bits: u8,
) -> DebugFlags<'a, 'f> {
let result = write!(fmt, "({:#x}", bits);
DebugFlags {
fmt,
result,
started: false,
}
}
pub(super) struct DebugFlags<'a, 'f: 'a> {
fmt: &'a mut fmt::Formatter<'f>,
result: fmt::Result,
started: bool,
}
impl<'a, 'f: 'a> DebugFlags<'a, 'f> {
pub(super) fn flag_if(&mut self, enabled: bool, name: &str) -> &mut Self {
if enabled {
self.result = self.result.and_then(|()| {
let prefix = if self.started {
" | "
} else {
self.started = true;
": "
};
write!(self.fmt, "{}{}", prefix, name)
});
}
self
}
pub(super) fn finish(&mut self) -> fmt::Result {
self.result.and_then(|()| write!(self.fmt, ")"))
}
}

62
vendor/h2/src/frame/window_update.rs vendored Normal file
View File

@@ -0,0 +1,62 @@
use crate::frame::{self, Error, Head, Kind, StreamId};
use bytes::BufMut;
const SIZE_INCREMENT_MASK: u32 = 1 << 31;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct WindowUpdate {
stream_id: StreamId,
size_increment: u32,
}
impl WindowUpdate {
pub fn new(stream_id: StreamId, size_increment: u32) -> WindowUpdate {
WindowUpdate {
stream_id,
size_increment,
}
}
pub fn stream_id(&self) -> StreamId {
self.stream_id
}
pub fn size_increment(&self) -> u32 {
self.size_increment
}
/// Builds a `WindowUpdate` frame from a raw frame.
pub fn load(head: Head, payload: &[u8]) -> Result<WindowUpdate, Error> {
debug_assert_eq!(head.kind(), crate::frame::Kind::WindowUpdate);
if payload.len() != 4 {
return Err(Error::BadFrameSize);
}
// Clear the most significant bit, as that is reserved and MUST be ignored
// when received.
let size_increment = unpack_octets_4!(payload, 0, u32) & !SIZE_INCREMENT_MASK;
if size_increment == 0 {
return Err(Error::InvalidWindowUpdateValue);
}
Ok(WindowUpdate {
stream_id: head.stream_id(),
size_increment,
})
}
pub fn encode<B: BufMut>(&self, dst: &mut B) {
tracing::trace!("encoding WINDOW_UPDATE; id={:?}", self.stream_id);
let head = Head::new(Kind::WindowUpdate, 0, self.stream_id);
head.encode(4, dst);
dst.put_u32(self.size_increment);
}
}
impl<B> From<WindowUpdate> for frame::Frame<B> {
fn from(src: WindowUpdate) -> Self {
frame::Frame::WindowUpdate(src)
}
}

28
vendor/h2/src/fuzz_bridge.rs vendored Normal file
View File

@@ -0,0 +1,28 @@
#[cfg(fuzzing)]
pub mod fuzz_logic {
use crate::hpack;
use bytes::BytesMut;
use http::header::HeaderName;
use std::io::Cursor;
pub fn fuzz_hpack(data_: &[u8]) {
let mut decoder_ = hpack::Decoder::new(0);
let mut buf = BytesMut::new();
buf.extend(data_);
let _dec_res = decoder_.decode(&mut Cursor::new(&mut buf), |_h| {});
if let Ok(s) = std::str::from_utf8(data_) {
if let Ok(h) = http::Method::from_bytes(s.as_bytes()) {
let m_ = hpack::Header::Method(h);
let mut encoder = hpack::Encoder::new(0, 0);
let _res = encode(&mut encoder, vec![m_]);
}
}
}
fn encode(e: &mut hpack::Encoder, hdrs: Vec<hpack::Header<Option<HeaderName>>>) -> BytesMut {
let mut dst = BytesMut::with_capacity(1024);
e.encode(&mut hdrs.into_iter(), &mut dst);
dst
}
}

936
vendor/h2/src/hpack/decoder.rs vendored Normal file
View File

@@ -0,0 +1,936 @@
use super::{header::BytesStr, huffman, Header};
use crate::frame;
use bytes::{Buf, Bytes, BytesMut};
use http::header;
use http::method::{self, Method};
use http::status::{self, StatusCode};
use std::cmp;
use std::collections::VecDeque;
use std::io::Cursor;
use std::str::Utf8Error;
/// Decodes headers using HPACK
#[derive(Debug)]
pub struct Decoder {
// Protocol indicated that the max table size will update
max_size_update: Option<usize>,
last_max_update: usize,
table: Table,
buffer: BytesMut,
}
/// Represents all errors that can be encountered while performing the decoding
/// of an HPACK header set.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DecoderError {
InvalidRepresentation,
InvalidIntegerPrefix,
InvalidTableIndex,
InvalidHuffmanCode,
InvalidUtf8,
InvalidStatusCode,
InvalidPseudoheader,
InvalidMaxDynamicSize,
IntegerOverflow,
NeedMore(NeedMore),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NeedMore {
UnexpectedEndOfStream,
IntegerUnderflow,
StringUnderflow,
}
enum Representation {
/// Indexed header field representation
///
/// An indexed header field representation identifies an entry in either the
/// static table or the dynamic table (see Section 2.3).
///
/// # Header encoding
///
/// ```text
/// 0 1 2 3 4 5 6 7
/// +---+---+---+---+---+---+---+---+
/// | 1 | Index (7+) |
/// +---+---------------------------+
/// ```
Indexed,
/// Literal Header Field with Incremental Indexing
///
/// A literal header field with incremental indexing representation results
/// in appending a header field to the decoded header list and inserting it
/// as a new entry into the dynamic table.
///
/// # Header encoding
///
/// ```text
/// 0 1 2 3 4 5 6 7
/// +---+---+---+---+---+---+---+---+
/// | 0 | 1 | Index (6+) |
/// +---+---+-----------------------+
/// | H | Value Length (7+) |
/// +---+---------------------------+
/// | Value String (Length octets) |
/// +-------------------------------+
/// ```
LiteralWithIndexing,
/// Literal Header Field without Indexing
///
/// A literal header field without indexing representation results in
/// appending a header field to the decoded header list without altering the
/// dynamic table.
///
/// # Header encoding
///
/// ```text
/// 0 1 2 3 4 5 6 7
/// +---+---+---+---+---+---+---+---+
/// | 0 | 0 | 0 | 0 | Index (4+) |
/// +---+---+-----------------------+
/// | H | Value Length (7+) |
/// +---+---------------------------+
/// | Value String (Length octets) |
/// +-------------------------------+
/// ```
LiteralWithoutIndexing,
/// Literal Header Field Never Indexed
///
/// A literal header field never-indexed representation results in appending
/// a header field to the decoded header list without altering the dynamic
/// table. Intermediaries MUST use the same representation for encoding this
/// header field.
///
/// ```text
/// 0 1 2 3 4 5 6 7
/// +---+---+---+---+---+---+---+---+
/// | 0 | 0 | 0 | 1 | Index (4+) |
/// +---+---+-----------------------+
/// | H | Value Length (7+) |
/// +---+---------------------------+
/// | Value String (Length octets) |
/// +-------------------------------+
/// ```
LiteralNeverIndexed,
/// Dynamic Table Size Update
///
/// A dynamic table size update signals a change to the size of the dynamic
/// table.
///
/// # Header encoding
///
/// ```text
/// 0 1 2 3 4 5 6 7
/// +---+---+---+---+---+---+---+---+
/// | 0 | 0 | 1 | Max size (5+) |
/// +---+---------------------------+
/// ```
SizeUpdate,
}
#[derive(Debug)]
struct Table {
entries: VecDeque<Header>,
size: usize,
max_size: usize,
}
struct StringMarker {
offset: usize,
len: usize,
string: Option<Bytes>,
}
// ===== impl Decoder =====
impl Decoder {
/// Creates a new `Decoder` with all settings set to default values.
pub fn new(size: usize) -> Decoder {
Decoder {
max_size_update: None,
last_max_update: size,
table: Table::new(size),
buffer: BytesMut::with_capacity(4096),
}
}
/// Queues a potential size update
#[allow(dead_code)]
pub fn queue_size_update(&mut self, size: usize) {
let size = match self.max_size_update {
Some(v) => cmp::max(v, size),
None => size,
};
self.max_size_update = Some(size);
}
/// Decodes the headers found in the given buffer.
pub fn decode<F>(
&mut self,
src: &mut Cursor<&mut BytesMut>,
mut f: F,
) -> Result<(), DecoderError>
where
F: FnMut(Header),
{
use self::Representation::*;
let mut can_resize = true;
if let Some(size) = self.max_size_update.take() {
self.last_max_update = size;
}
let span = tracing::trace_span!("hpack::decode");
let _e = span.enter();
tracing::trace!("decode");
while let Some(ty) = peek_u8(src) {
// At this point we are always at the beginning of the next block
// within the HPACK data. The type of the block can always be
// determined from the first byte.
match Representation::load(ty)? {
Indexed => {
tracing::trace!(rem = src.remaining(), kind = %"Indexed");
can_resize = false;
let entry = self.decode_indexed(src)?;
consume(src);
f(entry);
}
LiteralWithIndexing => {
tracing::trace!(rem = src.remaining(), kind = %"LiteralWithIndexing");
can_resize = false;
let entry = self.decode_literal(src, true)?;
// Insert the header into the table
self.table.insert(entry.clone());
consume(src);
f(entry);
}
LiteralWithoutIndexing => {
tracing::trace!(rem = src.remaining(), kind = %"LiteralWithoutIndexing");
can_resize = false;
let entry = self.decode_literal(src, false)?;
consume(src);
f(entry);
}
LiteralNeverIndexed => {
tracing::trace!(rem = src.remaining(), kind = %"LiteralNeverIndexed");
can_resize = false;
let entry = self.decode_literal(src, false)?;
consume(src);
// TODO: Track that this should never be indexed
f(entry);
}
SizeUpdate => {
tracing::trace!(rem = src.remaining(), kind = %"SizeUpdate");
if !can_resize {
return Err(DecoderError::InvalidMaxDynamicSize);
}
// Handle the dynamic table size update
self.process_size_update(src)?;
consume(src);
}
}
}
Ok(())
}
fn process_size_update(&mut self, buf: &mut Cursor<&mut BytesMut>) -> Result<(), DecoderError> {
let new_size = decode_int(buf, 5)?;
if new_size > self.last_max_update {
return Err(DecoderError::InvalidMaxDynamicSize);
}
tracing::debug!(
from = self.table.size(),
to = new_size,
"Decoder changed max table size"
);
self.table.set_max_size(new_size);
Ok(())
}
fn decode_indexed(&self, buf: &mut Cursor<&mut BytesMut>) -> Result<Header, DecoderError> {
let index = decode_int(buf, 7)?;
self.table.get(index)
}
fn decode_literal(
&mut self,
buf: &mut Cursor<&mut BytesMut>,
index: bool,
) -> Result<Header, DecoderError> {
let prefix = if index { 6 } else { 4 };
// Extract the table index for the name, or 0 if not indexed
let table_idx = decode_int(buf, prefix)?;
// First, read the header name
if table_idx == 0 {
let old_pos = buf.position();
let name_marker = self.try_decode_string(buf)?;
let value_marker = self.try_decode_string(buf)?;
buf.set_position(old_pos);
// Read the name as a literal
let name = name_marker.consume(buf);
let value = value_marker.consume(buf);
Header::new(name, value)
} else {
let e = self.table.get(table_idx)?;
let value = self.decode_string(buf)?;
e.name().into_entry(value)
}
}
fn try_decode_string(
&mut self,
buf: &mut Cursor<&mut BytesMut>,
) -> Result<StringMarker, DecoderError> {
let old_pos = buf.position();
const HUFF_FLAG: u8 = 0b1000_0000;
// The first bit in the first byte contains the huffman encoded flag.
let huff = match peek_u8(buf) {
Some(hdr) => (hdr & HUFF_FLAG) == HUFF_FLAG,
None => return Err(DecoderError::NeedMore(NeedMore::UnexpectedEndOfStream)),
};
// Decode the string length using 7 bit prefix
let len = decode_int(buf, 7)?;
if len > buf.remaining() {
tracing::trace!(len, remaining = buf.remaining(), "decode_string underflow",);
return Err(DecoderError::NeedMore(NeedMore::StringUnderflow));
}
let offset = (buf.position() - old_pos) as usize;
if huff {
let ret = {
let raw = &buf.chunk()[..len];
huffman::decode(raw, &mut self.buffer).map(|buf| StringMarker {
offset,
len,
string: Some(BytesMut::freeze(buf)),
})
};
buf.advance(len);
ret
} else {
buf.advance(len);
Ok(StringMarker {
offset,
len,
string: None,
})
}
}
fn decode_string(&mut self, buf: &mut Cursor<&mut BytesMut>) -> Result<Bytes, DecoderError> {
let old_pos = buf.position();
let marker = self.try_decode_string(buf)?;
buf.set_position(old_pos);
Ok(marker.consume(buf))
}
}
impl Default for Decoder {
fn default() -> Decoder {
Decoder::new(4096)
}
}
// ===== impl Representation =====
impl Representation {
pub fn load(byte: u8) -> Result<Representation, DecoderError> {
const INDEXED: u8 = 0b1000_0000;
const LITERAL_WITH_INDEXING: u8 = 0b0100_0000;
const LITERAL_WITHOUT_INDEXING: u8 = 0b1111_0000;
const LITERAL_NEVER_INDEXED: u8 = 0b0001_0000;
const SIZE_UPDATE_MASK: u8 = 0b1110_0000;
const SIZE_UPDATE: u8 = 0b0010_0000;
// TODO: What did I even write here?
if byte & INDEXED == INDEXED {
Ok(Representation::Indexed)
} else if byte & LITERAL_WITH_INDEXING == LITERAL_WITH_INDEXING {
Ok(Representation::LiteralWithIndexing)
} else if byte & LITERAL_WITHOUT_INDEXING == 0 {
Ok(Representation::LiteralWithoutIndexing)
} else if byte & LITERAL_WITHOUT_INDEXING == LITERAL_NEVER_INDEXED {
Ok(Representation::LiteralNeverIndexed)
} else if byte & SIZE_UPDATE_MASK == SIZE_UPDATE {
Ok(Representation::SizeUpdate)
} else {
Err(DecoderError::InvalidRepresentation)
}
}
}
fn decode_int<B: Buf>(buf: &mut B, prefix_size: u8) -> Result<usize, DecoderError> {
// The octet limit is chosen such that the maximum allowed *value* can
// never overflow an unsigned 32-bit integer. The maximum value of any
// integer that can be encoded with 5 octets is ~2^28
const MAX_BYTES: usize = 5;
const VARINT_MASK: u8 = 0b0111_1111;
const VARINT_FLAG: u8 = 0b1000_0000;
if prefix_size < 1 || prefix_size > 8 {
return Err(DecoderError::InvalidIntegerPrefix);
}
if !buf.has_remaining() {
return Err(DecoderError::NeedMore(NeedMore::IntegerUnderflow));
}
let mask = if prefix_size == 8 {
0xFF
} else {
(1u8 << prefix_size).wrapping_sub(1)
};
let mut ret = (buf.get_u8() & mask) as usize;
if ret < mask as usize {
// Value fits in the prefix bits
return Ok(ret);
}
// The int did not fit in the prefix bits, so continue reading.
//
// The total number of bytes used to represent the int. The first byte was
// the prefix, so start at 1.
let mut bytes = 1;
// The rest of the int is stored as a varint -- 7 bits for the value and 1
// bit to indicate if it is the last byte.
let mut shift = 0;
while buf.has_remaining() {
let b = buf.get_u8();
bytes += 1;
ret += ((b & VARINT_MASK) as usize) << shift;
shift += 7;
if b & VARINT_FLAG == 0 {
return Ok(ret);
}
if bytes == MAX_BYTES {
// The spec requires that this situation is an error
return Err(DecoderError::IntegerOverflow);
}
}
Err(DecoderError::NeedMore(NeedMore::IntegerUnderflow))
}
fn peek_u8<B: Buf>(buf: &B) -> Option<u8> {
if buf.has_remaining() {
Some(buf.chunk()[0])
} else {
None
}
}
fn take(buf: &mut Cursor<&mut BytesMut>, n: usize) -> Bytes {
let pos = buf.position() as usize;
let mut head = buf.get_mut().split_to(pos + n);
buf.set_position(0);
head.advance(pos);
head.freeze()
}
impl StringMarker {
fn consume(self, buf: &mut Cursor<&mut BytesMut>) -> Bytes {
buf.advance(self.offset);
match self.string {
Some(string) => {
buf.advance(self.len);
string
}
None => take(buf, self.len),
}
}
}
fn consume(buf: &mut Cursor<&mut BytesMut>) {
// remove bytes from the internal BytesMut when they have been successfully
// decoded. This is a more permanent cursor position, which will be
// used to resume if decoding was only partial.
take(buf, 0);
}
// ===== impl Table =====
impl Table {
fn new(max_size: usize) -> Table {
Table {
entries: VecDeque::new(),
size: 0,
max_size,
}
}
fn size(&self) -> usize {
self.size
}
/// Returns the entry located at the given index.
///
/// The table is 1-indexed and constructed in such a way that the first
/// entries belong to the static table, followed by entries in the dynamic
/// table. They are merged into a single index address space, though.
///
/// This is according to the [HPACK spec, section 2.3.3.]
/// (http://http2.github.io/http2-spec/compression.html#index.address.space)
pub fn get(&self, index: usize) -> Result<Header, DecoderError> {
if index == 0 {
return Err(DecoderError::InvalidTableIndex);
}
if index <= 61 {
return Ok(get_static(index));
}
// Convert the index for lookup in the entries structure.
match self.entries.get(index - 62) {
Some(e) => Ok(e.clone()),
None => Err(DecoderError::InvalidTableIndex),
}
}
fn insert(&mut self, entry: Header) {
let len = entry.len();
self.reserve(len);
if self.size + len <= self.max_size {
self.size += len;
// Track the entry
self.entries.push_front(entry);
}
}
fn set_max_size(&mut self, size: usize) {
self.max_size = size;
// Make the table size fit within the new constraints.
self.consolidate();
}
fn reserve(&mut self, size: usize) {
while self.size + size > self.max_size {
match self.entries.pop_back() {
Some(last) => {
self.size -= last.len();
}
None => return,
}
}
}
fn consolidate(&mut self) {
while self.size > self.max_size {
{
let last = match self.entries.back() {
Some(x) => x,
None => {
// Can never happen as the size of the table must reach
// 0 by the time we've exhausted all elements.
panic!("Size of table != 0, but no headers left!");
}
};
self.size -= last.len();
}
self.entries.pop_back();
}
}
}
// ===== impl DecoderError =====
impl From<Utf8Error> for DecoderError {
fn from(_: Utf8Error) -> DecoderError {
// TODO: Better error?
DecoderError::InvalidUtf8
}
}
impl From<header::InvalidHeaderValue> for DecoderError {
fn from(_: header::InvalidHeaderValue) -> DecoderError {
// TODO: Better error?
DecoderError::InvalidUtf8
}
}
impl From<header::InvalidHeaderName> for DecoderError {
fn from(_: header::InvalidHeaderName) -> DecoderError {
// TODO: Better error
DecoderError::InvalidUtf8
}
}
impl From<method::InvalidMethod> for DecoderError {
fn from(_: method::InvalidMethod) -> DecoderError {
// TODO: Better error
DecoderError::InvalidUtf8
}
}
impl From<status::InvalidStatusCode> for DecoderError {
fn from(_: status::InvalidStatusCode) -> DecoderError {
// TODO: Better error
DecoderError::InvalidUtf8
}
}
impl From<DecoderError> for frame::Error {
fn from(src: DecoderError) -> Self {
frame::Error::Hpack(src)
}
}
/// Get an entry from the static table
pub fn get_static(idx: usize) -> Header {
use http::header::HeaderValue;
match idx {
1 => Header::Authority(BytesStr::from_static("")),
2 => Header::Method(Method::GET),
3 => Header::Method(Method::POST),
4 => Header::Path(BytesStr::from_static("/")),
5 => Header::Path(BytesStr::from_static("/index.html")),
6 => Header::Scheme(BytesStr::from_static("http")),
7 => Header::Scheme(BytesStr::from_static("https")),
8 => Header::Status(StatusCode::OK),
9 => Header::Status(StatusCode::NO_CONTENT),
10 => Header::Status(StatusCode::PARTIAL_CONTENT),
11 => Header::Status(StatusCode::NOT_MODIFIED),
12 => Header::Status(StatusCode::BAD_REQUEST),
13 => Header::Status(StatusCode::NOT_FOUND),
14 => Header::Status(StatusCode::INTERNAL_SERVER_ERROR),
15 => Header::Field {
name: header::ACCEPT_CHARSET,
value: HeaderValue::from_static(""),
},
16 => Header::Field {
name: header::ACCEPT_ENCODING,
value: HeaderValue::from_static("gzip, deflate"),
},
17 => Header::Field {
name: header::ACCEPT_LANGUAGE,
value: HeaderValue::from_static(""),
},
18 => Header::Field {
name: header::ACCEPT_RANGES,
value: HeaderValue::from_static(""),
},
19 => Header::Field {
name: header::ACCEPT,
value: HeaderValue::from_static(""),
},
20 => Header::Field {
name: header::ACCESS_CONTROL_ALLOW_ORIGIN,
value: HeaderValue::from_static(""),
},
21 => Header::Field {
name: header::AGE,
value: HeaderValue::from_static(""),
},
22 => Header::Field {
name: header::ALLOW,
value: HeaderValue::from_static(""),
},
23 => Header::Field {
name: header::AUTHORIZATION,
value: HeaderValue::from_static(""),
},
24 => Header::Field {
name: header::CACHE_CONTROL,
value: HeaderValue::from_static(""),
},
25 => Header::Field {
name: header::CONTENT_DISPOSITION,
value: HeaderValue::from_static(""),
},
26 => Header::Field {
name: header::CONTENT_ENCODING,
value: HeaderValue::from_static(""),
},
27 => Header::Field {
name: header::CONTENT_LANGUAGE,
value: HeaderValue::from_static(""),
},
28 => Header::Field {
name: header::CONTENT_LENGTH,
value: HeaderValue::from_static(""),
},
29 => Header::Field {
name: header::CONTENT_LOCATION,
value: HeaderValue::from_static(""),
},
30 => Header::Field {
name: header::CONTENT_RANGE,
value: HeaderValue::from_static(""),
},
31 => Header::Field {
name: header::CONTENT_TYPE,
value: HeaderValue::from_static(""),
},
32 => Header::Field {
name: header::COOKIE,
value: HeaderValue::from_static(""),
},
33 => Header::Field {
name: header::DATE,
value: HeaderValue::from_static(""),
},
34 => Header::Field {
name: header::ETAG,
value: HeaderValue::from_static(""),
},
35 => Header::Field {
name: header::EXPECT,
value: HeaderValue::from_static(""),
},
36 => Header::Field {
name: header::EXPIRES,
value: HeaderValue::from_static(""),
},
37 => Header::Field {
name: header::FROM,
value: HeaderValue::from_static(""),
},
38 => Header::Field {
name: header::HOST,
value: HeaderValue::from_static(""),
},
39 => Header::Field {
name: header::IF_MATCH,
value: HeaderValue::from_static(""),
},
40 => Header::Field {
name: header::IF_MODIFIED_SINCE,
value: HeaderValue::from_static(""),
},
41 => Header::Field {
name: header::IF_NONE_MATCH,
value: HeaderValue::from_static(""),
},
42 => Header::Field {
name: header::IF_RANGE,
value: HeaderValue::from_static(""),
},
43 => Header::Field {
name: header::IF_UNMODIFIED_SINCE,
value: HeaderValue::from_static(""),
},
44 => Header::Field {
name: header::LAST_MODIFIED,
value: HeaderValue::from_static(""),
},
45 => Header::Field {
name: header::LINK,
value: HeaderValue::from_static(""),
},
46 => Header::Field {
name: header::LOCATION,
value: HeaderValue::from_static(""),
},
47 => Header::Field {
name: header::MAX_FORWARDS,
value: HeaderValue::from_static(""),
},
48 => Header::Field {
name: header::PROXY_AUTHENTICATE,
value: HeaderValue::from_static(""),
},
49 => Header::Field {
name: header::PROXY_AUTHORIZATION,
value: HeaderValue::from_static(""),
},
50 => Header::Field {
name: header::RANGE,
value: HeaderValue::from_static(""),
},
51 => Header::Field {
name: header::REFERER,
value: HeaderValue::from_static(""),
},
52 => Header::Field {
name: header::REFRESH,
value: HeaderValue::from_static(""),
},
53 => Header::Field {
name: header::RETRY_AFTER,
value: HeaderValue::from_static(""),
},
54 => Header::Field {
name: header::SERVER,
value: HeaderValue::from_static(""),
},
55 => Header::Field {
name: header::SET_COOKIE,
value: HeaderValue::from_static(""),
},
56 => Header::Field {
name: header::STRICT_TRANSPORT_SECURITY,
value: HeaderValue::from_static(""),
},
57 => Header::Field {
name: header::TRANSFER_ENCODING,
value: HeaderValue::from_static(""),
},
58 => Header::Field {
name: header::USER_AGENT,
value: HeaderValue::from_static(""),
},
59 => Header::Field {
name: header::VARY,
value: HeaderValue::from_static(""),
},
60 => Header::Field {
name: header::VIA,
value: HeaderValue::from_static(""),
},
61 => Header::Field {
name: header::WWW_AUTHENTICATE,
value: HeaderValue::from_static(""),
},
_ => unreachable!(),
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_peek_u8() {
let b = 0xff;
let mut buf = Cursor::new(vec![b]);
assert_eq!(peek_u8(&buf), Some(b));
assert_eq!(buf.get_u8(), b);
assert_eq!(peek_u8(&buf), None);
}
#[test]
fn test_decode_string_empty() {
let mut de = Decoder::new(0);
let mut buf = BytesMut::new();
let err = de.decode_string(&mut Cursor::new(&mut buf)).unwrap_err();
assert_eq!(err, DecoderError::NeedMore(NeedMore::UnexpectedEndOfStream));
}
#[test]
fn test_decode_empty() {
let mut de = Decoder::new(0);
let mut buf = BytesMut::new();
let _: () = de.decode(&mut Cursor::new(&mut buf), |_| {}).unwrap();
}
#[test]
fn test_decode_indexed_larger_than_table() {
let mut de = Decoder::new(0);
let mut buf = BytesMut::new();
buf.extend([0b01000000, 0x80 | 2]);
buf.extend(huff_encode(b"foo"));
buf.extend([0x80 | 3]);
buf.extend(huff_encode(b"bar"));
let mut res = vec![];
de.decode(&mut Cursor::new(&mut buf), |h| {
res.push(h);
})
.unwrap();
assert_eq!(res.len(), 1);
assert_eq!(de.table.size(), 0);
match res[0] {
Header::Field {
ref name,
ref value,
} => {
assert_eq!(name, "foo");
assert_eq!(value, "bar");
}
_ => panic!(),
}
}
fn huff_encode(src: &[u8]) -> BytesMut {
let mut buf = BytesMut::new();
huffman::encode(src, &mut buf);
buf
}
#[test]
fn test_decode_continuation_header_with_non_huff_encoded_name() {
let mut de = Decoder::new(0);
let value = huff_encode(b"bar");
let mut buf = BytesMut::new();
// header name is non_huff encoded
buf.extend([0b01000000, 3]);
buf.extend(b"foo");
// header value is partial
buf.extend([0x80 | 3]);
buf.extend(&value[0..1]);
let mut res = vec![];
let e = de
.decode(&mut Cursor::new(&mut buf), |h| {
res.push(h);
})
.unwrap_err();
// decode error because the header value is partial
assert_eq!(e, DecoderError::NeedMore(NeedMore::StringUnderflow));
// extend buf with the remaining header value
buf.extend(&value[1..]);
de.decode(&mut Cursor::new(&mut buf), |h| {
res.push(h);
})
.unwrap();
assert_eq!(res.len(), 1);
assert_eq!(de.table.size(), 0);
match res[0] {
Header::Field {
ref name,
ref value,
} => {
assert_eq!(name, "foo");
assert_eq!(value, "bar");
}
_ => panic!(),
}
}
}

720
vendor/h2/src/hpack/encoder.rs vendored Normal file
View File

@@ -0,0 +1,720 @@
use super::table::{Index, Table};
use super::{huffman, Header};
use bytes::{BufMut, BytesMut};
use http::header::{HeaderName, HeaderValue};
#[derive(Debug)]
pub struct Encoder {
table: Table,
size_update: Option<SizeUpdate>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum SizeUpdate {
One(usize),
Two(usize, usize), // min, max
}
impl Encoder {
pub fn new(max_size: usize, capacity: usize) -> Encoder {
Encoder {
table: Table::new(max_size, capacity),
size_update: None,
}
}
/// Queues a max size update.
///
/// The next call to `encode` will include a dynamic size update frame.
pub fn update_max_size(&mut self, val: usize) {
match self.size_update {
Some(SizeUpdate::One(old)) => {
if val > old {
if old > self.table.max_size() {
self.size_update = Some(SizeUpdate::One(val));
} else {
self.size_update = Some(SizeUpdate::Two(old, val));
}
} else {
self.size_update = Some(SizeUpdate::One(val));
}
}
Some(SizeUpdate::Two(min, _)) => {
if val < min {
self.size_update = Some(SizeUpdate::One(val));
} else {
self.size_update = Some(SizeUpdate::Two(min, val));
}
}
None => {
if val != self.table.max_size() {
// Don't bother writing a frame if the value already matches
// the table's max size.
self.size_update = Some(SizeUpdate::One(val));
}
}
}
}
/// Encode a set of headers into the provide buffer
pub fn encode<I>(&mut self, headers: I, dst: &mut BytesMut)
where
I: IntoIterator<Item = Header<Option<HeaderName>>>,
{
let span = tracing::trace_span!("hpack::encode");
let _e = span.enter();
self.encode_size_updates(dst);
let mut last_index = None;
for header in headers {
match header.reify() {
// The header has an associated name. In which case, try to
// index it in the table.
Ok(header) => {
let index = self.table.index(header);
self.encode_header(&index, dst);
last_index = Some(index);
}
// The header does not have an associated name. This means that
// the name is the same as the previously yielded header. In
// which case, we skip table lookup and just use the same index
// as the previous entry.
Err(value) => {
self.encode_header_without_name(
last_index.as_ref().unwrap_or_else(|| {
panic!("encoding header without name, but no previous index to use for name");
}),
&value,
dst,
);
}
}
}
}
fn encode_size_updates(&mut self, dst: &mut BytesMut) {
match self.size_update.take() {
Some(SizeUpdate::One(val)) => {
self.table.resize(val);
encode_size_update(val, dst);
}
Some(SizeUpdate::Two(min, max)) => {
self.table.resize(min);
self.table.resize(max);
encode_size_update(min, dst);
encode_size_update(max, dst);
}
None => {}
}
}
fn encode_header(&mut self, index: &Index, dst: &mut BytesMut) {
match *index {
Index::Indexed(idx, _) => {
encode_int(idx, 7, 0x80, dst);
}
Index::Name(idx, _) => {
let header = self.table.resolve(index);
encode_not_indexed(idx, header.value_slice(), header.is_sensitive(), dst);
}
Index::Inserted(_) => {
let header = self.table.resolve(index);
assert!(!header.is_sensitive());
dst.put_u8(0b0100_0000);
encode_str(header.name().as_slice(), dst);
encode_str(header.value_slice(), dst);
}
Index::InsertedValue(idx, _) => {
let header = self.table.resolve(index);
assert!(!header.is_sensitive());
encode_int(idx, 6, 0b0100_0000, dst);
encode_str(header.value_slice(), dst);
}
Index::NotIndexed(_) => {
let header = self.table.resolve(index);
encode_not_indexed2(
header.name().as_slice(),
header.value_slice(),
header.is_sensitive(),
dst,
);
}
}
}
fn encode_header_without_name(
&mut self,
last: &Index,
value: &HeaderValue,
dst: &mut BytesMut,
) {
match *last {
Index::Indexed(..)
| Index::Name(..)
| Index::Inserted(..)
| Index::InsertedValue(..) => {
let idx = self.table.resolve_idx(last);
encode_not_indexed(idx, value.as_ref(), value.is_sensitive(), dst);
}
Index::NotIndexed(_) => {
let last = self.table.resolve(last);
encode_not_indexed2(
last.name().as_slice(),
value.as_ref(),
value.is_sensitive(),
dst,
);
}
}
}
}
impl Default for Encoder {
fn default() -> Encoder {
Encoder::new(4096, 0)
}
}
fn encode_size_update(val: usize, dst: &mut BytesMut) {
encode_int(val, 5, 0b0010_0000, dst)
}
fn encode_not_indexed(name: usize, value: &[u8], sensitive: bool, dst: &mut BytesMut) {
if sensitive {
encode_int(name, 4, 0b10000, dst);
} else {
encode_int(name, 4, 0, dst);
}
encode_str(value, dst);
}
fn encode_not_indexed2(name: &[u8], value: &[u8], sensitive: bool, dst: &mut BytesMut) {
if sensitive {
dst.put_u8(0b10000);
} else {
dst.put_u8(0);
}
encode_str(name, dst);
encode_str(value, dst);
}
fn encode_str(val: &[u8], dst: &mut BytesMut) {
if !val.is_empty() {
let idx = position(dst);
// Push a placeholder byte for the length header
dst.put_u8(0);
// Encode with huffman
huffman::encode(val, dst);
let huff_len = position(dst) - (idx + 1);
if encode_int_one_byte(huff_len, 7) {
// Write the string head
dst[idx] = 0x80 | huff_len as u8;
} else {
// Write the head to a placeholder
const PLACEHOLDER_LEN: usize = 8;
let mut buf = [0u8; PLACEHOLDER_LEN];
let head_len = {
let mut head_dst = &mut buf[..];
encode_int(huff_len, 7, 0x80, &mut head_dst);
PLACEHOLDER_LEN - head_dst.remaining_mut()
};
// This is just done to reserve space in the destination
dst.put_slice(&buf[1..head_len]);
// Shift the header forward
for i in 0..huff_len {
let src_i = idx + 1 + (huff_len - (i + 1));
let dst_i = idx + head_len + (huff_len - (i + 1));
dst[dst_i] = dst[src_i];
}
// Copy in the head
for i in 0..head_len {
dst[idx + i] = buf[i];
}
}
} else {
// Write an empty string
dst.put_u8(0);
}
}
/// Encode an integer into the given destination buffer
fn encode_int<B: BufMut>(
mut value: usize, // The integer to encode
prefix_bits: usize, // The number of bits in the prefix
first_byte: u8, // The base upon which to start encoding the int
dst: &mut B,
) {
if encode_int_one_byte(value, prefix_bits) {
dst.put_u8(first_byte | value as u8);
return;
}
let low = (1 << prefix_bits) - 1;
value -= low;
dst.put_u8(first_byte | low as u8);
while value >= 128 {
dst.put_u8(0b1000_0000 | value as u8);
value >>= 7;
}
dst.put_u8(value as u8);
}
/// Returns true if the in the int can be fully encoded in the first byte.
fn encode_int_one_byte(value: usize, prefix_bits: usize) -> bool {
value < (1 << prefix_bits) - 1
}
fn position(buf: &BytesMut) -> usize {
buf.len()
}
#[cfg(test)]
mod test {
use super::*;
use http::*;
#[test]
fn test_encode_method_get() {
let mut encoder = Encoder::default();
let res = encode(&mut encoder, vec![method("GET")]);
assert_eq!(*res, [0x80 | 2]);
assert_eq!(encoder.table.len(), 0);
}
#[test]
fn test_encode_method_post() {
let mut encoder = Encoder::default();
let res = encode(&mut encoder, vec![method("POST")]);
assert_eq!(*res, [0x80 | 3]);
assert_eq!(encoder.table.len(), 0);
}
#[test]
fn test_encode_method_patch() {
let mut encoder = Encoder::default();
let res = encode(&mut encoder, vec![method("PATCH")]);
assert_eq!(res[0], 0b01000000 | 2); // Incremental indexing w/ name pulled from table
assert_eq!(res[1], 0x80 | 5); // header value w/ huffman coding
assert_eq!("PATCH", huff_decode(&res[2..7]));
assert_eq!(encoder.table.len(), 1);
let res = encode(&mut encoder, vec![method("PATCH")]);
assert_eq!(1 << 7 | 62, res[0]);
assert_eq!(1, res.len());
}
#[test]
fn test_encode_indexed_name_literal_value() {
let mut encoder = Encoder::default();
let res = encode(&mut encoder, vec![header("content-language", "foo")]);
assert_eq!(res[0], 0b01000000 | 27); // Indexed name
assert_eq!(res[1], 0x80 | 2); // header value w/ huffman coding
assert_eq!("foo", huff_decode(&res[2..4]));
// Same name, new value should still use incremental
let res = encode(&mut encoder, vec![header("content-language", "bar")]);
assert_eq!(res[0], 0b01000000 | 27); // Indexed name
assert_eq!(res[1], 0x80 | 3); // header value w/ huffman coding
assert_eq!("bar", huff_decode(&res[2..5]));
}
#[test]
fn test_repeated_headers_are_indexed() {
let mut encoder = Encoder::default();
let res = encode(&mut encoder, vec![header("foo", "hello")]);
assert_eq!(&[0b01000000, 0x80 | 2], &res[0..2]);
assert_eq!("foo", huff_decode(&res[2..4]));
assert_eq!(0x80 | 4, res[4]);
assert_eq!("hello", huff_decode(&res[5..]));
assert_eq!(9, res.len());
assert_eq!(1, encoder.table.len());
let res = encode(&mut encoder, vec![header("foo", "hello")]);
assert_eq!([0x80 | 62], *res);
assert_eq!(encoder.table.len(), 1);
}
#[test]
fn test_evicting_headers() {
let mut encoder = Encoder::default();
// Fill the table
for i in 0..64 {
let key = format!("x-hello-world-{:02}", i);
let res = encode(&mut encoder, vec![header(&key, &key)]);
assert_eq!(&[0b01000000, 0x80 | 12], &res[0..2]);
assert_eq!(key, huff_decode(&res[2..14]));
assert_eq!(0x80 | 12, res[14]);
assert_eq!(key, huff_decode(&res[15..]));
assert_eq!(27, res.len());
// Make sure the header can be found...
let res = encode(&mut encoder, vec![header(&key, &key)]);
// Only check that it is found
assert_eq!(0x80, res[0] & 0x80);
}
assert_eq!(4096, encoder.table.size());
assert_eq!(64, encoder.table.len());
// Find existing headers
for i in 0..64 {
let key = format!("x-hello-world-{:02}", i);
let res = encode(&mut encoder, vec![header(&key, &key)]);
assert_eq!(0x80, res[0] & 0x80);
}
// Insert a new header
let key = "x-hello-world-64";
let res = encode(&mut encoder, vec![header(key, key)]);
assert_eq!(&[0b01000000, 0x80 | 12], &res[0..2]);
assert_eq!(key, huff_decode(&res[2..14]));
assert_eq!(0x80 | 12, res[14]);
assert_eq!(key, huff_decode(&res[15..]));
assert_eq!(27, res.len());
assert_eq!(64, encoder.table.len());
// Now try encoding entries that should exist in the table
for i in 1..65 {
let key = format!("x-hello-world-{:02}", i);
let res = encode(&mut encoder, vec![header(&key, &key)]);
assert_eq!(0x80 | (61 + (65 - i)), res[0]);
}
}
#[test]
fn test_large_headers_are_not_indexed() {
let mut encoder = Encoder::new(128, 0);
let key = "hello-world-hello-world-HELLO-zzz";
let res = encode(&mut encoder, vec![header(key, key)]);
assert_eq!(&[0, 0x80 | 25], &res[..2]);
assert_eq!(0, encoder.table.len());
assert_eq!(0, encoder.table.size());
}
#[test]
fn test_sensitive_headers_are_never_indexed() {
use http::header::HeaderValue;
let name = "my-password".parse().unwrap();
let mut value = HeaderValue::from_bytes(b"12345").unwrap();
value.set_sensitive(true);
let header = Header::Field {
name: Some(name),
value,
};
// Now, try to encode the sensitive header
let mut encoder = Encoder::default();
let res = encode(&mut encoder, vec![header]);
assert_eq!(&[0b10000, 0x80 | 8], &res[..2]);
assert_eq!("my-password", huff_decode(&res[2..10]));
assert_eq!(0x80 | 4, res[10]);
assert_eq!("12345", huff_decode(&res[11..]));
// Now, try to encode a sensitive header w/ a name in the static table
let name = "authorization".parse().unwrap();
let mut value = HeaderValue::from_bytes(b"12345").unwrap();
value.set_sensitive(true);
let header = Header::Field {
name: Some(name),
value,
};
let mut encoder = Encoder::default();
let res = encode(&mut encoder, vec![header]);
assert_eq!(&[0b11111, 8], &res[..2]);
assert_eq!(0x80 | 4, res[2]);
assert_eq!("12345", huff_decode(&res[3..]));
// Using the name component of a previously indexed header (without
// sensitive flag set)
let _ = encode(
&mut encoder,
vec![self::header("my-password", "not-so-secret")],
);
let name = "my-password".parse().unwrap();
let mut value = HeaderValue::from_bytes(b"12345").unwrap();
value.set_sensitive(true);
let header = Header::Field {
name: Some(name),
value,
};
let res = encode(&mut encoder, vec![header]);
assert_eq!(&[0b11111, 47], &res[..2]);
assert_eq!(0x80 | 4, res[2]);
assert_eq!("12345", huff_decode(&res[3..]));
}
#[test]
fn test_content_length_value_not_indexed() {
let mut encoder = Encoder::default();
let res = encode(&mut encoder, vec![header("content-length", "1234")]);
assert_eq!(&[15, 13, 0x80 | 3], &res[0..3]);
assert_eq!("1234", huff_decode(&res[3..]));
assert_eq!(6, res.len());
}
#[test]
fn test_encoding_headers_with_same_name() {
let mut encoder = Encoder::default();
let name = "hello";
// Encode first one
let _ = encode(&mut encoder, vec![header(name, "one")]);
// Encode second one
let res = encode(&mut encoder, vec![header(name, "two")]);
assert_eq!(&[0x40 | 62, 0x80 | 3], &res[0..2]);
assert_eq!("two", huff_decode(&res[2..]));
assert_eq!(5, res.len());
// Encode the first one again
let res = encode(&mut encoder, vec![header(name, "one")]);
assert_eq!(&[0x80 | 63], &res[..]);
// Now the second one
let res = encode(&mut encoder, vec![header(name, "two")]);
assert_eq!(&[0x80 | 62], &res[..]);
}
#[test]
fn test_evicting_headers_when_multiple_of_same_name_are_in_table() {
// The encoder only has space for 2 headers
let mut encoder = Encoder::new(76, 0);
let _ = encode(&mut encoder, vec![header("foo", "bar")]);
assert_eq!(1, encoder.table.len());
let _ = encode(&mut encoder, vec![header("bar", "foo")]);
assert_eq!(2, encoder.table.len());
// This will evict the first header, while still referencing the header
// name
let res = encode(&mut encoder, vec![header("foo", "baz")]);
assert_eq!(&[0x40 | 63, 0, 0x80 | 3], &res[..3]);
assert_eq!(2, encoder.table.len());
// Try adding the same header again
let res = encode(&mut encoder, vec![header("foo", "baz")]);
assert_eq!(&[0x80 | 62], &res[..]);
assert_eq!(2, encoder.table.len());
}
#[test]
fn test_max_size_zero() {
// Static table only
let mut encoder = Encoder::new(0, 0);
let res = encode(&mut encoder, vec![method("GET")]);
assert_eq!(*res, [0x80 | 2]);
assert_eq!(encoder.table.len(), 0);
let res = encode(&mut encoder, vec![header("foo", "bar")]);
assert_eq!(&[0, 0x80 | 2], &res[..2]);
assert_eq!("foo", huff_decode(&res[2..4]));
assert_eq!(0x80 | 3, res[4]);
assert_eq!("bar", huff_decode(&res[5..8]));
assert_eq!(0, encoder.table.len());
// Encode a custom value
let res = encode(&mut encoder, vec![header("transfer-encoding", "chunked")]);
assert_eq!(&[15, 42, 0x80 | 6], &res[..3]);
assert_eq!("chunked", huff_decode(&res[3..]));
}
#[test]
fn test_update_max_size_combos() {
let mut encoder = Encoder::default();
assert!(encoder.size_update.is_none());
assert_eq!(4096, encoder.table.max_size());
encoder.update_max_size(4096); // Default size
assert!(encoder.size_update.is_none());
encoder.update_max_size(0);
assert_eq!(Some(SizeUpdate::One(0)), encoder.size_update);
encoder.update_max_size(100);
assert_eq!(Some(SizeUpdate::Two(0, 100)), encoder.size_update);
let mut encoder = Encoder::default();
encoder.update_max_size(8000);
assert_eq!(Some(SizeUpdate::One(8000)), encoder.size_update);
encoder.update_max_size(100);
assert_eq!(Some(SizeUpdate::One(100)), encoder.size_update);
encoder.update_max_size(8000);
assert_eq!(Some(SizeUpdate::Two(100, 8000)), encoder.size_update);
encoder.update_max_size(4000);
assert_eq!(Some(SizeUpdate::Two(100, 4000)), encoder.size_update);
encoder.update_max_size(50);
assert_eq!(Some(SizeUpdate::One(50)), encoder.size_update);
}
#[test]
fn test_resizing_table() {
let mut encoder = Encoder::default();
// Add a header
let _ = encode(&mut encoder, vec![header("foo", "bar")]);
encoder.update_max_size(1);
assert_eq!(1, encoder.table.len());
let res = encode(&mut encoder, vec![method("GET")]);
assert_eq!(&[32 | 1, 0x80 | 2], &res[..]);
assert_eq!(0, encoder.table.len());
let res = encode(&mut encoder, vec![header("foo", "bar")]);
assert_eq!(0, res[0]);
encoder.update_max_size(100);
let res = encode(&mut encoder, vec![header("foo", "bar")]);
assert_eq!(&[32 | 31, 69, 64], &res[..3]);
encoder.update_max_size(0);
let res = encode(&mut encoder, vec![header("foo", "bar")]);
assert_eq!(&[32, 0], &res[..2]);
}
#[test]
fn test_decreasing_table_size_without_eviction() {
let mut encoder = Encoder::default();
// Add a header
let _ = encode(&mut encoder, vec![header("foo", "bar")]);
encoder.update_max_size(100);
assert_eq!(1, encoder.table.len());
let res = encode(&mut encoder, vec![header("foo", "bar")]);
assert_eq!(&[32 | 31, 69, 0x80 | 62], &res[..]);
}
#[test]
fn test_nameless_header() {
let mut encoder = Encoder::default();
let res = encode(
&mut encoder,
vec![
Header::Field {
name: Some("hello".parse().unwrap()),
value: HeaderValue::from_bytes(b"world").unwrap(),
},
Header::Field {
name: None,
value: HeaderValue::from_bytes(b"zomg").unwrap(),
},
],
);
assert_eq!(&[0x40, 0x80 | 4], &res[0..2]);
assert_eq!("hello", huff_decode(&res[2..6]));
assert_eq!(0x80 | 4, res[6]);
assert_eq!("world", huff_decode(&res[7..11]));
// Next is not indexed
assert_eq!(&[15, 47, 0x80 | 3], &res[11..14]);
assert_eq!("zomg", huff_decode(&res[14..]));
}
#[test]
fn test_large_size_update() {
let mut encoder = Encoder::default();
encoder.update_max_size(1912930560);
assert_eq!(Some(SizeUpdate::One(1912930560)), encoder.size_update);
let mut dst = BytesMut::with_capacity(6);
encoder.encode_size_updates(&mut dst);
assert_eq!([63, 225, 129, 148, 144, 7], &dst[..]);
}
#[test]
#[ignore]
fn test_evicted_overflow() {
// Not sure what the best way to do this is.
}
fn encode(e: &mut Encoder, hdrs: Vec<Header<Option<HeaderName>>>) -> BytesMut {
let mut dst = BytesMut::with_capacity(1024);
e.encode(hdrs, &mut dst);
dst
}
fn method(s: &str) -> Header<Option<HeaderName>> {
Header::Method(Method::from_bytes(s.as_bytes()).unwrap())
}
fn header(name: &str, val: &str) -> Header<Option<HeaderName>> {
let name = HeaderName::from_bytes(name.as_bytes()).unwrap();
let value = HeaderValue::from_bytes(val.as_bytes()).unwrap();
Header::Field {
name: Some(name),
value,
}
}
fn huff_decode(src: &[u8]) -> BytesMut {
let mut buf = BytesMut::new();
huffman::decode(src, &mut buf).unwrap()
}
}

308
vendor/h2/src/hpack/header.rs vendored Normal file
View File

@@ -0,0 +1,308 @@
use super::{DecoderError, NeedMore};
use crate::ext::Protocol;
use bytes::Bytes;
use http::header::{HeaderName, HeaderValue};
use http::{Method, StatusCode};
use std::fmt;
/// HTTP/2 Header
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Header<T = HeaderName> {
Field { name: T, value: HeaderValue },
// TODO: Change these types to `http::uri` types.
Authority(BytesStr),
Method(Method),
Scheme(BytesStr),
Path(BytesStr),
Protocol(Protocol),
Status(StatusCode),
}
/// The header field name
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Name<'a> {
Field(&'a HeaderName),
Authority,
Method,
Scheme,
Path,
Protocol,
Status,
}
#[doc(hidden)]
#[derive(Clone, Eq, PartialEq, Default)]
pub struct BytesStr(Bytes);
pub fn len(name: &HeaderName, value: &HeaderValue) -> usize {
let n: &str = name.as_ref();
32 + n.len() + value.len()
}
impl Header<Option<HeaderName>> {
pub fn reify(self) -> Result<Header, HeaderValue> {
use self::Header::*;
Ok(match self {
Field {
name: Some(n),
value,
} => Field { name: n, value },
Field { name: None, value } => return Err(value),
Authority(v) => Authority(v),
Method(v) => Method(v),
Scheme(v) => Scheme(v),
Path(v) => Path(v),
Protocol(v) => Protocol(v),
Status(v) => Status(v),
})
}
}
impl Header {
pub fn new(name: Bytes, value: Bytes) -> Result<Header, DecoderError> {
if name.is_empty() {
return Err(DecoderError::NeedMore(NeedMore::UnexpectedEndOfStream));
}
if name[0] == b':' {
match &name[1..] {
b"authority" => {
let value = BytesStr::try_from(value)?;
Ok(Header::Authority(value))
}
b"method" => {
let method = Method::from_bytes(&value)?;
Ok(Header::Method(method))
}
b"scheme" => {
let value = BytesStr::try_from(value)?;
Ok(Header::Scheme(value))
}
b"path" => {
let value = BytesStr::try_from(value)?;
Ok(Header::Path(value))
}
b"protocol" => {
let value = Protocol::try_from(value)?;
Ok(Header::Protocol(value))
}
b"status" => {
let status = StatusCode::from_bytes(&value)?;
Ok(Header::Status(status))
}
_ => Err(DecoderError::InvalidPseudoheader),
}
} else {
// HTTP/2 requires lower case header names
let name = HeaderName::from_lowercase(&name)?;
let value = HeaderValue::from_bytes(&value)?;
Ok(Header::Field { name, value })
}
}
pub fn len(&self) -> usize {
match *self {
Header::Field {
ref name,
ref value,
} => len(name, value),
Header::Authority(ref v) => 32 + 10 + v.len(),
Header::Method(ref v) => 32 + 7 + v.as_ref().len(),
Header::Scheme(ref v) => 32 + 7 + v.len(),
Header::Path(ref v) => 32 + 5 + v.len(),
Header::Protocol(ref v) => 32 + 9 + v.as_str().len(),
Header::Status(_) => 32 + 7 + 3,
}
}
/// Returns the header name
pub fn name(&self) -> Name<'_> {
match *self {
Header::Field { ref name, .. } => Name::Field(name),
Header::Authority(..) => Name::Authority,
Header::Method(..) => Name::Method,
Header::Scheme(..) => Name::Scheme,
Header::Path(..) => Name::Path,
Header::Protocol(..) => Name::Protocol,
Header::Status(..) => Name::Status,
}
}
pub fn value_slice(&self) -> &[u8] {
match *self {
Header::Field { ref value, .. } => value.as_ref(),
Header::Authority(ref v) => v.as_ref(),
Header::Method(ref v) => v.as_ref().as_ref(),
Header::Scheme(ref v) => v.as_ref(),
Header::Path(ref v) => v.as_ref(),
Header::Protocol(ref v) => v.as_ref(),
Header::Status(ref v) => v.as_str().as_ref(),
}
}
pub fn value_eq(&self, other: &Header) -> bool {
match *self {
Header::Field { ref value, .. } => {
let a = value;
match *other {
Header::Field { ref value, .. } => a == value,
_ => false,
}
}
Header::Authority(ref a) => match *other {
Header::Authority(ref b) => a == b,
_ => false,
},
Header::Method(ref a) => match *other {
Header::Method(ref b) => a == b,
_ => false,
},
Header::Scheme(ref a) => match *other {
Header::Scheme(ref b) => a == b,
_ => false,
},
Header::Path(ref a) => match *other {
Header::Path(ref b) => a == b,
_ => false,
},
Header::Protocol(ref a) => match *other {
Header::Protocol(ref b) => a == b,
_ => false,
},
Header::Status(ref a) => match *other {
Header::Status(ref b) => a == b,
_ => false,
},
}
}
pub fn is_sensitive(&self) -> bool {
match *self {
Header::Field { ref value, .. } => value.is_sensitive(),
// TODO: Technically these other header values can be sensitive too.
_ => false,
}
}
pub fn skip_value_index(&self) -> bool {
use http::header;
match *self {
Header::Field { ref name, .. } => matches!(
*name,
header::AGE
| header::AUTHORIZATION
| header::CONTENT_LENGTH
| header::ETAG
| header::IF_MODIFIED_SINCE
| header::IF_NONE_MATCH
| header::LOCATION
| header::COOKIE
| header::SET_COOKIE
),
Header::Path(..) => true,
_ => false,
}
}
}
// Mostly for tests
impl From<Header> for Header<Option<HeaderName>> {
fn from(src: Header) -> Self {
match src {
Header::Field { name, value } => Header::Field {
name: Some(name),
value,
},
Header::Authority(v) => Header::Authority(v),
Header::Method(v) => Header::Method(v),
Header::Scheme(v) => Header::Scheme(v),
Header::Path(v) => Header::Path(v),
Header::Protocol(v) => Header::Protocol(v),
Header::Status(v) => Header::Status(v),
}
}
}
impl<'a> Name<'a> {
pub fn into_entry(self, value: Bytes) -> Result<Header, DecoderError> {
match self {
Name::Field(name) => Ok(Header::Field {
name: name.clone(),
value: HeaderValue::from_bytes(&value)?,
}),
Name::Authority => Ok(Header::Authority(BytesStr::try_from(value)?)),
Name::Method => Ok(Header::Method(Method::from_bytes(&value)?)),
Name::Scheme => Ok(Header::Scheme(BytesStr::try_from(value)?)),
Name::Path => Ok(Header::Path(BytesStr::try_from(value)?)),
Name::Protocol => Ok(Header::Protocol(Protocol::try_from(value)?)),
Name::Status => {
match StatusCode::from_bytes(&value) {
Ok(status) => Ok(Header::Status(status)),
// TODO: better error handling
Err(_) => Err(DecoderError::InvalidStatusCode),
}
}
}
}
pub fn as_slice(&self) -> &[u8] {
match *self {
Name::Field(ref name) => name.as_ref(),
Name::Authority => b":authority",
Name::Method => b":method",
Name::Scheme => b":scheme",
Name::Path => b":path",
Name::Protocol => b":protocol",
Name::Status => b":status",
}
}
}
// ===== impl BytesStr =====
impl BytesStr {
pub(crate) const fn from_static(value: &'static str) -> Self {
BytesStr(Bytes::from_static(value.as_bytes()))
}
pub(crate) fn from(value: &str) -> Self {
BytesStr(Bytes::copy_from_slice(value.as_bytes()))
}
#[doc(hidden)]
pub fn try_from(bytes: Bytes) -> Result<Self, std::str::Utf8Error> {
std::str::from_utf8(bytes.as_ref())?;
Ok(BytesStr(bytes))
}
pub(crate) fn as_str(&self) -> &str {
// Safety: check valid utf-8 in constructor
unsafe { std::str::from_utf8_unchecked(self.0.as_ref()) }
}
pub(crate) fn into_inner(self) -> Bytes {
self.0
}
}
impl std::ops::Deref for BytesStr {
type Target = str;
fn deref(&self) -> &str {
self.as_str()
}
}
impl AsRef<[u8]> for BytesStr {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl fmt::Debug for BytesStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

199
vendor/h2/src/hpack/huffman/mod.rs vendored Normal file
View File

@@ -0,0 +1,199 @@
mod table;
use self::table::{DECODE_TABLE, ENCODE_TABLE};
use crate::hpack::DecoderError;
use bytes::{BufMut, BytesMut};
// Constructed in the generated `table.rs` file
struct Decoder {
state: u8,
maybe_eos: bool,
}
// These flags must match the ones in genhuff.rs
const MAYBE_EOS: u8 = 1;
const DECODED: u8 = 2;
const ERROR: u8 = 4;
pub fn decode(src: &[u8], buf: &mut BytesMut) -> Result<BytesMut, DecoderError> {
let mut decoder = Decoder::new();
// Max compression ratio is >= 0.5
buf.reserve(src.len() << 1);
for b in src {
if let Some(b) = decoder.decode4(b >> 4)? {
buf.put_u8(b);
}
if let Some(b) = decoder.decode4(b & 0xf)? {
buf.put_u8(b);
}
}
if !decoder.is_final() {
return Err(DecoderError::InvalidHuffmanCode);
}
Ok(buf.split())
}
pub fn encode(src: &[u8], dst: &mut BytesMut) {
let mut bits: u64 = 0;
let mut bits_left = 40;
for &b in src {
let (nbits, code) = ENCODE_TABLE[b as usize];
bits |= code << (bits_left - nbits);
bits_left -= nbits;
while bits_left <= 32 {
dst.put_u8((bits >> 32) as u8);
bits <<= 8;
bits_left += 8;
}
}
if bits_left != 40 {
// This writes the EOS token
bits |= (1 << bits_left) - 1;
dst.put_u8((bits >> 32) as u8);
}
}
impl Decoder {
fn new() -> Decoder {
Decoder {
state: 0,
maybe_eos: false,
}
}
// Decodes 4 bits
fn decode4(&mut self, input: u8) -> Result<Option<u8>, DecoderError> {
// (next-state, byte, flags)
let (next, byte, flags) = DECODE_TABLE[self.state as usize][input as usize];
if flags & ERROR == ERROR {
// Data followed the EOS marker
return Err(DecoderError::InvalidHuffmanCode);
}
let mut ret = None;
if flags & DECODED == DECODED {
ret = Some(byte);
}
self.state = next;
self.maybe_eos = flags & MAYBE_EOS == MAYBE_EOS;
Ok(ret)
}
fn is_final(&self) -> bool {
self.state == 0 || self.maybe_eos
}
}
#[cfg(test)]
mod test {
use super::*;
fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
let mut buf = BytesMut::new();
super::decode(src, &mut buf)
}
#[test]
fn decode_single_byte() {
assert_eq!("o", decode(&[0b00111111]).unwrap());
assert_eq!("0", decode(&[7]).unwrap());
assert_eq!("A", decode(&[(0x21 << 2) + 3]).unwrap());
}
#[test]
fn single_char_multi_byte() {
assert_eq!("#", decode(&[255, 160 + 15]).unwrap());
assert_eq!("$", decode(&[255, 200 + 7]).unwrap());
assert_eq!("\x0a", decode(&[255, 255, 255, 240 + 3]).unwrap());
}
#[test]
fn multi_char() {
assert_eq!("!0", decode(&[254, 1]).unwrap());
assert_eq!(" !", decode(&[0b01010011, 0b11111000]).unwrap());
}
#[test]
fn encode_single_byte() {
let mut dst = BytesMut::with_capacity(1);
encode(b"o", &mut dst);
assert_eq!(&dst[..], &[0b00111111]);
dst.clear();
encode(b"0", &mut dst);
assert_eq!(&dst[..], &[7]);
dst.clear();
encode(b"A", &mut dst);
assert_eq!(&dst[..], &[(0x21 << 2) + 3]);
}
#[test]
fn encode_decode_str() {
const DATA: &[&str] = &[
"hello world",
":method",
":scheme",
":authority",
"yahoo.co.jp",
"GET",
"http",
":path",
"/images/top/sp2/cmn/logo-ns-130528.png",
"example.com",
"hpack-test",
"xxxxxxx1",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0",
"accept",
"Accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"cookie",
"B=76j09a189a6h4&b=3&s=0b",
"TE",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi non bibendum libero. \
Etiam ultrices lorem ut.",
];
for s in DATA {
let mut dst = BytesMut::with_capacity(s.len());
encode(s.as_bytes(), &mut dst);
let decoded = decode(&dst).unwrap();
assert_eq!(&decoded[..], s.as_bytes());
}
}
#[test]
fn encode_decode_u8() {
const DATA: &[&[u8]] = &[b"\0", b"\0\0\0", b"\0\x01\x02\x03\x04\x05", b"\xFF\xF8"];
for s in DATA {
let mut dst = BytesMut::with_capacity(s.len());
encode(s, &mut dst);
let decoded = decode(&dst).unwrap();
assert_eq!(&decoded[..], &s[..]);
}
}
}

5130
vendor/h2/src/hpack/huffman/table.rs vendored Normal file

File diff suppressed because it is too large Load Diff

12
vendor/h2/src/hpack/mod.rs vendored Normal file
View File

@@ -0,0 +1,12 @@
mod decoder;
mod encoder;
pub(crate) mod header;
pub(crate) mod huffman;
mod table;
#[cfg(test)]
mod test;
pub use self::decoder::{Decoder, DecoderError, NeedMore};
pub use self::encoder::Encoder;
pub use self::header::{BytesStr, Header};

766
vendor/h2/src/hpack/table.rs vendored Normal file
View File

@@ -0,0 +1,766 @@
use super::Header;
use fnv::FnvHasher;
use http::header;
use http::method::Method;
use std::collections::VecDeque;
use std::hash::{Hash, Hasher};
use std::{cmp, mem};
/// HPACK encoder table
#[derive(Debug)]
pub struct Table {
mask: usize,
indices: Vec<Option<Pos>>,
slots: VecDeque<Slot>,
inserted: usize,
// Size is in bytes
size: usize,
max_size: usize,
}
#[derive(Debug)]
pub enum Index {
// The header is already fully indexed
Indexed(usize, Header),
// The name is indexed, but not the value
Name(usize, Header),
// The full header has been inserted into the table.
Inserted(usize),
// Only the value has been inserted (hpack table idx, slots idx)
InsertedValue(usize, usize),
// The header is not indexed by this table
NotIndexed(Header),
}
#[derive(Debug)]
struct Slot {
hash: HashValue,
header: Header,
next: Option<usize>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
struct Pos {
index: usize,
hash: HashValue,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
struct HashValue(usize);
const MAX_SIZE: usize = 1 << 16;
const DYN_OFFSET: usize = 62;
macro_rules! probe_loop {
($probe_var: ident < $len: expr, $body: expr) => {
debug_assert!($len > 0);
loop {
if $probe_var < $len {
$body
$probe_var += 1;
} else {
$probe_var = 0;
}
}
};
}
impl Table {
pub fn new(max_size: usize, capacity: usize) -> Table {
if capacity == 0 {
Table {
mask: 0,
indices: vec![],
slots: VecDeque::new(),
inserted: 0,
size: 0,
max_size,
}
} else {
let capacity = cmp::max(to_raw_capacity(capacity).next_power_of_two(), 8);
Table {
mask: capacity.wrapping_sub(1),
indices: vec![None; capacity],
slots: VecDeque::with_capacity(usable_capacity(capacity)),
inserted: 0,
size: 0,
max_size,
}
}
}
#[inline]
pub fn capacity(&self) -> usize {
usable_capacity(self.indices.len())
}
pub fn max_size(&self) -> usize {
self.max_size
}
/// Gets the header stored in the table
pub fn resolve<'a>(&'a self, index: &'a Index) -> &'a Header {
use self::Index::*;
match *index {
Indexed(_, ref h) => h,
Name(_, ref h) => h,
Inserted(idx) => &self.slots[idx].header,
InsertedValue(_, idx) => &self.slots[idx].header,
NotIndexed(ref h) => h,
}
}
pub fn resolve_idx(&self, index: &Index) -> usize {
use self::Index::*;
match *index {
Indexed(idx, ..) => idx,
Name(idx, ..) => idx,
Inserted(idx) => idx + DYN_OFFSET,
InsertedValue(_name_idx, slot_idx) => slot_idx + DYN_OFFSET,
NotIndexed(_) => panic!("cannot resolve index"),
}
}
/// Index the header in the HPACK table.
pub fn index(&mut self, header: Header) -> Index {
// Check the static table
let statik = index_static(&header);
// Don't index certain headers. This logic is borrowed from nghttp2.
if header.skip_value_index() {
// Right now, if this is true, the header name is always in the
// static table. At some point in the future, this might not be true
// and this logic will need to be updated.
debug_assert!(statik.is_some(), "skip_value_index requires a static name",);
return Index::new(statik, header);
}
// If the header is already indexed by the static table, return that
if let Some((n, true)) = statik {
return Index::Indexed(n, header);
}
// Don't index large headers
if header.len() * 4 > self.max_size * 3 {
return Index::new(statik, header);
}
self.index_dynamic(header, statik)
}
fn index_dynamic(&mut self, header: Header, statik: Option<(usize, bool)>) -> Index {
debug_assert!(self.assert_valid_state("one"));
if header.len() + self.size < self.max_size || !header.is_sensitive() {
// Only grow internal storage if needed
self.reserve_one();
}
if self.indices.is_empty() {
// If `indices` is not empty, then it is impossible for all
// `indices` entries to be `Some`. So, we only need to check for the
// empty case.
return Index::new(statik, header);
}
let hash = hash_header(&header);
let desired_pos = desired_pos(self.mask, hash);
let mut probe = desired_pos;
let mut dist = 0;
// Start at the ideal position, checking all slots
probe_loop!(probe < self.indices.len(), {
if let Some(pos) = self.indices[probe] {
// The slot is already occupied, but check if it has a lower
// displacement.
let their_dist = probe_distance(self.mask, pos.hash, probe);
let slot_idx = pos.index.wrapping_add(self.inserted);
if their_dist < dist {
// Index robinhood
return self.index_vacant(header, hash, dist, probe, statik);
} else if pos.hash == hash && self.slots[slot_idx].header.name() == header.name() {
// Matching name, check values
return self.index_occupied(header, hash, pos.index, statik.map(|(n, _)| n));
}
} else {
return self.index_vacant(header, hash, dist, probe, statik);
}
dist += 1;
});
}
fn index_occupied(
&mut self,
header: Header,
hash: HashValue,
mut index: usize,
statik: Option<usize>,
) -> Index {
debug_assert!(self.assert_valid_state("top"));
// There already is a match for the given header name. Check if a value
// matches. The header will also only be inserted if the table is not at
// capacity.
loop {
// Compute the real index into the VecDeque
let real_idx = index.wrapping_add(self.inserted);
if self.slots[real_idx].header.value_eq(&header) {
// We have a full match!
return Index::Indexed(real_idx + DYN_OFFSET, header);
}
if let Some(next) = self.slots[real_idx].next {
index = next;
continue;
}
if header.is_sensitive() {
// Should we assert this?
// debug_assert!(statik.is_none());
return Index::Name(real_idx + DYN_OFFSET, header);
}
self.update_size(header.len(), Some(index));
// Insert the new header
self.insert(header, hash);
// Recompute real_idx as it just changed.
let new_real_idx = index.wrapping_add(self.inserted);
// The previous node in the linked list may have gotten evicted
// while making room for this header.
if new_real_idx < self.slots.len() {
let idx = 0usize.wrapping_sub(self.inserted);
self.slots[new_real_idx].next = Some(idx);
}
debug_assert!(self.assert_valid_state("bottom"));
// Even if the previous header was evicted, we can still reference
// it when inserting the new one...
return if let Some(n) = statik {
// If name is in static table, use it instead
Index::InsertedValue(n, 0)
} else {
Index::InsertedValue(real_idx + DYN_OFFSET, 0)
};
}
}
fn index_vacant(
&mut self,
header: Header,
hash: HashValue,
mut dist: usize,
mut probe: usize,
statik: Option<(usize, bool)>,
) -> Index {
if header.is_sensitive() {
return Index::new(statik, header);
}
debug_assert!(self.assert_valid_state("top"));
debug_assert!(dist == 0 || self.indices[probe.wrapping_sub(1) & self.mask].is_some());
// Passing in `usize::MAX` for prev_idx since there is no previous
// header in this case.
if self.update_size(header.len(), None) {
while dist != 0 {
let back = probe.wrapping_sub(1) & self.mask;
if let Some(pos) = self.indices[back] {
let their_dist = probe_distance(self.mask, pos.hash, back);
if their_dist < (dist - 1) {
probe = back;
dist -= 1;
} else {
break;
}
} else {
probe = back;
dist -= 1;
}
}
}
debug_assert!(self.assert_valid_state("after update"));
self.insert(header, hash);
let pos_idx = 0usize.wrapping_sub(self.inserted);
let prev = mem::replace(
&mut self.indices[probe],
Some(Pos {
index: pos_idx,
hash,
}),
);
if let Some(mut prev) = prev {
// Shift forward
let mut probe = probe + 1;
probe_loop!(probe < self.indices.len(), {
let pos = &mut self.indices[probe];
prev = match mem::replace(pos, Some(prev)) {
Some(p) => p,
None => break,
};
});
}
debug_assert!(self.assert_valid_state("bottom"));
if let Some((n, _)) = statik {
Index::InsertedValue(n, 0)
} else {
Index::Inserted(0)
}
}
fn insert(&mut self, header: Header, hash: HashValue) {
self.inserted = self.inserted.wrapping_add(1);
self.slots.push_front(Slot {
hash,
header,
next: None,
});
}
pub fn resize(&mut self, size: usize) {
self.max_size = size;
if size == 0 {
self.size = 0;
for i in &mut self.indices {
*i = None;
}
self.slots.clear();
self.inserted = 0;
} else {
self.converge(None);
}
}
fn update_size(&mut self, len: usize, prev_idx: Option<usize>) -> bool {
self.size += len;
self.converge(prev_idx)
}
fn converge(&mut self, prev_idx: Option<usize>) -> bool {
let mut ret = false;
while self.size > self.max_size {
ret = true;
self.evict(prev_idx);
}
ret
}
fn evict(&mut self, prev_idx: Option<usize>) {
let pos_idx = (self.slots.len() - 1).wrapping_sub(self.inserted);
debug_assert!(!self.slots.is_empty());
debug_assert!(self.assert_valid_state("one"));
// Remove the header
let slot = self.slots.pop_back().unwrap();
let mut probe = desired_pos(self.mask, slot.hash);
// Update the size
self.size -= slot.header.len();
debug_assert_eq!(
self.indices
.iter()
.filter_map(|p| *p)
.filter(|p| p.index == pos_idx)
.count(),
1
);
// Find the associated position
probe_loop!(probe < self.indices.len(), {
debug_assert!(self.indices[probe].is_some());
let mut pos = self.indices[probe].unwrap();
if pos.index == pos_idx {
if let Some(idx) = slot.next {
pos.index = idx;
self.indices[probe] = Some(pos);
} else if Some(pos.index) == prev_idx {
pos.index = 0usize.wrapping_sub(self.inserted + 1);
self.indices[probe] = Some(pos);
} else {
self.indices[probe] = None;
self.remove_phase_two(probe);
}
break;
}
});
debug_assert!(self.assert_valid_state("two"));
}
// Shifts all indices that were displaced by the header that has just been
// removed.
fn remove_phase_two(&mut self, probe: usize) {
let mut last_probe = probe;
let mut probe = probe + 1;
probe_loop!(probe < self.indices.len(), {
if let Some(pos) = self.indices[probe] {
if probe_distance(self.mask, pos.hash, probe) > 0 {
self.indices[last_probe] = self.indices[probe].take();
} else {
break;
}
} else {
break;
}
last_probe = probe;
});
debug_assert!(self.assert_valid_state("two"));
}
fn reserve_one(&mut self) {
let len = self.slots.len();
if len == self.capacity() {
if len == 0 {
let new_raw_cap = 8;
self.mask = 8 - 1;
self.indices = vec![None; new_raw_cap];
} else {
let raw_cap = self.indices.len();
self.grow(raw_cap << 1);
}
}
}
#[inline]
fn grow(&mut self, new_raw_cap: usize) {
// This path can never be reached when handling the first allocation in
// the map.
debug_assert!(self.assert_valid_state("top"));
// find first ideally placed element -- start of cluster
let mut first_ideal = 0;
for (i, pos) in self.indices.iter().enumerate() {
if let Some(pos) = *pos {
if 0 == probe_distance(self.mask, pos.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![None; new_raw_cap]);
self.mask = new_raw_cap.wrapping_sub(1);
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);
}
debug_assert!(self.assert_valid_state("bottom"));
}
fn reinsert_entry_in_order(&mut self, pos: Option<Pos>) {
if let Some(pos) = pos {
// Find first empty bucket and insert there
let mut probe = desired_pos(self.mask, pos.hash);
probe_loop!(probe < self.indices.len(), {
if self.indices[probe].is_none() {
// empty bucket, insert here
self.indices[probe] = Some(pos);
return;
}
debug_assert!({
let them = self.indices[probe].unwrap();
let their_distance = probe_distance(self.mask, them.hash, probe);
let our_distance = probe_distance(self.mask, pos.hash, probe);
their_distance >= our_distance
});
});
}
}
#[cfg(not(test))]
fn assert_valid_state(&self, _: &'static str) -> bool {
true
}
#[cfg(test)]
fn assert_valid_state(&self, _msg: &'static str) -> bool {
/*
// Checks that the internal map structure is valid
//
// Ensure all hash codes in indices match the associated slot
for pos in &self.indices {
if let Some(pos) = *pos {
let real_idx = pos.index.wrapping_add(self.inserted);
if real_idx.wrapping_add(1) != 0 {
assert!(real_idx < self.slots.len(),
"out of index; real={}; len={}, msg={}",
real_idx, self.slots.len(), msg);
assert_eq!(pos.hash, self.slots[real_idx].hash,
"index hash does not match slot; msg={}", msg);
}
}
}
// Every index is only available once
for i in 0..self.indices.len() {
if self.indices[i].is_none() {
continue;
}
for j in i+1..self.indices.len() {
assert_ne!(self.indices[i], self.indices[j],
"duplicate indices; msg={}", msg);
}
}
for (index, slot) in self.slots.iter().enumerate() {
let mut indexed = None;
// First, see if the slot is indexed
for (i, pos) in self.indices.iter().enumerate() {
if let Some(pos) = *pos {
let real_idx = pos.index.wrapping_add(self.inserted);
if real_idx == index {
indexed = Some(i);
// Already know that there is no dup, so break
break;
}
}
}
if let Some(actual) = indexed {
// Ensure that it is accessible..
let desired = desired_pos(self.mask, slot.hash);
let mut probe = desired;
let mut dist = 0;
probe_loop!(probe < self.indices.len(), {
assert!(self.indices[probe].is_some(),
"unexpected empty slot; probe={}; hash={:?}; msg={}",
probe, slot.hash, msg);
let pos = self.indices[probe].unwrap();
let their_dist = probe_distance(self.mask, pos.hash, probe);
let real_idx = pos.index.wrapping_add(self.inserted);
if real_idx == index {
break;
}
assert!(dist <= their_dist,
"could not find entry; actual={}; desired={}" +
"probe={}, dist={}; their_dist={}; index={}; msg={}",
actual, desired, probe, dist, their_dist,
index.wrapping_sub(self.inserted), msg);
dist += 1;
});
} else {
// There is exactly one next link
let cnt = self.slots.iter().map(|s| s.next)
.filter(|n| *n == Some(index.wrapping_sub(self.inserted)))
.count();
assert_eq!(1, cnt, "more than one node pointing here; msg={}", msg);
}
}
*/
// TODO: Ensure linked lists are correct: no cycles, etc...
true
}
}
#[cfg(test)]
impl Table {
/// Returns the number of headers in the table
pub fn len(&self) -> usize {
self.slots.len()
}
/// Returns the table size
pub fn size(&self) -> usize {
self.size
}
}
impl Index {
fn new(v: Option<(usize, bool)>, e: Header) -> Index {
match v {
None => Index::NotIndexed(e),
Some((n, true)) => Index::Indexed(n, e),
Some((n, false)) => Index::Name(n, e),
}
}
}
#[inline]
fn usable_capacity(cap: usize) -> usize {
cap - cap / 4
}
#[inline]
fn to_raw_capacity(n: usize) -> usize {
n + n / 3
}
#[inline]
fn desired_pos(mask: usize, hash: HashValue) -> usize {
hash.0 & mask
}
#[inline]
fn probe_distance(mask: usize, hash: HashValue, current: usize) -> usize {
current.wrapping_sub(desired_pos(mask, hash)) & mask
}
fn hash_header(header: &Header) -> HashValue {
const MASK: u64 = (MAX_SIZE as u64) - 1;
let mut h = FnvHasher::default();
header.name().hash(&mut h);
HashValue((h.finish() & MASK) as usize)
}
/// Checks the static table for the header. If found, returns the index and a
/// boolean representing if the value matched as well.
fn index_static(header: &Header) -> Option<(usize, bool)> {
match *header {
Header::Field {
ref name,
ref value,
} => match *name {
header::ACCEPT_CHARSET => Some((15, false)),
header::ACCEPT_ENCODING => {
if value == "gzip, deflate" {
Some((16, true))
} else {
Some((16, false))
}
}
header::ACCEPT_LANGUAGE => Some((17, false)),
header::ACCEPT_RANGES => Some((18, false)),
header::ACCEPT => Some((19, false)),
header::ACCESS_CONTROL_ALLOW_ORIGIN => Some((20, false)),
header::AGE => Some((21, false)),
header::ALLOW => Some((22, false)),
header::AUTHORIZATION => Some((23, false)),
header::CACHE_CONTROL => Some((24, false)),
header::CONTENT_DISPOSITION => Some((25, false)),
header::CONTENT_ENCODING => Some((26, false)),
header::CONTENT_LANGUAGE => Some((27, false)),
header::CONTENT_LENGTH => Some((28, false)),
header::CONTENT_LOCATION => Some((29, false)),
header::CONTENT_RANGE => Some((30, false)),
header::CONTENT_TYPE => Some((31, false)),
header::COOKIE => Some((32, false)),
header::DATE => Some((33, false)),
header::ETAG => Some((34, false)),
header::EXPECT => Some((35, false)),
header::EXPIRES => Some((36, false)),
header::FROM => Some((37, false)),
header::HOST => Some((38, false)),
header::IF_MATCH => Some((39, false)),
header::IF_MODIFIED_SINCE => Some((40, false)),
header::IF_NONE_MATCH => Some((41, false)),
header::IF_RANGE => Some((42, false)),
header::IF_UNMODIFIED_SINCE => Some((43, false)),
header::LAST_MODIFIED => Some((44, false)),
header::LINK => Some((45, false)),
header::LOCATION => Some((46, false)),
header::MAX_FORWARDS => Some((47, false)),
header::PROXY_AUTHENTICATE => Some((48, false)),
header::PROXY_AUTHORIZATION => Some((49, false)),
header::RANGE => Some((50, false)),
header::REFERER => Some((51, false)),
header::REFRESH => Some((52, false)),
header::RETRY_AFTER => Some((53, false)),
header::SERVER => Some((54, false)),
header::SET_COOKIE => Some((55, false)),
header::STRICT_TRANSPORT_SECURITY => Some((56, false)),
header::TRANSFER_ENCODING => Some((57, false)),
header::USER_AGENT => Some((58, false)),
header::VARY => Some((59, false)),
header::VIA => Some((60, false)),
header::WWW_AUTHENTICATE => Some((61, false)),
_ => None,
},
Header::Authority(_) => Some((1, false)),
Header::Method(ref v) => match *v {
Method::GET => Some((2, true)),
Method::POST => Some((3, true)),
_ => Some((2, false)),
},
Header::Scheme(ref v) => match &**v {
"http" => Some((6, true)),
"https" => Some((7, true)),
_ => Some((6, false)),
},
Header::Path(ref v) => match &**v {
"/" => Some((4, true)),
"/index.html" => Some((5, true)),
_ => Some((4, false)),
},
Header::Protocol(..) => None,
Header::Status(ref v) => match u16::from(*v) {
200 => Some((8, true)),
204 => Some((9, true)),
206 => Some((10, true)),
304 => Some((11, true)),
400 => Some((12, true)),
404 => Some((13, true)),
500 => Some((14, true)),
_ => Some((8, false)),
},
}
}

615
vendor/h2/src/hpack/test/fixture.rs vendored Normal file
View File

@@ -0,0 +1,615 @@
use crate::hpack::{Decoder, Encoder, Header};
use bytes::BytesMut;
use hex::FromHex;
use serde_json::Value;
use std::fs::File;
use std::io::prelude::*;
use std::io::Cursor;
use std::path::Path;
use std::str;
fn test_fixture(path: &Path) {
let mut file = File::open(path).unwrap();
let mut data = String::new();
file.read_to_string(&mut data).unwrap();
let story: Value = serde_json::from_str(&data).unwrap();
test_story(story);
}
fn test_story(story: Value) {
let story = story.as_object().unwrap();
if let Some(cases) = story.get("cases") {
let mut cases: Vec<_> = cases
.as_array()
.unwrap()
.iter()
.map(|case| {
let case = case.as_object().unwrap();
let size = case
.get("header_table_size")
.map(|v| v.as_u64().unwrap() as usize);
let wire = case.get("wire").unwrap().as_str().unwrap();
let wire: Vec<u8> = FromHex::from_hex(wire.as_bytes()).unwrap();
let expect: Vec<_> = case
.get("headers")
.unwrap()
.as_array()
.unwrap()
.iter()
.map(|h| {
let h = h.as_object().unwrap();
let (name, val) = h.iter().next().unwrap();
(name.clone(), val.as_str().unwrap().to_string())
})
.collect();
Case {
seqno: case.get("seqno").unwrap().as_u64().unwrap(),
wire,
expect,
header_table_size: size,
}
})
.collect();
cases.sort_by_key(|c| c.seqno);
let mut decoder = Decoder::default();
// First, check decoding against the fixtures
for case in &cases {
let mut expect = case.expect.clone();
if let Some(size) = case.header_table_size {
decoder.queue_size_update(size);
}
let mut buf = BytesMut::with_capacity(case.wire.len());
buf.extend_from_slice(&case.wire);
decoder
.decode(&mut Cursor::new(&mut buf), |e| {
let (name, value) = expect.remove(0);
assert_eq!(name, key_str(&e));
assert_eq!(value, value_str(&e));
})
.unwrap();
assert_eq!(0, expect.len());
}
let mut encoder = Encoder::default();
let mut decoder = Decoder::default();
// Now, encode the headers
for case in &cases {
let limit = 64 * 1024;
let mut buf = BytesMut::with_capacity(limit);
if let Some(size) = case.header_table_size {
encoder.update_max_size(size);
decoder.queue_size_update(size);
}
let mut input: Vec<_> = case
.expect
.iter()
.map(|(name, value)| {
Header::new(name.clone().into(), value.clone().into())
.unwrap()
.into()
})
.collect();
encoder.encode(input.clone(), &mut buf);
decoder
.decode(&mut Cursor::new(&mut buf), |e| {
assert_eq!(e, input.remove(0).reify().unwrap());
})
.unwrap();
assert_eq!(0, input.len());
}
}
}
struct Case {
seqno: u64,
wire: Vec<u8>,
expect: Vec<(String, String)>,
header_table_size: Option<usize>,
}
fn key_str(e: &Header) -> &str {
match *e {
Header::Field { ref name, .. } => name.as_str(),
Header::Authority(..) => ":authority",
Header::Method(..) => ":method",
Header::Scheme(..) => ":scheme",
Header::Path(..) => ":path",
Header::Protocol(..) => ":protocol",
Header::Status(..) => ":status",
}
}
fn value_str(e: &Header) -> &str {
match *e {
Header::Field { ref value, .. } => value.to_str().unwrap(),
Header::Authority(ref v) => v,
Header::Method(ref m) => m.as_str(),
Header::Scheme(ref v) => v,
Header::Path(ref v) => v,
Header::Protocol(ref v) => v.as_str(),
Header::Status(ref v) => v.as_str(),
}
}
macro_rules! fixture_mod {
($module:ident => {
$(
($fn:ident, $path:expr);
)+
}) => {
mod $module {
$(
#[test]
fn $fn() {
let path = ::std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("fixtures/hpack")
.join($path);
super::test_fixture(path.as_ref());
}
)+
}
}
}
fixture_mod!(
haskell_http2_linear_huffman => {
(story_00, "haskell-http2-linear-huffman/story_00.json");
(story_01, "haskell-http2-linear-huffman/story_01.json");
(story_02, "haskell-http2-linear-huffman/story_02.json");
(story_03, "haskell-http2-linear-huffman/story_03.json");
(story_04, "haskell-http2-linear-huffman/story_04.json");
(story_05, "haskell-http2-linear-huffman/story_05.json");
(story_06, "haskell-http2-linear-huffman/story_06.json");
(story_07, "haskell-http2-linear-huffman/story_07.json");
(story_08, "haskell-http2-linear-huffman/story_08.json");
(story_09, "haskell-http2-linear-huffman/story_09.json");
(story_10, "haskell-http2-linear-huffman/story_10.json");
(story_11, "haskell-http2-linear-huffman/story_11.json");
(story_12, "haskell-http2-linear-huffman/story_12.json");
(story_13, "haskell-http2-linear-huffman/story_13.json");
(story_14, "haskell-http2-linear-huffman/story_14.json");
(story_15, "haskell-http2-linear-huffman/story_15.json");
(story_16, "haskell-http2-linear-huffman/story_16.json");
(story_17, "haskell-http2-linear-huffman/story_17.json");
(story_18, "haskell-http2-linear-huffman/story_18.json");
(story_19, "haskell-http2-linear-huffman/story_19.json");
(story_20, "haskell-http2-linear-huffman/story_20.json");
(story_21, "haskell-http2-linear-huffman/story_21.json");
(story_22, "haskell-http2-linear-huffman/story_22.json");
(story_23, "haskell-http2-linear-huffman/story_23.json");
(story_24, "haskell-http2-linear-huffman/story_24.json");
(story_25, "haskell-http2-linear-huffman/story_25.json");
(story_26, "haskell-http2-linear-huffman/story_26.json");
(story_27, "haskell-http2-linear-huffman/story_27.json");
(story_28, "haskell-http2-linear-huffman/story_28.json");
(story_29, "haskell-http2-linear-huffman/story_29.json");
(story_30, "haskell-http2-linear-huffman/story_30.json");
(story_31, "haskell-http2-linear-huffman/story_31.json");
}
);
fixture_mod!(
python_hpack => {
(story_00, "python-hpack/story_00.json");
(story_01, "python-hpack/story_01.json");
(story_02, "python-hpack/story_02.json");
(story_03, "python-hpack/story_03.json");
(story_04, "python-hpack/story_04.json");
(story_05, "python-hpack/story_05.json");
(story_06, "python-hpack/story_06.json");
(story_07, "python-hpack/story_07.json");
(story_08, "python-hpack/story_08.json");
(story_09, "python-hpack/story_09.json");
(story_10, "python-hpack/story_10.json");
(story_11, "python-hpack/story_11.json");
(story_12, "python-hpack/story_12.json");
(story_13, "python-hpack/story_13.json");
(story_14, "python-hpack/story_14.json");
(story_15, "python-hpack/story_15.json");
(story_16, "python-hpack/story_16.json");
(story_17, "python-hpack/story_17.json");
(story_18, "python-hpack/story_18.json");
(story_19, "python-hpack/story_19.json");
(story_20, "python-hpack/story_20.json");
(story_21, "python-hpack/story_21.json");
(story_22, "python-hpack/story_22.json");
(story_23, "python-hpack/story_23.json");
(story_24, "python-hpack/story_24.json");
(story_25, "python-hpack/story_25.json");
(story_26, "python-hpack/story_26.json");
(story_27, "python-hpack/story_27.json");
(story_28, "python-hpack/story_28.json");
(story_29, "python-hpack/story_29.json");
(story_30, "python-hpack/story_30.json");
(story_31, "python-hpack/story_31.json");
}
);
fixture_mod!(
nghttp2_16384_4096 => {
(story_00, "nghttp2-16384-4096/story_00.json");
(story_01, "nghttp2-16384-4096/story_01.json");
(story_02, "nghttp2-16384-4096/story_02.json");
(story_03, "nghttp2-16384-4096/story_03.json");
(story_04, "nghttp2-16384-4096/story_04.json");
(story_05, "nghttp2-16384-4096/story_05.json");
(story_06, "nghttp2-16384-4096/story_06.json");
(story_07, "nghttp2-16384-4096/story_07.json");
(story_08, "nghttp2-16384-4096/story_08.json");
(story_09, "nghttp2-16384-4096/story_09.json");
(story_10, "nghttp2-16384-4096/story_10.json");
(story_11, "nghttp2-16384-4096/story_11.json");
(story_12, "nghttp2-16384-4096/story_12.json");
(story_13, "nghttp2-16384-4096/story_13.json");
(story_14, "nghttp2-16384-4096/story_14.json");
(story_15, "nghttp2-16384-4096/story_15.json");
(story_16, "nghttp2-16384-4096/story_16.json");
(story_17, "nghttp2-16384-4096/story_17.json");
(story_18, "nghttp2-16384-4096/story_18.json");
(story_19, "nghttp2-16384-4096/story_19.json");
(story_20, "nghttp2-16384-4096/story_20.json");
(story_21, "nghttp2-16384-4096/story_21.json");
(story_22, "nghttp2-16384-4096/story_22.json");
(story_23, "nghttp2-16384-4096/story_23.json");
(story_24, "nghttp2-16384-4096/story_24.json");
(story_25, "nghttp2-16384-4096/story_25.json");
(story_26, "nghttp2-16384-4096/story_26.json");
(story_27, "nghttp2-16384-4096/story_27.json");
(story_28, "nghttp2-16384-4096/story_28.json");
(story_29, "nghttp2-16384-4096/story_29.json");
(story_30, "nghttp2-16384-4096/story_30.json");
}
);
fixture_mod!(
node_http2_hpack => {
(story_00, "node-http2-hpack/story_00.json");
(story_01, "node-http2-hpack/story_01.json");
(story_02, "node-http2-hpack/story_02.json");
(story_03, "node-http2-hpack/story_03.json");
(story_04, "node-http2-hpack/story_04.json");
(story_05, "node-http2-hpack/story_05.json");
(story_06, "node-http2-hpack/story_06.json");
(story_07, "node-http2-hpack/story_07.json");
(story_08, "node-http2-hpack/story_08.json");
(story_09, "node-http2-hpack/story_09.json");
(story_10, "node-http2-hpack/story_10.json");
(story_11, "node-http2-hpack/story_11.json");
(story_12, "node-http2-hpack/story_12.json");
(story_13, "node-http2-hpack/story_13.json");
(story_14, "node-http2-hpack/story_14.json");
(story_15, "node-http2-hpack/story_15.json");
(story_16, "node-http2-hpack/story_16.json");
(story_17, "node-http2-hpack/story_17.json");
(story_18, "node-http2-hpack/story_18.json");
(story_19, "node-http2-hpack/story_19.json");
(story_20, "node-http2-hpack/story_20.json");
(story_21, "node-http2-hpack/story_21.json");
(story_22, "node-http2-hpack/story_22.json");
(story_23, "node-http2-hpack/story_23.json");
(story_24, "node-http2-hpack/story_24.json");
(story_25, "node-http2-hpack/story_25.json");
(story_26, "node-http2-hpack/story_26.json");
(story_27, "node-http2-hpack/story_27.json");
(story_28, "node-http2-hpack/story_28.json");
(story_29, "node-http2-hpack/story_29.json");
(story_30, "node-http2-hpack/story_30.json");
(story_31, "node-http2-hpack/story_31.json");
}
);
fixture_mod!(
nghttp2_change_table_size => {
(story_00, "nghttp2-change-table-size/story_00.json");
(story_01, "nghttp2-change-table-size/story_01.json");
(story_02, "nghttp2-change-table-size/story_02.json");
(story_03, "nghttp2-change-table-size/story_03.json");
(story_04, "nghttp2-change-table-size/story_04.json");
(story_05, "nghttp2-change-table-size/story_05.json");
(story_06, "nghttp2-change-table-size/story_06.json");
(story_07, "nghttp2-change-table-size/story_07.json");
(story_08, "nghttp2-change-table-size/story_08.json");
(story_09, "nghttp2-change-table-size/story_09.json");
(story_10, "nghttp2-change-table-size/story_10.json");
(story_11, "nghttp2-change-table-size/story_11.json");
(story_12, "nghttp2-change-table-size/story_12.json");
(story_13, "nghttp2-change-table-size/story_13.json");
(story_14, "nghttp2-change-table-size/story_14.json");
(story_15, "nghttp2-change-table-size/story_15.json");
(story_16, "nghttp2-change-table-size/story_16.json");
(story_17, "nghttp2-change-table-size/story_17.json");
(story_18, "nghttp2-change-table-size/story_18.json");
(story_19, "nghttp2-change-table-size/story_19.json");
(story_20, "nghttp2-change-table-size/story_20.json");
(story_21, "nghttp2-change-table-size/story_21.json");
(story_22, "nghttp2-change-table-size/story_22.json");
(story_23, "nghttp2-change-table-size/story_23.json");
(story_24, "nghttp2-change-table-size/story_24.json");
(story_25, "nghttp2-change-table-size/story_25.json");
(story_26, "nghttp2-change-table-size/story_26.json");
(story_27, "nghttp2-change-table-size/story_27.json");
(story_28, "nghttp2-change-table-size/story_28.json");
(story_29, "nghttp2-change-table-size/story_29.json");
(story_30, "nghttp2-change-table-size/story_30.json");
}
);
fixture_mod!(
haskell_http2_static_huffman => {
(story_00, "haskell-http2-static-huffman/story_00.json");
(story_01, "haskell-http2-static-huffman/story_01.json");
(story_02, "haskell-http2-static-huffman/story_02.json");
(story_03, "haskell-http2-static-huffman/story_03.json");
(story_04, "haskell-http2-static-huffman/story_04.json");
(story_05, "haskell-http2-static-huffman/story_05.json");
(story_06, "haskell-http2-static-huffman/story_06.json");
(story_07, "haskell-http2-static-huffman/story_07.json");
(story_08, "haskell-http2-static-huffman/story_08.json");
(story_09, "haskell-http2-static-huffman/story_09.json");
(story_10, "haskell-http2-static-huffman/story_10.json");
(story_11, "haskell-http2-static-huffman/story_11.json");
(story_12, "haskell-http2-static-huffman/story_12.json");
(story_13, "haskell-http2-static-huffman/story_13.json");
(story_14, "haskell-http2-static-huffman/story_14.json");
(story_15, "haskell-http2-static-huffman/story_15.json");
(story_16, "haskell-http2-static-huffman/story_16.json");
(story_17, "haskell-http2-static-huffman/story_17.json");
(story_18, "haskell-http2-static-huffman/story_18.json");
(story_19, "haskell-http2-static-huffman/story_19.json");
(story_20, "haskell-http2-static-huffman/story_20.json");
(story_21, "haskell-http2-static-huffman/story_21.json");
(story_22, "haskell-http2-static-huffman/story_22.json");
(story_23, "haskell-http2-static-huffman/story_23.json");
(story_24, "haskell-http2-static-huffman/story_24.json");
(story_25, "haskell-http2-static-huffman/story_25.json");
(story_26, "haskell-http2-static-huffman/story_26.json");
(story_27, "haskell-http2-static-huffman/story_27.json");
(story_28, "haskell-http2-static-huffman/story_28.json");
(story_29, "haskell-http2-static-huffman/story_29.json");
(story_30, "haskell-http2-static-huffman/story_30.json");
(story_31, "haskell-http2-static-huffman/story_31.json");
}
);
fixture_mod!(
haskell_http2_naive_huffman => {
(story_00, "haskell-http2-naive-huffman/story_00.json");
(story_01, "haskell-http2-naive-huffman/story_01.json");
(story_02, "haskell-http2-naive-huffman/story_02.json");
(story_03, "haskell-http2-naive-huffman/story_03.json");
(story_04, "haskell-http2-naive-huffman/story_04.json");
(story_05, "haskell-http2-naive-huffman/story_05.json");
(story_06, "haskell-http2-naive-huffman/story_06.json");
(story_07, "haskell-http2-naive-huffman/story_07.json");
(story_08, "haskell-http2-naive-huffman/story_08.json");
(story_09, "haskell-http2-naive-huffman/story_09.json");
(story_10, "haskell-http2-naive-huffman/story_10.json");
(story_11, "haskell-http2-naive-huffman/story_11.json");
(story_12, "haskell-http2-naive-huffman/story_12.json");
(story_13, "haskell-http2-naive-huffman/story_13.json");
(story_14, "haskell-http2-naive-huffman/story_14.json");
(story_15, "haskell-http2-naive-huffman/story_15.json");
(story_16, "haskell-http2-naive-huffman/story_16.json");
(story_17, "haskell-http2-naive-huffman/story_17.json");
(story_18, "haskell-http2-naive-huffman/story_18.json");
(story_19, "haskell-http2-naive-huffman/story_19.json");
(story_20, "haskell-http2-naive-huffman/story_20.json");
(story_21, "haskell-http2-naive-huffman/story_21.json");
(story_22, "haskell-http2-naive-huffman/story_22.json");
(story_23, "haskell-http2-naive-huffman/story_23.json");
(story_24, "haskell-http2-naive-huffman/story_24.json");
(story_25, "haskell-http2-naive-huffman/story_25.json");
(story_26, "haskell-http2-naive-huffman/story_26.json");
(story_27, "haskell-http2-naive-huffman/story_27.json");
(story_28, "haskell-http2-naive-huffman/story_28.json");
(story_29, "haskell-http2-naive-huffman/story_29.json");
(story_30, "haskell-http2-naive-huffman/story_30.json");
(story_31, "haskell-http2-naive-huffman/story_31.json");
}
);
fixture_mod!(
haskell_http2_naive => {
(story_00, "haskell-http2-naive/story_00.json");
(story_01, "haskell-http2-naive/story_01.json");
(story_02, "haskell-http2-naive/story_02.json");
(story_03, "haskell-http2-naive/story_03.json");
(story_04, "haskell-http2-naive/story_04.json");
(story_05, "haskell-http2-naive/story_05.json");
(story_06, "haskell-http2-naive/story_06.json");
(story_07, "haskell-http2-naive/story_07.json");
(story_08, "haskell-http2-naive/story_08.json");
(story_09, "haskell-http2-naive/story_09.json");
(story_10, "haskell-http2-naive/story_10.json");
(story_11, "haskell-http2-naive/story_11.json");
(story_12, "haskell-http2-naive/story_12.json");
(story_13, "haskell-http2-naive/story_13.json");
(story_14, "haskell-http2-naive/story_14.json");
(story_15, "haskell-http2-naive/story_15.json");
(story_16, "haskell-http2-naive/story_16.json");
(story_17, "haskell-http2-naive/story_17.json");
(story_18, "haskell-http2-naive/story_18.json");
(story_19, "haskell-http2-naive/story_19.json");
(story_20, "haskell-http2-naive/story_20.json");
(story_21, "haskell-http2-naive/story_21.json");
(story_22, "haskell-http2-naive/story_22.json");
(story_23, "haskell-http2-naive/story_23.json");
(story_24, "haskell-http2-naive/story_24.json");
(story_25, "haskell-http2-naive/story_25.json");
(story_26, "haskell-http2-naive/story_26.json");
(story_27, "haskell-http2-naive/story_27.json");
(story_28, "haskell-http2-naive/story_28.json");
(story_29, "haskell-http2-naive/story_29.json");
(story_30, "haskell-http2-naive/story_30.json");
(story_31, "haskell-http2-naive/story_31.json");
}
);
fixture_mod!(
haskell_http2_static => {
(story_00, "haskell-http2-static/story_00.json");
(story_01, "haskell-http2-static/story_01.json");
(story_02, "haskell-http2-static/story_02.json");
(story_03, "haskell-http2-static/story_03.json");
(story_04, "haskell-http2-static/story_04.json");
(story_05, "haskell-http2-static/story_05.json");
(story_06, "haskell-http2-static/story_06.json");
(story_07, "haskell-http2-static/story_07.json");
(story_08, "haskell-http2-static/story_08.json");
(story_09, "haskell-http2-static/story_09.json");
(story_10, "haskell-http2-static/story_10.json");
(story_11, "haskell-http2-static/story_11.json");
(story_12, "haskell-http2-static/story_12.json");
(story_13, "haskell-http2-static/story_13.json");
(story_14, "haskell-http2-static/story_14.json");
(story_15, "haskell-http2-static/story_15.json");
(story_16, "haskell-http2-static/story_16.json");
(story_17, "haskell-http2-static/story_17.json");
(story_18, "haskell-http2-static/story_18.json");
(story_19, "haskell-http2-static/story_19.json");
(story_20, "haskell-http2-static/story_20.json");
(story_21, "haskell-http2-static/story_21.json");
(story_22, "haskell-http2-static/story_22.json");
(story_23, "haskell-http2-static/story_23.json");
(story_24, "haskell-http2-static/story_24.json");
(story_25, "haskell-http2-static/story_25.json");
(story_26, "haskell-http2-static/story_26.json");
(story_27, "haskell-http2-static/story_27.json");
(story_28, "haskell-http2-static/story_28.json");
(story_29, "haskell-http2-static/story_29.json");
(story_30, "haskell-http2-static/story_30.json");
(story_31, "haskell-http2-static/story_31.json");
}
);
fixture_mod!(
nghttp2 => {
(story_00, "nghttp2/story_00.json");
(story_01, "nghttp2/story_01.json");
(story_02, "nghttp2/story_02.json");
(story_03, "nghttp2/story_03.json");
(story_04, "nghttp2/story_04.json");
(story_05, "nghttp2/story_05.json");
(story_06, "nghttp2/story_06.json");
(story_07, "nghttp2/story_07.json");
(story_08, "nghttp2/story_08.json");
(story_09, "nghttp2/story_09.json");
(story_10, "nghttp2/story_10.json");
(story_11, "nghttp2/story_11.json");
(story_12, "nghttp2/story_12.json");
(story_13, "nghttp2/story_13.json");
(story_14, "nghttp2/story_14.json");
(story_15, "nghttp2/story_15.json");
(story_16, "nghttp2/story_16.json");
(story_17, "nghttp2/story_17.json");
(story_18, "nghttp2/story_18.json");
(story_19, "nghttp2/story_19.json");
(story_20, "nghttp2/story_20.json");
(story_21, "nghttp2/story_21.json");
(story_22, "nghttp2/story_22.json");
(story_23, "nghttp2/story_23.json");
(story_24, "nghttp2/story_24.json");
(story_25, "nghttp2/story_25.json");
(story_26, "nghttp2/story_26.json");
(story_27, "nghttp2/story_27.json");
(story_28, "nghttp2/story_28.json");
(story_29, "nghttp2/story_29.json");
(story_30, "nghttp2/story_30.json");
(story_31, "nghttp2/story_31.json");
}
);
fixture_mod!(
haskell_http2_linear => {
(story_00, "haskell-http2-linear/story_00.json");
(story_01, "haskell-http2-linear/story_01.json");
(story_02, "haskell-http2-linear/story_02.json");
(story_03, "haskell-http2-linear/story_03.json");
(story_04, "haskell-http2-linear/story_04.json");
(story_05, "haskell-http2-linear/story_05.json");
(story_06, "haskell-http2-linear/story_06.json");
(story_07, "haskell-http2-linear/story_07.json");
(story_08, "haskell-http2-linear/story_08.json");
(story_09, "haskell-http2-linear/story_09.json");
(story_10, "haskell-http2-linear/story_10.json");
(story_11, "haskell-http2-linear/story_11.json");
(story_12, "haskell-http2-linear/story_12.json");
(story_13, "haskell-http2-linear/story_13.json");
(story_14, "haskell-http2-linear/story_14.json");
(story_15, "haskell-http2-linear/story_15.json");
(story_16, "haskell-http2-linear/story_16.json");
(story_17, "haskell-http2-linear/story_17.json");
(story_18, "haskell-http2-linear/story_18.json");
(story_19, "haskell-http2-linear/story_19.json");
(story_20, "haskell-http2-linear/story_20.json");
(story_21, "haskell-http2-linear/story_21.json");
(story_22, "haskell-http2-linear/story_22.json");
(story_23, "haskell-http2-linear/story_23.json");
(story_24, "haskell-http2-linear/story_24.json");
(story_25, "haskell-http2-linear/story_25.json");
(story_26, "haskell-http2-linear/story_26.json");
(story_27, "haskell-http2-linear/story_27.json");
(story_28, "haskell-http2-linear/story_28.json");
(story_29, "haskell-http2-linear/story_29.json");
(story_30, "haskell-http2-linear/story_30.json");
(story_31, "haskell-http2-linear/story_31.json");
}
);
fixture_mod!(
go_hpack => {
(story_00, "go-hpack/story_00.json");
(story_01, "go-hpack/story_01.json");
(story_02, "go-hpack/story_02.json");
(story_03, "go-hpack/story_03.json");
(story_04, "go-hpack/story_04.json");
(story_05, "go-hpack/story_05.json");
(story_06, "go-hpack/story_06.json");
(story_07, "go-hpack/story_07.json");
(story_08, "go-hpack/story_08.json");
(story_09, "go-hpack/story_09.json");
(story_10, "go-hpack/story_10.json");
(story_11, "go-hpack/story_11.json");
(story_12, "go-hpack/story_12.json");
(story_13, "go-hpack/story_13.json");
(story_14, "go-hpack/story_14.json");
(story_15, "go-hpack/story_15.json");
(story_16, "go-hpack/story_16.json");
(story_17, "go-hpack/story_17.json");
(story_18, "go-hpack/story_18.json");
(story_19, "go-hpack/story_19.json");
(story_20, "go-hpack/story_20.json");
(story_21, "go-hpack/story_21.json");
(story_22, "go-hpack/story_22.json");
(story_23, "go-hpack/story_23.json");
(story_24, "go-hpack/story_24.json");
(story_25, "go-hpack/story_25.json");
(story_26, "go-hpack/story_26.json");
(story_27, "go-hpack/story_27.json");
(story_28, "go-hpack/story_28.json");
(story_29, "go-hpack/story_29.json");
(story_30, "go-hpack/story_30.json");
(story_31, "go-hpack/story_31.json");
}
);

365
vendor/h2/src/hpack/test/fuzz.rs vendored Normal file
View File

@@ -0,0 +1,365 @@
use crate::hpack::{Decoder, Encoder, Header};
use http::header::{HeaderName, HeaderValue};
use bytes::BytesMut;
use quickcheck::{Arbitrary, Gen, QuickCheck, TestResult};
use rand::distributions::Slice;
use rand::rngs::StdRng;
use rand::{thread_rng, Rng, SeedableRng};
use std::io::Cursor;
const MAX_CHUNK: usize = 2 * 1024;
#[test]
fn hpack_fuzz() {
let _ = env_logger::try_init();
fn prop(fuzz: FuzzHpack) -> TestResult {
fuzz.run();
TestResult::from_bool(true)
}
QuickCheck::new()
.tests(100)
.quickcheck(prop as fn(FuzzHpack) -> TestResult)
}
/*
// If wanting to test with a specific feed, uncomment and fill in the seed.
#[test]
fn hpack_fuzz_seeded() {
let _ = env_logger::try_init();
let seed = [/* fill me in*/];
FuzzHpack::new(seed).run();
}
*/
#[derive(Debug, Clone)]
struct FuzzHpack {
// The set of headers to encode / decode
frames: Vec<HeaderFrame>,
}
#[derive(Debug, Clone)]
struct HeaderFrame {
resizes: Vec<usize>,
headers: Vec<Header<Option<HeaderName>>>,
}
impl FuzzHpack {
fn new(seed: [u8; 32]) -> FuzzHpack {
// Seed the RNG
let mut rng = StdRng::from_seed(seed);
// Generates a bunch of source headers
let mut source: Vec<Header<Option<HeaderName>>> = vec![];
for _ in 0..2000 {
source.push(gen_header(&mut rng));
}
// Actual test run headers
let num: usize = rng.gen_range(40..500);
let mut frames: Vec<HeaderFrame> = vec![];
let mut added = 0;
let skew: i32 = rng.gen_range(1..5);
// Rough number of headers to add
while added < num {
let mut frame = HeaderFrame {
resizes: vec![],
headers: vec![],
};
match rng.gen_range(0..20) {
0 => {
// Two resizes
let high = rng.gen_range(128..MAX_CHUNK * 2);
let low = rng.gen_range(0..high);
frame.resizes.extend([low, high]);
}
1..=3 => {
frame.resizes.push(rng.gen_range(128..MAX_CHUNK * 2));
}
_ => {}
}
let mut is_name_required = true;
for _ in 0..rng.gen_range(1..(num - added) + 1) {
let x: f64 = rng.gen_range(0.0..1.0);
let x = x.powi(skew);
let i = (x * source.len() as f64) as usize;
let header = &source[i];
match header {
Header::Field { name: None, .. } => {
if is_name_required {
continue;
}
}
Header::Field { .. } => {
is_name_required = false;
}
_ => {
// pseudos can't be followed by a header with no name
is_name_required = true;
}
}
frame.headers.push(header.clone());
added += 1;
}
frames.push(frame);
}
FuzzHpack { frames }
}
fn run(self) {
let frames = self.frames;
let mut expect = vec![];
let mut encoder = Encoder::default();
let mut decoder = Decoder::default();
for frame in frames {
// build "expected" frames, such that decoding headers always
// includes a name
let mut prev_name = None;
for header in &frame.headers {
match header.clone().reify() {
Ok(h) => {
prev_name = match h {
Header::Field { ref name, .. } => Some(name.clone()),
_ => None,
};
expect.push(h);
}
Err(value) => {
expect.push(Header::Field {
name: prev_name.as_ref().cloned().expect("previous header name"),
value,
});
}
}
}
let mut buf = BytesMut::new();
if let Some(max) = frame.resizes.iter().max() {
decoder.queue_size_update(*max);
}
// Apply resizes
for resize in &frame.resizes {
encoder.update_max_size(*resize);
}
encoder.encode(frame.headers, &mut buf);
// Decode the chunk!
decoder
.decode(&mut Cursor::new(&mut buf), |h| {
let e = expect.remove(0);
assert_eq!(h, e);
})
.expect("full decode");
}
assert_eq!(0, expect.len());
}
}
impl Arbitrary for FuzzHpack {
fn arbitrary(_: &mut Gen) -> Self {
FuzzHpack::new(thread_rng().gen())
}
}
fn gen_header(g: &mut StdRng) -> Header<Option<HeaderName>> {
use http::{Method, StatusCode};
if g.gen_ratio(1, 10) {
match g.gen_range(0u32..5) {
0 => {
let value = gen_string(g, 4, 20);
Header::Authority(to_shared(value))
}
1 => {
let method = match g.gen_range(0u32..6) {
0 => Method::GET,
1 => Method::POST,
2 => Method::PUT,
3 => Method::PATCH,
4 => Method::DELETE,
5 => {
let n: usize = g.gen_range(3..7);
let bytes: Vec<u8> = (0..n)
.map(|_| *g.sample(Slice::new(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ").unwrap()))
.collect();
Method::from_bytes(&bytes).unwrap()
}
_ => unreachable!(),
};
Header::Method(method)
}
2 => {
let value = match g.gen_range(0u32..2) {
0 => "http",
1 => "https",
_ => unreachable!(),
};
Header::Scheme(to_shared(value.to_string()))
}
3 => {
let value = match g.gen_range(0u32..100) {
0 => "/".to_string(),
1 => "/index.html".to_string(),
_ => gen_string(g, 2, 20),
};
Header::Path(to_shared(value))
}
4 => {
let status = (g.gen::<u16>() % 500) + 100;
Header::Status(StatusCode::from_u16(status).unwrap())
}
_ => unreachable!(),
}
} else {
let name = if g.gen_ratio(1, 10) {
None
} else {
Some(gen_header_name(g))
};
let mut value = gen_header_value(g);
if g.gen_ratio(1, 30) {
value.set_sensitive(true);
}
Header::Field { name, value }
}
}
fn gen_header_name(g: &mut StdRng) -> HeaderName {
use http::header;
if g.gen_ratio(1, 2) {
g.sample(
Slice::new(&[
header::ACCEPT,
header::ACCEPT_CHARSET,
header::ACCEPT_ENCODING,
header::ACCEPT_LANGUAGE,
header::ACCEPT_RANGES,
header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
header::ACCESS_CONTROL_ALLOW_HEADERS,
header::ACCESS_CONTROL_ALLOW_METHODS,
header::ACCESS_CONTROL_ALLOW_ORIGIN,
header::ACCESS_CONTROL_EXPOSE_HEADERS,
header::ACCESS_CONTROL_MAX_AGE,
header::ACCESS_CONTROL_REQUEST_HEADERS,
header::ACCESS_CONTROL_REQUEST_METHOD,
header::AGE,
header::ALLOW,
header::ALT_SVC,
header::AUTHORIZATION,
header::CACHE_CONTROL,
header::CONNECTION,
header::CONTENT_DISPOSITION,
header::CONTENT_ENCODING,
header::CONTENT_LANGUAGE,
header::CONTENT_LENGTH,
header::CONTENT_LOCATION,
header::CONTENT_RANGE,
header::CONTENT_SECURITY_POLICY,
header::CONTENT_SECURITY_POLICY_REPORT_ONLY,
header::CONTENT_TYPE,
header::COOKIE,
header::DNT,
header::DATE,
header::ETAG,
header::EXPECT,
header::EXPIRES,
header::FORWARDED,
header::FROM,
header::HOST,
header::IF_MATCH,
header::IF_MODIFIED_SINCE,
header::IF_NONE_MATCH,
header::IF_RANGE,
header::IF_UNMODIFIED_SINCE,
header::LAST_MODIFIED,
header::LINK,
header::LOCATION,
header::MAX_FORWARDS,
header::ORIGIN,
header::PRAGMA,
header::PROXY_AUTHENTICATE,
header::PROXY_AUTHORIZATION,
header::PUBLIC_KEY_PINS,
header::PUBLIC_KEY_PINS_REPORT_ONLY,
header::RANGE,
header::REFERER,
header::REFERRER_POLICY,
header::REFRESH,
header::RETRY_AFTER,
header::SERVER,
header::SET_COOKIE,
header::STRICT_TRANSPORT_SECURITY,
header::TE,
header::TRAILER,
header::TRANSFER_ENCODING,
header::USER_AGENT,
header::UPGRADE,
header::UPGRADE_INSECURE_REQUESTS,
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,
])
.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
*g.sample(Slice::new(b"ABCDEFGHIJKLMNOPQRSTUVabcdefghilpqrstuvwxyz----").unwrap())
})
.collect();
String::from_utf8(bytes).unwrap()
}
fn to_shared(src: String) -> crate::hpack::BytesStr {
crate::hpack::BytesStr::from(src.as_str())
}

2
vendor/h2/src/hpack/test/mod.rs vendored Normal file
View File

@@ -0,0 +1,2 @@
mod fixture;
mod fuzz;

162
vendor/h2/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,162 @@
//! An asynchronous, HTTP/2 server and client implementation.
//!
//! This library implements the [HTTP/2] specification. The implementation is
//! asynchronous, using [futures] as the basis for the API. The implementation
//! is also decoupled from TCP or TLS details. The user must handle ALPN and
//! HTTP/1.1 upgrades themselves.
//!
//! # Getting started
//!
//! Add the following to your `Cargo.toml` file:
//!
//! ```toml
//! [dependencies]
//! h2 = "0.4"
//! ```
//!
//! # Layout
//!
//! The crate is split into [`client`] and [`server`] modules. Types that are
//! common to both clients and servers are located at the root of the crate.
//!
//! See module level documentation for more details on how to use `h2`.
//!
//! # Handshake
//!
//! Both the client and the server require a connection to already be in a state
//! ready to start the HTTP/2 handshake. This library does not provide
//! facilities to do this.
//!
//! There are three ways to reach an appropriate state to start the HTTP/2
//! handshake.
//!
//! * Opening an HTTP/1.1 connection and performing an [upgrade].
//! * Opening a connection with TLS and use ALPN to negotiate the protocol.
//! * Open a connection with prior knowledge, i.e. both the client and the
//! server assume that the connection is immediately ready to start the
//! HTTP/2 handshake once opened.
//!
//! Once the connection is ready to start the HTTP/2 handshake, it can be
//! passed to [`server::handshake`] or [`client::handshake`]. At this point, the
//! library will start the handshake process, which consists of:
//!
//! * The client sends the connection preface (a predefined sequence of 24
//! octets).
//! * Both the client and the server sending a SETTINGS frame.
//!
//! See the [Starting HTTP/2] in the specification for more details.
//!
//! # Flow control
//!
//! [Flow control] is a fundamental feature of HTTP/2. The `h2` library
//! exposes flow control to the user.
//!
//! An HTTP/2 client or server may not send unlimited data to the peer. When a
//! stream is initiated, both the client and the server are provided with an
//! initial window size for that stream. A window size is the number of bytes
//! the endpoint can send to the peer. At any point in time, the peer may
//! increase this window size by sending a `WINDOW_UPDATE` frame. Once a client
//! or server has sent data filling the window for a stream, no further data may
//! be sent on that stream until the peer increases the window.
//!
//! There is also a **connection level** window governing data sent across all
//! streams.
//!
//! Managing flow control for inbound data is done through [`FlowControl`].
//! Managing flow control for outbound data is done through [`SendStream`]. See
//! the struct level documentation for those two types for more details.
//!
//! [HTTP/2]: https://http2.github.io/
//! [futures]: https://docs.rs/futures/
//! [`client`]: client/index.html
//! [`server`]: server/index.html
//! [Flow control]: http://httpwg.org/specs/rfc7540.html#FlowControl
//! [`FlowControl`]: struct.FlowControl.html
//! [`SendStream`]: struct.SendStream.html
//! [Starting HTTP/2]: http://httpwg.org/specs/rfc7540.html#starting
//! [upgrade]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism
//! [`server::handshake`]: server/fn.handshake.html
//! [`client::handshake`]: client/fn.handshake.html
#![deny(
missing_debug_implementations,
missing_docs,
clippy::missing_safety_doc,
clippy::undocumented_unsafe_blocks
)]
#![allow(clippy::type_complexity, clippy::manual_range_contains)]
#![cfg_attr(test, deny(warnings))]
macro_rules! proto_err {
(conn: $($msg:tt)+) => {
tracing::debug!("connection error PROTOCOL_ERROR -- {};", format_args!($($msg)+))
};
(stream: $($msg:tt)+) => {
tracing::debug!("stream error PROTOCOL_ERROR -- {};", format_args!($($msg)+))
};
}
macro_rules! ready {
($e:expr) => {
match $e {
::std::task::Poll::Ready(r) => r,
::std::task::Poll::Pending => return ::std::task::Poll::Pending,
}
};
}
#[cfg_attr(feature = "unstable", allow(missing_docs))]
mod codec;
mod error;
mod hpack;
#[cfg(not(feature = "unstable"))]
mod proto;
#[cfg(feature = "unstable")]
#[allow(missing_docs)]
pub mod proto;
#[cfg(not(feature = "unstable"))]
mod frame;
#[cfg(feature = "unstable")]
#[allow(missing_docs)]
pub mod frame;
pub mod client;
pub mod ext;
pub mod server;
mod share;
#[cfg(fuzzing)]
#[cfg_attr(feature = "unstable", allow(missing_docs))]
pub mod fuzz_bridge;
pub use crate::error::{Error, Reason};
pub use crate::share::{FlowControl, Ping, PingPong, Pong, RecvStream, SendStream, StreamId};
#[cfg(feature = "unstable")]
pub use codec::{Codec, SendError, UserError};
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
/// Creates a future from a function that returns `Poll`.
fn poll_fn<T, F: FnMut(&mut Context<'_>) -> T>(f: F) -> PollFn<F> {
PollFn(f)
}
/// The future created by `poll_fn`.
struct PollFn<F>(F);
impl<F> Unpin for PollFn<F> {}
impl<T, F: FnMut(&mut Context<'_>) -> Poll<T>> Future for PollFn<F> {
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
(self.0)(cx)
}
}

635
vendor/h2/src/proto/connection.rs vendored Normal file
View File

@@ -0,0 +1,635 @@
use crate::codec::UserError;
use crate::frame::{Reason, StreamId};
use crate::{client, server};
use crate::frame::DEFAULT_INITIAL_WINDOW_SIZE;
use crate::proto::*;
use bytes::Bytes;
use futures_core::Stream;
use std::io;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use tokio::io::AsyncRead;
/// An H2 connection
#[derive(Debug)]
pub(crate) struct Connection<T, P, B: Buf = Bytes>
where
P: Peer,
{
/// Read / write frame values
codec: Codec<T, Prioritized<B>>,
inner: ConnectionInner<P, B>,
}
// Extracted part of `Connection` which does not depend on `T`. Reduces the amount of duplicated
// method instantiations.
#[derive(Debug)]
struct ConnectionInner<P, B: Buf = Bytes>
where
P: Peer,
{
/// Tracks the connection level state transitions.
state: State,
/// An error to report back once complete.
///
/// This exists separately from State in order to support
/// graceful shutdown.
error: Option<frame::GoAway>,
/// Pending GOAWAY frames to write.
go_away: GoAway,
/// Ping/pong handler
ping_pong: PingPong,
/// Connection settings
settings: Settings,
/// Stream state handler
streams: Streams<B, P>,
/// A `tracing` span tracking the lifetime of the connection.
span: tracing::Span,
/// Client or server
_phantom: PhantomData<P>,
}
struct DynConnection<'a, B: Buf = Bytes> {
state: &'a mut State,
go_away: &'a mut GoAway,
streams: DynStreams<'a, B>,
error: &'a mut Option<frame::GoAway>,
ping_pong: &'a mut PingPong,
}
#[derive(Debug, Clone)]
pub(crate) struct Config {
pub next_stream_id: StreamId,
pub initial_max_send_streams: usize,
pub max_send_buffer_size: usize,
pub reset_stream_duration: Duration,
pub reset_stream_max: usize,
pub remote_reset_stream_max: usize,
pub local_error_reset_streams_max: Option<usize>,
pub settings: frame::Settings,
}
#[derive(Debug)]
enum State {
/// Currently open in a sane state
Open,
/// The codec must be flushed
Closing(Reason, Initiator),
/// In a closed state
Closed(Reason, Initiator),
}
impl<T, P, B> Connection<T, P, B>
where
T: AsyncRead + AsyncWrite + Unpin,
P: Peer,
B: Buf,
{
pub fn new(codec: Codec<T, Prioritized<B>>, config: Config) -> Connection<T, P, B> {
fn streams_config(config: &Config) -> streams::Config {
streams::Config {
initial_max_send_streams: config.initial_max_send_streams,
local_max_buffer_size: config.max_send_buffer_size,
local_next_stream_id: config.next_stream_id,
local_push_enabled: config.settings.is_push_enabled().unwrap_or(true),
extended_connect_protocol_enabled: config
.settings
.is_extended_connect_protocol_enabled()
.unwrap_or(false),
local_reset_duration: config.reset_stream_duration,
local_reset_max: config.reset_stream_max,
remote_reset_max: config.remote_reset_stream_max,
remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
remote_max_initiated: config
.settings
.max_concurrent_streams()
.map(|max| max as usize),
local_max_error_reset_streams: config.local_error_reset_streams_max,
}
}
let streams = Streams::new(streams_config(&config));
let span = tracing::debug_span!(parent: None, "Connection", peer = %P::NAME);
span.follows_from(tracing::Span::current());
Connection {
codec,
inner: ConnectionInner {
state: State::Open,
error: None,
go_away: GoAway::new(),
ping_pong: PingPong::new(),
settings: Settings::new(config.settings),
streams,
span,
_phantom: PhantomData,
},
}
}
/// connection flow control
pub(crate) fn set_target_window_size(&mut self, size: WindowSize) {
let _res = self.inner.streams.set_target_connection_window_size(size);
// TODO: proper error handling
debug_assert!(_res.is_ok());
}
/// Send a new SETTINGS frame with an updated initial window size.
pub(crate) fn set_initial_window_size(&mut self, size: WindowSize) -> Result<(), UserError> {
let mut settings = frame::Settings::default();
settings.set_initial_window_size(Some(size));
self.inner.settings.send_settings(settings)
}
/// Send a new SETTINGS frame with extended CONNECT protocol enabled.
pub(crate) fn set_enable_connect_protocol(&mut self) -> Result<(), UserError> {
let mut settings = frame::Settings::default();
settings.set_enable_connect_protocol(Some(1));
self.inner.settings.send_settings(settings)
}
/// Returns the maximum number of concurrent streams that may be initiated
/// by this peer.
pub(crate) fn max_send_streams(&self) -> usize {
self.inner.streams.max_send_streams()
}
/// Returns the maximum number of concurrent streams that may be initiated
/// by the remote peer.
pub(crate) fn max_recv_streams(&self) -> usize {
self.inner.streams.max_recv_streams()
}
#[cfg(feature = "unstable")]
pub fn num_wired_streams(&self) -> usize {
self.inner.streams.num_wired_streams()
}
/// Returns `Ready` when the connection is ready to receive a frame.
///
/// Returns `Error` as this may raise errors that are caused by delayed
/// processing of received frames.
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
let _e = self.inner.span.enter();
let span = tracing::trace_span!("poll_ready");
let _e = span.enter();
// The order of these calls don't really matter too much
ready!(self.inner.ping_pong.send_pending_pong(cx, &mut self.codec))?;
ready!(self.inner.ping_pong.send_pending_ping(cx, &mut self.codec))?;
ready!(self
.inner
.settings
.poll_send(cx, &mut self.codec, &mut self.inner.streams))?;
ready!(self.inner.streams.send_pending_refusal(cx, &mut self.codec))?;
Poll::Ready(Ok(()))
}
/// Send any pending GOAWAY frames.
///
/// This will return `Some(reason)` if the connection should be closed
/// afterwards. If this is a graceful shutdown, this returns `None`.
fn poll_go_away(&mut self, cx: &mut Context) -> Poll<Option<io::Result<Reason>>> {
self.inner.go_away.send_pending_go_away(cx, &mut self.codec)
}
pub fn go_away_from_user(&mut self, e: Reason) {
self.inner.as_dyn().go_away_from_user(e)
}
fn take_error(&mut self, ours: Reason, initiator: Initiator) -> Result<(), Error> {
let (debug_data, theirs) = self
.inner
.error
.take()
.as_ref()
.map_or((Bytes::new(), Reason::NO_ERROR), |frame| {
(frame.debug_data().clone(), frame.reason())
});
match (ours, theirs) {
(Reason::NO_ERROR, Reason::NO_ERROR) => Ok(()),
(ours, Reason::NO_ERROR) => Err(Error::GoAway(Bytes::new(), ours, initiator)),
// If both sides reported an error, give their
// error back to th user. We assume our error
// was a consequence of their error, and less
// important.
(_, theirs) => Err(Error::remote_go_away(debug_data, theirs)),
}
}
/// Closes the connection by transitioning to a GOAWAY state
/// iff there are no streams or references
pub fn maybe_close_connection_if_no_streams(&mut self) {
// If we poll() and realize that there are no streams or references
// then we can close the connection by transitioning to GOAWAY
if !self.inner.streams.has_streams_or_other_references() {
self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
}
}
/// Checks if there are any streams
pub fn has_streams(&self) -> bool {
self.inner.streams.has_streams()
}
/// Checks if there are any streams or references left
pub fn has_streams_or_other_references(&self) -> bool {
// If we poll() and realize that there are no streams or references
// then we can close the connection by transitioning to GOAWAY
self.inner.streams.has_streams_or_other_references()
}
pub(crate) fn take_user_pings(&mut self) -> Option<UserPings> {
self.inner.ping_pong.take_user_pings()
}
/// Advances the internal state of the connection.
pub fn poll(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
// XXX(eliza): cloning the span is unfortunately necessary here in
// order to placate the borrow checker — `self` is mutably borrowed by
// `poll2`, which means that we can't borrow `self.span` to enter it.
// The clone is just an atomic ref bump.
let span = self.inner.span.clone();
let _e = span.enter();
let span = tracing::trace_span!("poll");
let _e = span.enter();
loop {
tracing::trace!(connection.state = ?self.inner.state);
// TODO: probably clean up this glob of code
match self.inner.state {
// When open, continue to poll a frame
State::Open => {
let result = match self.poll2(cx) {
Poll::Ready(result) => result,
// The connection is not ready to make progress
Poll::Pending => {
// Ensure all window updates have been sent.
//
// This will also handle flushing `self.codec`
ready!(self.inner.streams.poll_complete(cx, &mut self.codec))?;
if (self.inner.error.is_some()
|| self.inner.go_away.should_close_on_idle())
&& !self.inner.streams.has_streams()
{
self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
continue;
}
return Poll::Pending;
}
};
self.inner.as_dyn().handle_poll2_result(result)?
}
State::Closing(reason, initiator) => {
tracing::trace!("connection closing after flush");
// Flush/shutdown the codec
ready!(self.codec.shutdown(cx))?;
// Transition the state to error
self.inner.state = State::Closed(reason, initiator);
}
State::Closed(reason, initiator) => {
return Poll::Ready(self.take_error(reason, initiator));
}
}
}
}
fn poll2(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
// This happens outside of the loop to prevent needing to do a clock
// check and then comparison of the queue possibly multiple times a
// second (and thus, the clock wouldn't have changed enough to matter).
self.clear_expired_reset_streams();
loop {
// First, ensure that the `Connection` is able to receive a frame
//
// The order here matters:
// - poll_go_away may buffer a graceful shutdown GOAWAY frame
// - If it has, we've also added a PING to be sent in poll_ready
if let Some(reason) = ready!(self.poll_go_away(cx)?) {
if self.inner.go_away.should_close_now() {
if self.inner.go_away.is_user_initiated() {
// A user initiated abrupt shutdown shouldn't return
// the same error back to the user.
return Poll::Ready(Ok(()));
} else {
return Poll::Ready(Err(Error::library_go_away(reason)));
}
}
// Only NO_ERROR should be waiting for idle
debug_assert_eq!(
reason,
Reason::NO_ERROR,
"graceful GOAWAY should be NO_ERROR"
);
}
ready!(self.poll_ready(cx))?;
match self
.inner
.as_dyn()
.recv_frame(ready!(Pin::new(&mut self.codec).poll_next(cx)?))?
{
ReceivedFrame::Settings(frame) => {
self.inner.settings.recv_settings(
frame,
&mut self.codec,
&mut self.inner.streams,
)?;
}
ReceivedFrame::Continue => (),
ReceivedFrame::Done => {
return Poll::Ready(Ok(()));
}
}
}
}
fn clear_expired_reset_streams(&mut self) {
self.inner.streams.clear_expired_reset_streams();
}
}
impl<P, B> ConnectionInner<P, B>
where
P: Peer,
B: Buf,
{
fn as_dyn(&mut self) -> DynConnection<'_, B> {
let ConnectionInner {
state,
go_away,
streams,
error,
ping_pong,
..
} = self;
let streams = streams.as_dyn();
DynConnection {
state,
go_away,
streams,
error,
ping_pong,
}
}
}
impl<B> DynConnection<'_, B>
where
B: Buf,
{
fn go_away(&mut self, id: StreamId, e: Reason) {
let frame = frame::GoAway::new(id, e);
self.streams.send_go_away(id);
self.go_away.go_away(frame);
}
fn go_away_now(&mut self, e: Reason) {
let last_processed_id = self.streams.last_processed_id();
let frame = frame::GoAway::new(last_processed_id, e);
self.go_away.go_away_now(frame);
}
fn go_away_now_data(&mut self, e: Reason, data: Bytes) {
let last_processed_id = self.streams.last_processed_id();
let frame = frame::GoAway::with_debug_data(last_processed_id, e, data);
self.go_away.go_away_now(frame);
}
fn go_away_from_user(&mut self, e: Reason) {
let last_processed_id = self.streams.last_processed_id();
let frame = frame::GoAway::new(last_processed_id, e);
self.go_away.go_away_from_user(frame);
// Notify all streams of reason we're abruptly closing.
self.streams.handle_error(Error::user_go_away(e));
}
fn handle_poll2_result(&mut self, result: Result<(), Error>) -> Result<(), Error> {
match result {
// The connection has shutdown normally
Ok(()) => {
*self.state = State::Closing(Reason::NO_ERROR, Initiator::Library);
Ok(())
}
// Attempting to read a frame resulted in a connection level
// error. This is handled by setting a GOAWAY frame followed by
// terminating the connection.
Err(Error::GoAway(debug_data, reason, initiator)) => {
self.handle_go_away(reason, debug_data, initiator);
Ok(())
}
// Attempting to read a frame resulted in a stream level error.
// This is handled by resetting the frame then trying to read
// another frame.
Err(Error::Reset(id, reason, initiator)) => {
debug_assert_eq!(initiator, Initiator::Library);
tracing::trace!(?id, ?reason, "stream error");
match self.streams.send_reset(id, reason) {
Ok(()) => (),
Err(crate::proto::error::GoAway { debug_data, reason }) => {
self.handle_go_away(reason, debug_data, Initiator::Library);
}
}
Ok(())
}
// Attempting to read a frame resulted in an I/O error. All
// active streams must be reset.
//
// TODO: Are I/O errors recoverable?
Err(Error::Io(kind, inner)) => {
tracing::debug!(error = ?kind, "Connection::poll; IO error");
let e = Error::Io(kind, inner);
// Reset all active streams
self.streams.handle_error(e.clone());
// Some client implementations drop the connections without notifying its peer
// Attempting to read after the client dropped the connection results in UnexpectedEof
// If as a server, we don't have anything more to send, just close the connection
// without error
//
// See https://github.com/hyperium/hyper/issues/3427
if self.streams.is_buffer_empty()
&& matches!(kind, io::ErrorKind::UnexpectedEof)
&& (self.streams.is_server()
|| self.error.as_ref().map(|f| f.reason() == Reason::NO_ERROR)
== Some(true))
{
*self.state = State::Closed(Reason::NO_ERROR, Initiator::Library);
return Ok(());
}
// Return the error
Err(e)
}
}
}
fn handle_go_away(&mut self, reason: Reason, debug_data: Bytes, initiator: Initiator) {
let e = Error::GoAway(debug_data.clone(), reason, initiator);
tracing::debug!(error = ?e, "Connection::poll; connection error");
// We may have already sent a GOAWAY for this error,
// if so, don't send another, just flush and close up.
if self
.go_away
.going_away()
.map_or(false, |frame| frame.reason() == reason)
{
tracing::trace!(" -> already going away");
*self.state = State::Closing(reason, initiator);
return;
}
// Reset all active streams
self.streams.handle_error(e);
self.go_away_now_data(reason, debug_data);
}
fn recv_frame(&mut self, frame: Option<Frame>) -> Result<ReceivedFrame, Error> {
use crate::frame::Frame::*;
match frame {
Some(Headers(frame)) => {
tracing::trace!(?frame, "recv HEADERS");
self.streams.recv_headers(frame)?;
}
Some(Data(frame)) => {
tracing::trace!(?frame, "recv DATA");
self.streams.recv_data(frame)?;
}
Some(Reset(frame)) => {
tracing::trace!(?frame, "recv RST_STREAM");
self.streams.recv_reset(frame)?;
}
Some(PushPromise(frame)) => {
tracing::trace!(?frame, "recv PUSH_PROMISE");
self.streams.recv_push_promise(frame)?;
}
Some(Settings(frame)) => {
tracing::trace!(?frame, "recv SETTINGS");
return Ok(ReceivedFrame::Settings(frame));
}
Some(GoAway(frame)) => {
tracing::trace!(?frame, "recv GOAWAY");
// This should prevent starting new streams,
// but should allow continuing to process current streams
// until they are all EOS. Once they are, State should
// transition to GoAway.
self.streams.recv_go_away(&frame)?;
*self.error = Some(frame);
}
Some(Ping(frame)) => {
tracing::trace!(?frame, "recv PING");
let status = self.ping_pong.recv_ping(frame);
if status.is_shutdown() {
assert!(
self.go_away.is_going_away(),
"received unexpected shutdown ping"
);
let last_processed_id = self.streams.last_processed_id();
self.go_away(last_processed_id, Reason::NO_ERROR);
}
}
Some(WindowUpdate(frame)) => {
tracing::trace!(?frame, "recv WINDOW_UPDATE");
self.streams.recv_window_update(frame)?;
}
Some(Priority(frame)) => {
tracing::trace!(?frame, "recv PRIORITY");
// TODO: handle
}
None => {
tracing::trace!("codec closed");
self.streams.recv_eof(false).expect("mutex poisoned");
return Ok(ReceivedFrame::Done);
}
}
Ok(ReceivedFrame::Continue)
}
}
enum ReceivedFrame {
Settings(frame::Settings),
Continue,
Done,
}
impl<T, B> Connection<T, client::Peer, B>
where
T: AsyncRead + AsyncWrite,
B: Buf,
{
pub(crate) fn streams(&self) -> &Streams<B, client::Peer> {
&self.inner.streams
}
}
impl<T, B> Connection<T, server::Peer, B>
where
T: AsyncRead + AsyncWrite + Unpin,
B: Buf,
{
pub fn next_incoming(&mut self) -> Option<StreamRef<B>> {
self.inner.streams.next_incoming()
}
// Graceful shutdown only makes sense for server peers.
pub fn go_away_gracefully(&mut self) {
if self.inner.go_away.is_going_away() {
// No reason to start a new one.
return;
}
// According to http://httpwg.org/specs/rfc7540.html#GOAWAY:
//
// > A server that is attempting to gracefully shut down a connection
// > SHOULD send an initial GOAWAY frame with the last stream
// > identifier set to 2^31-1 and a NO_ERROR code. This signals to the
// > client that a shutdown is imminent and that initiating further
// > requests is prohibited. After allowing time for any in-flight
// > stream creation (at least one round-trip time), the server can
// > send another GOAWAY frame with an updated last stream identifier.
// > This ensures that a connection can be cleanly shut down without
// > losing requests.
self.inner.as_dyn().go_away(StreamId::MAX, Reason::NO_ERROR);
// We take the advice of waiting 1 RTT literally, and wait
// for a pong before proceeding.
self.inner.ping_pong.ping_shutdown();
}
}
impl<T, P, B> Drop for Connection<T, P, B>
where
P: Peer,
B: Buf,
{
fn drop(&mut self) {
// Ignore errors as this indicates that the mutex is poisoned.
let _ = self.inner.streams.recv_eof(true);
}
}

100
vendor/h2/src/proto/error.rs vendored Normal file
View File

@@ -0,0 +1,100 @@
use crate::codec::SendError;
use crate::frame::{Reason, StreamId};
use bytes::Bytes;
use std::fmt;
use std::io;
/// Either an H2 reason or an I/O error
#[derive(Clone, Debug)]
pub enum Error {
Reset(StreamId, Reason, Initiator),
GoAway(Bytes, Reason, Initiator),
Io(io::ErrorKind, Option<String>),
}
pub struct GoAway {
pub debug_data: Bytes,
pub reason: Reason,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Initiator {
User,
Library,
Remote,
}
impl Error {
pub(crate) fn is_local(&self) -> bool {
match *self {
Self::Reset(_, _, initiator) | Self::GoAway(_, _, initiator) => initiator.is_local(),
Self::Io(..) => true,
}
}
pub(crate) fn user_go_away(reason: Reason) -> Self {
Self::GoAway(Bytes::new(), reason, Initiator::User)
}
pub(crate) fn library_reset(stream_id: StreamId, reason: Reason) -> Self {
Self::Reset(stream_id, reason, Initiator::Library)
}
pub(crate) fn library_go_away(reason: Reason) -> Self {
Self::GoAway(Bytes::new(), reason, Initiator::Library)
}
pub(crate) fn library_go_away_data(reason: Reason, debug_data: impl Into<Bytes>) -> Self {
Self::GoAway(debug_data.into(), reason, Initiator::Library)
}
pub(crate) fn remote_reset(stream_id: StreamId, reason: Reason) -> Self {
Self::Reset(stream_id, reason, Initiator::Remote)
}
pub(crate) fn remote_go_away(debug_data: Bytes, reason: Reason) -> Self {
Self::GoAway(debug_data, reason, Initiator::Remote)
}
}
impl Initiator {
fn is_local(&self) -> bool {
match *self {
Self::User | Self::Library => true,
Self::Remote => false,
}
}
pub(crate) fn is_library(&self) -> bool {
matches!(self, Self::Library)
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Reset(_, reason, _) | Self::GoAway(_, reason, _) => reason.fmt(fmt),
Self::Io(_, Some(ref inner)) => inner.fmt(fmt),
Self::Io(kind, None) => io::Error::from(kind).fmt(fmt),
}
}
}
impl From<io::ErrorKind> for Error {
fn from(src: io::ErrorKind) -> Self {
Error::Io(src, None)
}
}
impl From<io::Error> for Error {
fn from(src: io::Error) -> Self {
Error::Io(src.kind(), src.get_ref().map(|inner| inner.to_string()))
}
}
impl From<Error> for SendError {
fn from(src: Error) -> Self {
Self::Connection(src)
}
}

154
vendor/h2/src/proto/go_away.rs vendored Normal file
View File

@@ -0,0 +1,154 @@
use crate::codec::Codec;
use crate::frame::{self, Reason, StreamId};
use bytes::Buf;
use std::io;
use std::task::{Context, Poll};
use tokio::io::AsyncWrite;
/// Manages our sending of GOAWAY frames.
#[derive(Debug)]
pub(super) struct GoAway {
/// Whether the connection should close now, or wait until idle.
close_now: bool,
/// Records if we've sent any GOAWAY before.
going_away: Option<GoingAway>,
/// Whether the user started the GOAWAY by calling `abrupt_shutdown`.
is_user_initiated: bool,
/// A GOAWAY frame that must be buffered in the Codec immediately.
pending: Option<frame::GoAway>,
}
/// Keeps a memory of any GOAWAY frames we've sent before.
///
/// This looks very similar to a `frame::GoAway`, but is a separate type. Why?
/// Mostly for documentation purposes. This type is to record status. If it
/// were a `frame::GoAway`, it might appear like we eventually wanted to
/// serialize it. We **only** want to be able to look up these fields at a
/// later time.
#[derive(Debug)]
pub(crate) struct GoingAway {
/// Stores the highest stream ID of a GOAWAY that has been sent.
///
/// It's illegal to send a subsequent GOAWAY with a higher ID.
last_processed_id: StreamId,
/// Records the error code of any GOAWAY frame sent.
reason: Reason,
}
impl GoAway {
pub fn new() -> Self {
GoAway {
close_now: false,
going_away: None,
is_user_initiated: false,
pending: None,
}
}
/// Enqueue a GOAWAY frame to be written.
///
/// The connection is expected to continue to run until idle.
pub fn go_away(&mut self, f: frame::GoAway) {
if let Some(ref going_away) = self.going_away {
assert!(
f.last_stream_id() <= going_away.last_processed_id,
"GOAWAY stream IDs shouldn't be higher; \
last_processed_id = {:?}, f.last_stream_id() = {:?}",
going_away.last_processed_id,
f.last_stream_id(),
);
}
self.going_away = Some(GoingAway {
last_processed_id: f.last_stream_id(),
reason: f.reason(),
});
self.pending = Some(f);
}
pub fn go_away_now(&mut self, f: frame::GoAway) {
self.close_now = true;
if let Some(ref going_away) = self.going_away {
// Prevent sending the same GOAWAY twice.
if going_away.last_processed_id == f.last_stream_id() && going_away.reason == f.reason()
{
return;
}
}
self.go_away(f);
}
pub fn go_away_from_user(&mut self, f: frame::GoAway) {
self.is_user_initiated = true;
self.go_away_now(f);
}
/// Return if a GOAWAY has ever been scheduled.
pub fn is_going_away(&self) -> bool {
self.going_away.is_some()
}
pub fn is_user_initiated(&self) -> bool {
self.is_user_initiated
}
/// Returns the going away info, if any.
pub fn going_away(&self) -> Option<&GoingAway> {
self.going_away.as_ref()
}
/// Returns if the connection should close now, or wait until idle.
pub fn should_close_now(&self) -> bool {
self.pending.is_none() && self.close_now
}
/// Returns if the connection should be closed when idle.
pub fn should_close_on_idle(&self) -> bool {
!self.close_now
&& self
.going_away
.as_ref()
.map(|g| g.last_processed_id != StreamId::MAX)
.unwrap_or(false)
}
/// Try to write a pending GOAWAY frame to the buffer.
///
/// If a frame is written, the `Reason` of the GOAWAY is returned.
pub fn send_pending_go_away<T, B>(
&mut self,
cx: &mut Context,
dst: &mut Codec<T, B>,
) -> Poll<Option<io::Result<Reason>>>
where
T: AsyncWrite + Unpin,
B: Buf,
{
if let Some(frame) = self.pending.take() {
if !dst.poll_ready(cx)?.is_ready() {
self.pending = Some(frame);
return Poll::Pending;
}
let reason = frame.reason();
dst.buffer(frame.into()).expect("invalid GOAWAY frame");
return Poll::Ready(Some(Ok(reason)));
} else if self.should_close_now() {
return match self.going_away().map(|going_away| going_away.reason) {
Some(reason) => Poll::Ready(Some(Ok(reason))),
None => Poll::Ready(None),
};
}
Poll::Ready(None)
}
}
impl GoingAway {
pub(crate) fn reason(&self) -> Reason {
self.reason
}
}

42
vendor/h2/src/proto/mod.rs vendored Normal file
View File

@@ -0,0 +1,42 @@
mod connection;
mod error;
mod go_away;
mod peer;
mod ping_pong;
mod settings;
mod streams;
pub(crate) use self::connection::{Config, Connection};
pub use self::error::{Error, Initiator};
pub(crate) use self::peer::{Dyn as DynPeer, Peer};
pub(crate) use self::ping_pong::UserPings;
pub(crate) use self::streams::{DynStreams, OpaqueStreamRef, StreamRef, Streams};
pub(crate) use self::streams::{Open, PollReset, Prioritized};
use crate::codec::Codec;
use self::go_away::GoAway;
use self::ping_pong::PingPong;
use self::settings::Settings;
use crate::frame::{self, Frame};
use bytes::Buf;
use tokio::io::AsyncWrite;
pub type PingPayload = [u8; 8];
pub type WindowSize = u32;
// Constants
pub const MAX_WINDOW_SIZE: WindowSize = (1 << 31) - 1; // i32::MAX as u32
pub const DEFAULT_REMOTE_RESET_STREAM_MAX: usize = 20;
pub const DEFAULT_LOCAL_RESET_COUNT_MAX: usize = 1024;
// RFC 9113 suggests allowing at minimum 100 streams, it seems reasonable to
// by default allow a portion of that to be remembered as reset for some time.
pub const DEFAULT_RESET_STREAM_MAX: usize = 50;
// RFC 9113#5.4.2 suggests ~1 RTT. We don't track that closely, but use a
// reasonable guess of the average here.
pub const DEFAULT_RESET_STREAM_SECS: u64 = 1;
pub const DEFAULT_MAX_SEND_BUFFER_SIZE: usize = 1024 * 400;

95
vendor/h2/src/proto/peer.rs vendored Normal file
View File

@@ -0,0 +1,95 @@
use crate::error::Reason;
use crate::frame::{Pseudo, StreamId};
use crate::proto::{Error, Open};
use http::{HeaderMap, Request, Response};
use std::fmt;
/// Either a Client or a Server
pub(crate) trait Peer {
/// Message type polled from the transport
type Poll: fmt::Debug;
const NAME: &'static str;
fn r#dyn() -> Dyn;
//fn is_server() -> bool;
fn convert_poll_message(
pseudo: Pseudo,
fields: HeaderMap,
stream_id: StreamId,
) -> Result<Self::Poll, Error>;
/*
fn is_local_init(id: StreamId) -> bool {
assert!(!id.is_zero());
Self::is_server() == id.is_server_initiated()
}
*/
}
/// A dynamic representation of `Peer`.
///
/// This is used internally to avoid incurring a generic on all internal types.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum Dyn {
Client,
Server,
}
#[derive(Debug)]
pub enum PollMessage {
Client(Response<()>),
Server(Request<()>),
}
// ===== impl Dyn =====
impl Dyn {
pub fn is_server(&self) -> bool {
*self == Dyn::Server
}
pub fn is_local_init(&self, id: StreamId) -> bool {
assert!(!id.is_zero());
self.is_server() == id.is_server_initiated()
}
pub fn convert_poll_message(
&self,
pseudo: Pseudo,
fields: HeaderMap,
stream_id: StreamId,
) -> Result<PollMessage, Error> {
if self.is_server() {
crate::server::Peer::convert_poll_message(pseudo, fields, stream_id)
.map(PollMessage::Server)
} else {
crate::client::Peer::convert_poll_message(pseudo, fields, stream_id)
.map(PollMessage::Client)
}
}
/// Returns true if the remote peer can initiate a stream with the given ID.
pub fn ensure_can_open(&self, id: StreamId, mode: Open) -> Result<(), Error> {
if self.is_server() {
// Ensure that the ID is a valid client initiated ID
if mode.is_push_promise() || !id.is_client_initiated() {
proto_err!(conn: "cannot open stream {:?} - not client initiated", id);
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
Ok(())
} else {
// Ensure that the ID is a valid server initiated ID
if !mode.is_push_promise() || !id.is_server_initiated() {
proto_err!(conn: "cannot open stream {:?} - not server initiated", id);
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
Ok(())
}
}
}

291
vendor/h2/src/proto/ping_pong.rs vendored Normal file
View File

@@ -0,0 +1,291 @@
use crate::codec::Codec;
use crate::frame::Ping;
use crate::proto::{self, PingPayload};
use atomic_waker::AtomicWaker;
use bytes::Buf;
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::io::AsyncWrite;
/// Acknowledges ping requests from the remote.
#[derive(Debug)]
pub(crate) struct PingPong {
pending_ping: Option<PendingPing>,
pending_pong: Option<PingPayload>,
user_pings: Option<UserPingsRx>,
}
#[derive(Debug)]
pub(crate) struct UserPings(Arc<UserPingsInner>);
#[derive(Debug)]
struct UserPingsRx(Arc<UserPingsInner>);
#[derive(Debug)]
struct UserPingsInner {
state: AtomicUsize,
/// Task to wake up the main `Connection`.
ping_task: AtomicWaker,
/// Task to wake up `share::PingPong::poll_pong`.
pong_task: AtomicWaker,
}
#[derive(Debug)]
struct PendingPing {
payload: PingPayload,
sent: bool,
}
/// Status returned from `PingPong::recv_ping`.
#[derive(Debug)]
pub(crate) enum ReceivedPing {
MustAck,
Unknown,
Shutdown,
}
/// No user ping pending.
const USER_STATE_EMPTY: usize = 0;
/// User has called `send_ping`, but PING hasn't been written yet.
const USER_STATE_PENDING_PING: usize = 1;
/// User PING has been written, waiting for PONG.
const USER_STATE_PENDING_PONG: usize = 2;
/// We've received user PONG, waiting for user to `poll_pong`.
const USER_STATE_RECEIVED_PONG: usize = 3;
/// The connection is closed.
const USER_STATE_CLOSED: usize = 4;
// ===== impl PingPong =====
impl PingPong {
pub(crate) fn new() -> Self {
PingPong {
pending_ping: None,
pending_pong: None,
user_pings: None,
}
}
/// Can only be called once. If called a second time, returns `None`.
pub(crate) fn take_user_pings(&mut self) -> Option<UserPings> {
if self.user_pings.is_some() {
return None;
}
let user_pings = Arc::new(UserPingsInner {
state: AtomicUsize::new(USER_STATE_EMPTY),
ping_task: AtomicWaker::new(),
pong_task: AtomicWaker::new(),
});
self.user_pings = Some(UserPingsRx(user_pings.clone()));
Some(UserPings(user_pings))
}
pub(crate) fn ping_shutdown(&mut self) {
assert!(self.pending_ping.is_none());
self.pending_ping = Some(PendingPing {
payload: Ping::SHUTDOWN,
sent: false,
});
}
/// Process a ping
pub(crate) fn recv_ping(&mut self, ping: Ping) -> ReceivedPing {
// The caller should always check that `send_pongs` returns ready before
// calling `recv_ping`.
assert!(self.pending_pong.is_none());
if ping.is_ack() {
if let Some(pending) = self.pending_ping.take() {
if &pending.payload == ping.payload() {
assert_eq!(
&pending.payload,
&Ping::SHUTDOWN,
"pending_ping should be for shutdown",
);
tracing::trace!("recv PING SHUTDOWN ack");
return ReceivedPing::Shutdown;
}
// if not the payload we expected, put it back.
self.pending_ping = Some(pending);
}
if let Some(ref users) = self.user_pings {
if ping.payload() == &Ping::USER && users.receive_pong() {
tracing::trace!("recv PING USER ack");
return ReceivedPing::Unknown;
}
}
// else we were acked a ping we didn't send?
// The spec doesn't require us to do anything about this,
// so for resiliency, just ignore it for now.
tracing::warn!("recv PING ack that we never sent: {:?}", ping);
ReceivedPing::Unknown
} else {
// Save the ping's payload to be sent as an acknowledgement.
self.pending_pong = Some(ping.into_payload());
ReceivedPing::MustAck
}
}
/// Send any pending pongs.
pub(crate) fn send_pending_pong<T, B>(
&mut self,
cx: &mut Context,
dst: &mut Codec<T, B>,
) -> Poll<io::Result<()>>
where
T: AsyncWrite + Unpin,
B: Buf,
{
if let Some(pong) = self.pending_pong.take() {
if !dst.poll_ready(cx)?.is_ready() {
self.pending_pong = Some(pong);
return Poll::Pending;
}
dst.buffer(Ping::pong(pong).into())
.expect("invalid pong frame");
}
Poll::Ready(Ok(()))
}
/// Send any pending pings.
pub(crate) fn send_pending_ping<T, B>(
&mut self,
cx: &mut Context,
dst: &mut Codec<T, B>,
) -> Poll<io::Result<()>>
where
T: AsyncWrite + Unpin,
B: Buf,
{
if let Some(ref mut ping) = self.pending_ping {
if !ping.sent {
if !dst.poll_ready(cx)?.is_ready() {
return Poll::Pending;
}
dst.buffer(Ping::new(ping.payload).into())
.expect("invalid ping frame");
ping.sent = true;
}
} else if let Some(ref users) = self.user_pings {
if users.0.state.load(Ordering::Acquire) == USER_STATE_PENDING_PING {
if !dst.poll_ready(cx)?.is_ready() {
return Poll::Pending;
}
dst.buffer(Ping::new(Ping::USER).into())
.expect("invalid ping frame");
users
.0
.state
.store(USER_STATE_PENDING_PONG, Ordering::Release);
} else {
users.0.ping_task.register(cx.waker());
}
}
Poll::Ready(Ok(()))
}
}
impl ReceivedPing {
pub(crate) fn is_shutdown(&self) -> bool {
matches!(*self, Self::Shutdown)
}
}
// ===== impl UserPings =====
impl UserPings {
pub(crate) fn send_ping(&self) -> Result<(), Option<proto::Error>> {
let prev = self
.0
.state
.compare_exchange(
USER_STATE_EMPTY, // current
USER_STATE_PENDING_PING, // new
Ordering::AcqRel,
Ordering::Acquire,
)
.unwrap_or_else(|v| v);
match prev {
USER_STATE_EMPTY => {
self.0.ping_task.wake();
Ok(())
}
USER_STATE_CLOSED => Err(Some(broken_pipe().into())),
_ => {
// Was already pending, user error!
Err(None)
}
}
}
pub(crate) fn poll_pong(&self, cx: &mut Context) -> Poll<Result<(), proto::Error>> {
// Must register before checking state, in case state were to change
// before we could register, and then the ping would just be lost.
self.0.pong_task.register(cx.waker());
let prev = self
.0
.state
.compare_exchange(
USER_STATE_RECEIVED_PONG, // current
USER_STATE_EMPTY, // new
Ordering::AcqRel,
Ordering::Acquire,
)
.unwrap_or_else(|v| v);
match prev {
USER_STATE_RECEIVED_PONG => Poll::Ready(Ok(())),
USER_STATE_CLOSED => Poll::Ready(Err(broken_pipe().into())),
_ => Poll::Pending,
}
}
}
// ===== impl UserPingsRx =====
impl UserPingsRx {
fn receive_pong(&self) -> bool {
let prev = self
.0
.state
.compare_exchange(
USER_STATE_PENDING_PONG, // current
USER_STATE_RECEIVED_PONG, // new
Ordering::AcqRel,
Ordering::Acquire,
)
.unwrap_or_else(|v| v);
if prev == USER_STATE_PENDING_PONG {
self.0.pong_task.wake();
true
} else {
false
}
}
}
impl Drop for UserPingsRx {
fn drop(&mut self) {
self.0.state.store(USER_STATE_CLOSED, Ordering::Release);
self.0.pong_task.wake();
}
}
fn broken_pipe() -> io::Error {
io::ErrorKind::BrokenPipe.into()
}

168
vendor/h2/src/proto/settings.rs vendored Normal file
View File

@@ -0,0 +1,168 @@
use crate::codec::UserError;
use crate::error::Reason;
use crate::proto::*;
use std::task::{Context, Poll};
#[derive(Debug)]
pub(crate) struct Settings {
/// Our local SETTINGS sync state with the remote.
local: Local,
/// Received SETTINGS frame pending processing. The ACK must be written to
/// the socket first then the settings applied **before** receiving any
/// further frames.
remote: Option<frame::Settings>,
/// Whether the connection has received the initial SETTINGS frame from the
/// remote peer.
has_received_remote_initial_settings: bool,
}
#[derive(Debug)]
enum Local {
/// We want to send these SETTINGS to the remote when the socket is ready.
ToSend(frame::Settings),
/// We have sent these SETTINGS and are waiting for the remote to ACK
/// before we apply them.
WaitingAck(frame::Settings),
/// Our local settings are in sync with the remote.
Synced,
}
impl Settings {
pub(crate) fn new(local: frame::Settings) -> Self {
Settings {
// We assume the initial local SETTINGS were flushed during
// the handshake process.
local: Local::WaitingAck(local),
remote: None,
has_received_remote_initial_settings: false,
}
}
pub(crate) fn recv_settings<T, B, C, P>(
&mut self,
frame: frame::Settings,
codec: &mut Codec<T, B>,
streams: &mut Streams<C, P>,
) -> Result<(), Error>
where
T: AsyncWrite + Unpin,
B: Buf,
C: Buf,
P: Peer,
{
if frame.is_ack() {
match &self.local {
Local::WaitingAck(local) => {
tracing::debug!("received settings ACK; applying {:?}", local);
if let Some(max) = local.max_frame_size() {
codec.set_max_recv_frame_size(max as usize);
}
if let Some(max) = local.max_header_list_size() {
codec.set_max_recv_header_list_size(max as usize);
}
if let Some(val) = local.header_table_size() {
codec.set_recv_header_table_size(val as usize);
}
streams.apply_local_settings(local)?;
self.local = Local::Synced;
Ok(())
}
Local::ToSend(..) | Local::Synced => {
// We haven't sent any SETTINGS frames to be ACKed, so
// this is very bizarre! Remote is either buggy or malicious.
proto_err!(conn: "received unexpected settings ack");
Err(Error::library_go_away(Reason::PROTOCOL_ERROR))
}
}
} else {
// We always ACK before reading more frames, so `remote` should
// always be none!
assert!(self.remote.is_none());
self.remote = Some(frame);
Ok(())
}
}
pub(crate) fn send_settings(&mut self, frame: frame::Settings) -> Result<(), UserError> {
assert!(!frame.is_ack());
match &self.local {
Local::ToSend(..) | Local::WaitingAck(..) => Err(UserError::SendSettingsWhilePending),
Local::Synced => {
tracing::trace!("queue to send local settings: {:?}", frame);
self.local = Local::ToSend(frame);
Ok(())
}
}
}
/// Sets `true` to `self.has_received_remote_initial_settings`.
/// Returns `true` if this method is called for the first time.
/// (i.e. it is the initial SETTINGS frame from the remote peer)
fn mark_remote_initial_settings_as_received(&mut self) -> bool {
let has_received = self.has_received_remote_initial_settings;
self.has_received_remote_initial_settings = true;
!has_received
}
pub(crate) fn poll_send<T, B, C, P>(
&mut self,
cx: &mut Context,
dst: &mut Codec<T, B>,
streams: &mut Streams<C, P>,
) -> Poll<Result<(), Error>>
where
T: AsyncWrite + Unpin,
B: Buf,
C: Buf,
P: Peer,
{
if let Some(settings) = self.remote.clone() {
if !dst.poll_ready(cx)?.is_ready() {
return Poll::Pending;
}
// Create an ACK settings frame
let frame = frame::Settings::ack();
// Buffer the settings frame
dst.buffer(frame.into()).expect("invalid settings frame");
tracing::trace!("ACK sent; applying settings");
let is_initial = self.mark_remote_initial_settings_as_received();
streams.apply_remote_settings(&settings, is_initial)?;
if let Some(val) = settings.header_table_size() {
dst.set_send_header_table_size(val as usize);
}
if let Some(val) = settings.max_frame_size() {
dst.set_max_send_frame_size(val as usize);
}
}
self.remote = None;
match &self.local {
Local::ToSend(settings) => {
if !dst.poll_ready(cx)?.is_ready() {
return Poll::Pending;
}
// Buffer the settings frame
dst.buffer(settings.clone().into())
.expect("invalid settings frame");
tracing::trace!("local settings sent; waiting for ack: {:?}", settings);
self.local = Local::WaitingAck(settings.clone());
}
Local::WaitingAck(..) | Local::Synced => {}
}
Poll::Ready(Ok(()))
}
}

99
vendor/h2/src/proto/streams/buffer.rs vendored Normal file
View File

@@ -0,0 +1,99 @@
use slab::Slab;
/// Buffers frames for multiple streams.
#[derive(Debug)]
pub struct Buffer<T> {
slab: Slab<Slot<T>>,
}
/// A sequence of frames in a `Buffer`
#[derive(Debug)]
pub struct Deque {
indices: Option<Indices>,
}
/// Tracks the head & tail for a sequence of frames in a `Buffer`.
#[derive(Debug, Default, Copy, Clone)]
struct Indices {
head: usize,
tail: usize,
}
#[derive(Debug)]
struct Slot<T> {
value: T,
next: Option<usize>,
}
impl<T> Buffer<T> {
pub fn new() -> Self {
Buffer { slab: Slab::new() }
}
pub fn is_empty(&self) -> bool {
self.slab.is_empty()
}
}
impl Deque {
pub fn new() -> Self {
Deque { indices: None }
}
pub fn is_empty(&self) -> bool {
self.indices.is_none()
}
pub fn push_back<T>(&mut self, buf: &mut Buffer<T>, value: T) {
let key = buf.slab.insert(Slot { value, next: None });
match self.indices {
Some(ref mut idxs) => {
buf.slab[idxs.tail].next = Some(key);
idxs.tail = key;
}
None => {
self.indices = Some(Indices {
head: key,
tail: key,
});
}
}
}
pub fn push_front<T>(&mut self, buf: &mut Buffer<T>, value: T) {
let key = buf.slab.insert(Slot { value, next: None });
match self.indices {
Some(ref mut idxs) => {
buf.slab[key].next = Some(idxs.head);
idxs.head = key;
}
None => {
self.indices = Some(Indices {
head: key,
tail: key,
});
}
}
}
pub fn pop_front<T>(&mut self, buf: &mut Buffer<T>) -> Option<T> {
match self.indices {
Some(mut idxs) => {
let mut slot = buf.slab.remove(idxs.head);
if idxs.head == idxs.tail {
assert!(slot.next.is_none());
self.indices = None;
} else {
idxs.head = slot.next.take().unwrap();
self.indices = Some(idxs);
}
Some(slot.value)
}
None => None,
}
}
}

285
vendor/h2/src/proto/streams/counts.rs vendored Normal file
View File

@@ -0,0 +1,285 @@
use super::*;
#[derive(Debug)]
pub(super) struct Counts {
/// Acting as a client or server. This allows us to track which values to
/// inc / dec.
peer: peer::Dyn,
/// Maximum number of locally initiated streams
max_send_streams: usize,
/// Current number of remote initiated streams
num_send_streams: usize,
/// Maximum number of remote initiated streams
max_recv_streams: usize,
/// Current number of locally initiated streams
num_recv_streams: usize,
/// Maximum number of pending locally reset streams
max_local_reset_streams: usize,
/// Current number of pending locally reset streams
num_local_reset_streams: usize,
/// Max number of "pending accept" streams that were remotely reset
max_remote_reset_streams: usize,
/// Current number of "pending accept" streams that were remotely reset
num_remote_reset_streams: usize,
/// Maximum number of locally reset streams due to protocol error across
/// the lifetime of the connection.
///
/// When this gets exceeded, we issue GOAWAYs.
max_local_error_reset_streams: Option<usize>,
/// Total number of locally reset streams due to protocol error across the
/// lifetime of the connection.
num_local_error_reset_streams: usize,
}
impl Counts {
/// Create a new `Counts` using the provided configuration values.
pub fn new(peer: peer::Dyn, config: &Config) -> Self {
Counts {
peer,
max_send_streams: config.initial_max_send_streams,
num_send_streams: 0,
max_recv_streams: config.remote_max_initiated.unwrap_or(usize::MAX),
num_recv_streams: 0,
max_local_reset_streams: config.local_reset_max,
num_local_reset_streams: 0,
max_remote_reset_streams: config.remote_reset_max,
num_remote_reset_streams: 0,
max_local_error_reset_streams: config.local_max_error_reset_streams,
num_local_error_reset_streams: 0,
}
}
/// Returns true when the next opened stream will reach capacity of outbound streams
///
/// The number of client send streams is incremented in prioritize; send_request has to guess if
/// it should wait before allowing another request to be sent.
pub fn next_send_stream_will_reach_capacity(&self) -> bool {
self.max_send_streams <= (self.num_send_streams + 1)
}
/// Returns the current peer
pub fn peer(&self) -> peer::Dyn {
self.peer
}
pub fn has_streams(&self) -> bool {
self.num_send_streams != 0 || self.num_recv_streams != 0
}
/// Returns true if we can issue another local reset due to protocol error.
pub fn can_inc_num_local_error_resets(&self) -> bool {
if let Some(max) = self.max_local_error_reset_streams {
max > self.num_local_error_reset_streams
} else {
true
}
}
pub fn inc_num_local_error_resets(&mut self) {
assert!(self.can_inc_num_local_error_resets());
// Increment the number of remote initiated streams
self.num_local_error_reset_streams += 1;
}
pub(crate) fn max_local_error_resets(&self) -> Option<usize> {
self.max_local_error_reset_streams
}
/// Returns true if the receive stream concurrency can be incremented
pub fn can_inc_num_recv_streams(&self) -> bool {
self.max_recv_streams > self.num_recv_streams
}
/// Increments the number of concurrent receive streams.
///
/// # Panics
///
/// Panics on failure as this should have been validated before hand.
pub fn inc_num_recv_streams(&mut self, stream: &mut store::Ptr) {
assert!(self.can_inc_num_recv_streams());
assert!(!stream.is_counted);
// Increment the number of remote initiated streams
self.num_recv_streams += 1;
stream.is_counted = true;
}
/// Returns true if the send stream concurrency can be incremented
pub fn can_inc_num_send_streams(&self) -> bool {
self.max_send_streams > self.num_send_streams
}
/// Increments the number of concurrent send streams.
///
/// # Panics
///
/// Panics on failure as this should have been validated before hand.
pub fn inc_num_send_streams(&mut self, stream: &mut store::Ptr) {
assert!(self.can_inc_num_send_streams());
assert!(!stream.is_counted);
// Increment the number of remote initiated streams
self.num_send_streams += 1;
stream.is_counted = true;
}
/// Returns true if the number of pending reset streams can be incremented.
pub fn can_inc_num_reset_streams(&self) -> bool {
self.max_local_reset_streams > self.num_local_reset_streams
}
/// Increments the number of pending reset streams.
///
/// # Panics
///
/// Panics on failure as this should have been validated before hand.
pub fn inc_num_reset_streams(&mut self) {
assert!(self.can_inc_num_reset_streams());
self.num_local_reset_streams += 1;
}
pub(crate) fn max_remote_reset_streams(&self) -> usize {
self.max_remote_reset_streams
}
/// Returns true if the number of pending REMOTE reset streams can be
/// incremented.
pub(crate) fn can_inc_num_remote_reset_streams(&self) -> bool {
self.max_remote_reset_streams > self.num_remote_reset_streams
}
/// Increments the number of pending REMOTE reset streams.
///
/// # Panics
///
/// Panics on failure as this should have been validated before hand.
pub(crate) fn inc_num_remote_reset_streams(&mut self) {
assert!(self.can_inc_num_remote_reset_streams());
self.num_remote_reset_streams += 1;
}
pub(crate) fn dec_num_remote_reset_streams(&mut self) {
assert!(self.num_remote_reset_streams > 0);
self.num_remote_reset_streams -= 1;
}
pub fn apply_remote_settings(&mut self, settings: &frame::Settings, is_initial: bool) {
match settings.max_concurrent_streams() {
Some(val) => self.max_send_streams = val as usize,
None if is_initial => self.max_send_streams = usize::MAX,
None => {}
}
}
/// Run a block of code that could potentially transition a stream's state.
///
/// If the stream state transitions to closed, this function will perform
/// all necessary cleanup.
///
/// TODO: Is this function still needed?
pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
where
F: FnOnce(&mut Self, &mut store::Ptr) -> U,
{
// TODO: Does this need to be computed before performing the action?
let is_pending_reset = stream.is_pending_reset_expiration();
// Run the action
let ret = f(self, &mut stream);
self.transition_after(stream, is_pending_reset);
ret
}
// TODO: move this to macro?
pub fn transition_after(&mut self, mut stream: store::Ptr, is_reset_counted: bool) {
tracing::trace!(
"transition_after; stream={:?}; state={:?}; is_closed={:?}; \
pending_send_empty={:?}; buffered_send_data={}; \
num_recv={}; num_send={}",
stream.id,
stream.state,
stream.is_closed(),
stream.pending_send.is_empty(),
stream.buffered_send_data,
self.num_recv_streams,
self.num_send_streams
);
if stream.is_closed() {
if !stream.is_pending_reset_expiration() {
stream.unlink();
if is_reset_counted {
self.dec_num_reset_streams();
}
}
if !stream.state.is_scheduled_reset() && stream.is_counted {
tracing::trace!("dec_num_streams; stream={:?}", stream.id);
// Decrement the number of active streams.
self.dec_num_streams(&mut stream);
}
}
// Release the stream if it requires releasing
if stream.is_released() {
stream.remove();
}
}
/// Returns the maximum number of streams that can be initiated by this
/// peer.
pub(crate) fn max_send_streams(&self) -> usize {
self.max_send_streams
}
/// Returns the maximum number of streams that can be initiated by the
/// remote peer.
pub(crate) fn max_recv_streams(&self) -> usize {
self.max_recv_streams
}
fn dec_num_streams(&mut self, stream: &mut store::Ptr) {
assert!(stream.is_counted);
if self.peer.is_local_init(stream.id) {
assert!(self.num_send_streams > 0);
self.num_send_streams -= 1;
stream.is_counted = false;
} else {
assert!(self.num_recv_streams > 0);
self.num_recv_streams -= 1;
stream.is_counted = false;
}
}
fn dec_num_reset_streams(&mut self) {
assert!(self.num_local_reset_streams > 0);
self.num_local_reset_streams -= 1;
}
}
impl Drop for Counts {
fn drop(&mut self) {
use std::thread;
if !thread::panicking() {
debug_assert!(!self.has_streams());
}
}
}

View File

@@ -0,0 +1,269 @@
use crate::frame::Reason;
use crate::proto::{WindowSize, MAX_WINDOW_SIZE};
use std::fmt;
// We don't want to send WINDOW_UPDATE frames for tiny changes, but instead
// aggregate them when the changes are significant. Many implementations do
// this by keeping a "ratio" of the update version the allowed window size.
//
// While some may wish to represent this ratio as percentage, using a f32,
// we skip having to deal with float math and stick to integers. To do so,
// the "ratio" is represented by 2 i32s, split into the numerator and
// denominator. For example, a 50% ratio is simply represented as 1/2.
//
// An example applying this ratio: If a stream has an allowed window size of
// 100 bytes, WINDOW_UPDATE frames are scheduled when the unclaimed change
// becomes greater than 1/2, or 50 bytes.
const UNCLAIMED_NUMERATOR: i32 = 1;
const UNCLAIMED_DENOMINATOR: i32 = 2;
#[test]
#[allow(clippy::assertions_on_constants)]
fn sanity_unclaimed_ratio() {
assert!(UNCLAIMED_NUMERATOR < UNCLAIMED_DENOMINATOR);
assert!(UNCLAIMED_NUMERATOR >= 0);
assert!(UNCLAIMED_DENOMINATOR > 0);
}
#[derive(Copy, Clone, Debug)]
pub struct FlowControl {
/// Window the peer knows about.
///
/// This can go negative if a SETTINGS_INITIAL_WINDOW_SIZE is received.
///
/// For example, say the peer sends a request and uses 32kb of the window.
/// We send a SETTINGS_INITIAL_WINDOW_SIZE of 16kb. The peer has to adjust
/// its understanding of the capacity of the window, and that would be:
///
/// ```notrust
/// default (64kb) - used (32kb) - settings_diff (64kb - 16kb): -16kb
/// ```
window_size: Window,
/// Window that we know about.
///
/// This can go negative if a user declares a smaller target window than
/// the peer knows about.
available: Window,
}
impl FlowControl {
pub fn new() -> FlowControl {
FlowControl {
window_size: Window(0),
available: Window(0),
}
}
/// Returns the window size as known by the peer
pub fn window_size(&self) -> WindowSize {
self.window_size.as_size()
}
/// Returns the window size available to the consumer
pub fn available(&self) -> Window {
self.available
}
/// Returns true if there is unavailable window capacity
pub fn has_unavailable(&self) -> bool {
if self.window_size < 0 {
return false;
}
self.window_size > self.available
}
pub fn claim_capacity(&mut self, capacity: WindowSize) -> Result<(), Reason> {
self.available.decrease_by(capacity)
}
pub fn assign_capacity(&mut self, capacity: WindowSize) -> Result<(), Reason> {
self.available.increase_by(capacity)
}
/// If a WINDOW_UPDATE frame should be sent, returns a positive number
/// representing the increment to be used.
///
/// If there is no available bytes to be reclaimed, or the number of
/// available bytes does not reach the threshold, this returns `None`.
///
/// This represents pending outbound WINDOW_UPDATE frames.
pub fn unclaimed_capacity(&self) -> Option<WindowSize> {
let available = self.available;
if self.window_size >= available {
return None;
}
let unclaimed = available.0 - self.window_size.0;
let threshold = self.window_size.0 / UNCLAIMED_DENOMINATOR * UNCLAIMED_NUMERATOR;
if unclaimed < threshold {
None
} else {
Some(unclaimed as WindowSize)
}
}
/// Increase the window size.
///
/// This is called after receiving a WINDOW_UPDATE frame
pub fn inc_window(&mut self, sz: WindowSize) -> Result<(), Reason> {
let (val, overflow) = self.window_size.0.overflowing_add(sz as i32);
if overflow {
return Err(Reason::FLOW_CONTROL_ERROR);
}
if val > MAX_WINDOW_SIZE as i32 {
return Err(Reason::FLOW_CONTROL_ERROR);
}
tracing::trace!(
"inc_window; sz={}; old={}; new={}",
sz,
self.window_size,
val
);
self.window_size = Window(val);
Ok(())
}
/// Decrement the send-side window size.
///
/// This is called after receiving a SETTINGS frame with a lower
/// INITIAL_WINDOW_SIZE value.
pub fn dec_send_window(&mut self, sz: WindowSize) -> Result<(), Reason> {
tracing::trace!(
"dec_window; sz={}; window={}, available={}",
sz,
self.window_size,
self.available
);
// ~~This should not be able to overflow `window_size` from the bottom.~~ wrong. it can.
self.window_size.decrease_by(sz)?;
Ok(())
}
/// Decrement the recv-side window size.
///
/// This is called after receiving a SETTINGS ACK frame with a lower
/// INITIAL_WINDOW_SIZE value.
pub fn dec_recv_window(&mut self, sz: WindowSize) -> Result<(), Reason> {
tracing::trace!(
"dec_recv_window; sz={}; window={}, available={}",
sz,
self.window_size,
self.available
);
// This should not be able to overflow `window_size` from the bottom.
self.window_size.decrease_by(sz)?;
self.available.decrease_by(sz)?;
Ok(())
}
/// Decrements the window reflecting data has actually been sent. The caller
/// must ensure that the window has capacity.
pub fn send_data(&mut self, sz: WindowSize) -> Result<(), Reason> {
tracing::trace!(
"send_data; sz={}; window={}; available={}",
sz,
self.window_size,
self.available
);
// If send size is zero it's meaningless to update flow control window
if sz > 0 {
// Ensure that the argument is correct
assert!(self.window_size.0 >= sz as i32);
// Update values
self.window_size.decrease_by(sz)?;
self.available.decrease_by(sz)?;
}
Ok(())
}
}
/// The current capacity of a flow-controlled Window.
///
/// This number can go negative when either side has used a certain amount
/// of capacity when the other side advertises a reduction in size.
///
/// This type tries to centralize the knowledge of addition and subtraction
/// to this capacity, instead of having integer casts throughout the source.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
pub struct Window(i32);
impl Window {
pub fn as_size(&self) -> WindowSize {
if self.0 < 0 {
0
} else {
self.0 as WindowSize
}
}
pub fn checked_size(&self) -> WindowSize {
assert!(self.0 >= 0, "negative Window");
self.0 as WindowSize
}
pub fn decrease_by(&mut self, other: WindowSize) -> Result<(), Reason> {
if let Some(v) = self.0.checked_sub(other as i32) {
self.0 = v;
Ok(())
} else {
Err(Reason::FLOW_CONTROL_ERROR)
}
}
pub fn increase_by(&mut self, other: WindowSize) -> Result<(), Reason> {
let other = self.add(other)?;
self.0 = other.0;
Ok(())
}
pub fn add(&self, other: WindowSize) -> Result<Self, Reason> {
if let Some(v) = self.0.checked_add(other as i32) {
Ok(Self(v))
} else {
Err(Reason::FLOW_CONTROL_ERROR)
}
}
}
impl PartialEq<usize> for Window {
fn eq(&self, other: &usize) -> bool {
if self.0 < 0 {
false
} else {
(self.0 as usize).eq(other)
}
}
}
impl PartialOrd<usize> for Window {
fn partial_cmp(&self, other: &usize) -> Option<::std::cmp::Ordering> {
if self.0 < 0 {
Some(::std::cmp::Ordering::Less)
} else {
(self.0 as usize).partial_cmp(other)
}
}
}
impl fmt::Display for Window {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl From<Window> for isize {
fn from(w: Window) -> isize {
w.0 as isize
}
}

128
vendor/h2/src/proto/streams/mod.rs vendored Normal file
View File

@@ -0,0 +1,128 @@
mod buffer;
mod counts;
mod flow_control;
mod prioritize;
mod recv;
mod send;
mod state;
mod store;
mod stream;
#[allow(clippy::module_inception)]
mod streams;
pub(crate) use self::prioritize::Prioritized;
pub(crate) use self::recv::Open;
pub(crate) use self::send::PollReset;
pub(crate) use self::streams::{DynStreams, OpaqueStreamRef, StreamRef, Streams};
use self::buffer::Buffer;
use self::counts::Counts;
use self::flow_control::FlowControl;
use self::prioritize::Prioritize;
use self::recv::Recv;
use self::send::Send;
use self::state::State;
use self::store::Store;
use self::stream::Stream;
use crate::frame::{StreamId, StreamIdOverflow};
use crate::proto::*;
use bytes::Bytes;
use std::time::Duration;
#[derive(Debug)]
pub struct Config {
/// Initial maximum number of locally initiated streams.
/// After receiving a Settings frame from the remote peer,
/// the connection will overwrite this value with the
/// MAX_CONCURRENT_STREAMS specified in the frame.
pub initial_max_send_streams: usize,
/// Max amount of DATA bytes to buffer per stream.
pub local_max_buffer_size: usize,
/// The stream ID to start the next local stream with
pub local_next_stream_id: StreamId,
/// If the local peer is willing to receive push promises
pub local_push_enabled: bool,
/// If extended connect protocol is enabled.
pub extended_connect_protocol_enabled: bool,
/// How long a locally reset stream should ignore frames
pub local_reset_duration: Duration,
/// Maximum number of locally reset streams to keep at a time
pub local_reset_max: usize,
/// Maximum number of remotely reset "pending accept" streams to keep at a
/// time. Going over this number results in a connection error.
pub remote_reset_max: usize,
/// Initial window size of remote initiated streams
pub remote_init_window_sz: WindowSize,
/// Maximum number of remote initiated streams
pub remote_max_initiated: Option<usize>,
/// Maximum number of locally reset streams due to protocol error across
/// the lifetime of the connection.
///
/// When this gets exceeded, we issue GOAWAYs.
pub local_max_error_reset_streams: Option<usize>,
}
trait DebugStructExt<'a, 'b> {
// h2_ prefixes to protect against possible future name collisions
fn h2_field_if(&mut self, name: &str, val: &bool) -> &mut std::fmt::DebugStruct<'a, 'b>;
fn h2_field_if_then<T: std::fmt::Debug>(
&mut self,
name: &str,
cond: bool,
val: &T,
) -> &mut std::fmt::DebugStruct<'a, 'b>;
fn h2_field_some<T: std::fmt::Debug>(
&mut self,
name: &str,
val: &Option<T>,
) -> &mut std::fmt::DebugStruct<'a, 'b>;
}
impl<'a, 'b> DebugStructExt<'a, 'b> for std::fmt::DebugStruct<'a, 'b> {
fn h2_field_if(&mut self, name: &str, val: &bool) -> &mut std::fmt::DebugStruct<'a, 'b> {
if *val {
self.field(name, val)
} else {
self
}
}
fn h2_field_if_then<T: std::fmt::Debug>(
&mut self,
name: &str,
cond: bool,
val: &T,
) -> &mut std::fmt::DebugStruct<'a, 'b> {
if cond {
self.field(name, val)
} else {
self
}
}
fn h2_field_some<T: std::fmt::Debug>(
&mut self,
name: &str,
val: &Option<T>,
) -> &mut std::fmt::DebugStruct<'a, 'b> {
if val.is_some() {
self.field(name, val)
} else {
self
}
}
}

View File

@@ -0,0 +1,945 @@
use super::store::Resolve;
use super::*;
use crate::frame::Reason;
use crate::codec::UserError;
use crate::codec::UserError::*;
use bytes::buf::Take;
use std::{
cmp::{self, Ordering},
fmt, io, mem,
task::{Context, Poll, Waker},
};
/// # Warning
///
/// Queued streams are ordered by stream ID, as we need to ensure that
/// lower-numbered streams are sent headers before higher-numbered ones.
/// This is because "idle" stream IDs those which have been initiated but
/// have yet to receive frames will be implicitly closed on receipt of a
/// frame on a higher stream ID. If these queues was not ordered by stream
/// IDs, some mechanism would be necessary to ensure that the lowest-numbered]
/// idle stream is opened first.
#[derive(Debug)]
pub(super) struct Prioritize {
/// Queue of streams waiting for socket capacity to send a frame.
pending_send: store::Queue<stream::NextSend>,
/// Queue of streams waiting for window capacity to produce data.
pending_capacity: store::Queue<stream::NextSendCapacity>,
/// Streams waiting for capacity due to max concurrency
///
/// The `SendRequest` handle is `Clone`. This enables initiating requests
/// from many tasks. However, offering this capability while supporting
/// backpressure at some level is tricky. If there are many `SendRequest`
/// handles and a single stream becomes available, which handle gets
/// assigned that stream? Maybe that handle is no longer ready to send a
/// request.
///
/// The strategy used is to allow each `SendRequest` handle one buffered
/// request. A `SendRequest` handle is ready to send a request if it has no
/// associated buffered requests. This is the same strategy as `mpsc` in the
/// futures library.
pending_open: store::Queue<stream::NextOpen>,
/// Connection level flow control governing sent data
flow: FlowControl,
/// Stream ID of the last stream opened.
last_opened_id: StreamId,
/// What `DATA` frame is currently being sent in the codec.
in_flight_data_frame: InFlightData,
/// The maximum amount of bytes a stream should buffer.
max_buffer_size: usize,
}
#[derive(Debug, Eq, PartialEq)]
enum InFlightData {
/// There is no `DATA` frame in flight.
Nothing,
/// There is a `DATA` frame in flight belonging to the given stream.
DataFrame(store::Key),
/// There was a `DATA` frame, but the stream's queue was since cleared.
Drop,
}
pub(crate) struct Prioritized<B> {
// The buffer
inner: Take<B>,
end_of_stream: bool,
// The stream that this is associated with
stream: store::Key,
}
// ===== impl Prioritize =====
impl Prioritize {
pub fn new(config: &Config) -> Prioritize {
let mut flow = FlowControl::new();
flow.inc_window(config.remote_init_window_sz)
.expect("invalid initial window size");
// TODO: proper error handling
let _res = flow.assign_capacity(config.remote_init_window_sz);
debug_assert!(_res.is_ok());
tracing::trace!("Prioritize::new; flow={:?}", flow);
Prioritize {
pending_send: store::Queue::new(),
pending_capacity: store::Queue::new(),
pending_open: store::Queue::new(),
flow,
last_opened_id: StreamId::ZERO,
in_flight_data_frame: InFlightData::Nothing,
max_buffer_size: config.local_max_buffer_size,
}
}
pub(crate) fn max_buffer_size(&self) -> usize {
self.max_buffer_size
}
/// Queue a frame to be sent to the remote
pub fn queue_frame<B>(
&mut self,
frame: Frame<B>,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
task: &mut Option<Waker>,
) {
let span = tracing::trace_span!("Prioritize::queue_frame", ?stream.id);
let _e = span.enter();
// Queue the frame in the buffer
stream.pending_send.push_back(buffer, frame);
self.schedule_send(stream, task);
}
pub fn schedule_send(&mut self, stream: &mut store::Ptr, task: &mut Option<Waker>) {
// If the stream is waiting to be opened, nothing more to do.
if stream.is_send_ready() {
tracing::trace!(?stream.id, "schedule_send");
// Queue the stream
self.pending_send.push(stream);
// Notify the connection.
if let Some(task) = task.take() {
task.wake();
}
}
}
pub fn queue_open(&mut self, stream: &mut store::Ptr) {
self.pending_open.push(stream);
}
/// Send a data frame
pub fn send_data<B>(
&mut self,
frame: frame::Data<B>,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
counts: &mut Counts,
task: &mut Option<Waker>,
) -> Result<(), UserError>
where
B: Buf,
{
let sz = frame.payload().remaining();
if sz > MAX_WINDOW_SIZE as usize {
return Err(UserError::PayloadTooBig);
}
let sz = sz as WindowSize;
if !stream.state.is_send_streaming() {
if stream.state.is_closed() {
return Err(InactiveStreamId);
} else {
return Err(UnexpectedFrameType);
}
}
// Update the buffered data counter
stream.buffered_send_data += sz as usize;
let span =
tracing::trace_span!("send_data", sz, requested = stream.requested_send_capacity);
let _e = span.enter();
tracing::trace!(buffered = stream.buffered_send_data);
// Implicitly request more send capacity if not enough has been
// requested yet.
if (stream.requested_send_capacity as usize) < stream.buffered_send_data {
// Update the target requested capacity
stream.requested_send_capacity =
cmp::min(stream.buffered_send_data, WindowSize::MAX as usize) as WindowSize;
// `try_assign_capacity` will queue the stream to `pending_capacity` if the capcaity
// cannot be assigned at the time it is called.
self.try_assign_capacity(stream);
}
if frame.is_end_stream() {
stream.state.send_close();
self.reserve_capacity(0, stream, counts);
}
tracing::trace!(
available = %stream.send_flow.available(),
buffered = stream.buffered_send_data,
);
// The `stream.buffered_send_data == 0` check is here so that, if a zero
// length data frame is queued to the front (there is no previously
// queued data), it gets sent out immediately even if there is no
// available send window.
//
// Sending out zero length data frames can be done to signal
// end-of-stream.
//
if stream.send_flow.available() > 0 || stream.buffered_send_data == 0 {
// The stream currently has capacity to send the data frame, so
// queue it up and notify the connection task.
self.queue_frame(frame.into(), buffer, stream, task);
} else {
// The stream has no capacity to send the frame now, save it but
// don't notify the connection task. Once additional capacity
// becomes available, the frame will be flushed.
stream.pending_send.push_back(buffer, frame.into());
}
Ok(())
}
/// Request capacity to send data
pub fn reserve_capacity(
&mut self,
capacity: WindowSize,
stream: &mut store::Ptr,
counts: &mut Counts,
) {
let span = tracing::trace_span!(
"reserve_capacity",
?stream.id,
requested = capacity,
effective = (capacity as usize) + stream.buffered_send_data,
curr = stream.requested_send_capacity
);
let _e = span.enter();
// Actual capacity is `capacity` + the current amount of buffered data.
// If it were less, then we could never send out the buffered data.
let capacity = (capacity as usize) + stream.buffered_send_data;
match capacity.cmp(&(stream.requested_send_capacity as usize)) {
Ordering::Equal => {
// Nothing to do
}
Ordering::Less => {
// Update the target requested capacity
stream.requested_send_capacity = capacity as WindowSize;
// Currently available capacity assigned to the stream
let available = stream.send_flow.available().as_size();
// If the stream has more assigned capacity than requested, reclaim
// some for the connection
if available as usize > capacity {
let diff = available - capacity as WindowSize;
// TODO: proper error handling
let _res = stream.send_flow.claim_capacity(diff);
debug_assert!(_res.is_ok());
self.assign_connection_capacity(diff, stream, counts);
}
}
Ordering::Greater => {
// If trying to *add* capacity, but the stream send side is closed,
// there's nothing to be done.
if stream.state.is_send_closed() {
return;
}
// Update the target requested capacity
stream.requested_send_capacity =
cmp::min(capacity, WindowSize::MAX as usize) as WindowSize;
// Try to assign additional capacity to the stream. If none is
// currently available, the stream will be queued to receive some
// when more becomes available.
self.try_assign_capacity(stream);
}
}
}
pub fn recv_stream_window_update(
&mut self,
inc: WindowSize,
stream: &mut store::Ptr,
) -> Result<(), Reason> {
let span = tracing::trace_span!(
"recv_stream_window_update",
?stream.id,
?stream.state,
inc,
flow = ?stream.send_flow
);
let _e = span.enter();
if stream.state.is_send_closed() && stream.buffered_send_data == 0 {
// We can't send any data, so don't bother doing anything else.
return Ok(());
}
// Update the stream level flow control.
stream.send_flow.inc_window(inc)?;
// If the stream is waiting on additional capacity, then this will
// assign it (if available on the connection) and notify the producer
self.try_assign_capacity(stream);
Ok(())
}
pub fn recv_connection_window_update(
&mut self,
inc: WindowSize,
store: &mut Store,
counts: &mut Counts,
) -> Result<(), Reason> {
// Update the connection's window
self.flow.inc_window(inc)?;
self.assign_connection_capacity(inc, store, counts);
Ok(())
}
/// Reclaim all capacity assigned to the stream and re-assign it to the
/// connection
pub fn reclaim_all_capacity(&mut self, stream: &mut store::Ptr, counts: &mut Counts) {
let available = stream.send_flow.available().as_size();
if available > 0 {
// TODO: proper error handling
let _res = stream.send_flow.claim_capacity(available);
debug_assert!(_res.is_ok());
// Re-assign all capacity to the connection
self.assign_connection_capacity(available, stream, counts);
}
}
/// Reclaim just reserved capacity, not buffered capacity, and re-assign
/// it to the connection
pub fn reclaim_reserved_capacity(&mut self, stream: &mut store::Ptr, counts: &mut Counts) {
// only reclaim reserved capacity that isn't already buffered
if stream.send_flow.available().as_size() as usize > stream.buffered_send_data {
let reserved =
stream.send_flow.available().as_size() - stream.buffered_send_data as WindowSize;
// Panic safety: due to how `reserved` is computed it can't be greater
// than what's available.
stream
.send_flow
.claim_capacity(reserved)
.expect("window size should be greater than reserved");
self.assign_connection_capacity(reserved, stream, counts);
}
}
pub fn clear_pending_capacity(&mut self, store: &mut Store, counts: &mut Counts) {
let span = tracing::trace_span!("clear_pending_capacity");
let _e = span.enter();
while let Some(stream) = self.pending_capacity.pop(store) {
counts.transition(stream, |_, stream| {
tracing::trace!(?stream.id, "clear_pending_capacity");
})
}
}
pub fn assign_connection_capacity<R>(
&mut self,
inc: WindowSize,
store: &mut R,
counts: &mut Counts,
) where
R: Resolve,
{
let span = tracing::trace_span!("assign_connection_capacity", inc);
let _e = span.enter();
// TODO: proper error handling
let _res = self.flow.assign_capacity(inc);
debug_assert!(_res.is_ok());
// Assign newly acquired capacity to streams pending capacity.
while self.flow.available() > 0 {
let stream = match self.pending_capacity.pop(store) {
Some(stream) => stream,
None => return,
};
// Streams pending capacity may have been reset before capacity
// became available. In that case, the stream won't want any
// capacity, and so we shouldn't "transition" on it, but just evict
// it and continue the loop.
if !(stream.state.is_send_streaming() || stream.buffered_send_data > 0) {
continue;
}
counts.transition(stream, |_, stream| {
// Try to assign capacity to the stream. This will also re-queue the
// stream if there isn't enough connection level capacity to fulfill
// the capacity request.
self.try_assign_capacity(stream);
})
}
}
/// Request capacity to send data
fn try_assign_capacity(&mut self, stream: &mut store::Ptr) {
// Streams over the max concurrent count should not have capacity assign to avoid starving the connection
// capacity for open streams
if stream.is_pending_open {
return;
}
let total_requested = stream.requested_send_capacity;
// Total requested should never go below actual assigned
// (Note: the window size can go lower than assigned)
debug_assert!(stream.send_flow.available() <= total_requested as usize);
// The amount of additional capacity that the stream requests.
// Don't assign more than the window has available!
let additional = cmp::min(
total_requested - stream.send_flow.available().as_size(),
// Can't assign more than what is available
stream.send_flow.window_size() - stream.send_flow.available().as_size(),
);
let span = tracing::trace_span!("try_assign_capacity", ?stream.id);
let _e = span.enter();
tracing::trace!(
requested = total_requested,
additional,
buffered = stream.buffered_send_data,
window = stream.send_flow.window_size(),
conn = %self.flow.available()
);
if additional == 0 {
// Nothing more to do
return;
}
// If the stream has requested capacity, then it must be in the
// streaming state (more data could be sent) or there is buffered data
// waiting to be sent.
debug_assert!(
stream.state.is_send_streaming() || stream.buffered_send_data > 0,
"state={:?}",
stream.state
);
// The amount of currently available capacity on the connection
let conn_available = self.flow.available().as_size();
// First check if capacity is immediately available
if conn_available > 0 {
// The amount of capacity to assign to the stream
// TODO: Should prioritization factor into this?
let assign = cmp::min(conn_available, additional);
tracing::trace!(capacity = assign, "assigning");
// Assign the capacity to the stream
stream.assign_capacity(assign, self.max_buffer_size);
// Claim the capacity from the connection
// TODO: proper error handling
let _res = self.flow.claim_capacity(assign);
debug_assert!(_res.is_ok());
}
tracing::trace!(
available = %stream.send_flow.available(),
requested = stream.requested_send_capacity,
buffered = stream.buffered_send_data,
has_unavailable = %stream.send_flow.has_unavailable()
);
if stream.send_flow.available() < stream.requested_send_capacity as usize
&& stream.send_flow.has_unavailable()
{
// The stream requires additional capacity and the stream's
// window has available capacity, but the connection window
// does not.
//
// In this case, the stream needs to be queued up for when the
// connection has more capacity.
self.pending_capacity.push(stream);
}
// If data is buffered and the stream is send ready, then
// schedule the stream for execution
if stream.buffered_send_data > 0 && stream.is_send_ready() {
// TODO: This assertion isn't *exactly* correct. There can still be
// buffered send data while the stream's pending send queue is
// empty. This can happen when a large data frame is in the process
// of being **partially** sent. Once the window has been sent, the
// data frame will be returned to the prioritization layer to be
// re-scheduled.
//
// That said, it would be nice to figure out how to make this
// assertion correctly.
//
// debug_assert!(!stream.pending_send.is_empty());
self.pending_send.push(stream);
}
}
pub fn poll_complete<T, B>(
&mut self,
cx: &mut Context,
buffer: &mut Buffer<Frame<B>>,
store: &mut Store,
counts: &mut Counts,
dst: &mut Codec<T, Prioritized<B>>,
) -> Poll<io::Result<()>>
where
T: AsyncWrite + Unpin,
B: Buf,
{
// Ensure codec is ready
ready!(dst.poll_ready(cx))?;
// Reclaim any frame that has previously been written
self.reclaim_frame(buffer, store, dst);
// The max frame length
let max_frame_len = dst.max_send_frame_size();
tracing::trace!("poll_complete");
loop {
if let Some(mut stream) = self.pop_pending_open(store, counts) {
self.pending_send.push_front(&mut stream);
self.try_assign_capacity(&mut stream);
}
match self.pop_frame(buffer, store, max_frame_len, counts) {
Some(frame) => {
tracing::trace!(?frame, "writing");
debug_assert_eq!(self.in_flight_data_frame, InFlightData::Nothing);
if let Frame::Data(ref frame) = frame {
self.in_flight_data_frame = InFlightData::DataFrame(frame.payload().stream);
}
dst.buffer(frame).expect("invalid frame");
// Ensure the codec is ready to try the loop again.
ready!(dst.poll_ready(cx))?;
// Because, always try to reclaim...
self.reclaim_frame(buffer, store, dst);
}
None => {
// Try to flush the codec.
ready!(dst.flush(cx))?;
// This might release a data frame...
if !self.reclaim_frame(buffer, store, dst) {
return Poll::Ready(Ok(()));
}
// No need to poll ready as poll_complete() does this for
// us...
}
}
}
}
/// Tries to reclaim a pending data frame from the codec.
///
/// Returns true if a frame was reclaimed.
///
/// When a data frame is written to the codec, it may not be written in its
/// entirety (large chunks are split up into potentially many data frames).
/// In this case, the stream needs to be reprioritized.
fn reclaim_frame<T, B>(
&mut self,
buffer: &mut Buffer<Frame<B>>,
store: &mut Store,
dst: &mut Codec<T, Prioritized<B>>,
) -> bool
where
B: Buf,
{
let span = tracing::trace_span!("try_reclaim_frame");
let _e = span.enter();
// First check if there are any data chunks to take back
if let Some(frame) = dst.take_last_data_frame() {
self.reclaim_frame_inner(buffer, store, frame)
} else {
false
}
}
fn reclaim_frame_inner<B>(
&mut self,
buffer: &mut Buffer<Frame<B>>,
store: &mut Store,
frame: frame::Data<Prioritized<B>>,
) -> bool
where
B: Buf,
{
tracing::trace!(
?frame,
sz = frame.payload().inner.get_ref().remaining(),
"reclaimed"
);
let mut eos = false;
let key = frame.payload().stream;
match mem::replace(&mut self.in_flight_data_frame, InFlightData::Nothing) {
InFlightData::Nothing => panic!("wasn't expecting a frame to reclaim"),
InFlightData::Drop => {
tracing::trace!("not reclaiming frame for cancelled stream");
return false;
}
InFlightData::DataFrame(k) => {
debug_assert_eq!(k, key);
}
}
let mut frame = frame.map(|prioritized| {
// TODO: Ensure fully written
eos = prioritized.end_of_stream;
prioritized.inner.into_inner()
});
if frame.payload().has_remaining() {
let mut stream = store.resolve(key);
if eos {
frame.set_end_stream(true);
}
self.push_back_frame(frame.into(), buffer, &mut stream);
return true;
}
false
}
/// Push the frame to the front of the stream's deque, scheduling the
/// stream if needed.
fn push_back_frame<B>(
&mut self,
frame: Frame<B>,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
) {
// Push the frame to the front of the stream's deque
stream.pending_send.push_front(buffer, frame);
// If needed, schedule the sender
if stream.send_flow.available() > 0 {
debug_assert!(!stream.pending_send.is_empty());
self.pending_send.push(stream);
}
}
pub fn clear_queue<B>(&mut self, buffer: &mut Buffer<Frame<B>>, stream: &mut store::Ptr) {
let span = tracing::trace_span!("clear_queue", ?stream.id);
let _e = span.enter();
// TODO: make this more efficient?
while let Some(frame) = stream.pending_send.pop_front(buffer) {
tracing::trace!(?frame, "dropping");
}
stream.buffered_send_data = 0;
stream.requested_send_capacity = 0;
if let InFlightData::DataFrame(key) = self.in_flight_data_frame {
if stream.key() == key {
// This stream could get cleaned up now - don't allow the buffered frame to get reclaimed.
self.in_flight_data_frame = InFlightData::Drop;
}
}
}
pub fn clear_pending_send(&mut self, store: &mut Store, counts: &mut Counts) {
while let Some(mut stream) = self.pending_send.pop(store) {
let is_pending_reset = stream.is_pending_reset_expiration();
if let Some(reason) = stream.state.get_scheduled_reset() {
stream.set_reset(reason, Initiator::Library);
}
counts.transition_after(stream, is_pending_reset);
}
}
pub fn clear_pending_open(&mut self, store: &mut Store, counts: &mut Counts) {
while let Some(stream) = self.pending_open.pop(store) {
let is_pending_reset = stream.is_pending_reset_expiration();
counts.transition_after(stream, is_pending_reset);
}
}
fn pop_frame<B>(
&mut self,
buffer: &mut Buffer<Frame<B>>,
store: &mut Store,
max_len: usize,
counts: &mut Counts,
) -> Option<Frame<Prioritized<B>>>
where
B: Buf,
{
let span = tracing::trace_span!("pop_frame");
let _e = span.enter();
loop {
match self.pending_send.pop(store) {
Some(mut stream) => {
let span = tracing::trace_span!("popped", ?stream.id, ?stream.state);
let _e = span.enter();
// It's possible that this stream, besides having data to send,
// is also queued to send a reset, and thus is already in the queue
// to wait for "some time" after a reset.
//
// To be safe, we just always ask the stream.
let is_pending_reset = stream.is_pending_reset_expiration();
tracing::trace!(is_pending_reset);
let frame = match stream.pending_send.pop_front(buffer) {
Some(Frame::Data(mut frame)) => {
// Get the amount of capacity remaining for stream's
// window.
let stream_capacity = stream.send_flow.available();
let sz = frame.payload().remaining();
tracing::trace!(
sz,
eos = frame.is_end_stream(),
window = %stream_capacity,
available = %stream.send_flow.available(),
requested = stream.requested_send_capacity,
buffered = stream.buffered_send_data,
"data frame"
);
// Zero length data frames always have capacity to
// be sent.
if sz > 0 && stream_capacity == 0 {
tracing::trace!("stream capacity is 0");
// Ensure that the stream is waiting for
// connection level capacity
//
// TODO: uncomment
// debug_assert!(stream.is_pending_send_capacity);
// The stream has no more capacity, this can
// happen if the remote reduced the stream
// window. In this case, we need to buffer the
// frame and wait for a window update...
stream.pending_send.push_front(buffer, frame.into());
continue;
}
// Only send up to the max frame length
let len = cmp::min(sz, max_len);
// Only send up to the stream's window capacity
let len =
cmp::min(len, stream_capacity.as_size() as usize) as WindowSize;
// There *must* be be enough connection level
// capacity at this point.
debug_assert!(len <= self.flow.window_size());
// Check if the stream level window the peer knows is available. In some
// scenarios, maybe the window we know is available but the window which
// peer knows is not.
if len > 0 && len > stream.send_flow.window_size() {
stream.pending_send.push_front(buffer, frame.into());
continue;
}
tracing::trace!(len, "sending data frame");
// Update the flow control
tracing::trace_span!("updating stream flow").in_scope(|| {
stream.send_data(len, self.max_buffer_size);
// Assign the capacity back to the connection that
// was just consumed from the stream in the previous
// line.
// TODO: proper error handling
let _res = self.flow.assign_capacity(len);
debug_assert!(_res.is_ok());
});
let (eos, len) = tracing::trace_span!("updating connection flow")
.in_scope(|| {
// TODO: proper error handling
let _res = self.flow.send_data(len);
debug_assert!(_res.is_ok());
// Wrap the frame's data payload to ensure that the
// correct amount of data gets written.
let eos = frame.is_end_stream();
let len = len as usize;
if frame.payload().remaining() > len {
frame.set_end_stream(false);
}
(eos, len)
});
Frame::Data(frame.map(|buf| Prioritized {
inner: buf.take(len),
end_of_stream: eos,
stream: stream.key(),
}))
}
Some(Frame::PushPromise(pp)) => {
let mut pushed =
stream.store_mut().find_mut(&pp.promised_id()).unwrap();
pushed.is_pending_push = false;
// Transition stream from pending_push to pending_open
// if possible
if !pushed.pending_send.is_empty() {
if counts.can_inc_num_send_streams() {
counts.inc_num_send_streams(&mut pushed);
self.pending_send.push(&mut pushed);
} else {
self.queue_open(&mut pushed);
}
}
Frame::PushPromise(pp)
}
Some(frame) => frame.map(|_| {
unreachable!(
"Frame::map closure will only be called \
on DATA frames."
)
}),
None => {
if let Some(reason) = stream.state.get_scheduled_reset() {
stream.set_reset(reason, Initiator::Library);
let frame = frame::Reset::new(stream.id, reason);
Frame::Reset(frame)
} else {
// If the stream receives a RESET from the peer, it may have
// had data buffered to be sent, but all the frames are cleared
// in clear_queue(). Instead of doing O(N) traversal through queue
// to remove, lets just ignore the stream here.
tracing::trace!("removing dangling stream from pending_send");
// Since this should only happen as a consequence of `clear_queue`,
// we must be in a closed state of some kind.
debug_assert!(stream.state.is_closed());
counts.transition_after(stream, is_pending_reset);
continue;
}
}
};
tracing::trace!("pop_frame; frame={:?}", frame);
if cfg!(debug_assertions) && stream.state.is_idle() {
debug_assert!(stream.id > self.last_opened_id);
self.last_opened_id = stream.id;
}
if !stream.pending_send.is_empty() || stream.state.is_scheduled_reset() {
// TODO: Only requeue the sender IF it is ready to send
// the next frame. i.e. don't requeue it if the next
// frame is a data frame and the stream does not have
// any more capacity.
self.pending_send.push(&mut stream);
}
counts.transition_after(stream, is_pending_reset);
return Some(frame);
}
None => return None,
}
}
}
fn pop_pending_open<'s>(
&mut self,
store: &'s mut Store,
counts: &mut Counts,
) -> Option<store::Ptr<'s>> {
tracing::trace!("schedule_pending_open");
// check for any pending open streams
if counts.can_inc_num_send_streams() {
if let Some(mut stream) = self.pending_open.pop(store) {
tracing::trace!("schedule_pending_open; stream={:?}", stream.id);
counts.inc_num_send_streams(&mut stream);
stream.notify_send();
return Some(stream);
}
}
None
}
}
// ===== impl Prioritized =====
impl<B> Buf for Prioritized<B>
where
B: Buf,
{
fn remaining(&self) -> usize {
self.inner.remaining()
}
fn chunk(&self) -> &[u8] {
self.inner.chunk()
}
fn chunks_vectored<'a>(&'a self, dst: &mut [std::io::IoSlice<'a>]) -> usize {
self.inner.chunks_vectored(dst)
}
fn advance(&mut self, cnt: usize) {
self.inner.advance(cnt)
}
}
impl<B: Buf> fmt::Debug for Prioritized<B> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Prioritized")
.field("remaining", &self.inner.get_ref().remaining())
.field("end_of_stream", &self.end_of_stream)
.field("stream", &self.stream)
.finish()
}
}

1255
vendor/h2/src/proto/streams/recv.rs vendored Normal file

File diff suppressed because it is too large Load Diff

627
vendor/h2/src/proto/streams/send.rs vendored Normal file
View File

@@ -0,0 +1,627 @@
use super::{
store, Buffer, Codec, Config, Counts, Frame, Prioritize, Prioritized, Store, Stream, StreamId,
StreamIdOverflow, WindowSize,
};
use crate::codec::UserError;
use crate::frame::{self, Reason};
use crate::proto::{self, Error, Initiator};
use bytes::Buf;
use tokio::io::AsyncWrite;
use std::cmp::Ordering;
use std::io;
use std::task::{Context, Poll, Waker};
/// Manages state transitions related to outbound frames.
#[derive(Debug)]
pub(super) struct Send {
/// Stream identifier to use for next initialized stream.
next_stream_id: Result<StreamId, StreamIdOverflow>,
/// Any streams with a higher ID are ignored.
///
/// This starts as MAX, but is lowered when a GOAWAY is received.
///
/// > After sending a GOAWAY frame, the sender can discard frames for
/// > streams initiated by the receiver with identifiers higher than
/// > the identified last stream.
max_stream_id: StreamId,
/// Initial window size of locally initiated streams
init_window_sz: WindowSize,
/// Prioritization layer
prioritize: Prioritize,
is_push_enabled: bool,
/// If extended connect protocol is enabled.
is_extended_connect_protocol_enabled: bool,
}
/// A value to detect which public API has called `poll_reset`.
#[derive(Debug)]
pub(crate) enum PollReset {
AwaitingHeaders,
Streaming,
}
impl Send {
/// Create a new `Send`
pub fn new(config: &Config) -> Self {
Send {
init_window_sz: config.remote_init_window_sz,
max_stream_id: StreamId::MAX,
next_stream_id: Ok(config.local_next_stream_id),
prioritize: Prioritize::new(config),
is_push_enabled: true,
is_extended_connect_protocol_enabled: false,
}
}
/// Returns the initial send window size
pub fn init_window_sz(&self) -> WindowSize {
self.init_window_sz
}
pub fn open(&mut self) -> Result<StreamId, UserError> {
let stream_id = self.ensure_next_stream_id()?;
self.next_stream_id = stream_id.next_id();
Ok(stream_id)
}
pub fn reserve_local(&mut self) -> Result<StreamId, UserError> {
let stream_id = self.ensure_next_stream_id()?;
self.next_stream_id = stream_id.next_id();
Ok(stream_id)
}
fn check_headers(fields: &http::HeaderMap) -> Result<(), UserError> {
// 8.1.2.2. Connection-Specific Header Fields
if fields.contains_key(http::header::CONNECTION)
|| fields.contains_key(http::header::TRANSFER_ENCODING)
|| fields.contains_key(http::header::UPGRADE)
|| fields.contains_key("keep-alive")
|| fields.contains_key("proxy-connection")
{
tracing::debug!("illegal connection-specific headers found");
return Err(UserError::MalformedHeaders);
} else if let Some(te) = fields.get(http::header::TE) {
if te != "trailers" {
tracing::debug!("illegal connection-specific headers found");
return Err(UserError::MalformedHeaders);
}
}
Ok(())
}
pub fn send_push_promise<B>(
&mut self,
frame: frame::PushPromise,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
task: &mut Option<Waker>,
) -> Result<(), UserError> {
if !self.is_push_enabled {
return Err(UserError::PeerDisabledServerPush);
}
tracing::trace!(
"send_push_promise; frame={:?}; init_window={:?}",
frame,
self.init_window_sz
);
Self::check_headers(frame.fields())?;
// Queue the frame for sending
self.prioritize
.queue_frame(frame.into(), buffer, stream, task);
Ok(())
}
pub fn send_headers<B>(
&mut self,
frame: frame::Headers,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
counts: &mut Counts,
task: &mut Option<Waker>,
) -> Result<(), UserError> {
tracing::trace!(
"send_headers; frame={:?}; init_window={:?}",
frame,
self.init_window_sz
);
Self::check_headers(frame.fields())?;
let end_stream = frame.is_end_stream();
// Update the state
stream.state.send_open(end_stream)?;
let mut pending_open = false;
if counts.peer().is_local_init(frame.stream_id()) && !stream.is_pending_push {
self.prioritize.queue_open(stream);
pending_open = true;
}
// Queue the frame for sending
//
// This call expects that, since new streams are in the open queue, new
// streams won't be pushed on pending_send.
self.prioritize
.queue_frame(frame.into(), buffer, stream, task);
// Need to notify the connection when pushing onto pending_open since
// queue_frame only notifies for pending_send.
if pending_open {
if let Some(task) = task.take() {
task.wake();
}
}
Ok(())
}
/// Send interim informational headers (1xx responses) without changing stream state.
/// This allows multiple interim informational responses to be sent before the final response.
pub fn send_interim_informational_headers<B>(
&mut self,
frame: frame::Headers,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
_counts: &mut Counts,
task: &mut Option<Waker>,
) -> Result<(), UserError> {
tracing::trace!(
"send_interim_informational_headers; frame={:?}; stream_id={:?}",
frame,
frame.stream_id()
);
// Validate headers
Self::check_headers(frame.fields())?;
debug_assert!(frame.is_informational(),
"Frame must be informational (1xx status code) at this point. Validation should happen at the public API boundary.");
debug_assert!(!frame.is_end_stream(),
"Informational frames must not have end_stream flag set. Validation should happen at the internal send informational header streams.");
// Queue the frame for sending WITHOUT changing stream state
// This is the key difference from send_headers - we don't call stream.state.send_open()
self.prioritize
.queue_frame(frame.into(), buffer, stream, task);
Ok(())
}
/// Send an explicit RST_STREAM frame
pub fn send_reset<B>(
&mut self,
reason: Reason,
initiator: Initiator,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
counts: &mut Counts,
task: &mut Option<Waker>,
) {
let is_reset = stream.state.is_reset();
let is_closed = stream.state.is_closed();
let is_empty = stream.pending_send.is_empty();
let stream_id = stream.id;
tracing::trace!(
"send_reset(..., reason={:?}, initiator={:?}, stream={:?}, ..., \
is_reset={:?}; is_closed={:?}; pending_send.is_empty={:?}; \
state={:?} \
",
reason,
initiator,
stream_id,
is_reset,
is_closed,
is_empty,
stream.state
);
if is_reset {
// Don't double reset
tracing::trace!(
" -> not sending RST_STREAM ({:?} is already reset)",
stream_id
);
return;
}
// Transition the state to reset no matter what.
stream.set_reset(reason, initiator);
// If closed AND the send queue is flushed, then the stream cannot be
// reset explicitly, either. Implicit resets can still be queued.
if is_closed && is_empty {
tracing::trace!(
" -> not sending explicit RST_STREAM ({:?} was closed \
and send queue was flushed)",
stream_id
);
return;
}
// Clear all pending outbound frames.
// Note that we don't call `self.recv_err` because we want to enqueue
// the reset frame before transitioning the stream inside
// `reclaim_all_capacity`.
self.prioritize.clear_queue(buffer, stream);
let frame = frame::Reset::new(stream.id, reason);
tracing::trace!("send_reset -- queueing; frame={:?}", frame);
self.prioritize
.queue_frame(frame.into(), buffer, stream, task);
self.prioritize.reclaim_all_capacity(stream, counts);
}
pub fn schedule_implicit_reset(
&mut self,
stream: &mut store::Ptr,
reason: Reason,
counts: &mut Counts,
task: &mut Option<Waker>,
) {
if stream.state.is_closed() {
// Stream is already closed, nothing more to do
return;
}
stream.state.set_scheduled_reset(reason);
self.prioritize.reclaim_reserved_capacity(stream, counts);
self.prioritize.schedule_send(stream, task);
}
pub fn send_data<B>(
&mut self,
frame: frame::Data<B>,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
counts: &mut Counts,
task: &mut Option<Waker>,
) -> Result<(), UserError>
where
B: Buf,
{
self.prioritize
.send_data(frame, buffer, stream, counts, task)
}
pub fn send_trailers<B>(
&mut self,
frame: frame::Headers,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
counts: &mut Counts,
task: &mut Option<Waker>,
) -> Result<(), UserError> {
// TODO: Should this logic be moved into state.rs?
if !stream.state.is_send_streaming() {
return Err(UserError::UnexpectedFrameType);
}
stream.state.send_close();
tracing::trace!("send_trailers -- queuing; frame={:?}", frame);
self.prioritize
.queue_frame(frame.into(), buffer, stream, task);
// Release any excess capacity
self.prioritize.reserve_capacity(0, stream, counts);
Ok(())
}
pub fn poll_complete<T, B>(
&mut self,
cx: &mut Context,
buffer: &mut Buffer<Frame<B>>,
store: &mut Store,
counts: &mut Counts,
dst: &mut Codec<T, Prioritized<B>>,
) -> Poll<io::Result<()>>
where
T: AsyncWrite + Unpin,
B: Buf,
{
self.prioritize
.poll_complete(cx, buffer, store, counts, dst)
}
/// Request capacity to send data
pub fn reserve_capacity(
&mut self,
capacity: WindowSize,
stream: &mut store::Ptr,
counts: &mut Counts,
) {
self.prioritize.reserve_capacity(capacity, stream, counts)
}
pub fn poll_capacity(
&mut self,
cx: &Context,
stream: &mut store::Ptr,
) -> Poll<Option<Result<WindowSize, UserError>>> {
if !stream.state.is_send_streaming() {
return Poll::Ready(None);
}
if !stream.send_capacity_inc {
stream.wait_send(cx);
return Poll::Pending;
}
stream.send_capacity_inc = false;
Poll::Ready(Some(Ok(self.capacity(stream))))
}
/// Current available stream send capacity
pub fn capacity(&self, stream: &mut store::Ptr) -> WindowSize {
stream.capacity(self.prioritize.max_buffer_size())
}
pub fn poll_reset(
&self,
cx: &Context,
stream: &mut Stream,
mode: PollReset,
) -> Poll<Result<Reason, crate::Error>> {
match stream.state.ensure_reason(mode)? {
Some(reason) => Poll::Ready(Ok(reason)),
None => {
stream.wait_send(cx);
Poll::Pending
}
}
}
pub fn recv_connection_window_update(
&mut self,
frame: frame::WindowUpdate,
store: &mut Store,
counts: &mut Counts,
) -> Result<(), Reason> {
self.prioritize
.recv_connection_window_update(frame.size_increment(), store, counts)
}
pub fn recv_stream_window_update<B>(
&mut self,
sz: WindowSize,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
counts: &mut Counts,
task: &mut Option<Waker>,
) -> Result<(), Reason> {
if let Err(e) = self.prioritize.recv_stream_window_update(sz, stream) {
tracing::debug!("recv_stream_window_update !!; err={:?}", e);
self.send_reset(
Reason::FLOW_CONTROL_ERROR,
Initiator::Library,
buffer,
stream,
counts,
task,
);
return Err(e);
}
Ok(())
}
pub(super) fn recv_go_away(&mut self, last_stream_id: StreamId) -> Result<(), Error> {
if last_stream_id > self.max_stream_id {
// The remote endpoint sent a `GOAWAY` frame indicating a stream
// that we never sent, or that we have already terminated on account
// of previous `GOAWAY` frame. In either case, that is illegal.
// (When sending multiple `GOAWAY`s, "Endpoints MUST NOT increase
// the value they send in the last stream identifier, since the
// peers might already have retried unprocessed requests on another
// connection.")
proto_err!(conn:
"recv_go_away: last_stream_id ({:?}) > max_stream_id ({:?})",
last_stream_id, self.max_stream_id,
);
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
self.max_stream_id = last_stream_id;
Ok(())
}
pub fn handle_error<B>(
&mut self,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
counts: &mut Counts,
) {
// Clear all pending outbound frames
self.prioritize.clear_queue(buffer, stream);
self.prioritize.reclaim_all_capacity(stream, counts);
}
pub fn apply_remote_settings<B>(
&mut self,
settings: &frame::Settings,
buffer: &mut Buffer<Frame<B>>,
store: &mut Store,
counts: &mut Counts,
task: &mut Option<Waker>,
) -> Result<(), Error> {
if let Some(val) = settings.is_extended_connect_protocol_enabled() {
self.is_extended_connect_protocol_enabled = val;
}
// Applies an update to the remote endpoint's initial window size.
//
// Per RFC 7540 §6.9.2:
//
// In addition to changing the flow-control window for streams that are
// not yet active, a SETTINGS frame can alter the initial flow-control
// window size for streams with active flow-control windows (that is,
// streams in the "open" or "half-closed (remote)" state). When the
// value of SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST adjust
// the size of all stream flow-control windows that it maintains by the
// difference between the new value and the old value.
//
// A change to `SETTINGS_INITIAL_WINDOW_SIZE` can cause the available
// space in a flow-control window to become negative. A sender MUST
// track the negative flow-control window and MUST NOT send new
// flow-controlled frames until it receives WINDOW_UPDATE frames that
// cause the flow-control window to become positive.
if let Some(val) = settings.initial_window_size() {
let old_val = self.init_window_sz;
self.init_window_sz = val;
match val.cmp(&old_val) {
Ordering::Less => {
// We must decrease the (remote) window on every open stream.
let dec = old_val - val;
tracing::trace!("decrementing all windows; dec={}", dec);
let mut total_reclaimed = 0;
store.try_for_each(|mut stream| {
let stream = &mut *stream;
if stream.state.is_send_closed() && stream.buffered_send_data == 0 {
tracing::trace!(
"skipping send-closed stream; id={:?}; flow={:?}",
stream.id,
stream.send_flow
);
return Ok(());
}
tracing::trace!(
"decrementing stream window; id={:?}; decr={}; flow={:?}",
stream.id,
dec,
stream.send_flow
);
// TODO: this decrement can underflow based on received frames!
stream
.send_flow
.dec_send_window(dec)
.map_err(proto::Error::library_go_away)?;
// It's possible that decreasing the window causes
// `window_size` (the stream-specific window) to fall below
// `available` (the portion of the connection-level window
// that we have allocated to the stream).
// In this case, we should take that excess allocation away
// and reassign it to other streams.
let window_size = stream.send_flow.window_size();
let available = stream.send_flow.available().as_size();
let reclaimed = if available > window_size {
// Drop down to `window_size`.
let reclaim = available - window_size;
stream
.send_flow
.claim_capacity(reclaim)
.map_err(proto::Error::library_go_away)?;
total_reclaimed += reclaim;
reclaim
} else {
0
};
tracing::trace!(
"decremented stream window; id={:?}; decr={}; reclaimed={}; flow={:?}",
stream.id,
dec,
reclaimed,
stream.send_flow
);
// TODO: Should this notify the producer when the capacity
// of a stream is reduced? Maybe it should if the capacity
// is reduced to zero, allowing the producer to stop work.
Ok::<_, proto::Error>(())
})?;
self.prioritize
.assign_connection_capacity(total_reclaimed, store, counts);
}
Ordering::Greater => {
let inc = val - old_val;
store.try_for_each(|mut stream| {
self.recv_stream_window_update(inc, buffer, &mut stream, counts, task)
.map_err(Error::library_go_away)
})?;
}
Ordering::Equal => (),
}
}
if let Some(val) = settings.is_push_enabled() {
self.is_push_enabled = val
}
Ok(())
}
pub fn clear_queues(&mut self, store: &mut Store, counts: &mut Counts) {
self.prioritize.clear_pending_capacity(store, counts);
self.prioritize.clear_pending_send(store, counts);
self.prioritize.clear_pending_open(store, counts);
}
pub fn ensure_not_idle(&self, id: StreamId) -> Result<(), Reason> {
if let Ok(next) = self.next_stream_id {
if id >= next {
return Err(Reason::PROTOCOL_ERROR);
}
}
// if next_stream_id is overflowed, that's ok.
Ok(())
}
pub fn ensure_next_stream_id(&self) -> Result<StreamId, UserError> {
self.next_stream_id
.map_err(|_| UserError::OverflowedStreamId)
}
pub fn may_have_created_stream(&self, id: StreamId) -> bool {
if let Ok(next_id) = self.next_stream_id {
// Peer::is_local_init should have been called beforehand
debug_assert_eq!(id.is_server_initiated(), next_id.is_server_initiated(),);
id < next_id
} else {
true
}
}
pub(super) fn maybe_reset_next_stream_id(&mut self, id: StreamId) {
if let Ok(next_id) = self.next_stream_id {
// Peer::is_local_init should have been called beforehand
debug_assert_eq!(id.is_server_initiated(), next_id.is_server_initiated());
if id >= next_id {
self.next_stream_id = id.next_id();
}
}
}
pub(crate) fn is_extended_connect_protocol_enabled(&self) -> bool {
self.is_extended_connect_protocol_enabled
}
}

475
vendor/h2/src/proto/streams/state.rs vendored Normal file
View File

@@ -0,0 +1,475 @@
use std::fmt;
use std::io;
use crate::codec::UserError;
use crate::frame::{self, Reason, StreamId};
use crate::proto::{self, Error, Initiator, PollReset};
use self::Inner::*;
use self::Peer::*;
/// Represents the state of an H2 stream
///
/// ```not_rust
/// +--------+
/// send PP | | recv PP
/// ,--------| idle |--------.
/// / | | \
/// v +--------+ v
/// +----------+ | +----------+
/// | | | send H / | |
/// ,------| reserved | | recv H | reserved |------.
/// | | (local) | | | (remote) | |
/// | +----------+ v +----------+ |
/// | | +--------+ | |
/// | | recv ES | | send ES | |
/// | send H | ,-------| open |-------. | recv H |
/// | | / | | \ | |
/// | v v +--------+ v v |
/// | +----------+ | +----------+ |
/// | | half | | | half | |
/// | | closed | | send R / | closed | |
/// | | (remote) | | recv R | (local) | |
/// | +----------+ | +----------+ |
/// | | | | |
/// | | send ES / | recv ES / | |
/// | | send R / v send R / | |
/// | | recv R +--------+ recv R | |
/// | send R / `----------->| |<-----------' send R / |
/// | recv R | closed | recv R |
/// `----------------------->| |<----------------------'
/// +--------+
///
/// send: endpoint sends this frame
/// recv: endpoint receives this frame
///
/// H: HEADERS frame (with implied CONTINUATIONs)
/// PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
/// ES: END_STREAM flag
/// R: RST_STREAM frame
/// ```
#[derive(Clone)]
pub struct State {
inner: Inner,
}
#[derive(Debug, Clone)]
enum Inner {
Idle,
// TODO: these states shouldn't count against concurrency limits:
ReservedLocal,
ReservedRemote,
Open { local: Peer, remote: Peer },
HalfClosedLocal(Peer), // TODO: explicitly name this value
HalfClosedRemote(Peer),
Closed(Cause),
}
#[derive(Debug, Copy, Clone, Default)]
enum Peer {
#[default]
AwaitingHeaders,
Streaming,
}
#[derive(Debug, Clone)]
enum Cause {
EndStream,
Error(Error),
/// This indicates to the connection that a reset frame must be sent out
/// once the send queue has been flushed.
///
/// Examples of when this could happen:
/// - User drops all references to a stream, so we want to CANCEL the it.
/// - Header block size was too large, so we want to REFUSE, possibly
/// after sending a 431 response frame.
ScheduledLibraryReset(Reason),
}
impl State {
/// Opens the send-half of a stream if it is not already open.
pub fn send_open(&mut self, eos: bool) -> Result<(), UserError> {
let local = Streaming;
self.inner = match self.inner {
Idle => {
if eos {
HalfClosedLocal(AwaitingHeaders)
} else {
Open {
local,
remote: AwaitingHeaders,
}
}
}
Open {
local: AwaitingHeaders,
remote,
} => {
if eos {
HalfClosedLocal(remote)
} else {
Open { local, remote }
}
}
HalfClosedRemote(AwaitingHeaders) | ReservedLocal => {
if eos {
Closed(Cause::EndStream)
} else {
HalfClosedRemote(local)
}
}
_ => {
// All other transitions result in a protocol error
return Err(UserError::UnexpectedFrameType);
}
};
Ok(())
}
/// Opens the receive-half of the stream when a HEADERS frame is received.
///
/// Returns true if this transitions the state to Open.
pub fn recv_open(&mut self, frame: &frame::Headers) -> Result<bool, Error> {
let mut initial = false;
let eos = frame.is_end_stream();
self.inner = match self.inner {
Idle => {
initial = true;
if eos {
HalfClosedRemote(AwaitingHeaders)
} else {
Open {
local: AwaitingHeaders,
remote: if frame.is_informational() {
tracing::trace!("skipping 1xx response headers");
AwaitingHeaders
} else {
Streaming
},
}
}
}
ReservedRemote => {
initial = true;
if eos {
Closed(Cause::EndStream)
} else if frame.is_informational() {
tracing::trace!("skipping 1xx response headers");
ReservedRemote
} else {
HalfClosedLocal(Streaming)
}
}
Open {
local,
remote: AwaitingHeaders,
} => {
if eos {
HalfClosedRemote(local)
} else {
Open {
local,
remote: if frame.is_informational() {
tracing::trace!("skipping 1xx response headers");
AwaitingHeaders
} else {
Streaming
},
}
}
}
HalfClosedLocal(AwaitingHeaders) => {
if eos {
Closed(Cause::EndStream)
} else if frame.is_informational() {
tracing::trace!("skipping 1xx response headers");
HalfClosedLocal(AwaitingHeaders)
} else {
HalfClosedLocal(Streaming)
}
}
ref state => {
// All other transitions result in a protocol error
proto_err!(conn: "recv_open: in unexpected state {:?}", state);
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
};
Ok(initial)
}
/// Transition from Idle -> ReservedRemote
pub fn reserve_remote(&mut self) -> Result<(), Error> {
match self.inner {
Idle => {
self.inner = ReservedRemote;
Ok(())
}
ref state => {
proto_err!(conn: "reserve_remote: in unexpected state {:?}", state);
Err(Error::library_go_away(Reason::PROTOCOL_ERROR))
}
}
}
/// Transition from Idle -> ReservedLocal
pub fn reserve_local(&mut self) -> Result<(), UserError> {
match self.inner {
Idle => {
self.inner = ReservedLocal;
Ok(())
}
_ => Err(UserError::UnexpectedFrameType),
}
}
/// Indicates that the remote side will not send more data to the local.
pub fn recv_close(&mut self) -> Result<(), Error> {
match self.inner {
Open { local, .. } => {
// The remote side will continue to receive data.
tracing::trace!("recv_close: Open => HalfClosedRemote({:?})", local);
self.inner = HalfClosedRemote(local);
Ok(())
}
HalfClosedLocal(..) => {
tracing::trace!("recv_close: HalfClosedLocal => Closed");
self.inner = Closed(Cause::EndStream);
Ok(())
}
ref state => {
proto_err!(conn: "recv_close: in unexpected state {:?}", state);
Err(Error::library_go_away(Reason::PROTOCOL_ERROR))
}
}
}
/// The remote explicitly sent a RST_STREAM.
///
/// # Arguments
/// - `frame`: the received RST_STREAM frame.
/// - `queued`: true if this stream has frames in the pending send queue.
pub fn recv_reset(&mut self, frame: frame::Reset, queued: bool) {
match self.inner {
// If the stream is already in a `Closed` state, do nothing,
// provided that there are no frames still in the send queue.
Closed(..) if !queued => {}
// A notionally `Closed` stream may still have queued frames in
// the following cases:
//
// - if the cause is `Cause::Scheduled(..)` (i.e. we have not
// actually closed the stream yet).
// - if the cause is `Cause::EndStream`: we transition to this
// state when an EOS frame is *enqueued* (so that it's invalid
// to enqueue more frames), not when the EOS frame is *sent*;
// therefore, there may still be frames ahead of the EOS frame
// in the send queue.
//
// In either of these cases, we want to overwrite the stream's
// previous state with the received RST_STREAM, so that the queue
// will be cleared by `Prioritize::pop_frame`.
ref state => {
tracing::trace!(
"recv_reset; frame={:?}; state={:?}; queued={:?}",
frame,
state,
queued
);
self.inner = Closed(Cause::Error(Error::remote_reset(
frame.stream_id(),
frame.reason(),
)));
}
}
}
/// Handle a connection-level error.
pub fn handle_error(&mut self, err: &proto::Error) {
match self.inner {
Closed(..) => {}
_ => {
tracing::trace!("handle_error; err={:?}", err);
self.inner = Closed(Cause::Error(err.clone()));
}
}
}
pub fn recv_eof(&mut self) {
match self.inner {
Closed(..) => {}
ref state => {
tracing::trace!("recv_eof; state={:?}", state);
self.inner = Closed(Cause::Error(
io::Error::new(
io::ErrorKind::BrokenPipe,
"stream closed because of a broken pipe",
)
.into(),
));
}
}
}
/// Indicates that the local side will not send more data to the local.
pub fn send_close(&mut self) {
match self.inner {
Open { remote, .. } => {
// The remote side will continue to receive data.
tracing::trace!("send_close: Open => HalfClosedLocal({:?})", remote);
self.inner = HalfClosedLocal(remote);
}
HalfClosedRemote(..) => {
tracing::trace!("send_close: HalfClosedRemote => Closed");
self.inner = Closed(Cause::EndStream);
}
ref state => panic!("send_close: unexpected state {:?}", state),
}
}
/// Set the stream state to reset locally.
pub fn set_reset(&mut self, stream_id: StreamId, reason: Reason, initiator: Initiator) {
self.inner = Closed(Cause::Error(Error::Reset(stream_id, reason, initiator)));
}
/// Set the stream state to a scheduled reset.
pub fn set_scheduled_reset(&mut self, reason: Reason) {
debug_assert!(!self.is_closed());
self.inner = Closed(Cause::ScheduledLibraryReset(reason));
}
pub fn get_scheduled_reset(&self) -> Option<Reason> {
match self.inner {
Closed(Cause::ScheduledLibraryReset(reason)) => Some(reason),
_ => None,
}
}
pub fn is_scheduled_reset(&self) -> bool {
matches!(self.inner, Closed(Cause::ScheduledLibraryReset(..)))
}
pub fn is_local_error(&self) -> bool {
match self.inner {
Closed(Cause::Error(ref e)) => e.is_local(),
Closed(Cause::ScheduledLibraryReset(..)) => true,
_ => false,
}
}
pub fn is_remote_reset(&self) -> bool {
matches!(
self.inner,
Closed(Cause::Error(Error::Reset(_, _, Initiator::Remote)))
)
}
/// Returns true if the stream is already reset.
pub fn is_reset(&self) -> bool {
match self.inner {
Closed(Cause::EndStream) => false,
Closed(_) => true,
_ => false,
}
}
pub fn is_send_streaming(&self) -> bool {
matches!(
self.inner,
Open {
local: Streaming,
..
} | HalfClosedRemote(Streaming)
)
}
/// Returns true when the stream is in a state to receive headers
pub fn is_recv_headers(&self) -> bool {
matches!(
self.inner,
Idle | Open {
remote: AwaitingHeaders,
..
} | HalfClosedLocal(AwaitingHeaders)
| ReservedRemote
)
}
pub fn is_recv_streaming(&self) -> bool {
matches!(
self.inner,
Open {
remote: Streaming,
..
} | HalfClosedLocal(Streaming)
)
}
pub fn is_recv_end_stream(&self) -> bool {
// In either case END_STREAM has been received
matches!(self.inner, Closed(Cause::EndStream) | HalfClosedRemote(..))
}
pub fn is_closed(&self) -> bool {
matches!(self.inner, Closed(_))
}
pub fn is_send_closed(&self) -> bool {
matches!(
self.inner,
Closed(..) | HalfClosedLocal(..) | ReservedRemote
)
}
pub fn is_idle(&self) -> bool {
matches!(self.inner, Idle)
}
pub fn ensure_recv_open(&self) -> Result<bool, proto::Error> {
// TODO: Is this correct?
match self.inner {
Closed(Cause::Error(ref e)) => Err(e.clone()),
Closed(Cause::ScheduledLibraryReset(reason)) => {
Err(proto::Error::library_go_away(reason))
}
Closed(Cause::EndStream) | HalfClosedRemote(..) | ReservedLocal => Ok(false),
_ => Ok(true),
}
}
/// Returns a reason if the stream has been reset.
pub(super) fn ensure_reason(&self, mode: PollReset) -> Result<Option<Reason>, crate::Error> {
match self.inner {
Closed(Cause::Error(Error::Reset(_, reason, _)))
| Closed(Cause::Error(Error::GoAway(_, reason, _)))
| Closed(Cause::ScheduledLibraryReset(reason)) => Ok(Some(reason)),
Closed(Cause::Error(ref e)) => Err(e.clone().into()),
Open {
local: Streaming, ..
}
| HalfClosedRemote(Streaming) => match mode {
PollReset::AwaitingHeaders => Err(UserError::PollResetAfterSendResponse.into()),
PollReset::Streaming => Ok(None),
},
_ => Ok(None),
}
}
}
impl Default for State {
fn default() -> State {
State { inner: Inner::Idle }
}
}
// remove some noise for debug output
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}

474
vendor/h2/src/proto/streams/store.rs vendored Normal file
View File

@@ -0,0 +1,474 @@
use super::*;
use indexmap::{self, IndexMap};
use std::convert::Infallible;
use std::fmt;
use std::marker::PhantomData;
use std::ops;
/// Storage for streams
#[derive(Debug)]
pub(super) struct Store {
slab: slab::Slab<Stream>,
ids: IndexMap<StreamId, SlabIndex>,
}
/// "Pointer" to an entry in the store
pub(super) struct Ptr<'a> {
key: Key,
store: &'a mut Store,
}
/// References an entry in the store.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Key {
index: SlabIndex,
/// Keep the stream ID in the key as an ABA guard, since slab indices
/// could be re-used with a new stream.
stream_id: StreamId,
}
// We can never have more than `StreamId::MAX` streams in the store,
// so we can save a smaller index (u32 vs usize).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct SlabIndex(u32);
pub(super) struct Queue<N> {
indices: Option<store::Indices>,
_p: PhantomData<N>,
}
pub(super) trait Next {
fn next(stream: &Stream) -> Option<Key>;
fn set_next(stream: &mut Stream, key: Option<Key>);
fn take_next(stream: &mut Stream) -> Option<Key>;
fn is_queued(stream: &Stream) -> bool;
fn set_queued(stream: &mut Stream, val: bool);
}
/// A linked list
#[derive(Debug, Clone, Copy)]
struct Indices {
pub head: Key,
pub tail: Key,
}
pub(super) enum Entry<'a> {
Occupied(OccupiedEntry<'a>),
Vacant(VacantEntry<'a>),
}
pub(super) struct OccupiedEntry<'a> {
ids: indexmap::map::OccupiedEntry<'a, StreamId, SlabIndex>,
}
pub(super) struct VacantEntry<'a> {
ids: indexmap::map::VacantEntry<'a, StreamId, SlabIndex>,
slab: &'a mut slab::Slab<Stream>,
}
pub(super) trait Resolve {
fn resolve(&mut self, key: Key) -> Ptr<'_>;
}
// ===== impl Store =====
impl Store {
pub fn new() -> Self {
Store {
slab: slab::Slab::new(),
ids: IndexMap::new(),
}
}
pub fn find_mut(&mut self, id: &StreamId) -> Option<Ptr<'_>> {
let index = match self.ids.get(id) {
Some(key) => *key,
None => return None,
};
Some(Ptr {
key: Key {
index,
stream_id: *id,
},
store: self,
})
}
pub fn insert(&mut self, id: StreamId, val: Stream) -> Ptr<'_> {
let index = SlabIndex(self.slab.insert(val) as u32);
assert!(self.ids.insert(id, index).is_none());
Ptr {
key: Key {
index,
stream_id: id,
},
store: self,
}
}
pub fn find_entry(&mut self, id: StreamId) -> Entry<'_> {
use self::indexmap::map::Entry::*;
match self.ids.entry(id) {
Occupied(e) => Entry::Occupied(OccupiedEntry { ids: e }),
Vacant(e) => Entry::Vacant(VacantEntry {
ids: e,
slab: &mut self.slab,
}),
}
}
#[allow(clippy::blocks_in_conditions)]
pub(crate) fn for_each<F>(&mut self, mut f: F)
where
F: FnMut(Ptr),
{
match self.try_for_each(|ptr| {
f(ptr);
Ok::<_, Infallible>(())
}) {
Ok(()) => (),
#[allow(unused)]
Err(infallible) => match infallible {},
}
}
pub fn try_for_each<F, E>(&mut self, mut f: F) -> Result<(), E>
where
F: FnMut(Ptr) -> Result<(), E>,
{
let mut len = self.ids.len();
let mut i = 0;
while i < len {
// Get the key by index, this makes the borrow checker happy
let (stream_id, index) = {
let entry = self.ids.get_index(i).unwrap();
(*entry.0, *entry.1)
};
f(Ptr {
key: Key { index, stream_id },
store: self,
})?;
// TODO: This logic probably could be better...
let new_len = self.ids.len();
if new_len < len {
debug_assert!(new_len == len - 1);
len -= 1;
} else {
i += 1;
}
}
Ok(())
}
}
impl Resolve for Store {
fn resolve(&mut self, key: Key) -> Ptr<'_> {
Ptr { key, store: self }
}
}
impl ops::Index<Key> for Store {
type Output = Stream;
fn index(&self, key: Key) -> &Self::Output {
self.slab
.get(key.index.0 as usize)
.filter(|s| s.id == key.stream_id)
.unwrap_or_else(|| {
panic!("dangling store key for stream_id={:?}", key.stream_id);
})
}
}
impl ops::IndexMut<Key> for Store {
fn index_mut(&mut self, key: Key) -> &mut Self::Output {
self.slab
.get_mut(key.index.0 as usize)
.filter(|s| s.id == key.stream_id)
.unwrap_or_else(|| {
panic!("dangling store key for stream_id={:?}", key.stream_id);
})
}
}
impl Store {
#[cfg(feature = "unstable")]
pub fn num_active_streams(&self) -> usize {
self.ids.len()
}
#[cfg(feature = "unstable")]
pub fn num_wired_streams(&self) -> usize {
self.slab.len()
}
}
// While running h2 unit/integration tests, enable this debug assertion.
//
// In practice, we don't need to ensure this. But the integration tests
// help to make sure we've cleaned up in cases where we could (like, the
// runtime isn't suddenly dropping the task for unknown reasons).
#[cfg(feature = "unstable")]
impl Drop for Store {
fn drop(&mut self) {
use std::thread;
if !thread::panicking() {
debug_assert!(self.slab.is_empty());
}
}
}
// ===== impl Queue =====
impl<N> Queue<N>
where
N: Next,
{
pub fn new() -> Self {
Queue {
indices: None,
_p: PhantomData,
}
}
pub fn take(&mut self) -> Self {
Queue {
indices: self.indices.take(),
_p: PhantomData,
}
}
/// Queue the stream.
///
/// If the stream is already contained by the list, return `false`.
pub fn push(&mut self, stream: &mut store::Ptr) -> bool {
tracing::trace!("Queue::push_back");
if N::is_queued(stream) {
tracing::trace!(" -> already queued");
return false;
}
N::set_queued(stream, true);
// The next pointer shouldn't be set
debug_assert!(N::next(stream).is_none());
// Queue the stream
match self.indices {
Some(ref mut idxs) => {
tracing::trace!(" -> existing entries");
// Update the current tail node to point to `stream`
let key = stream.key();
N::set_next(&mut stream.resolve(idxs.tail), Some(key));
// Update the tail pointer
idxs.tail = stream.key();
}
None => {
tracing::trace!(" -> first entry");
self.indices = Some(store::Indices {
head: stream.key(),
tail: stream.key(),
});
}
}
true
}
/// Queue the stream
///
/// If the stream is already contained by the list, return `false`.
pub fn push_front(&mut self, stream: &mut store::Ptr) -> bool {
tracing::trace!("Queue::push_front");
if N::is_queued(stream) {
tracing::trace!(" -> already queued");
return false;
}
N::set_queued(stream, true);
// The next pointer shouldn't be set
debug_assert!(N::next(stream).is_none());
// Queue the stream
match self.indices {
Some(ref mut idxs) => {
tracing::trace!(" -> existing entries");
// Update the provided stream to point to the head node
let head_key = stream.resolve(idxs.head).key();
N::set_next(stream, Some(head_key));
// Update the head pointer
idxs.head = stream.key();
}
None => {
tracing::trace!(" -> first entry");
self.indices = Some(store::Indices {
head: stream.key(),
tail: stream.key(),
});
}
}
true
}
pub fn pop<'a, R>(&mut self, store: &'a mut R) -> Option<store::Ptr<'a>>
where
R: Resolve,
{
if let Some(mut idxs) = self.indices {
let mut stream = store.resolve(idxs.head);
if idxs.head == idxs.tail {
assert!(N::next(&stream).is_none());
self.indices = None;
} else {
idxs.head = N::take_next(&mut stream).unwrap();
self.indices = Some(idxs);
}
debug_assert!(N::is_queued(&stream));
N::set_queued(&mut stream, false);
return Some(stream);
}
None
}
pub fn is_empty(&self) -> bool {
self.indices.is_none()
}
pub fn pop_if<'a, R, F>(&mut self, store: &'a mut R, f: F) -> Option<store::Ptr<'a>>
where
R: Resolve,
F: Fn(&Stream) -> bool,
{
if let Some(idxs) = self.indices {
let should_pop = f(&store.resolve(idxs.head));
if should_pop {
return self.pop(store);
}
}
None
}
}
impl<N> fmt::Debug for Queue<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Queue")
.field("indices", &self.indices)
// skip phantom data
.finish()
}
}
// ===== impl Ptr =====
impl<'a> Ptr<'a> {
/// Returns the Key associated with the stream
pub fn key(&self) -> Key {
self.key
}
pub fn store_mut(&mut self) -> &mut Store {
self.store
}
/// Remove the stream from the store
pub fn remove(self) -> StreamId {
// The stream must have been unlinked before this point
debug_assert!(!self.store.ids.contains_key(&self.key.stream_id));
// Remove the stream state
let stream = self.store.slab.remove(self.key.index.0 as usize);
assert_eq!(stream.id, self.key.stream_id);
stream.id
}
/// Remove the StreamId -> stream state association.
///
/// This will effectively remove the stream as far as the H2 protocol is
/// concerned.
pub fn unlink(&mut self) {
let id = self.key.stream_id;
self.store.ids.swap_remove(&id);
}
}
impl<'a> Resolve for Ptr<'a> {
fn resolve(&mut self, key: Key) -> Ptr<'_> {
Ptr {
key,
store: &mut *self.store,
}
}
}
impl<'a> ops::Deref for Ptr<'a> {
type Target = Stream;
fn deref(&self) -> &Stream {
&self.store[self.key]
}
}
impl<'a> ops::DerefMut for Ptr<'a> {
fn deref_mut(&mut self) -> &mut Stream {
&mut self.store[self.key]
}
}
impl<'a> fmt::Debug for Ptr<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
(**self).fmt(fmt)
}
}
// ===== impl OccupiedEntry =====
impl<'a> OccupiedEntry<'a> {
pub fn key(&self) -> Key {
let stream_id = *self.ids.key();
let index = *self.ids.get();
Key { index, stream_id }
}
}
// ===== impl VacantEntry =====
impl<'a> VacantEntry<'a> {
pub fn insert(self, value: Stream) -> Key {
// Insert the value in the slab
let stream_id = value.id;
let index = SlabIndex(self.slab.insert(value) as u32);
// Insert the handle in the ID map
self.ids.insert(index);
Key { index, stream_id }
}
}

599
vendor/h2/src/proto/streams/stream.rs vendored Normal file
View File

@@ -0,0 +1,599 @@
use crate::Reason;
use super::*;
use std::fmt;
use std::task::{Context, Waker};
use std::time::Instant;
/// Tracks Stream related state
///
/// # Reference counting
///
/// There can be a number of outstanding handles to a single Stream. These are
/// tracked using reference counting. The `ref_count` field represents the
/// number of outstanding userspace handles that can reach this stream.
///
/// It's important to note that when the stream is placed in an internal queue
/// (such as an accept queue), this is **not** tracked by a reference count.
/// Thus, `ref_count` can be zero and the stream still has to be kept around.
pub(super) struct Stream {
/// The h2 stream identifier
pub id: StreamId,
/// Current state of the stream
pub state: State,
/// Set to `true` when the stream is counted against the connection's max
/// concurrent streams.
pub is_counted: bool,
/// Number of outstanding handles pointing to this stream
pub ref_count: usize,
// ===== Fields related to sending =====
/// Next node in the accept linked list
pub next_pending_send: Option<store::Key>,
/// Set to true when the stream is pending accept
pub is_pending_send: bool,
/// Send data flow control
pub send_flow: FlowControl,
/// Amount of send capacity that has been requested, but not yet allocated.
pub requested_send_capacity: WindowSize,
/// Amount of data buffered at the prioritization layer.
/// TODO: Technically this could be greater than the window size...
pub buffered_send_data: usize,
/// Task tracking additional send capacity (i.e. window updates).
send_task: Option<Waker>,
/// Frames pending for this stream being sent to the socket
pub pending_send: buffer::Deque,
/// Next node in the linked list of streams waiting for additional
/// connection level capacity.
pub next_pending_send_capacity: Option<store::Key>,
/// True if the stream is waiting for outbound connection capacity
pub is_pending_send_capacity: bool,
/// Set to true when the send capacity has been incremented
pub send_capacity_inc: bool,
/// Next node in the open linked list
pub next_open: Option<store::Key>,
/// Set to true when the stream is pending to be opened
pub is_pending_open: bool,
/// Set to true when a push is pending for this stream
pub is_pending_push: bool,
// ===== Fields related to receiving =====
/// Next node in the accept linked list
pub next_pending_accept: Option<store::Key>,
/// Set to true when the stream is pending accept
pub is_pending_accept: bool,
/// Receive data flow control
pub recv_flow: FlowControl,
pub in_flight_recv_data: WindowSize,
/// Next node in the linked list of streams waiting to send window updates.
pub next_window_update: Option<store::Key>,
/// True if the stream is waiting to send a window update
pub is_pending_window_update: bool,
/// The time when this stream may have been locally reset.
pub reset_at: Option<Instant>,
/// Next node in list of reset streams that should expire eventually
pub next_reset_expire: Option<store::Key>,
/// Frames pending for this stream to read
pub pending_recv: buffer::Deque,
/// When the RecvStream drop occurs, no data should be received.
pub is_recv: bool,
/// Task tracking receiving frames
pub recv_task: Option<Waker>,
/// Task tracking pushed promises.
pub push_task: Option<Waker>,
/// The stream's pending push promises
pub pending_push_promises: store::Queue<NextAccept>,
/// Validate content-length headers
pub content_length: ContentLength,
}
/// State related to validating a stream's content-length
#[derive(Debug)]
pub enum ContentLength {
Omitted,
Head,
Remaining(u64),
}
#[derive(Debug)]
pub(super) struct NextAccept;
#[derive(Debug)]
pub(super) struct NextSend;
#[derive(Debug)]
pub(super) struct NextSendCapacity;
#[derive(Debug)]
pub(super) struct NextWindowUpdate;
#[derive(Debug)]
pub(super) struct NextOpen;
#[derive(Debug)]
pub(super) struct NextResetExpire;
impl Stream {
pub fn new(id: StreamId, init_send_window: WindowSize, init_recv_window: WindowSize) -> Stream {
let mut send_flow = FlowControl::new();
let mut recv_flow = FlowControl::new();
recv_flow
.inc_window(init_recv_window)
.expect("invalid initial receive window");
// TODO: proper error handling?
let _res = recv_flow.assign_capacity(init_recv_window);
debug_assert!(_res.is_ok());
send_flow
.inc_window(init_send_window)
.expect("invalid initial send window size");
Stream {
id,
state: State::default(),
ref_count: 0,
is_counted: false,
// ===== Fields related to sending =====
next_pending_send: None,
is_pending_send: false,
send_flow,
requested_send_capacity: 0,
buffered_send_data: 0,
send_task: None,
pending_send: buffer::Deque::new(),
is_pending_send_capacity: false,
next_pending_send_capacity: None,
send_capacity_inc: false,
is_pending_open: false,
next_open: None,
is_pending_push: false,
// ===== Fields related to receiving =====
next_pending_accept: None,
is_pending_accept: false,
recv_flow,
in_flight_recv_data: 0,
next_window_update: None,
is_pending_window_update: false,
reset_at: None,
next_reset_expire: None,
pending_recv: buffer::Deque::new(),
is_recv: true,
recv_task: None,
push_task: None,
pending_push_promises: store::Queue::new(),
content_length: ContentLength::Omitted,
}
}
/// Increment the stream's ref count
pub fn ref_inc(&mut self) {
assert!(self.ref_count < usize::MAX);
self.ref_count += 1;
}
/// Decrements the stream's ref count
pub fn ref_dec(&mut self) {
assert!(self.ref_count > 0);
self.ref_count -= 1;
}
/// Returns true if stream is currently being held for some time because of
/// a local reset.
pub fn is_pending_reset_expiration(&self) -> bool {
self.reset_at.is_some()
}
/// Returns true if frames for this stream are ready to be sent over the wire
pub fn is_send_ready(&self) -> bool {
// Why do we check pending_open?
//
// We allow users to call send_request() which schedules a stream to be pending_open
// if there is no room according to the concurrency limit (max_send_streams), and we
// also allow data to be buffered for send with send_data() if there is no capacity for
// the stream to send the data, which attempts to place the stream in pending_send.
// If the stream is not open, we don't want the stream to be scheduled for
// execution (pending_send). Note that if the stream is in pending_open, it will be
// pushed to pending_send when there is room for an open stream.
//
// In pending_push we track whether a PushPromise still needs to be sent
// from a different stream before we can start sending frames on this one.
// This is different from the "open" check because reserved streams don't count
// toward the concurrency limit.
// See https://httpwg.org/specs/rfc7540.html#rfc.section.5.1.2
!self.is_pending_open && !self.is_pending_push
}
/// Returns true if the stream is closed
pub fn is_closed(&self) -> bool {
// The state has fully transitioned to closed.
self.state.is_closed() &&
// Because outbound frames transition the stream state before being
// buffered, we have to ensure that all frames have been flushed.
self.pending_send.is_empty() &&
// Sometimes large data frames are sent out in chunks. After a chunk
// of the frame is sent, the remainder is pushed back onto the send
// queue to be rescheduled.
//
// Checking for additional buffered data lets us catch this case.
self.buffered_send_data == 0
}
/// Returns true if the stream is no longer in use
pub fn is_released(&self) -> bool {
// The stream is closed and fully flushed
self.is_closed() &&
// There are no more outstanding references to the stream
self.ref_count == 0 &&
// The stream is not in any queue
!self.is_pending_send && !self.is_pending_send_capacity &&
!self.is_pending_accept && !self.is_pending_window_update &&
!self.is_pending_open && self.reset_at.is_none()
}
/// Returns true when the consumer of the stream has dropped all handles
/// (indicating no further interest in the stream) and the stream state is
/// not actually closed.
///
/// In this case, a reset should be sent.
pub fn is_canceled_interest(&self) -> bool {
self.ref_count == 0 && !self.state.is_closed()
}
/// Current available stream send capacity
pub fn capacity(&self, max_buffer_size: usize) -> WindowSize {
let available = self.send_flow.available().as_size() as usize;
let buffered = self.buffered_send_data;
available.min(max_buffer_size).saturating_sub(buffered) as WindowSize
}
pub fn assign_capacity(&mut self, capacity: WindowSize, max_buffer_size: usize) {
let prev_capacity = self.capacity(max_buffer_size);
debug_assert!(capacity > 0);
// TODO: proper error handling
let _res = self.send_flow.assign_capacity(capacity);
debug_assert!(_res.is_ok());
tracing::trace!(
" assigned capacity to stream; available={}; buffered={}; id={:?}; max_buffer_size={} prev={}",
self.send_flow.available(),
self.buffered_send_data,
self.id,
max_buffer_size,
prev_capacity,
);
if prev_capacity < self.capacity(max_buffer_size) {
self.notify_capacity();
}
}
pub fn send_data(&mut self, len: WindowSize, max_buffer_size: usize) {
let prev_capacity = self.capacity(max_buffer_size);
// TODO: proper error handling
let _res = self.send_flow.send_data(len);
debug_assert!(_res.is_ok());
// Decrement the stream's buffered data counter
debug_assert!(self.buffered_send_data >= len as usize);
self.buffered_send_data -= len as usize;
self.requested_send_capacity -= len;
tracing::trace!(
" sent stream data; available={}; buffered={}; id={:?}; max_buffer_size={} prev={}",
self.send_flow.available(),
self.buffered_send_data,
self.id,
max_buffer_size,
prev_capacity,
);
if prev_capacity < self.capacity(max_buffer_size) {
self.notify_capacity();
}
}
/// If the capacity was limited because of the max_send_buffer_size,
/// then consider waking the send task again...
pub fn notify_capacity(&mut self) {
self.send_capacity_inc = true;
tracing::trace!(" notifying task");
self.notify_send();
}
/// Returns `Err` when the decrement cannot be completed due to overflow.
pub fn dec_content_length(&mut self, len: usize) -> Result<(), ()> {
match self.content_length {
ContentLength::Remaining(ref mut rem) => match rem.checked_sub(len as u64) {
Some(val) => *rem = val,
None => return Err(()),
},
ContentLength::Head => {
if len != 0 {
return Err(());
}
}
_ => {}
}
Ok(())
}
pub fn ensure_content_length_zero(&self) -> Result<(), ()> {
match self.content_length {
ContentLength::Remaining(0) => Ok(()),
ContentLength::Remaining(_) => Err(()),
_ => Ok(()),
}
}
pub fn notify_send(&mut self) {
if let Some(task) = self.send_task.take() {
task.wake();
}
}
pub fn wait_send(&mut self, cx: &Context) {
self.send_task = Some(cx.waker().clone());
}
pub fn notify_recv(&mut self) {
if let Some(task) = self.recv_task.take() {
task.wake();
}
}
pub(super) fn notify_push(&mut self) {
if let Some(task) = self.push_task.take() {
task.wake();
}
}
/// Set the stream's state to `Closed` with the given reason and initiator.
/// Notify the send and receive tasks, if they exist.
pub(super) fn set_reset(&mut self, reason: Reason, initiator: Initiator) {
self.state.set_reset(self.id, reason, initiator);
self.notify_push();
self.notify_recv();
}
}
impl fmt::Debug for Stream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Stream")
.field("id", &self.id)
.field("state", &self.state)
.field("is_counted", &self.is_counted)
.field("ref_count", &self.ref_count)
.h2_field_some("next_pending_send", &self.next_pending_send)
.h2_field_if("is_pending_send", &self.is_pending_send)
.field("send_flow", &self.send_flow)
.field("requested_send_capacity", &self.requested_send_capacity)
.field("buffered_send_data", &self.buffered_send_data)
.h2_field_some("send_task", &self.send_task.as_ref().map(|_| ()))
.h2_field_if_then(
"pending_send",
!self.pending_send.is_empty(),
&self.pending_send,
)
.h2_field_some(
"next_pending_send_capacity",
&self.next_pending_send_capacity,
)
.h2_field_if("is_pending_send_capacity", &self.is_pending_send_capacity)
.h2_field_if("send_capacity_inc", &self.send_capacity_inc)
.h2_field_some("next_open", &self.next_open)
.h2_field_if("is_pending_open", &self.is_pending_open)
.h2_field_if("is_pending_push", &self.is_pending_push)
.h2_field_some("next_pending_accept", &self.next_pending_accept)
.h2_field_if("is_pending_accept", &self.is_pending_accept)
.field("recv_flow", &self.recv_flow)
.field("in_flight_recv_data", &self.in_flight_recv_data)
.h2_field_some("next_window_update", &self.next_window_update)
.h2_field_if("is_pending_window_update", &self.is_pending_window_update)
.h2_field_some("reset_at", &self.reset_at)
.h2_field_some("next_reset_expire", &self.next_reset_expire)
.h2_field_if_then(
"pending_recv",
!self.pending_recv.is_empty(),
&self.pending_recv,
)
.h2_field_if("is_recv", &self.is_recv)
.h2_field_some("recv_task", &self.recv_task.as_ref().map(|_| ()))
.h2_field_some("push_task", &self.push_task.as_ref().map(|_| ()))
.h2_field_if_then(
"pending_push_promises",
!self.pending_push_promises.is_empty(),
&self.pending_push_promises,
)
.field("content_length", &self.content_length)
.finish()
}
}
impl store::Next for NextAccept {
fn next(stream: &Stream) -> Option<store::Key> {
stream.next_pending_accept
}
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
stream.next_pending_accept = key;
}
fn take_next(stream: &mut Stream) -> Option<store::Key> {
stream.next_pending_accept.take()
}
fn is_queued(stream: &Stream) -> bool {
stream.is_pending_accept
}
fn set_queued(stream: &mut Stream, val: bool) {
stream.is_pending_accept = val;
}
}
impl store::Next for NextSend {
fn next(stream: &Stream) -> Option<store::Key> {
stream.next_pending_send
}
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
stream.next_pending_send = key;
}
fn take_next(stream: &mut Stream) -> Option<store::Key> {
stream.next_pending_send.take()
}
fn is_queued(stream: &Stream) -> bool {
stream.is_pending_send
}
fn set_queued(stream: &mut Stream, val: bool) {
if val {
// ensure that stream is not queued for being opened
// if it's being put into queue for sending data
debug_assert!(!stream.is_pending_open);
}
stream.is_pending_send = val;
}
}
impl store::Next for NextSendCapacity {
fn next(stream: &Stream) -> Option<store::Key> {
stream.next_pending_send_capacity
}
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
stream.next_pending_send_capacity = key;
}
fn take_next(stream: &mut Stream) -> Option<store::Key> {
stream.next_pending_send_capacity.take()
}
fn is_queued(stream: &Stream) -> bool {
stream.is_pending_send_capacity
}
fn set_queued(stream: &mut Stream, val: bool) {
stream.is_pending_send_capacity = val;
}
}
impl store::Next for NextWindowUpdate {
fn next(stream: &Stream) -> Option<store::Key> {
stream.next_window_update
}
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
stream.next_window_update = key;
}
fn take_next(stream: &mut Stream) -> Option<store::Key> {
stream.next_window_update.take()
}
fn is_queued(stream: &Stream) -> bool {
stream.is_pending_window_update
}
fn set_queued(stream: &mut Stream, val: bool) {
stream.is_pending_window_update = val;
}
}
impl store::Next for NextOpen {
fn next(stream: &Stream) -> Option<store::Key> {
stream.next_open
}
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
stream.next_open = key;
}
fn take_next(stream: &mut Stream) -> Option<store::Key> {
stream.next_open.take()
}
fn is_queued(stream: &Stream) -> bool {
stream.is_pending_open
}
fn set_queued(stream: &mut Stream, val: bool) {
if val {
// ensure that stream is not queued for being sent
// if it's being put into queue for opening the stream
debug_assert!(!stream.is_pending_send);
}
stream.is_pending_open = val;
}
}
impl store::Next for NextResetExpire {
fn next(stream: &Stream) -> Option<store::Key> {
stream.next_reset_expire
}
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
stream.next_reset_expire = key;
}
fn take_next(stream: &mut Stream) -> Option<store::Key> {
stream.next_reset_expire.take()
}
fn is_queued(stream: &Stream) -> bool {
stream.reset_at.is_some()
}
fn set_queued(stream: &mut Stream, val: bool) {
if val {
stream.reset_at = Some(Instant::now());
} else {
stream.reset_at = None;
}
}
}
// ===== impl ContentLength =====
impl ContentLength {
pub fn is_head(&self) -> bool {
matches!(*self, Self::Head)
}
}

1731
vendor/h2/src/proto/streams/streams.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1746
vendor/h2/src/server.rs vendored Normal file

File diff suppressed because it is too large Load Diff

606
vendor/h2/src/share.rs vendored Normal file
View File

@@ -0,0 +1,606 @@
use crate::codec::UserError;
use crate::frame::Reason;
use crate::proto::{self, WindowSize};
use bytes::{Buf, Bytes};
use http::HeaderMap;
use std::fmt;
#[cfg(feature = "stream")]
use std::pin::Pin;
use std::task::{Context, Poll};
/// Sends the body stream and trailers to the remote peer.
///
/// # Overview
///
/// A `SendStream` is provided by [`SendRequest`] and [`SendResponse`] once the
/// HTTP/2 message header has been sent sent. It is used to stream the message
/// body and send the message trailers. See method level documentation for more
/// details.
///
/// The `SendStream` instance is also used to manage outbound flow control.
///
/// If a `SendStream` is dropped without explicitly closing the send stream, a
/// `RST_STREAM` frame will be sent. This essentially cancels the request /
/// response exchange.
///
/// The ways to explicitly close the send stream are:
///
/// * Set `end_of_stream` to true when calling [`send_request`],
/// [`send_response`], or [`send_data`].
/// * Send trailers with [`send_trailers`].
/// * Explicitly reset the stream with [`send_reset`].
///
/// # Flow control
///
/// In HTTP/2, data cannot be sent to the remote peer unless there is
/// available window capacity on both the stream and the connection. When a data
/// frame is sent, both the stream window and the connection window are
/// decremented. When the stream level window reaches zero, no further data can
/// be sent on that stream. When the connection level window reaches zero, no
/// further data can be sent on any stream for that connection.
///
/// When the remote peer is ready to receive more data, it sends `WINDOW_UPDATE`
/// frames. These frames increment the windows. See the [specification] for more
/// details on the principles of HTTP/2 flow control.
///
/// The implications for sending data are that the caller **should** ensure that
/// both the stream and the connection has available window capacity before
/// loading the data to send into memory. The `SendStream` instance provides the
/// necessary APIs to perform this logic. This, however, is not an obligation.
/// If the caller attempts to send data on a stream when there is no available
/// window capacity, the library will buffer the data until capacity becomes
/// available, at which point the buffer will be flushed to the connection.
///
/// **NOTE**: There is no bound on the amount of data that the library will
/// buffer. If you are sending large amounts of data, you really should hook
/// into the flow control lifecycle. Otherwise, you risk using up significant
/// amounts of memory.
///
/// To hook into the flow control lifecycle, the caller signals to the library
/// that it intends to send data by calling [`reserve_capacity`], specifying the
/// amount of data, in octets, that the caller intends to send. After this,
/// `poll_capacity` is used to be notified when the requested capacity is
/// assigned to the stream. Once [`poll_capacity`] returns `Ready` with the number
/// of octets available to the stream, the caller is able to actually send the
/// data using [`send_data`].
///
/// Because there is also a connection level window that applies to **all**
/// streams on a connection, when capacity is assigned to a stream (indicated by
/// `poll_capacity` returning `Ready`), this capacity is reserved on the
/// connection and will **not** be assigned to any other stream. If data is
/// never written to the stream, that capacity is effectively lost to other
/// streams and this introduces the risk of deadlocking a connection.
///
/// To avoid throttling data on a connection, the caller should not reserve
/// capacity until ready to send data and once any capacity is assigned to the
/// stream, the caller should immediately send data consuming this capacity.
/// There is no guarantee as to when the full capacity requested will become
/// available. For example, if the caller requests 64 KB of data and 512 bytes
/// become available, the caller should immediately send 512 bytes of data.
///
/// See [`reserve_capacity`] documentation for more details.
///
/// [`SendRequest`]: client/struct.SendRequest.html
/// [`SendResponse`]: server/struct.SendResponse.html
/// [specification]: http://httpwg.org/specs/rfc7540.html#FlowControl
/// [`reserve_capacity`]: #method.reserve_capacity
/// [`poll_capacity`]: #method.poll_capacity
/// [`send_data`]: #method.send_data
/// [`send_request`]: client/struct.SendRequest.html#method.send_request
/// [`send_response`]: server/struct.SendResponse.html#method.send_response
/// [`send_data`]: #method.send_data
/// [`send_trailers`]: #method.send_trailers
/// [`send_reset`]: #method.send_reset
#[derive(Debug)]
pub struct SendStream<B> {
inner: proto::StreamRef<B>,
}
/// A stream identifier, as described in [Section 5.1.1] of RFC 7540.
///
/// Streams are identified with an unsigned 31-bit integer. Streams
/// initiated by a client MUST use odd-numbered stream identifiers; those
/// initiated by the server MUST use even-numbered stream identifiers. A
/// stream identifier of zero (0x0) is used for connection control
/// messages; the stream identifier of zero cannot be used to establish a
/// new stream.
///
/// [Section 5.1.1]: https://tools.ietf.org/html/rfc7540#section-5.1.1
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct StreamId(u32);
impl From<StreamId> for u32 {
fn from(src: StreamId) -> Self {
src.0
}
}
/// Receives the body stream and trailers from the remote peer.
///
/// A `RecvStream` is provided by [`client::ResponseFuture`] and
/// [`server::Connection`] with the received HTTP/2 message head (the response
/// and request head respectively).
///
/// A `RecvStream` instance is used to receive the streaming message body and
/// any trailers from the remote peer. It is also used to manage inbound flow
/// control.
///
/// See method level documentation for more details on receiving data. See
/// [`FlowControl`] for more details on inbound flow control.
///
/// [`client::ResponseFuture`]: client/struct.ResponseFuture.html
/// [`server::Connection`]: server/struct.Connection.html
/// [`FlowControl`]: struct.FlowControl.html
/// [`Stream`]: https://docs.rs/futures/0.1/futures/stream/trait.Stream.html
#[must_use = "streams do nothing unless polled"]
pub struct RecvStream {
inner: FlowControl,
}
/// A handle to release window capacity to a remote stream.
///
/// This type allows the caller to manage inbound data [flow control]. The
/// caller is expected to call [`release_capacity`] after dropping data frames.
///
/// # Overview
///
/// Each stream has a window size. This window size is the maximum amount of
/// inbound data that can be in-flight. In-flight data is defined as data that
/// has been received, but not yet released.
///
/// When a stream is created, the window size is set to the connection's initial
/// window size value. When a data frame is received, the window size is then
/// decremented by size of the data frame before the data is provided to the
/// caller. As the caller finishes using the data, [`release_capacity`] must be
/// called. This will then increment the window size again, allowing the peer to
/// send more data.
///
/// There is also a connection level window as well as the stream level window.
/// Received data counts against the connection level window as well and calls
/// to [`release_capacity`] will also increment the connection level window.
///
/// # Sending `WINDOW_UPDATE` frames
///
/// `WINDOW_UPDATE` frames will not be sent out for **every** call to
/// `release_capacity`, as this would end up slowing down the protocol. Instead,
/// `h2` waits until the window size is increased to a certain threshold and
/// then sends out a single `WINDOW_UPDATE` frame representing all the calls to
/// `release_capacity` since the last `WINDOW_UPDATE` frame.
///
/// This essentially batches window updating.
///
/// # Scenarios
///
/// Following is a basic scenario with an HTTP/2 connection containing a
/// single active stream.
///
/// * A new stream is activated. The receive window is initialized to 1024 (the
/// value of the initial window size for this connection).
/// * A `DATA` frame is received containing a payload of 600 bytes.
/// * The receive window size is reduced to 424 bytes.
/// * [`release_capacity`] is called with 200.
/// * The receive window size is now 624 bytes. The peer may send no more than
/// this.
/// * A `DATA` frame is received with a payload of 624 bytes.
/// * The window size is now 0 bytes. The peer may not send any more data.
/// * [`release_capacity`] is called with 1024.
/// * The receive window size is now 1024 bytes. The peer may now send more
/// data.
///
/// [flow control]: ../index.html#flow-control
/// [`release_capacity`]: struct.FlowControl.html#method.release_capacity
#[derive(Clone, Debug)]
pub struct FlowControl {
inner: proto::OpaqueStreamRef,
}
/// A handle to send and receive PING frames with the peer.
// NOT Clone on purpose
pub struct PingPong {
inner: proto::UserPings,
}
/// Sent via [`PingPong`][] to send a PING frame to a peer.
///
/// [`PingPong`]: struct.PingPong.html
pub struct Ping {
_p: (),
}
/// Received via [`PingPong`][] when a peer acknowledges a [`Ping`][].
///
/// [`PingPong`]: struct.PingPong.html
/// [`Ping`]: struct.Ping.html
pub struct Pong {
_p: (),
}
// ===== impl SendStream =====
impl<B: Buf> SendStream<B> {
pub(crate) fn new(inner: proto::StreamRef<B>) -> Self {
SendStream { inner }
}
/// Requests capacity to send data.
///
/// This function is used to express intent to send data. This requests
/// connection level capacity. Once the capacity is available, it is
/// assigned to the stream and not reused by other streams.
///
/// This function may be called repeatedly. The `capacity` argument is the
/// **total** amount of requested capacity. Sequential calls to
/// `reserve_capacity` are *not* additive. Given the following:
///
/// ```rust
/// # use h2::*;
/// # fn doc(mut send_stream: SendStream<&'static [u8]>) {
/// send_stream.reserve_capacity(100);
/// send_stream.reserve_capacity(200);
/// # }
/// ```
///
/// After the second call to `reserve_capacity`, the *total* requested
/// capacity will be 200.
///
/// `reserve_capacity` is also used to cancel previous capacity requests.
/// Given the following:
///
/// ```rust
/// # use h2::*;
/// # fn doc(mut send_stream: SendStream<&'static [u8]>) {
/// send_stream.reserve_capacity(100);
/// send_stream.reserve_capacity(0);
/// # }
/// ```
///
/// After the second call to `reserve_capacity`, the *total* requested
/// capacity will be 0, i.e. there is no requested capacity for the stream.
///
/// If `reserve_capacity` is called with a lower value than the amount of
/// capacity **currently** assigned to the stream, this capacity will be
/// returned to the connection to be re-assigned to other streams.
///
/// Also, the amount of capacity that is reserved gets decremented as data
/// is sent. For example:
///
/// ```rust
/// # use h2::*;
/// # async fn doc(mut send_stream: SendStream<&'static [u8]>) {
/// send_stream.reserve_capacity(100);
///
/// send_stream.send_data(b"hello", false).unwrap();
/// // At this point, the total amount of requested capacity is 95 bytes.
///
/// // Calling `reserve_capacity` with `100` again essentially requests an
/// // additional 5 bytes.
/// send_stream.reserve_capacity(100);
/// # }
/// ```
///
/// See [Flow control](struct.SendStream.html#flow-control) for an overview
/// of how send flow control works.
pub fn reserve_capacity(&mut self, capacity: usize) {
// TODO: Check for overflow
self.inner.reserve_capacity(capacity as WindowSize)
}
/// Returns the stream's current send capacity.
///
/// This allows the caller to check the current amount of available capacity
/// before sending data.
pub fn capacity(&self) -> usize {
self.inner.capacity() as usize
}
/// Requests to be notified when the stream's capacity increases.
///
/// Before calling this, capacity should be requested with
/// `reserve_capacity`. Once capacity is requested, the connection will
/// assign capacity to the stream **as it becomes available**. There is no
/// guarantee as to when and in what increments capacity gets assigned to
/// the stream.
///
/// To get notified when the available capacity increases, the caller calls
/// `poll_capacity`, which returns `Ready(Some(n))` when `n` has been
/// increased by the connection. Note that `n` here represents the **total**
/// amount of assigned capacity at that point in time. It is also possible
/// that `n` is lower than the previous call if, since then, the caller has
/// sent data.
pub fn poll_capacity(&mut self, cx: &mut Context) -> Poll<Option<Result<usize, crate::Error>>> {
self.inner
.poll_capacity(cx)
.map_ok(|w| w as usize)
.map_err(Into::into)
}
/// Sends a single data frame to the remote peer.
///
/// This function may be called repeatedly as long as `end_of_stream` is set
/// to `false`. Setting `end_of_stream` to `true` sets the end stream flag
/// on the data frame. Any further calls to `send_data` or `send_trailers`
/// will return an [`Error`].
///
/// `send_data` can be called without reserving capacity. In this case, the
/// data is buffered and the capacity is implicitly requested. Once the
/// capacity becomes available, the data is flushed to the connection.
/// However, this buffering is unbounded. As such, sending large amounts of
/// data without reserving capacity before hand could result in large
/// amounts of data being buffered in memory.
///
/// [`Error`]: struct.Error.html
pub fn send_data(&mut self, data: B, end_of_stream: bool) -> Result<(), crate::Error> {
self.inner
.send_data(data, end_of_stream)
.map_err(Into::into)
}
/// Sends trailers to the remote peer.
///
/// Sending trailers implicitly closes the send stream. Once the send stream
/// is closed, no more data can be sent.
pub fn send_trailers(&mut self, trailers: HeaderMap) -> Result<(), crate::Error> {
self.inner.send_trailers(trailers).map_err(Into::into)
}
/// Resets the stream.
///
/// This cancels the request / response exchange. If the response has not
/// yet been received, the associated `ResponseFuture` will return an
/// [`Error`] to reflect the canceled exchange.
///
/// [`Error`]: struct.Error.html
pub fn send_reset(&mut self, reason: Reason) {
self.inner.send_reset(reason)
}
/// Polls to be notified when the client resets this stream.
///
/// If stream is still open, this returns `Poll::Pending`, and
/// registers the task to be notified if a `RST_STREAM` is received.
///
/// If a `RST_STREAM` frame is received for this stream, calling this
/// method will yield the `Reason` for the reset.
///
/// # Error
///
/// If connection sees an error, this returns that error instead of a
/// `Reason`.
pub fn poll_reset(&mut self, cx: &mut Context) -> Poll<Result<Reason, crate::Error>> {
self.inner.poll_reset(cx, proto::PollReset::Streaming)
}
/// Returns the stream ID of this `SendStream`.
///
/// # Panics
///
/// If the lock on the stream store has been poisoned.
pub fn stream_id(&self) -> StreamId {
StreamId::from_internal(self.inner.stream_id())
}
}
// ===== impl StreamId =====
impl StreamId {
pub(crate) fn from_internal(id: crate::frame::StreamId) -> Self {
StreamId(id.into())
}
/// Returns the `u32` corresponding to this `StreamId`
///
/// # Note
///
/// This is the same as the `From<StreamId>` 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.
pub fn as_u32(&self) -> u32 {
(*self).into()
}
}
// ===== impl RecvStream =====
impl RecvStream {
pub(crate) fn new(inner: FlowControl) -> Self {
RecvStream { inner }
}
/// Get the next data frame.
pub async fn data(&mut self) -> Option<Result<Bytes, crate::Error>> {
crate::poll_fn(move |cx| self.poll_data(cx)).await
}
/// Get optional trailers for this stream.
pub async fn trailers(&mut self) -> Result<Option<HeaderMap>, crate::Error> {
crate::poll_fn(move |cx| self.poll_trailers(cx)).await
}
/// Poll for the next data frame.
pub fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, crate::Error>>> {
self.inner.inner.poll_data(cx).map_err(Into::into)
}
#[doc(hidden)]
pub fn poll_trailers(
&mut self,
cx: &mut Context,
) -> Poll<Result<Option<HeaderMap>, crate::Error>> {
match ready!(self.inner.inner.poll_trailers(cx)) {
Some(Ok(map)) => Poll::Ready(Ok(Some(map))),
Some(Err(e)) => Poll::Ready(Err(e.into())),
None => Poll::Ready(Ok(None)),
}
}
/// Returns true if the receive half has reached the end of stream.
///
/// A return value of `true` means that calls to `poll` and `poll_trailers`
/// will both return `None`.
pub fn is_end_stream(&self) -> bool {
self.inner.inner.is_end_stream()
}
/// Get a mutable reference to this stream's `FlowControl`.
///
/// It can be used immediately, or cloned to be used later.
pub fn flow_control(&mut self) -> &mut FlowControl {
&mut self.inner
}
/// Returns the stream ID of this stream.
///
/// # Panics
///
/// If the lock on the stream store has been poisoned.
pub fn stream_id(&self) -> StreamId {
self.inner.stream_id()
}
}
#[cfg(feature = "stream")]
impl futures_core::Stream for RecvStream {
type Item = Result<Bytes, crate::Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.poll_data(cx)
}
}
impl fmt::Debug for RecvStream {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("RecvStream")
.field("inner", &self.inner)
.finish()
}
}
impl Drop for RecvStream {
fn drop(&mut self) {
// Eagerly clear any received DATA frames now, since its no longer
// possible to retrieve them. However, this will be called
// again once *all* stream refs have been dropped, since
// this won't send a RST_STREAM frame, in case the user wishes to
// still *send* DATA.
self.inner.inner.clear_recv_buffer();
}
}
// ===== impl FlowControl =====
impl FlowControl {
pub(crate) fn new(inner: proto::OpaqueStreamRef) -> Self {
FlowControl { inner }
}
/// Returns the stream ID of the stream whose capacity will
/// be released by this `FlowControl`.
pub fn stream_id(&self) -> StreamId {
StreamId::from_internal(self.inner.stream_id())
}
/// Get the current available capacity of data this stream *could* receive.
pub fn available_capacity(&self) -> isize {
self.inner.available_recv_capacity()
}
/// Get the currently *used* capacity for this stream.
///
/// This is the amount of bytes that can be released back to the remote.
pub fn used_capacity(&self) -> usize {
self.inner.used_recv_capacity() as usize
}
/// Release window capacity back to remote stream.
///
/// This releases capacity back to the stream level and the connection level
/// windows. Both window sizes will be increased by `sz`.
///
/// See [struct level] documentation for more details.
///
/// # Errors
///
/// This function errors if increasing the receive window size by `sz` would
/// result in a window size greater than the target window size. In other
/// words, the caller cannot release more capacity than data has been
/// received. If 1024 bytes of data have been received, at most 1024 bytes
/// can be released.
///
/// [struct level]: #
pub fn release_capacity(&mut self, sz: usize) -> Result<(), crate::Error> {
if sz > proto::MAX_WINDOW_SIZE as usize {
return Err(UserError::ReleaseCapacityTooBig.into());
}
self.inner
.release_capacity(sz as proto::WindowSize)
.map_err(Into::into)
}
}
// ===== impl PingPong =====
impl PingPong {
pub(crate) fn new(inner: proto::UserPings) -> Self {
PingPong { inner }
}
/// Send a PING frame and wait for the peer to send the pong.
pub async fn ping(&mut self, ping: Ping) -> Result<Pong, crate::Error> {
self.send_ping(ping)?;
crate::poll_fn(|cx| self.poll_pong(cx)).await
}
#[doc(hidden)]
pub fn send_ping(&mut self, ping: Ping) -> Result<(), crate::Error> {
// Passing a `Ping` here is just to be forwards-compatible with
// eventually allowing choosing a ping payload. For now, we can
// just ignore it.
let _ = ping;
self.inner.send_ping().map_err(|err| match err {
Some(err) => err.into(),
None => UserError::SendPingWhilePending.into(),
})
}
#[doc(hidden)]
pub fn poll_pong(&mut self, cx: &mut Context) -> Poll<Result<Pong, crate::Error>> {
ready!(self.inner.poll_pong(cx))?;
Poll::Ready(Ok(Pong { _p: () }))
}
}
impl fmt::Debug for PingPong {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("PingPong").finish()
}
}
// ===== impl Ping =====
impl Ping {
/// Creates a new opaque `Ping` to be sent via a [`PingPong`][].
///
/// The payload is "opaque", such that it shouldn't be depended on.
///
/// [`PingPong`]: struct.PingPong.html
pub fn opaque() -> Ping {
Ping { _p: () }
}
}
impl fmt::Debug for Ping {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Ping").finish()
}
}
// ===== impl Pong =====
impl fmt::Debug for Pong {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Pong").finish()
}
}