mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
161
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2afa276c8c | ||
|
|
312c981e3f | ||
|
|
62e24b710c | ||
|
|
dccc58aa37 | ||
|
|
95b943506b | ||
|
|
3d17488d9a | ||
|
|
c63057ebc5 | ||
|
|
1016de28fc | ||
|
|
d2ad49aa6e | ||
|
|
85d8029e9b | ||
|
|
b69d4a6108 | ||
|
|
a517dbf605 | ||
|
|
c0c7124a4b | ||
|
|
2b96b1773d | ||
|
|
f0328f7810 | ||
|
|
30d4ec0a20 | ||
|
|
1167c09ae8 | ||
|
|
7276d47072 | ||
|
|
9f0b6d3166 | ||
|
|
8fda719845 | ||
|
|
646fbae765 | ||
|
|
1562bb3144 | ||
|
|
0366a3e6d1 | ||
|
|
03b68f4e75 | ||
|
|
084fcd7954 | ||
|
|
cc2c358d25 | ||
|
|
51e7933c35 | ||
|
|
027351dd3a | ||
|
|
ff6130da65 | ||
|
|
018e345add | ||
|
|
e3e7cdeaff | ||
|
|
7f29acd964 | ||
|
|
2d97d5ad15 | ||
|
|
d1744bf260 | ||
|
|
de7b8914a9 | ||
|
|
9943acda81 | ||
|
|
4fca1974e9 | ||
|
|
08872c55d1 | ||
|
|
844d9c6acb | ||
|
|
ff7125ec7b | ||
|
|
7a60a0b362 | ||
|
|
94b64cd70d | ||
|
|
b5d2b0d05b | ||
|
|
0e090b7ae2 | ||
|
|
21f726041c | ||
|
|
c344aac925 | ||
|
|
cbb4abc8ae | ||
|
|
14723f9786 | ||
|
|
04a2826084 | ||
|
|
28a93e6044 | ||
|
|
dd28831e13 | ||
|
|
6dcce1901a | ||
|
|
32f46d7b88 | ||
|
|
356c81c977 | ||
|
|
d685bceb03 | ||
|
|
b094ee90e2 | ||
|
|
7e4edb8963 | ||
|
|
fc63fa2606 | ||
|
|
b9e3d2edde | ||
|
|
a23d2b2274 | ||
|
|
98e7831479 | ||
|
|
8411a6945f | ||
|
|
f69e5bfb87 | ||
|
|
2aa8751261 | ||
|
|
be02d36a86 | ||
|
|
cf2c05317c | ||
|
|
f0b2b708a7 | ||
|
|
f75e5a7ef4 | ||
|
|
0ab28627e2 | ||
|
|
a43ec11daf | ||
|
|
3db22e29d1 | ||
|
|
e2adf2612d | ||
|
|
2bc6bc14a8 | ||
|
|
d2f81b506a | ||
|
|
6b6e76080a | ||
|
|
68b4ca9f55 | ||
|
|
1769f65d37 | ||
|
|
1636910f0a | ||
|
|
adaa6849a5 | ||
|
|
0a422593f0 | ||
|
|
d22301967b | ||
|
|
4010335c84 | ||
|
|
be4577e22f | ||
|
|
e70a1b6d64 | ||
|
|
9264b837d8 | ||
|
|
db0d6d75b3 | ||
|
|
f2f30d4cf6 | ||
|
|
c624cb8ce3 | ||
|
|
f7574d9023 | ||
|
|
954f2b7304 | ||
|
|
d562e58871 | ||
|
|
9f63911adc | ||
|
|
1e54a35325 | ||
|
|
4f4f4807c3 | ||
|
|
8fda5f1984 | ||
|
|
7cb5e3460c | ||
|
|
9b81580be6 | ||
|
|
9b6744cc8e | ||
|
|
4563699838 | ||
|
|
f48065910e | ||
|
|
a5c1a7de03 | ||
|
|
a343b1d180 | ||
|
|
b44ab27359 | ||
|
|
02661ba30a | ||
|
|
fb7dfcf432 | ||
|
|
a32f918671 | ||
|
|
221f421464 | ||
|
|
1cc0168335 | ||
|
|
67220eac37 | ||
|
|
3ba818a177 | ||
|
|
6aeeeff6e8 | ||
|
|
a75fe38ba5 | ||
|
|
adce911b02 | ||
|
|
8565a98601 | ||
|
|
bff21aba6c | ||
|
|
07533a5255 | ||
|
|
4748b2571f | ||
|
|
66fef4a9bc | ||
|
|
13e2a366de | ||
|
|
cc8a662598 | ||
|
|
264ae3bdb2 | ||
|
|
187af2e6a3 | ||
|
|
31315b9463 | ||
|
|
20b5df9037 | ||
|
|
fa9743f0d4 | ||
|
|
7a89d66513 | ||
|
|
45773c5641 | ||
|
|
c52b78b792 | ||
|
|
1d28060836 | ||
|
|
947045b944 | ||
|
|
2c53bebe56 | ||
|
|
0f4287ac2b | ||
|
|
1bf1928088 | ||
|
|
a26d3aec96 | ||
|
|
a819584849 | ||
|
|
70ed3c7f04 | ||
|
|
ce9eabfdd1 | ||
|
|
894eb8b83f | ||
|
|
3572ba5a7b | ||
|
|
d8139fef7a | ||
|
|
9bcb50660e | ||
|
|
a3aab864d7 | ||
|
|
236629d1be | ||
|
|
f83f6388c4 | ||
|
|
13974068f9 | ||
|
|
6349efd237 | ||
|
|
2da15b5f24 | ||
|
|
7e88b56be5 | ||
|
|
43bbbf61a2 | ||
|
|
282b00cbe8 | ||
|
|
5a548044d7 | ||
|
|
a748da1031 | ||
|
|
6edc64afc7 | ||
|
|
8f3a265972 | ||
|
|
800574b4e0 | ||
|
|
19a87e090e | ||
|
|
6f00d7158b | ||
|
|
67c4cc0391 | ||
|
|
8381dff39b | ||
|
|
9553355c27 | ||
|
|
770d0ec452 |
+2
-14
@@ -1,5 +1,5 @@
|
||||
freebsd_instance:
|
||||
image: freebsd-12-0-release-amd64
|
||||
image: freebsd-12-1-release-amd64
|
||||
|
||||
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
|
||||
# same VM. The binary will be built in 32-bit mode, but will execute on a
|
||||
@@ -19,18 +19,6 @@ task:
|
||||
- |
|
||||
echo "~~~~ rustc --version ~~~~"
|
||||
rustc --version
|
||||
|
||||
# Remove any existing patch statements
|
||||
mv Cargo.toml Cargo.toml.bck
|
||||
sed -n '/\[patch.crates-io\]/q;p' Cargo.toml.bck > Cargo.toml
|
||||
|
||||
# Patch all crates
|
||||
cat ci/patch.toml >> Cargo.toml
|
||||
|
||||
# Print `Cargo.toml` for debugging
|
||||
echo "~~~~ Cargo.toml ~~~~"
|
||||
cat Cargo.toml
|
||||
echo "~~~~~~~~~~~~~~~~~~~~"
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all
|
||||
@@ -39,4 +27,4 @@ task:
|
||||
# i686_test_script:
|
||||
# - . $HOME/.cargo/env
|
||||
# - |
|
||||
# cargo test --all --exclude tokio-tls --exclude tokio-macros --target i686-unknown-freebsd
|
||||
# cargo test --all --exclude tokio-macros --target i686-unknown-freebsd
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
<!--
|
||||
Thank you for reporting an issue.
|
||||
|
||||
Please fill in as much of the template below as you're able.
|
||||
-->
|
||||
|
||||
## Version
|
||||
|
||||
<!--
|
||||
List the versions of all `tokio` crates you are using. The easiest way to get
|
||||
this information is using `cargo-tree`.
|
||||
|
||||
`cargo install cargo-tree`
|
||||
(see install here: https://github.com/sfackler/cargo-tree)
|
||||
|
||||
Then:
|
||||
|
||||
`cargo tree | grep tokio`
|
||||
-->
|
||||
|
||||
## Platform
|
||||
|
||||
<!---
|
||||
Output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows)
|
||||
-->
|
||||
|
||||
## Subcrates
|
||||
|
||||
<!--
|
||||
If known, please specify the affected Tokio sub crates. Otherwise, delete this
|
||||
section.
|
||||
-->
|
||||
|
||||
## Description
|
||||
|
||||
<!--
|
||||
|
||||
Enter your issue details below this comment.
|
||||
|
||||
One way to structure the description:
|
||||
|
||||
<short summary of the bug>
|
||||
|
||||
I tried this code:
|
||||
|
||||
<code sample that causes the bug>
|
||||
|
||||
I expected to see this happen: <explanation>
|
||||
|
||||
Instead, this happened: <explanation>
|
||||
-->
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: A-tokio, C-bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Version**
|
||||
List the versions of all `tokio` crates you are using. The easiest way to get
|
||||
this information is using `cargo-tree`.
|
||||
|
||||
`cargo install cargo-tree`
|
||||
(see install here: https://github.com/sfackler/cargo-tree)
|
||||
|
||||
Then:
|
||||
|
||||
`cargo tree | grep tokio`
|
||||
|
||||
**Platform**
|
||||
The output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows)
|
||||
|
||||
**Description**
|
||||
Enter your issue details here.
|
||||
One way to structure the description:
|
||||
|
||||
[short summary of the bug]
|
||||
|
||||
I tried this code:
|
||||
|
||||
[code sample that causes the bug]
|
||||
|
||||
I expected to see this happen: [explanation]
|
||||
|
||||
Instead, this happened: [explanation]
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: A-tokio, C-feature-request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Question
|
||||
about: Please use the discussions tab for questions
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please post your question as a discussion here:
|
||||
https://github.com/tokio-rs/tokio/discussions
|
||||
|
||||
|
||||
You may also be able to find help here:
|
||||
https://discord.gg/tokio
|
||||
https://users.rust-lang.org/
|
||||
@@ -5,6 +5,9 @@ the requirements below.
|
||||
Bug fixes and new features should include tests.
|
||||
|
||||
Contributors guide: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md
|
||||
|
||||
The contributors guide includes instructions for running rustfmt and building the
|
||||
documentation, which requires special commands beyond `cargo fmt` and `cargo doc`.
|
||||
-->
|
||||
|
||||
## Motivation
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Security Audit
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '**/Cargo.toml'
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||
|
||||
jobs:
|
||||
security-audit:
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, 'ci skip')"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Audit Check
|
||||
uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,266 @@
|
||||
on:
|
||||
push:
|
||||
branches: ["v0.2.x"]
|
||||
pull_request:
|
||||
branches: ["v0.2.x"]
|
||||
|
||||
name: CI
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
nightly: nightly-2020-09-21
|
||||
minrust: 1.39.0
|
||||
|
||||
jobs:
|
||||
# Depends on all action sthat are required for a "successful" CI run.
|
||||
tests-pass:
|
||||
name: all systems go
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- test
|
||||
- test-unstable
|
||||
- miri
|
||||
- cross
|
||||
- features
|
||||
- minrust
|
||||
- fmt
|
||||
# - clippy
|
||||
- docs
|
||||
- loom
|
||||
steps:
|
||||
- run: exit 0
|
||||
|
||||
test:
|
||||
name: test tokio full
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- windows-latest
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- name: Install cargo-hack
|
||||
run: cargo install cargo-hack
|
||||
|
||||
# Run `tokio` with `full` features. This excludes testing utilities which
|
||||
# can alter the runtime behavior of Tokio.
|
||||
- name: test tokio full
|
||||
run: cargo test --features full
|
||||
working-directory: tokio
|
||||
|
||||
# Check `tokio` with `full + parking_lot` to make sure it compiles.
|
||||
- name: check tokio full,parking_lot
|
||||
run: cargo check --features full,parking_lot
|
||||
working-directory: tokio
|
||||
|
||||
# Test **all** crates in the workspace with all features.
|
||||
- name: test all --all-features
|
||||
run: cargo test --workspace --all-features
|
||||
|
||||
# Run integration tests for each feature
|
||||
- name: test tests-integration --each-feature
|
||||
run: cargo hack test --each-feature
|
||||
working-directory: tests-integration
|
||||
|
||||
# Run macro build tests
|
||||
- name: test tests-build --each-feature
|
||||
run: cargo hack test --each-feature
|
||||
working-directory: tests-build
|
||||
|
||||
test-unstable:
|
||||
name: test tokio full --unstable
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- windows-latest
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
|
||||
# Run `tokio` with "unstable" cfg flag.
|
||||
- name: test tokio full --cfg unstable
|
||||
run: cargo test --features full
|
||||
working-directory: tokio
|
||||
env:
|
||||
RUSTFLAGS: '--cfg tokio_unstable'
|
||||
|
||||
miri:
|
||||
name: miri
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
override: true
|
||||
- name: Install Miri
|
||||
run: |
|
||||
set -e
|
||||
rustup component add miri
|
||||
cargo miri setup
|
||||
rm -rf tokio/tests
|
||||
|
||||
- name: miri
|
||||
run: cargo miri test --features rt-core,rt-threaded,rt-util,sync task
|
||||
working-directory: tokio
|
||||
|
||||
cross:
|
||||
name: cross
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
- i686-unknown-linux-gnu
|
||||
- powerpc-unknown-linux-gnu
|
||||
- powerpc64-unknown-linux-gnu
|
||||
- mips-unknown-linux-gnu
|
||||
- arm-linux-androideabi
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
command: check
|
||||
args: --workspace --target ${{ matrix.target }}
|
||||
|
||||
features:
|
||||
name: features
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
override: true
|
||||
- name: Install cargo-hack
|
||||
run: cargo install cargo-hack
|
||||
|
||||
- name: check --each-feature
|
||||
run: cargo hack check --all --each-feature -Z avoid-dev-deps
|
||||
|
||||
# Try with unstable feature flags
|
||||
- name: check --each-feature --unstable
|
||||
run: cargo hack check --all --each-feature -Z avoid-dev-deps
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable
|
||||
|
||||
minrust:
|
||||
name: minrust
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.minrust }}
|
||||
override: true
|
||||
|
||||
- name: "test --workspace --all-features"
|
||||
run: cargo check --workspace --all-features
|
||||
|
||||
minimal-versions:
|
||||
name: minimal-versions
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
override: true
|
||||
- name: Install cargo-hack
|
||||
run: cargo install cargo-hack
|
||||
- name: "check --all-features -Z minimal-versions"
|
||||
run: |
|
||||
# Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
|
||||
# from determining minimal versions based on dev-dependencies.
|
||||
cargo hack --remove-dev-deps --workspace
|
||||
# Update Cargo.lock to minimal version dependencies.
|
||||
cargo update -Z minimal-versions
|
||||
cargo check --all-features
|
||||
|
||||
fmt:
|
||||
name: fmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- name: Install rustfmt
|
||||
run: rustup component add rustfmt
|
||||
|
||||
# Check fmt
|
||||
- name: "rustfmt --check"
|
||||
# Workaround for rust-lang/cargo#7732
|
||||
run: |
|
||||
if ! rustfmt --check --edition 2018 $(find . -name '*.rs' -print); then
|
||||
printf "Please run \`rustfmt --edition 2018 \$(find . -name '*.rs' -print)\` to fix rustfmt errors.\nSee CONTRIBUTING.md for more details.\n" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# This branch no longer actively developed. Most commits to this
|
||||
# branch are backporting and should not be blocked by clippy.
|
||||
# clippy:
|
||||
# name: clippy
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Install Rust
|
||||
# run: rustup update stable
|
||||
# - name: Install clippy
|
||||
# run: rustup component add clippy
|
||||
#
|
||||
# # Run clippy
|
||||
# - name: "clippy --all"
|
||||
# run: cargo clippy --all --tests
|
||||
|
||||
docs:
|
||||
name: docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
override: true
|
||||
|
||||
- name: "doc --lib --all-features"
|
||||
run: cargo doc --lib --no-deps --all-features
|
||||
env:
|
||||
RUSTDOCFLAGS: --cfg docsrs
|
||||
|
||||
loom:
|
||||
name: loom
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
scope:
|
||||
- --skip loom_pool
|
||||
- loom_pool::group_a
|
||||
- loom_pool::group_b
|
||||
- loom_pool::group_c
|
||||
- loom_pool::group_d
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
|
||||
- name: loom ${{ matrix.scope }}
|
||||
run: cargo test --lib --release --features full -- --nocapture $SCOPE
|
||||
working-directory: tokio
|
||||
env:
|
||||
RUSTFLAGS: --cfg loom --cfg tokio_unstable
|
||||
LOOM_MAX_PREEMPTIONS: 2
|
||||
SCOPE: ${{ matrix.scope }}
|
||||
@@ -0,0 +1,32 @@
|
||||
name: Pull Request Security Audit
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '**/Cargo.toml'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**/Cargo.toml'
|
||||
|
||||
jobs:
|
||||
security-audit:
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, 'ci skip')"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install cargo-audit
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: cargo-audit
|
||||
|
||||
- name: Generate lockfile
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: generate-lockfile
|
||||
|
||||
- name: Audit dependencies
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: audit
|
||||
+97
-10
@@ -15,12 +15,14 @@ It should be considered a map to help you navigate the process.
|
||||
The [dev channel][dev] is available for any concerns not covered in this guide, please join
|
||||
us!
|
||||
|
||||
[dev]: https://discord.gg/6yGkFeN
|
||||
[dev]: https://discord.gg/tokio
|
||||
|
||||
## Conduct
|
||||
|
||||
The Tokio project adheres to the [Rust Code of Conduct][coc]. This describes
|
||||
the _minimum_ behavior expected from all contributors. Instances of violations of the Code of Conduct can be reported by contacting the project team at [[email protected]](mailto:[email protected]).
|
||||
the _minimum_ behavior expected from all contributors. Instances of violations of the
|
||||
Code of Conduct can be reported by contacting the project team at
|
||||
[[email protected]](mailto:[email protected]).
|
||||
|
||||
[coc]: https://github.com/rust-lang/rust/blob/master/CODE_OF_CONDUCT.md
|
||||
|
||||
@@ -29,8 +31,8 @@ the _minimum_ behavior expected from all contributors. Instances of violations o
|
||||
For any issue, there are fundamentally three ways an individual can contribute:
|
||||
|
||||
1. By opening the issue for discussion: For instance, if you believe that you
|
||||
have uncovered a bug in Tokio, creating a new issue in the tokio-rs/tokio
|
||||
issue tracker is the way to report it.
|
||||
have discovered a bug in Tokio, creating a new issue in [the tokio-rs/tokio
|
||||
issue tracker][issue] is the way to report it.
|
||||
|
||||
2. By helping to triage the issue: This can be done by providing
|
||||
supporting details (a test case that demonstrates a bug), providing
|
||||
@@ -42,21 +44,25 @@ For any issue, there are fundamentally three ways an individual can contribute:
|
||||
often, by opening a Pull Request that changes some bit of something in
|
||||
Tokio in a concrete and reviewable manner.
|
||||
|
||||
[issue]: https://github.com/tokio-rs/tokio/issues
|
||||
|
||||
**Anybody can participate in any stage of contribution**. We urge you to
|
||||
participate in the discussion around bugs and participate in reviewing PRs.
|
||||
|
||||
### Asking for General Help
|
||||
|
||||
If you have reviewed existing documentation and still have questions or are
|
||||
having problems, you can open an issue asking for help.
|
||||
having problems, you can [open a discussion] asking for help.
|
||||
|
||||
In exchange for receiving help, we ask that you contribute back a documentation
|
||||
PR that helps others avoid the problems that you encountered.
|
||||
|
||||
[open a discussion]: https://github.com/tokio-rs/tokio/discussions/new
|
||||
|
||||
### Submitting a Bug Report
|
||||
|
||||
When opening a new issue in the Tokio issue tracker, users will be presented
|
||||
with a [basic template][template] that should be filled in. If you believe that you have
|
||||
When opening a new issue in the Tokio issue tracker, you will be presented
|
||||
with a basic template that should be filled in. If you believe that you have
|
||||
uncovered a bug, please fill out this form, following the template to the best
|
||||
of your ability. Do not worry if you cannot answer every detail, just fill in
|
||||
what you can.
|
||||
@@ -72,7 +78,6 @@ cases should be limited, as much as possible, to using only Tokio APIs.
|
||||
See [How to create a Minimal, Complete, and Verifiable example][mcve].
|
||||
|
||||
[mcve]: https://stackoverflow.com/help/mcve
|
||||
[template]: .github/PULL_REQUEST_TEMPLATE.md
|
||||
|
||||
### Triaging a Bug Report
|
||||
|
||||
@@ -132,8 +137,13 @@ RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
|
||||
```
|
||||
The `cargo fmt` command does not work on the Tokio codebase. You can use the
|
||||
command below instead:
|
||||
|
||||
```
|
||||
# Mac or Linux
|
||||
rustfmt --check --edition 2018 $(find . -name '*.rs' -print)
|
||||
|
||||
# Powershell
|
||||
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2018 $_.FullName }
|
||||
```
|
||||
The `--check` argument prints the things that need to be fixed. If you remove
|
||||
it, `rustfmt` will update your files locally instead.
|
||||
@@ -250,7 +260,7 @@ That said, if you have a number of commits that are "checkpoints" and don't
|
||||
represent a single logical change, please squash those together.
|
||||
|
||||
Note that multiple commits often get squashed when they are landed (see the
|
||||
notes about [commit squashing]).
|
||||
notes about [commit squashing](#commit-squashing)).
|
||||
|
||||
#### Commit message guidelines
|
||||
|
||||
@@ -321,7 +331,7 @@ in order to evaluate whether the changes are correct and necessary.
|
||||
Keep an eye out for comments from code owners to provide guidance on conflicting
|
||||
feedback.
|
||||
|
||||
**Once the PR is open, do not rebase the commits**. See [Commit Squashing] for
|
||||
**Once the PR is open, do not rebase the commits**. See [Commit Squashing](#commit-squashing) for
|
||||
more details.
|
||||
|
||||
### Commit Squashing
|
||||
@@ -415,6 +425,83 @@ _Adapted from the [Node.js contributing guide][node]_.
|
||||
[hiding-a-comment]: https://help.github.com/articles/managing-disruptive-comments/#hiding-a-comment
|
||||
[documentation test]: https://doc.rust-lang.org/rustdoc/documentation-tests.html
|
||||
|
||||
## Keeping track of issues and PRs
|
||||
|
||||
The Tokio GitHub repository has a lot of issues and PRs, which is not easy to
|
||||
keep track of. This section explains the meaning of various labels, as well as
|
||||
our [GitHub project][project]. The section is primarily targeted at maintainers.
|
||||
|
||||
**Area.** The area label describes the crates relevant to this issue or PR.
|
||||
|
||||
- **A-tokio** This issue concerns the main Tokio crate.
|
||||
- **A-tokio-util** This issue concerns the `tokio-util` crate.
|
||||
- **A-tokio-tls** This issue concerns the `tokio-tls` crate. Only used for
|
||||
older issues, as the crate has been moved to another repository.
|
||||
- **A-tokio-test** The issue concerns the `tokio-test` crate.
|
||||
- **A-tokio-macros** This issue concerns the `tokio-macros` crate. Should only
|
||||
be used for the procedural macros, and not `join!` or `select!`.
|
||||
- **A-ci** This issue concerns our GitHub Actions setup.
|
||||
|
||||
**Category.** The category label describes the category.
|
||||
|
||||
- **C-bug** This is a bug-report. Bug-fix PRs use `C-enhancement` instead.
|
||||
- **C-enhancement** This is a PR that adds a new features.
|
||||
- **C-maintenance** This is an issue or PR about stuff such as documentation,
|
||||
GitHub Actions or code quality.
|
||||
- **C-feature-request** This is a feature request. Implementations of feature
|
||||
requests use `C-enhancement` instead.
|
||||
- **C-feature-accepted** If you submit a PR for this feature request, we wont
|
||||
close it with the reason "we don't want this". Issues with this label should
|
||||
also have the `C-feature-request` label.
|
||||
- **C-musing** Stuff like tracking issues or roadmaps. "musings about a better
|
||||
world"
|
||||
- **C-proposal** A proposal of some kind, and a request for comments.
|
||||
- **C-question** A user question. Large overlap with GitHub discussions.
|
||||
- **C-request** A non-feature request, e.g. "please add deprecation notices to
|
||||
`-alpha.*` versions of crates"
|
||||
|
||||
**Call for participation.** I don't know why it's called `E-`. Many issues are
|
||||
missing a difficulty rating, and you should feel free to add one.
|
||||
|
||||
- **E-help-wanted** Stuff where we want help. Often seen together with `C-bug`
|
||||
or `C-feature-accepted`.
|
||||
- **E-easy** This is easy, ranging from quick documentation fixes to stuff you
|
||||
can do after reading the tutorial on our website.
|
||||
- **E-medium** This is not `E-easy` or `E-hard`.
|
||||
- **E-hard** This either involves very tricky code, is something we don't know
|
||||
how to solve, or is difficult for some other reason.
|
||||
- **E-needs-mvce** This bug is missing a minimal complete and verifiable
|
||||
example.
|
||||
|
||||
**Module.** A more fine groaned categorization than area.
|
||||
|
||||
- **M-blocking** Things relevant to `spawn_blocking`, `block_in_place`.
|
||||
- **M-codec** The `tokio_util::codec` module.
|
||||
- **M-compat** The `tokio_util::compat` module.
|
||||
- **M-coop** Things relevant to coop.
|
||||
- **M-fs** The `tokio::fs` module.
|
||||
- **M-io** The `tokio::io` module.
|
||||
- **M-macros** Issues about any kind of macro.
|
||||
- **M-net** The `tokio::net` module.
|
||||
- **M-process** The `tokio::process` module.
|
||||
- **M-runtime** The `tokio::runtime` module.
|
||||
- **M-signal** The `tokio::signal` module.
|
||||
- **M-stream** The `tokio::stream` module.
|
||||
- **M-sync** The `tokio::sync` module.
|
||||
- **M-task** The `tokio::task` module.
|
||||
- **M-time** The `tokio::time` module.
|
||||
- **M-tracing** Tracing support in Tokio.
|
||||
|
||||
**Topic.** Some extra information.
|
||||
|
||||
- **T-docs** This is about documentation.
|
||||
- **T-performance** This is about performance.
|
||||
- **T-v0.1.x** This is about old Tokio.
|
||||
|
||||
Any label not listed here is not in active use.
|
||||
|
||||
[project]: https://github.com/orgs/tokio-rs/projects/1
|
||||
|
||||
## Releasing
|
||||
|
||||
Since the Tokio project consists of a number of crates, many of which depend on
|
||||
|
||||
@@ -4,7 +4,6 @@ members = [
|
||||
"tokio",
|
||||
"tokio-macros",
|
||||
"tokio-test",
|
||||
"tokio-tls",
|
||||
"tokio-util",
|
||||
|
||||
# Internal
|
||||
|
||||
@@ -20,14 +20,14 @@ the Rust programming language. It is:
|
||||
[crates-badge]: https://img.shields.io/crates/v/tokio.svg
|
||||
[crates-url]: https://crates.io/crates/tokio
|
||||
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[mit-url]: LICENSE
|
||||
[mit-url]: https://github.com/tokio-rs/tokio/blob/master/LICENSE
|
||||
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.tokio?branchName=master
|
||||
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=1&branchName=master
|
||||
[discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord&style=flat-square
|
||||
[discord-url]: https://discord.gg/tokio
|
||||
|
||||
[Website](https://tokio.rs) |
|
||||
[Guides](https://tokio.rs/docs/overview/) |
|
||||
[Guides](https://tokio.rs/tokio/tutorial) |
|
||||
[API Docs](https://docs.rs/tokio/latest/tokio) |
|
||||
[Roadmap](https://github.com/tokio-rs/tokio/blob/master/ROADMAP.md) |
|
||||
[Chat](https://discord.gg/tokio)
|
||||
@@ -90,19 +90,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
```
|
||||
|
||||
More examples can be found [here](examples).
|
||||
More examples can be found [here][examples]. For a larger "real world" example, see the
|
||||
[mini-redis] repository.
|
||||
|
||||
[examples]: https://github.com/tokio-rs/tokio/tree/master/examples
|
||||
[mini-redis]: https://github.com/tokio-rs/mini-redis/
|
||||
|
||||
To see a list of the available features flags that can be enabled, check our
|
||||
[docs][feature-flag-docs].
|
||||
|
||||
## Getting Help
|
||||
|
||||
First, see if the answer to your question can be found in the [Guides] or the
|
||||
[API documentation]. If the answer is not there, there is an active community in
|
||||
the [Tokio Discord server][chat]. We would be happy to try to answer your
|
||||
question. Last, if that doesn't work, try opening an [issue] with the question.
|
||||
question. You can also ask your question on [the discussions page][discussions].
|
||||
|
||||
[Guides]: https://tokio.rs/docs/overview/
|
||||
[Guides]: https://tokio.rs/tokio/tutorial
|
||||
[API documentation]: https://docs.rs/tokio/latest/tokio
|
||||
[chat]: https://discord.gg/tokio
|
||||
[issue]: https://github.com/tokio-rs/tokio/issues/new
|
||||
[discussions]: https://github.com/tokio-rs/tokio/discussions
|
||||
[feature-flag-docs]: https://docs.rs/tokio/#feature-flags
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -149,15 +157,15 @@ several other libraries, including:
|
||||
|
||||
## Supported Rust Versions
|
||||
|
||||
Tokio is built against the latest stable, nightly, and beta Rust releases. The
|
||||
minimum version supported is the stable release from three months before the
|
||||
current stable release version. For example, if the latest stable Rust is 1.29,
|
||||
the minimum version supported is 1.26. The current Tokio version is not
|
||||
guaranteed to build on Rust versions earlier than the minimum supported version.
|
||||
Tokio is built against the latest stable release. The minimum supported version is 1.39.
|
||||
The current Tokio version is not guaranteed to build on Rust versions earlier than the
|
||||
minimum supported version.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
This project is licensed under the [MIT license].
|
||||
|
||||
[MIT license]: https://github.com/tokio-rs/tokio/blob/master/LICENSE
|
||||
|
||||
### Contribution
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
## Report a security issue
|
||||
|
||||
The Tokio project team welcomes security reports and is committed to providing prompt attention to security issues. Security issues should be reported privately via [[email protected]](mailto:[email protected]). Security issues should not be reported via the public Github Issue tracker.
|
||||
|
||||
## Vulnerability coordination
|
||||
|
||||
Remediation of security vulnerabilities is prioritized by the project team. The project team coordinates remediation with third-party project stakeholders via [Github Security Advisories](https://help.github.com/en/github/managing-security-vulnerabilities/about-github-security-advisories). Third-party stakeholders may include the reporter of the issue, affected direct or indirect users of Tokio, and maintainers of upstream dependencies if applicable.
|
||||
|
||||
Downstream project maintainers and Tokio users can request participation in coordination of applicable security issues by sending your contact email address, Github username(s) and any other salient information to [[email protected]](mailto:[email protected]). Participation in security issue coordination processes is at the discretion of the Tokio team.
|
||||
|
||||
## Security advisories
|
||||
|
||||
The project team is committed to transparency in the security issue disclosure process. The Tokio team announces security issues via [project Github Release notes](https://github.com/tokio-rs/tokio/releases) and the [RustSec advisory database](https://github.com/RustSec/advisory-db) (i.e. `cargo-audit`).
|
||||
@@ -1,121 +0,0 @@
|
||||
trigger: ["master"]
|
||||
pr: ["master"]
|
||||
|
||||
variables:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
nightly: nightly-2020-01-25
|
||||
|
||||
jobs:
|
||||
# Test top level crate
|
||||
- template: ci/azure-test-stable.yml
|
||||
parameters:
|
||||
name: test_tokio
|
||||
rust: stable
|
||||
displayName: Test tokio
|
||||
cross: true
|
||||
crates:
|
||||
- tokio
|
||||
- tests-integration
|
||||
|
||||
# Test sub crates
|
||||
- template: ci/azure-test-stable.yml
|
||||
parameters:
|
||||
name: test_linux
|
||||
displayName: Test sub crates -
|
||||
rust: stable
|
||||
crates:
|
||||
- tokio-macros
|
||||
- tokio-test
|
||||
- tokio-tls
|
||||
- tokio-util
|
||||
- examples
|
||||
|
||||
# Run integration tests
|
||||
- template: ci/azure-test-integration.yml
|
||||
parameters:
|
||||
name: test_integration
|
||||
displayName: Integration tests
|
||||
rust: stable
|
||||
|
||||
# Run tests from `tests-build`. This requires a different process
|
||||
- template: ci/azure-test-build.yml
|
||||
parameters:
|
||||
name: test_build
|
||||
displayName: Test build permutations
|
||||
rust: stable
|
||||
|
||||
# Run miri tests
|
||||
- template: ci/azure-miri.yml
|
||||
parameters:
|
||||
name: miri
|
||||
|
||||
# Try cross compiling
|
||||
- template: ci/azure-cross-compile.yml
|
||||
parameters:
|
||||
name: cross
|
||||
rust: stable
|
||||
|
||||
# Check each feature works properly
|
||||
- template: ci/azure-check-features.yml
|
||||
parameters:
|
||||
rust: $(nightly)
|
||||
name: check_features
|
||||
|
||||
# This represents the minimum Rust version supported by
|
||||
# Tokio. Updating this should be done in a dedicated PR and
|
||||
# cannot be greater than two 0.x releases prior to the
|
||||
# current stable.
|
||||
#
|
||||
# Tests are not run as tests may require newer versions of
|
||||
# rust.
|
||||
- template: ci/azure-check-minrust.yml
|
||||
parameters:
|
||||
name: minrust
|
||||
rust: 1.39.0
|
||||
|
||||
# Check formatting
|
||||
- template: ci/azure-rustfmt.yml
|
||||
parameters:
|
||||
rust: stable
|
||||
name: rustfmt
|
||||
|
||||
# Apply clippy lints to all crates
|
||||
- template: ci/azure-clippy.yml
|
||||
parameters:
|
||||
rust: stable
|
||||
name: clippy
|
||||
|
||||
# Check doc generation
|
||||
- template: ci/azure-check-docs.yml
|
||||
parameters:
|
||||
rust: $(nightly)
|
||||
name: docs
|
||||
|
||||
# - template: ci/azure-tsan.yml
|
||||
# parameters:
|
||||
# name: tsan
|
||||
# rust: stable
|
||||
|
||||
# Run loom tests
|
||||
- template: ci/azure-loom.yml
|
||||
parameters:
|
||||
name: loom
|
||||
rust: stable
|
||||
|
||||
- template: ci/azure-deploy-docs.yml
|
||||
parameters:
|
||||
rust: stable
|
||||
dependsOn:
|
||||
- rustfmt
|
||||
- docs
|
||||
- clippy
|
||||
- test_tokio
|
||||
- test_linux
|
||||
- test_integration
|
||||
- test_build
|
||||
- loom
|
||||
- miri
|
||||
- cross
|
||||
- minrust
|
||||
- check_features
|
||||
# - tsan
|
||||
@@ -1,29 +0,0 @@
|
||||
parameters:
|
||||
noDefaultFeatures: '--no-default-features'
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-is-release.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
- ${{ each feature in crate.value }}:
|
||||
- script: cargo check ${{ parameters.noDefaultFeatures }} --features ${{ feature }}
|
||||
displayName: Check `${{ crate.key }}`, features = ${{ feature }}
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate.key }}
|
||||
condition: and(succeeded(), not(variables['isRelease']))
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
- ${{ each feature in crate.value }}:
|
||||
- script: cargo check ${{ parameters.noDefaultFeatures }} --features ${{ feature }}
|
||||
displayName: Check `${{ crate.key }}`, features = ${{ feature }}
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate.key }}
|
||||
@@ -1,15 +0,0 @@
|
||||
jobs:
|
||||
# Check docs
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Check docs
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- script: |
|
||||
RUSTDOCFLAGS="--cfg docsrs" cargo doc --lib --no-deps --all-features
|
||||
displayName: Check docs
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Check features
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
vmImage: ubuntu-16.04
|
||||
MacOS:
|
||||
vmImage: macos-latest
|
||||
Windows:
|
||||
vmImage: vs2017-win2016
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- script: cargo install cargo-hack
|
||||
displayName: Install cargo-hack
|
||||
|
||||
# Check each feature works properly
|
||||
# * --each-feature
|
||||
# run for each feature which includes --no-default-features and default features of package
|
||||
# * -Z avoid-dev-deps
|
||||
# build without dev-dependencies to avoid https://github.com/rust-lang/cargo/issues/4866
|
||||
# tracking-issue: https://github.com/rust-lang/cargo/issues/5133
|
||||
- script: cargo hack check --all --each-feature -Z avoid-dev-deps
|
||||
displayName: cargo hack check --all --each-feature
|
||||
@@ -1,14 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Min supported Rust version
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- script: cargo check --all
|
||||
displayName: cargo check --all
|
||||
@@ -1,16 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Clippy
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
- script: |
|
||||
rustup component add clippy
|
||||
cargo clippy --version
|
||||
displayName: Install clippy
|
||||
- script: |
|
||||
cargo clippy --all --all-features
|
||||
displayName: cargo clippy --all
|
||||
@@ -1,44 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
strategy:
|
||||
matrix:
|
||||
i686:
|
||||
vmImage: ubuntu-16.04
|
||||
target: i686-unknown-linux-gnu
|
||||
powerpc:
|
||||
vmImage: ubuntu-16.04
|
||||
target: powerpc-unknown-linux-gnu
|
||||
powerpc64:
|
||||
vmImage: ubuntu-16.04
|
||||
target: powerpc64-unknown-linux-gnu
|
||||
mips:
|
||||
vmImage: ubuntu-16.04
|
||||
target: mips-unknown-linux-gnu
|
||||
arm:
|
||||
vmImage: ubuntu-16.04
|
||||
target: arm-linux-androideabi
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- script: sudo apt-get update
|
||||
displayName: apt-get update
|
||||
|
||||
- script: sudo apt-get install gcc-multilib
|
||||
displayName: Install gcc-multilib
|
||||
|
||||
- script: cargo install cross
|
||||
displayName: Install cross
|
||||
|
||||
# Always patch
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- script: cross check --all --exclude tokio-tls --target $(target)
|
||||
displayName: Check source
|
||||
|
||||
# - script: cross check --tests --all --exclude tokio-tls --target $(target)
|
||||
# displayName: Check tests
|
||||
@@ -1,39 +0,0 @@
|
||||
parameters:
|
||||
dependsOn: []
|
||||
|
||||
jobs:
|
||||
- job: documentation
|
||||
displayName: 'Deploy API Documentation'
|
||||
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
dependsOn:
|
||||
- ${{ parameters.dependsOn }}
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
# rust_version: stable
|
||||
rust_version: ${{ parameters.rust }}
|
||||
- script: |
|
||||
cargo doc --all --no-deps --all-features
|
||||
cp -R target/doc '$(Build.BinariesDirectory)'
|
||||
displayName: 'Generate Documentation'
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
git --version
|
||||
ls -la
|
||||
git init
|
||||
git config user.name 'Deployment Bot (from Azure Pipelines)'
|
||||
git config user.email '[email protected]'
|
||||
git config --global credential.helper 'store --file ~/.my-credentials'
|
||||
printf "protocol=https\nhost=github.com\nusername=carllerche\npassword=%s\n\n" "$GITHUB_TOKEN" | git credential-store --file ~/.my-credentials store
|
||||
git remote add origin https://github.com/tokio-rs/tokio
|
||||
git checkout -b gh-pages
|
||||
git add .
|
||||
git commit -m 'Deploy Tokio API documentation'
|
||||
git push -f origin gh-pages
|
||||
env:
|
||||
GITHUB_TOKEN: $(githubPersonalToken)
|
||||
workingDirectory: '$(Build.BinariesDirectory)'
|
||||
displayName: 'Deploy Documentation'
|
||||
@@ -1,40 +0,0 @@
|
||||
steps:
|
||||
# Linux and macOS.
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
if [ "$RUSTUP_TOOLCHAIN" == "nightly" ]; then
|
||||
echo "++ getting latest miri version"
|
||||
export RUSTUP_TOOLCHAIN="nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)"
|
||||
echo "$RUSTUP_TOOLCHAIN"
|
||||
fi
|
||||
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal --default-toolchain none
|
||||
export PATH=$PATH:$HOME/.cargo/bin
|
||||
rustup toolchain install $RUSTUP_TOOLCHAIN
|
||||
rustup default $RUSTUP_TOOLCHAIN
|
||||
echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
|
||||
displayName: "Install rust (*nix)"
|
||||
condition: not(eq(variables['Agent.OS'], 'Windows_NT'))
|
||||
|
||||
# Windows.
|
||||
- script: |
|
||||
curl -sSf -o rustup-init.exe https://win.rustup.rs
|
||||
rustup-init.exe -y --profile minimal --default-toolchain none
|
||||
set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
rustup toolchain install %RUSTUP_TOOLCHAIN%
|
||||
rustup default %RUSTUP_TOOLCHAIN%
|
||||
echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
|
||||
displayName: "Install rust (windows)"
|
||||
condition: eq(variables['Agent.OS'], 'Windows_NT')
|
||||
|
||||
# All platforms.
|
||||
- script: |
|
||||
rustup toolchain list
|
||||
rustc -Vv
|
||||
cargo -V
|
||||
displayName: Query rust and cargo versions
|
||||
@@ -1,9 +0,0 @@
|
||||
steps:
|
||||
- bash: |
|
||||
set -e
|
||||
|
||||
if git log --no-merges -1 --format='%B' | grep -qF '[ci-release]'; then
|
||||
echo "##vso[task.setvariable variable=isRelease]true"
|
||||
fi
|
||||
failOnStderr: true
|
||||
displayName: Check if release commit
|
||||
@@ -1,29 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Loom tests
|
||||
strategy:
|
||||
matrix:
|
||||
rest:
|
||||
scope: --skip loom_pool
|
||||
pool_group_a:
|
||||
scope: loom_pool::group_a
|
||||
pool_group_b:
|
||||
scope: loom_pool::group_b
|
||||
pool_group_c:
|
||||
scope: loom_pool::group_c
|
||||
pool_group_d:
|
||||
scope: loom_pool::group_d
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- script: RUSTFLAGS="--cfg loom" cargo test --lib --release --features "full" -- --nocapture $(scope)
|
||||
env:
|
||||
LOOM_MAX_PREEMPTIONS: 2
|
||||
CI: 'True'
|
||||
displayName: $(scope)
|
||||
workingDirectory: $(Build.SourcesDirectory)/tokio
|
||||
@@ -1,23 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Miri
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: nightly
|
||||
|
||||
- script: |
|
||||
rustup component add miri
|
||||
cargo miri setup
|
||||
rm -rf $(Build.SourcesDirectory)/tokio/tests
|
||||
displayName: Install miri
|
||||
|
||||
# TODO: enable all tests once they pass
|
||||
- script: cargo miri test --features rt-core,rt-threaded,rt-util,sync -- -- task
|
||||
env:
|
||||
CI: 'True'
|
||||
displayName: cargo miri test
|
||||
workingDirectory: $(Build.SourcesDirectory)/tokio
|
||||
@@ -1,16 +0,0 @@
|
||||
steps:
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
# Remove any existing patch statements
|
||||
mv Cargo.toml Cargo.toml.bck
|
||||
sed -n '/\[patch.crates-io\]/q;p' Cargo.toml.bck > Cargo.toml
|
||||
|
||||
# Patch all crates
|
||||
cat ci/patch.toml >> Cargo.toml
|
||||
|
||||
# Print `Cargo.toml` for debugging
|
||||
echo "~~~~ Cargo.toml ~~~~"
|
||||
cat Cargo.toml
|
||||
echo "~~~~~~~~~~~~~~~~~~~~"
|
||||
displayName: Patch Cargo.toml
|
||||
@@ -1,18 +0,0 @@
|
||||
jobs:
|
||||
# Check formatting
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: Check rustfmt
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
- script: |
|
||||
rustup component add rustfmt
|
||||
cargo fmt --version
|
||||
displayName: Install rustfmt
|
||||
- script: |
|
||||
# Workaround for rust-lang/cargo#7732
|
||||
rustfmt --check --edition 2018 $(find . -name '*.rs' -print)
|
||||
displayName: Check formatting
|
||||
@@ -1,17 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- script: cargo install cargo-hack
|
||||
displayName: Install cargo-hack
|
||||
|
||||
- script: cargo hack test --each-feature
|
||||
displayName: cargo hack test --each-feature
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests-build
|
||||
@@ -1,28 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
vmImage: ubuntu-16.04
|
||||
MacOS:
|
||||
vmImage: macos-latest
|
||||
Windows:
|
||||
vmImage: vs2017-win2016
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- script: cargo install cargo-hack
|
||||
displayName: Install cargo-hack
|
||||
|
||||
# Run with all crate features
|
||||
- script: cargo hack test --each-feature
|
||||
env:
|
||||
CI: 'True'
|
||||
displayName: cargo hack test --each-feature
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests-integration
|
||||
@@ -1,19 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- script: cargo check --all
|
||||
displayName: cargo check --all
|
||||
|
||||
# Check benches
|
||||
- script: cargo check --benches --all
|
||||
displayName: Check benchmarks
|
||||
@@ -1,47 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
vmImage: ubuntu-16.04
|
||||
|
||||
${{ if parameters.cross }}:
|
||||
MacOS:
|
||||
vmImage: macos-latest
|
||||
Windows:
|
||||
vmImage: vs2017-win2016
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-is-release.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
# Run with all crate features
|
||||
- script: cargo test --all-features
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
CI: 'True'
|
||||
displayName: ${{ crate }} - cargo test --all-features
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
|
||||
# Check benches
|
||||
- script: cargo check --all-features --benches
|
||||
displayName: ${{ crate }} - cargo check --benches
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
# Run with all crate features
|
||||
- script: cargo test --all-features
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
CI: 'True'
|
||||
displayName: ${{ crate }} - cargo test --all-features
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
@@ -1,34 +0,0 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: TSAN
|
||||
strategy:
|
||||
matrix:
|
||||
Timer:
|
||||
cmd: cargo test -p tokio-timer --test hammer
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
# Make sure the benchmarks compile
|
||||
export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0"
|
||||
export TSAN_OPTIONS="suppressions=`pwd`/ci/tsan"
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
# Run address sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
$(cmd) --target x86_64-unknown-linux-gnu
|
||||
|
||||
# Run thread sanitizer
|
||||
RUSTFLAGS="-Z sanitizer=thread" \
|
||||
$(cmd) --target x86_64-unknown-linux-gnu
|
||||
displayName: TSAN / MSAN
|
||||
env:
|
||||
TSAN: yes
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# Patch dependencies to run all tests against versions of the crate in the
|
||||
# repository.
|
||||
[patch.crates-io]
|
||||
tokio = { path = "tokio" }
|
||||
tokio-macros = { path = "tokio-macros" }
|
||||
tokio-test = { path = "tokio-test" }
|
||||
tokio-tls = { path = "tokio-tls" }
|
||||
tokio-util = { path = "tokio-util" }
|
||||
@@ -1,39 +0,0 @@
|
||||
# TSAN suppressions file for Tokio
|
||||
|
||||
# TSAN does not understand fences and `Arc::drop` is implemented using a fence.
|
||||
# This causes many false positives.
|
||||
race:Arc*drop
|
||||
race:Weak*drop
|
||||
|
||||
# `std` mpsc is not used in any Tokio code base. This race is triggered by some
|
||||
# rust runtime logic.
|
||||
race:std*mpsc_queue
|
||||
race:std*lang_start
|
||||
race:drop*std::thread*
|
||||
|
||||
# Probably more fences in std.
|
||||
race:__call_tls_dtors
|
||||
|
||||
# The epoch-based GC uses fences.
|
||||
race:crossbeam_epoch
|
||||
|
||||
# Push and steal operations in crossbeam-deque may cause data races, but such
|
||||
# data races are safe. If a data race happens, the value read by `steal` is
|
||||
# forgotten and the steal operation is then retried.
|
||||
race:crossbeam_deque*push
|
||||
race:crossbeam_deque*steal
|
||||
|
||||
# This filters out expected data race in the Treiber stack implementations.
|
||||
# Treiber stacks are inherently racy. The pop operation will attempt to access
|
||||
# the "next" pointer on the node it is attempting to pop. However, at this
|
||||
# point it has not gained ownership of the node and another thread might beat
|
||||
# it and take ownership of the node first (touching the next pointer). The
|
||||
# original pop operation will fail due to the ABA guard, but tsan still picks
|
||||
# up the access on the next pointer.
|
||||
race:Backup::next_sleeper
|
||||
race:Backup::set_next_sleeper
|
||||
race:WorkerEntry::set_next_sleeper
|
||||
|
||||
# This ignores a false positive caused by `thread::park()`/`thread::unpark()`.
|
||||
# See: https://github.com/rust-lang/rust/pull/54806#issuecomment-436193353
|
||||
race:pthread_cond_destroy
|
||||
+3
-1
@@ -7,7 +7,9 @@ edition = "2018"
|
||||
# If you copy one of the examples into a new project, you should be using
|
||||
# [dependencies] instead.
|
||||
[dev-dependencies]
|
||||
tokio = { version = "0.2.0", path = "../tokio", features = ["full"] }
|
||||
tokio = { version = "0.2.0", path = "../tokio", features = ["full", "tracing"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.2.7", default-features = false, features = ["fmt", "ansi", "env-filter", "chrono", "tracing-log"] }
|
||||
tokio-util = { version = "0.3.0", path = "../tokio-util", features = ["full"] }
|
||||
bytes = "0.5"
|
||||
futures = "0.3.0"
|
||||
|
||||
+4
-1
@@ -13,8 +13,11 @@ A good starting point for the examples would be [`hello_world`](hello_world.rs)
|
||||
and [`echo`](echo.rs). Additionally [the tokio website][tokioweb] contains
|
||||
additional guides for some of the examples.
|
||||
|
||||
For a larger "real world" example, see the [`mini-redis`][redis] repository.
|
||||
|
||||
If you've got an example you'd like to see here, please feel free to open an
|
||||
issue. Otherwise if you've got an example you'd like to add, please feel free
|
||||
to make a PR!
|
||||
|
||||
[tokioweb]: https://tokio.rs/docs/overview/
|
||||
[tokioweb]: https://tokio.rs/tokio/tutorial
|
||||
[redis]: https://github.com/tokio-rs/mini-redis
|
||||
|
||||
+29
-7
@@ -43,6 +43,26 @@ use std::task::{Context, Poll};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter};
|
||||
// Configure a `tracing` subscriber that logs traces emitted by the chat
|
||||
// server.
|
||||
tracing_subscriber::fmt()
|
||||
// Filter what traces are displayed based on the RUST_LOG environment
|
||||
// variable.
|
||||
//
|
||||
// Traces emitted by the example code will always be displayed. You
|
||||
// can set `RUST_LOG=tokio=trace` to enable additional traces emitted by
|
||||
// Tokio itself.
|
||||
.with_env_filter(EnvFilter::from_default_env().add_directive("chat=info".parse()?))
|
||||
// Log events when `tracing` spans are created, entered, exited, or
|
||||
// closed. When Tokio's internal tracing support is enabled (as
|
||||
// described above), this can be used to track the lifecycle of spawned
|
||||
// tasks on the Tokio runtime.
|
||||
.with_span_events(FmtSpan::FULL)
|
||||
// Set this subscriber as the default, to collect all traces emitted by
|
||||
// the program.
|
||||
.init();
|
||||
|
||||
// Create the shared state. This is how all the peers communicate.
|
||||
//
|
||||
// The server task will hold a handle to this. For every new client, the
|
||||
@@ -59,7 +79,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Note that this is the Tokio TcpListener, which is fully async.
|
||||
let mut listener = TcpListener::bind(&addr).await?;
|
||||
|
||||
println!("server running on {}", addr);
|
||||
tracing::info!("server running on {}", addr);
|
||||
|
||||
loop {
|
||||
// Asynchronously wait for an inbound TcpStream.
|
||||
@@ -70,8 +90,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
// Spawn our handler to be run asynchronously.
|
||||
tokio::spawn(async move {
|
||||
tracing::debug!("accepted connection");
|
||||
if let Err(e) = process(state, stream, addr).await {
|
||||
println!("an error occurred; error = {:?}", e);
|
||||
tracing::info!("an error occurred; error = {:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -200,7 +221,7 @@ async fn process(
|
||||
Some(Ok(line)) => line,
|
||||
// We didn't get a line so we return early here.
|
||||
_ => {
|
||||
println!("Failed to get username from {}. Client disconnected.", addr);
|
||||
tracing::error!("Failed to get username from {}. Client disconnected.", addr);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
@@ -212,7 +233,7 @@ async fn process(
|
||||
{
|
||||
let mut state = state.lock().await;
|
||||
let msg = format!("{} has joined the chat", username);
|
||||
println!("{}", msg);
|
||||
tracing::info!("{}", msg);
|
||||
state.broadcast(addr, &msg).await;
|
||||
}
|
||||
|
||||
@@ -233,9 +254,10 @@ async fn process(
|
||||
peer.lines.send(&msg).await?;
|
||||
}
|
||||
Err(e) => {
|
||||
println!(
|
||||
tracing::error!(
|
||||
"an error occurred while processing messages for {}; error = {:?}",
|
||||
username, e
|
||||
username,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -248,7 +270,7 @@ async fn process(
|
||||
state.peers.remove(&addr);
|
||||
|
||||
let msg = format!("{} has left the chat", username);
|
||||
println!("{}", msg);
|
||||
tracing::info!("{}", msg);
|
||||
state.broadcast(addr, &msg).await;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
use std::{env, io};
|
||||
use tokio;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
struct Server {
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use tokio;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
|
||||
+10
-2
@@ -23,6 +23,7 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use tokio::io;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
use futures::future::try_join;
|
||||
@@ -63,8 +64,15 @@ async fn transfer(mut inbound: TcpStream, proxy_addr: String) -> Result<(), Box<
|
||||
let (mut ri, mut wi) = inbound.split();
|
||||
let (mut ro, mut wo) = outbound.split();
|
||||
|
||||
let client_to_server = io::copy(&mut ri, &mut wo);
|
||||
let server_to_client = io::copy(&mut ro, &mut wi);
|
||||
let client_to_server = async {
|
||||
io::copy(&mut ri, &mut wo).await?;
|
||||
wo.shutdown().await
|
||||
};
|
||||
|
||||
let server_to_client = async {
|
||||
io::copy(&mut ro, &mut wi).await?;
|
||||
wi.shutdown().await
|
||||
};
|
||||
|
||||
try_join(client_to_server, server_to_client).await?;
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ use futures::SinkExt;
|
||||
use http::{header::HeaderValue, Request, Response, StatusCode};
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
use serde_json;
|
||||
use std::{env, error::Error, fmt, io};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::stream::StreamExt;
|
||||
|
||||
@@ -1,31 +1,50 @@
|
||||
# 0.2.5 (February 27, 2019)
|
||||
# 0.2.6 (November 11, 2020)
|
||||
|
||||
### Changes
|
||||
|
||||
- disambiguate the built-in `#[test]` attribute in macro expansion ([#2503])
|
||||
- warn about renaming the Tokio dependency ([#2521])
|
||||
- various documentation changes ([#2683], [#2697])
|
||||
|
||||
# 0.2.5 (February 27, 2020)
|
||||
|
||||
### Fixed
|
||||
- doc improvements (#2225).
|
||||
- doc improvements ([#2225]).
|
||||
|
||||
# 0.2.4 (January 27, 2019)
|
||||
# 0.2.4 (January 27, 2020)
|
||||
|
||||
### Fixed
|
||||
- generics on `#[tokio::main]` function (#2177).
|
||||
- generics on `#[tokio::main]` function ([#2177]).
|
||||
|
||||
### Added
|
||||
- support for `tokio::select!` (#2152).
|
||||
- support for `tokio::select!` ([#2152]).
|
||||
|
||||
# 0.2.3 (January 7, 2019)
|
||||
# 0.2.3 (January 7, 2020)
|
||||
|
||||
### Fixed
|
||||
- Revert breaking change.
|
||||
|
||||
# 0.2.2 (January 7, 2019)
|
||||
# 0.2.2 (January 7, 2020)
|
||||
|
||||
### Added
|
||||
- General refactoring and inclusion of additional runtime options (#2022 and #2038)
|
||||
- General refactoring and inclusion of additional runtime options ([#2022] and [#2038])
|
||||
|
||||
# 0.2.1 (December 18, 2019)
|
||||
|
||||
### Fixes
|
||||
- inherit visibility when wrapping async fn (#1954).
|
||||
- inherit visibility when wrapping async fn ([#1954]).
|
||||
|
||||
# 0.2.0 (November 26, 2019)
|
||||
|
||||
- Initial release
|
||||
|
||||
[#2697]: https://github.com/tokio-rs/tokio/pull/2697
|
||||
[#2683]: https://github.com/tokio-rs/tokio/pull/2683
|
||||
[#2521]: https://github.com/tokio-rs/tokio/pull/2521
|
||||
[#2503]: https://github.com/tokio-rs/tokio/pull/2503
|
||||
[#2225]: https://github.com/tokio-rs/tokio/pull/2225
|
||||
[#2177]: https://github.com/tokio-rs/tokio/pull/2177
|
||||
[#2152]: https://github.com/tokio-rs/tokio/pull/2152
|
||||
[#2038]: https://github.com/tokio-rs/tokio/pull/2038
|
||||
[#2022]: https://github.com/tokio-rs/tokio/pull/2022
|
||||
[#1954]: https://github.com/tokio-rs/tokio/pull/1954
|
||||
|
||||
@@ -6,14 +6,14 @@ name = "tokio-macros"
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.2.5"
|
||||
# - Create "v0.2.x" git tag.
|
||||
version = "0.2.6"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-macros/0.2.5/tokio_macros"
|
||||
documentation = "https://docs.rs/tokio-macros/0.2.6/tokio_macros"
|
||||
description = """
|
||||
Tokio's proc macros.
|
||||
"""
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::unnecessary_lazy_evaluations)]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use std::num::NonZeroUsize;
|
||||
@@ -142,7 +144,7 @@ fn parse_knobs(
|
||||
let header = {
|
||||
if is_test {
|
||||
quote! {
|
||||
#[test]
|
||||
#[::core::prelude::v1::test]
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
@@ -334,14 +336,14 @@ pub(crate) mod old {
|
||||
|
||||
let result = match runtime {
|
||||
Runtime::Threaded => quote! {
|
||||
#[test]
|
||||
#[::core::prelude::v1::test]
|
||||
#(#attrs)*
|
||||
#vis fn #name() #ret {
|
||||
tokio::runtime::Runtime::new().unwrap().block_on(async { #body })
|
||||
}
|
||||
},
|
||||
Runtime::Basic | Runtime::Auto => quote! {
|
||||
#[test]
|
||||
#[::core::prelude::v1::test]
|
||||
#(#attrs)*
|
||||
#vis fn #name() #ret {
|
||||
tokio::runtime::Builder::new()
|
||||
|
||||
+163
-7
@@ -1,4 +1,4 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-macros/0.2.5")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-macros/0.2.6")]
|
||||
#![allow(clippy::needless_doctest_main)]
|
||||
#![warn(
|
||||
missing_debug_implementations,
|
||||
@@ -6,7 +6,7 @@
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
# or
|
||||
/// [Builder](../tokio/runtime/struct.builder.html) directly.
|
||||
///
|
||||
/// ## Options:
|
||||
///
|
||||
/// If you want to set the number of worker threads used for asynchronous code, use the
|
||||
/// `core_threads` option.
|
||||
///
|
||||
/// - `core_threads=n` - Sets core threads to `n` (requires `rt-threaded` feature).
|
||||
/// - `max_threads=n` - Sets max threads to `n` (requires `rt-core` or `rt-threaded` feature).
|
||||
/// - `basic_scheduler` - Use the basic schduler (requires `rt-core`).
|
||||
///
|
||||
/// ## Function arguments:
|
||||
///
|
||||
@@ -47,21 +52,88 @@ use proc_macro::TokenStream;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Set number of core threads
|
||||
/// Equivalent code not using `#[tokio::main]`
|
||||
///
|
||||
/// ```rust
|
||||
/// #[tokio::main(core_threads = 1)]
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new()
|
||||
/// .threaded_scheduler()
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Using basic scheduler
|
||||
///
|
||||
/// The basic scheduler is single-threaded.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[tokio::main(basic_scheduler)]
|
||||
/// async fn main() {
|
||||
/// println!("Hello world");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Equivalent code not using `#[tokio::main]`
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new()
|
||||
/// .basic_scheduler()
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Set number of core threads
|
||||
///
|
||||
/// ```rust
|
||||
/// #[tokio::main(core_threads = 2)]
|
||||
/// async fn main() {
|
||||
/// println!("Hello world");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Equivalent code not using `#[tokio::main]`
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new()
|
||||
/// .threaded_scheduler()
|
||||
/// .core_threads(2)
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### NOTE:
|
||||
///
|
||||
/// If you rename the tokio crate in your dependencies this macro
|
||||
/// will not work. If you must rename the 0.2 version of tokio because
|
||||
/// you're also using the 0.1 version of tokio, you _must_ make the
|
||||
/// tokio 0.2 crate available as `tokio` in the module where this
|
||||
/// macro is expanded.
|
||||
#[proc_macro_attribute]
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
entry::main(args, item, true)
|
||||
}
|
||||
|
||||
/// Marks async function to be executed by selected runtime.
|
||||
/// Marks async function to be executed by selected runtime. This macro helps set up a `Runtime`
|
||||
/// without requiring the user to use [Runtime](../tokio/runtime/struct.Runtime.html) or
|
||||
/// [Builder](../tokio/runtime/struct.builder.html) directly.
|
||||
///
|
||||
/// ## Options:
|
||||
///
|
||||
@@ -83,6 +155,18 @@ pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Equivalent code not using `#[tokio::main]`
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Runtime::new()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Select runtime
|
||||
///
|
||||
/// ```rust
|
||||
@@ -91,13 +175,38 @@ pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// println!("Hello world");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Equivalent code not using `#[tokio::main]`
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new()
|
||||
/// .basic_scheduler()
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### NOTE:
|
||||
///
|
||||
/// If you rename the tokio crate in your dependencies this macro
|
||||
/// will not work. If you must rename the 0.2 version of tokio because
|
||||
/// you're also using the 0.1 version of tokio, you _must_ make the
|
||||
/// tokio 0.2 crate available as `tokio` in the module where this
|
||||
/// macro is expanded.
|
||||
#[proc_macro_attribute]
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
entry::old::main(args, item)
|
||||
}
|
||||
|
||||
/// Marks async function to be executed by selected runtime.
|
||||
/// Marks async function to be executed by selected runtime. This macro helps set up a `Runtime`
|
||||
/// without requiring the user to use [Runtime](../tokio/runtime/struct.Runtime.html) or
|
||||
/// [Builder](../tokio/runtime/struct.builder.html) directly.
|
||||
///
|
||||
/// ## Options:
|
||||
///
|
||||
@@ -117,6 +226,29 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// println!("Hello world");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Equivalent code not using `#[tokio::main]`
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new()
|
||||
/// .basic_scheduler()
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### NOTE:
|
||||
///
|
||||
/// If you rename the tokio crate in your dependencies this macro
|
||||
/// will not work. If you must rename the 0.2 version of tokio because
|
||||
/// you're also using the 0.1 version of tokio, you _must_ make the
|
||||
/// tokio 0.2 crate available as `tokio` in the module where this
|
||||
/// macro is expanded.
|
||||
#[proc_macro_attribute]
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub fn main_basic(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
@@ -149,6 +281,14 @@ pub fn main_basic(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// assert!(true);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### NOTE:
|
||||
///
|
||||
/// If you rename the tokio crate in your dependencies this macro
|
||||
/// will not work. If you must rename the 0.2 version of tokio because
|
||||
/// you're also using the 0.1 version of tokio, you _must_ make the
|
||||
/// tokio 0.2 crate available as `tokio` in the module where this
|
||||
/// macro is expanded.
|
||||
#[proc_macro_attribute]
|
||||
pub fn test_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
entry::test(args, item, true)
|
||||
@@ -180,6 +320,14 @@ pub fn test_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// assert!(true);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### NOTE:
|
||||
///
|
||||
/// If you rename the tokio crate in your dependencies this macro
|
||||
/// will not work. If you must rename the 0.2 version of tokio because
|
||||
/// you're also using the 0.1 version of tokio, you _must_ make the
|
||||
/// tokio 0.2 crate available as `tokio` in the module where this
|
||||
/// macro is expanded.
|
||||
#[proc_macro_attribute]
|
||||
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
entry::old::test(args, item)
|
||||
@@ -199,6 +347,14 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// assert!(true);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### NOTE:
|
||||
///
|
||||
/// If you rename the tokio crate in your dependencies this macro
|
||||
/// will not work. If you must rename the 0.2 version of tokio because
|
||||
/// you're also using the 0.1 version of tokio, you _must_ make the
|
||||
/// tokio 0.2 crate available as `tokio` in the module where this
|
||||
/// macro is expanded.
|
||||
#[proc_macro_attribute]
|
||||
pub fn test_basic(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
entry::test(args, item, false)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# 0.2.1 (April 17, 2020)
|
||||
|
||||
- Add `Future` and `Stream` implementations for `task::Spawn<T>`.
|
||||
|
||||
# 0.2.0 (November 25, 2019)
|
||||
|
||||
- Initial release
|
||||
|
||||
@@ -7,13 +7,13 @@ name = "tokio-test"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.2.x" git tag.
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-test/0.2.0/tokio_test"
|
||||
documentation = "https://docs.rs/tokio-test/0.2.1/tokio_test"
|
||||
description = """
|
||||
Testing utilities for Tokio- and futures-based code
|
||||
"""
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! Attempting to write data that the mock isn't expected will result in a
|
||||
//! Attempting to write data that the mock isn't expecting will result in a
|
||||
//! panic.
|
||||
//!
|
||||
//! [`AsyncRead`]: tokio::io::AsyncRead
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-test/0.2.0")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-test/0.2.1")]
|
||||
#![warn(
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
|
||||
+18
-12
@@ -46,21 +46,11 @@ const SLEEP: usize = 2;
|
||||
|
||||
impl<T> Spawn<T> {
|
||||
/// Consumes `self` returning the inner value
|
||||
pub fn into_inner(mut self) -> T
|
||||
pub fn into_inner(self) -> T
|
||||
where
|
||||
T: Unpin,
|
||||
{
|
||||
drop(self.task);
|
||||
|
||||
// Pin::into_inner is unstable, so we work around it
|
||||
//
|
||||
// Safety: `T` is bound by `Unpin`.
|
||||
unsafe {
|
||||
let ptr = Pin::get_mut(self.future.as_mut()) as *mut T;
|
||||
let future = Box::from_raw(ptr);
|
||||
mem::forget(self.future);
|
||||
*future
|
||||
}
|
||||
*Pin::into_inner(self.future)
|
||||
}
|
||||
|
||||
/// Returns `true` if the inner future has received a wake notification
|
||||
@@ -116,6 +106,22 @@ impl<T: Stream> Spawn<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Future> Future for Spawn<T> {
|
||||
type Output = T::Output;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
self.future.as_mut().poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stream> Stream for Spawn<T> {
|
||||
type Item = T::Item;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
self.future.as_mut().poll_next(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl MockTask {
|
||||
/// Creates new mock task
|
||||
fn new() -> Self {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# 0.3.0 (November 26, 2019)
|
||||
|
||||
- Updates for tokio 0.2 release
|
||||
|
||||
# 0.3.0-alpha.6 (September 30, 2019)
|
||||
|
||||
- Move to `futures-*-preview 0.3.0-alpha.19`
|
||||
- Move to `pin-project 0.4`
|
||||
|
||||
# 0.3.0-alpha.5 (September 19, 2019)
|
||||
|
||||
### Added
|
||||
- `TlsStream::get_ref` and `TlsStream::get_mut` (#1537).
|
||||
|
||||
# 0.3.0-alpha.4 (August 30, 2019)
|
||||
|
||||
### Changed
|
||||
- Track `tokio` 0.2.0-alpha.4
|
||||
|
||||
# 0.3.0-alpha.2 (August 17, 2019)
|
||||
|
||||
### Changed
|
||||
- Update `futures` dependency to 0.3.0-alpha.18.
|
||||
|
||||
# 0.3.0-alpha.1 (August 8, 2019)
|
||||
|
||||
### Changed
|
||||
- Switch to `async`, `await`, and `std::future`.
|
||||
|
||||
# 0.2.1 (January 6, 2019)
|
||||
|
||||
* Implement `Clone` for `TlsConnector` and `TlsAcceptor` (#777)
|
||||
|
||||
# 0.2.0 (August 8, 2018)
|
||||
|
||||
* Initial release with `tokio` support.
|
||||
@@ -1,63 +0,0 @@
|
||||
[package]
|
||||
name = "tokio-tls"
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.3.x" git tag.
|
||||
version = "0.3.0"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-tls/0.3.0-alpha.6/tokio_tls/"
|
||||
description = """
|
||||
An implementation of TLS/SSL streams for Tokio giving an implementation of TLS
|
||||
for nonblocking I/O streams.
|
||||
"""
|
||||
categories = ["asynchronous", "network-programming"]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "tokio-rs/tokio-tls" }
|
||||
|
||||
[dependencies]
|
||||
native-tls = "0.2"
|
||||
tokio = { version = "0.2.0", path = "../tokio" }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "0.2.0", path = "../tokio", features = ["macros", "stream", "rt-core", "io-util", "net"] }
|
||||
tokio-util = { version = "0.3.0", path = "../tokio-util", features = ["full"] }
|
||||
|
||||
cfg-if = "0.1"
|
||||
env_logger = { version = "0.6", default-features = false }
|
||||
futures = { version = "0.3.0", features = ["async-await"] }
|
||||
|
||||
[target.'cfg(all(not(target_os = "macos"), not(windows), not(target_os = "ios")))'.dev-dependencies]
|
||||
openssl = "0.10"
|
||||
|
||||
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dev-dependencies]
|
||||
security-framework = "0.2"
|
||||
|
||||
[target.'cfg(windows)'.dev-dependencies]
|
||||
schannel = "0.1"
|
||||
|
||||
[target.'cfg(windows)'.dev-dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = [
|
||||
"lmcons",
|
||||
"basetsd",
|
||||
"minwinbase",
|
||||
"minwindef",
|
||||
"ntdef",
|
||||
"sysinfoapi",
|
||||
"timezoneapi",
|
||||
"wincrypt",
|
||||
"winerror",
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
@@ -1,25 +0,0 @@
|
||||
Copyright (c) 2019 Tokio Contributors
|
||||
|
||||
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.
|
||||
@@ -1,14 +0,0 @@
|
||||
# tokio-tls
|
||||
|
||||
An implementation of TLS/SSL streams for Tokio built on top of the [`native-tls`
|
||||
crate]
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](./LICENSE).
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
|
||||
terms or conditions.
|
||||
@@ -1,40 +0,0 @@
|
||||
// #![warn(rust_2018_idioms)]
|
||||
|
||||
use native_tls::TlsConnector;
|
||||
use std::error::Error;
|
||||
use std::net::ToSocketAddrs;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tls;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let addr = "www.rust-lang.org:443"
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or("failed to resolve www.rust-lang.org")?;
|
||||
|
||||
let socket = TcpStream::connect(&addr).await?;
|
||||
let cx = TlsConnector::builder().build()?;
|
||||
let cx = tokio_tls::TlsConnector::from(cx);
|
||||
|
||||
let mut socket = cx.connect("www.rust-lang.org", socket).await?;
|
||||
|
||||
socket
|
||||
.write_all(
|
||||
"\
|
||||
GET / HTTP/1.0\r\n\
|
||||
Host: www.rust-lang.org\r\n\
|
||||
\r\n\
|
||||
"
|
||||
.as_bytes(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
socket.read_to_end(&mut data).await?;
|
||||
|
||||
// println!("data: {:?}", &data);
|
||||
println!("{}", String::from_utf8_lossy(&data[..]));
|
||||
Ok(())
|
||||
}
|
||||
Binary file not shown.
@@ -1,55 +0,0 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
// A tiny async TLS echo server with Tokio
|
||||
use native_tls;
|
||||
use native_tls::Identity;
|
||||
use tokio;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_tls;
|
||||
|
||||
/**
|
||||
an example to setup a tls server.
|
||||
how to test:
|
||||
wget https://127.0.0.1:12345 --no-check-certificate
|
||||
*/
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Bind the server's socket
|
||||
let addr = "127.0.0.1:12345".to_string();
|
||||
let mut tcp: TcpListener = TcpListener::bind(&addr).await?;
|
||||
|
||||
// Create the TLS acceptor.
|
||||
let der = include_bytes!("identity.p12");
|
||||
let cert = Identity::from_pkcs12(der, "mypass")?;
|
||||
let tls_acceptor =
|
||||
tokio_tls::TlsAcceptor::from(native_tls::TlsAcceptor::builder(cert).build()?);
|
||||
loop {
|
||||
// Asynchronously wait for an inbound socket.
|
||||
let (socket, remote_addr) = tcp.accept().await?;
|
||||
let tls_acceptor = tls_acceptor.clone();
|
||||
println!("accept connection from {}", remote_addr);
|
||||
tokio::spawn(async move {
|
||||
// Accept the TLS connection.
|
||||
let mut tls_stream = tls_acceptor.accept(socket).await.expect("accept error");
|
||||
// In a loop, read data from the socket and write the data back.
|
||||
|
||||
let mut buf = [0; 1024];
|
||||
let n = tls_stream
|
||||
.read(&mut buf)
|
||||
.await
|
||||
.expect("failed to read data from socket");
|
||||
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
println!("read={}", unsafe {
|
||||
String::from_utf8_unchecked(buf[0..n].into())
|
||||
});
|
||||
tls_stream
|
||||
.write_all(&buf[0..n])
|
||||
.await
|
||||
.expect("failed to write data to socket");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,361 +0,0 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-tls/0.3.0")]
|
||||
#![warn(
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
))]
|
||||
|
||||
//! Async TLS streams
|
||||
//!
|
||||
//! This library is an implementation of TLS streams using the most appropriate
|
||||
//! system library by default for negotiating the connection. That is, on
|
||||
//! Windows this library uses SChannel, on OSX it uses SecureTransport, and on
|
||||
//! other platforms it uses OpenSSL.
|
||||
//!
|
||||
//! Each TLS stream implements the `Read` and `Write` traits to interact and
|
||||
//! interoperate with the rest of the futures I/O ecosystem. Client connections
|
||||
//! initiated from this crate verify hostnames automatically and by default.
|
||||
//!
|
||||
//! This crate primarily exports this ability through two newtypes,
|
||||
//! `TlsConnector` and `TlsAcceptor`. These newtypes augment the
|
||||
//! functionality provided by the `native-tls` crate, on which this crate is
|
||||
//! built. Configuration of TLS parameters is still primarily done through the
|
||||
//! `native-tls` crate.
|
||||
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use native_tls::{Error, HandshakeError, MidHandshakeTlsStream};
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::marker::Unpin;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::pin::Pin;
|
||||
use std::ptr::null_mut;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AllowStd<S> {
|
||||
inner: S,
|
||||
context: *mut (),
|
||||
}
|
||||
|
||||
/// A wrapper around an underlying raw stream which implements the TLS or SSL
|
||||
/// protocol.
|
||||
///
|
||||
/// A `TlsStream<S>` represents a handshake that has been completed successfully
|
||||
/// and both the server and the client are ready for receiving and sending
|
||||
/// data. Bytes read from a `TlsStream` are decrypted from `S` and bytes written
|
||||
/// to a `TlsStream` are encrypted when passing through to `S`.
|
||||
#[derive(Debug)]
|
||||
pub struct TlsStream<S>(native_tls::TlsStream<AllowStd<S>>);
|
||||
|
||||
/// A wrapper around a `native_tls::TlsConnector`, providing an async `connect`
|
||||
/// method.
|
||||
#[derive(Clone)]
|
||||
pub struct TlsConnector(native_tls::TlsConnector);
|
||||
|
||||
/// A wrapper around a `native_tls::TlsAcceptor`, providing an async `accept`
|
||||
/// method.
|
||||
#[derive(Clone)]
|
||||
pub struct TlsAcceptor(native_tls::TlsAcceptor);
|
||||
|
||||
struct MidHandshake<S>(Option<MidHandshakeTlsStream<AllowStd<S>>>);
|
||||
|
||||
enum StartedHandshake<S> {
|
||||
Done(TlsStream<S>),
|
||||
Mid(MidHandshakeTlsStream<AllowStd<S>>),
|
||||
}
|
||||
|
||||
struct StartedHandshakeFuture<F, S>(Option<StartedHandshakeFutureInner<F, S>>);
|
||||
struct StartedHandshakeFutureInner<F, S> {
|
||||
f: F,
|
||||
stream: S,
|
||||
}
|
||||
|
||||
struct Guard<'a, S>(&'a mut TlsStream<S>)
|
||||
where
|
||||
AllowStd<S>: Read + Write;
|
||||
|
||||
impl<S> Drop for Guard<'_, S>
|
||||
where
|
||||
AllowStd<S>: Read + Write,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
(self.0).0.get_mut().context = null_mut();
|
||||
}
|
||||
}
|
||||
|
||||
// *mut () context is neither Send nor Sync
|
||||
unsafe impl<S: Send> Send for AllowStd<S> {}
|
||||
unsafe impl<S: Sync> Sync for AllowStd<S> {}
|
||||
|
||||
impl<S> AllowStd<S>
|
||||
where
|
||||
S: Unpin,
|
||||
{
|
||||
fn with_context<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Context<'_>, Pin<&mut S>) -> R,
|
||||
{
|
||||
unsafe {
|
||||
assert!(!self.context.is_null());
|
||||
let waker = &mut *(self.context as *mut _);
|
||||
f(waker, Pin::new(&mut self.inner))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Read for AllowStd<S>
|
||||
where
|
||||
S: AsyncRead + Unpin,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self.with_context(|ctx, stream| stream.poll_read(ctx, buf)) {
|
||||
Poll::Ready(r) => r,
|
||||
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Write for AllowStd<S>
|
||||
where
|
||||
S: AsyncWrite + Unpin,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self.with_context(|ctx, stream| stream.poll_write(ctx, buf)) {
|
||||
Poll::Ready(r) => r,
|
||||
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self.with_context(|ctx, stream| stream.poll_flush(ctx)) {
|
||||
Poll::Ready(r) => r,
|
||||
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cvt<T>(r: io::Result<T>) -> Poll<io::Result<T>> {
|
||||
match r {
|
||||
Ok(v) => Poll::Ready(Ok(v)),
|
||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending,
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> TlsStream<S> {
|
||||
fn with_context<F, R>(&mut self, ctx: &mut Context<'_>, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut native_tls::TlsStream<AllowStd<S>>) -> R,
|
||||
AllowStd<S>: Read + Write,
|
||||
{
|
||||
self.0.get_mut().context = ctx as *mut _ as *mut ();
|
||||
let g = Guard(self);
|
||||
f(&mut (g.0).0)
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the inner stream.
|
||||
pub fn get_ref(&self) -> &S
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
&self.0.get_ref().inner
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the inner stream.
|
||||
pub fn get_mut(&mut self) -> &mut S
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
&mut self.0.get_mut().inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> AsyncRead for TlsStream<S>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _: &mut [MaybeUninit<u8>]) -> bool {
|
||||
// Note that this does not forward to `S` because the buffer is
|
||||
// unconditionally filled in by OpenSSL, not the actual object `S`.
|
||||
// We're decrypting bytes from `S` into the buffer above!
|
||||
false
|
||||
}
|
||||
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
ctx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
self.with_context(ctx, |s| cvt(s.read(buf)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> AsyncWrite for TlsStream<S>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
ctx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
self.with_context(ctx, |s| cvt(s.write(buf)))
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.with_context(ctx, |s| cvt(s.flush()))
|
||||
}
|
||||
|
||||
fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
match self.with_context(ctx, |s| s.shutdown()) {
|
||||
Ok(()) => Poll::Ready(Ok(())),
|
||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending,
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handshake<F, S>(f: F, stream: S) -> Result<TlsStream<S>, Error>
|
||||
where
|
||||
F: FnOnce(
|
||||
AllowStd<S>,
|
||||
) -> Result<native_tls::TlsStream<AllowStd<S>>, HandshakeError<AllowStd<S>>>
|
||||
+ Unpin,
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
let start = StartedHandshakeFuture(Some(StartedHandshakeFutureInner { f, stream }));
|
||||
|
||||
match start.await {
|
||||
Err(e) => Err(e),
|
||||
Ok(StartedHandshake::Done(s)) => Ok(s),
|
||||
Ok(StartedHandshake::Mid(s)) => MidHandshake(Some(s)).await,
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, S> Future for StartedHandshakeFuture<F, S>
|
||||
where
|
||||
F: FnOnce(
|
||||
AllowStd<S>,
|
||||
) -> Result<native_tls::TlsStream<AllowStd<S>>, HandshakeError<AllowStd<S>>>
|
||||
+ Unpin,
|
||||
S: Unpin,
|
||||
AllowStd<S>: Read + Write,
|
||||
{
|
||||
type Output = Result<StartedHandshake<S>, Error>;
|
||||
|
||||
fn poll(
|
||||
mut self: Pin<&mut Self>,
|
||||
ctx: &mut Context<'_>,
|
||||
) -> Poll<Result<StartedHandshake<S>, Error>> {
|
||||
let inner = self.0.take().expect("future polled after completion");
|
||||
let stream = AllowStd {
|
||||
inner: inner.stream,
|
||||
context: ctx as *mut _ as *mut (),
|
||||
};
|
||||
|
||||
match (inner.f)(stream) {
|
||||
Ok(mut s) => {
|
||||
s.get_mut().context = null_mut();
|
||||
Poll::Ready(Ok(StartedHandshake::Done(TlsStream(s))))
|
||||
}
|
||||
Err(HandshakeError::WouldBlock(mut s)) => {
|
||||
s.get_mut().context = null_mut();
|
||||
Poll::Ready(Ok(StartedHandshake::Mid(s)))
|
||||
}
|
||||
Err(HandshakeError::Failure(e)) => Poll::Ready(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TlsConnector {
|
||||
/// Connects the provided stream with this connector, assuming the provided
|
||||
/// domain.
|
||||
///
|
||||
/// This function will internally call `TlsConnector::connect` to connect
|
||||
/// the stream and returns a future representing the resolution of the
|
||||
/// connection operation. The returned future will resolve to either
|
||||
/// `TlsStream<S>` or `Error` depending if it's successful or not.
|
||||
///
|
||||
/// This is typically used for clients who have already established, for
|
||||
/// example, a TCP connection to a remote server. That stream is then
|
||||
/// provided here to perform the client half of a connection to a
|
||||
/// TLS-powered server.
|
||||
pub async fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, Error>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
handshake(move |s| self.0.connect(domain, s), stream).await
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TlsConnector {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("TlsConnector").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<native_tls::TlsConnector> for TlsConnector {
|
||||
fn from(inner: native_tls::TlsConnector) -> TlsConnector {
|
||||
TlsConnector(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl TlsAcceptor {
|
||||
/// Accepts a new client connection with the provided stream.
|
||||
///
|
||||
/// This function will internally call `TlsAcceptor::accept` to connect
|
||||
/// the stream and returns a future representing the resolution of the
|
||||
/// connection operation. The returned future will resolve to either
|
||||
/// `TlsStream<S>` or `Error` depending if it's successful or not.
|
||||
///
|
||||
/// This is typically used after a new socket has been accepted from a
|
||||
/// `TcpListener`. That socket is then passed to this function to perform
|
||||
/// the server half of accepting a client connection.
|
||||
pub async fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, Error>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
handshake(move |s| self.0.accept(s), stream).await
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TlsAcceptor {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("TlsAcceptor").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<native_tls::TlsAcceptor> for TlsAcceptor {
|
||||
fn from(inner: native_tls::TlsAcceptor) -> TlsAcceptor {
|
||||
TlsAcceptor(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsyncRead + AsyncWrite + Unpin> Future for MidHandshake<S> {
|
||||
type Output = Result<TlsStream<S>, Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut_self = self.get_mut();
|
||||
let mut s = mut_self.0.take().expect("future polled after completion");
|
||||
|
||||
s.get_mut().context = cx as *mut _ as *mut ();
|
||||
match s.handshake() {
|
||||
Ok(stream) => Poll::Ready(Ok(TlsStream(stream))),
|
||||
Err(HandshakeError::Failure(e)) => Poll::Ready(Err(e)),
|
||||
Err(HandshakeError::WouldBlock(mut s)) => {
|
||||
s.get_mut().context = null_mut();
|
||||
mut_self.0 = Some(s);
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use env_logger;
|
||||
use native_tls::TlsConnector;
|
||||
use std::io::{self, Error};
|
||||
use std::net::ToSocketAddrs;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tls;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "force-rustls")] {
|
||||
fn verify_failed(err: &Error, s: &str) {
|
||||
let err = err.to_string();
|
||||
assert!(err.contains(s), "bad error: {}", err);
|
||||
}
|
||||
|
||||
fn assert_expired_error(err: &Error) {
|
||||
verify_failed(err, "CertExpired");
|
||||
}
|
||||
|
||||
fn assert_wrong_host(err: &Error) {
|
||||
verify_failed(err, "CertNotValidForName");
|
||||
}
|
||||
|
||||
fn assert_self_signed(err: &Error) {
|
||||
verify_failed(err, "UnknownIssuer");
|
||||
}
|
||||
|
||||
fn assert_untrusted_root(err: &Error) {
|
||||
verify_failed(err, "UnknownIssuer");
|
||||
}
|
||||
} else if #[cfg(any(feature = "force-openssl",
|
||||
all(not(target_os = "macos"),
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "ios"))))] {
|
||||
fn verify_failed(err: &Error) {
|
||||
assert!(format!("{}", err).contains("certificate verify failed"))
|
||||
}
|
||||
|
||||
use verify_failed as assert_expired_error;
|
||||
use verify_failed as assert_wrong_host;
|
||||
use verify_failed as assert_self_signed;
|
||||
use verify_failed as assert_untrusted_root;
|
||||
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
|
||||
|
||||
fn assert_invalid_cert_chain(err: &Error) {
|
||||
assert!(format!("{}", err).contains("was not trusted."))
|
||||
}
|
||||
|
||||
use crate::assert_invalid_cert_chain as assert_expired_error;
|
||||
use crate::assert_invalid_cert_chain as assert_wrong_host;
|
||||
use crate::assert_invalid_cert_chain as assert_self_signed;
|
||||
use crate::assert_invalid_cert_chain as assert_untrusted_root;
|
||||
} else {
|
||||
fn assert_expired_error(err: &Error) {
|
||||
let s = err.to_string();
|
||||
assert!(s.contains("system clock"), "error = {:?}", s);
|
||||
}
|
||||
|
||||
fn assert_wrong_host(err: &Error) {
|
||||
let s = err.to_string();
|
||||
assert!(s.contains("CN name"), "error = {:?}", s);
|
||||
}
|
||||
|
||||
fn assert_self_signed(err: &Error) {
|
||||
let s = err.to_string();
|
||||
assert!(s.contains("root certificate which is not trusted"), "error = {:?}", s);
|
||||
}
|
||||
|
||||
use assert_self_signed as assert_untrusted_root;
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_host(host: &'static str) -> Error {
|
||||
drop(env_logger::try_init());
|
||||
|
||||
let addr = format!("{}:443", host);
|
||||
let addr = t!(addr.to_socket_addrs()).next().unwrap();
|
||||
|
||||
let socket = t!(TcpStream::connect(&addr).await);
|
||||
let builder = TlsConnector::builder();
|
||||
let cx = t!(builder.build());
|
||||
let cx = tokio_tls::TlsConnector::from(cx);
|
||||
let res = cx
|
||||
.connect(host, socket)
|
||||
.await
|
||||
.map_err(|e| Error::new(io::ErrorKind::Other, e));
|
||||
|
||||
assert!(res.is_err());
|
||||
res.err().unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn expired() {
|
||||
assert_expired_error(&get_host("expired.badssl.com").await)
|
||||
}
|
||||
|
||||
// TODO: the OSX builders on Travis apparently fail this tests spuriously?
|
||||
// passes locally though? Seems... bad!
|
||||
#[tokio::test]
|
||||
#[cfg_attr(all(target_os = "macos", feature = "force-openssl"), ignore)]
|
||||
async fn wrong_host() {
|
||||
assert_wrong_host(&get_host("wrong.host.badssl.com").await)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn self_signed() {
|
||||
assert_self_signed(&get_host("self-signed.badssl.com").await)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn untrusted_root() {
|
||||
assert_untrusted_root(&get_host("untrusted-root.badssl.com").await)
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use env_logger;
|
||||
use native_tls;
|
||||
use native_tls::TlsConnector;
|
||||
use std::io;
|
||||
use std::net::ToSocketAddrs;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tls;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "force-rustls")] {
|
||||
fn assert_bad_hostname_error(err: &io::Error) {
|
||||
let err = err.to_string();
|
||||
assert!(err.contains("CertNotValidForName"), "bad error: {}", err);
|
||||
}
|
||||
} else if #[cfg(any(feature = "force-openssl",
|
||||
all(not(target_os = "macos"),
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "ios"))))] {
|
||||
fn assert_bad_hostname_error(err: &io::Error) {
|
||||
let err = err.get_ref().unwrap();
|
||||
let err = err.downcast_ref::<native_tls::Error>().unwrap();
|
||||
assert!(format!("{}", err).contains("certificate verify failed"));
|
||||
}
|
||||
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
|
||||
fn assert_bad_hostname_error(err: &io::Error) {
|
||||
let err = err.get_ref().unwrap();
|
||||
let err = err.downcast_ref::<native_tls::Error>().unwrap();
|
||||
assert!(format!("{}", err).contains("was not trusted."));
|
||||
}
|
||||
} else {
|
||||
fn assert_bad_hostname_error(err: &io::Error) {
|
||||
let err = err.get_ref().unwrap();
|
||||
let err = err.downcast_ref::<native_tls::Error>().unwrap();
|
||||
assert!(format!("{}", err).contains("CN name"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetch_google() {
|
||||
drop(env_logger::try_init());
|
||||
|
||||
// First up, resolve google.com
|
||||
let addr = t!("google.com:443".to_socket_addrs()).next().unwrap();
|
||||
|
||||
let socket = TcpStream::connect(&addr).await.unwrap();
|
||||
|
||||
// Send off the request by first negotiating an SSL handshake, then writing
|
||||
// of our request, then flushing, then finally read off the response.
|
||||
let builder = TlsConnector::builder();
|
||||
let connector = t!(builder.build());
|
||||
let connector = tokio_tls::TlsConnector::from(connector);
|
||||
let mut socket = t!(connector.connect("google.com", socket).await);
|
||||
t!(socket.write_all(b"GET / HTTP/1.0\r\n\r\n").await);
|
||||
let mut data = Vec::new();
|
||||
t!(socket.read_to_end(&mut data).await);
|
||||
|
||||
// any response code is fine
|
||||
assert!(data.starts_with(b"HTTP/1.0 "));
|
||||
|
||||
let data = String::from_utf8_lossy(&data);
|
||||
let data = data.trim_end();
|
||||
assert!(data.ends_with("</html>") || data.ends_with("</HTML>"));
|
||||
}
|
||||
|
||||
fn native2io(e: native_tls::Error) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::Other, e)
|
||||
}
|
||||
|
||||
// see comment in bad.rs for ignore reason
|
||||
#[cfg_attr(all(target_os = "macos", feature = "force-openssl"), ignore)]
|
||||
#[tokio::test]
|
||||
async fn wrong_hostname_error() {
|
||||
drop(env_logger::try_init());
|
||||
|
||||
let addr = t!("google.com:443".to_socket_addrs()).next().unwrap();
|
||||
|
||||
let socket = t!(TcpStream::connect(&addr).await);
|
||||
let builder = TlsConnector::builder();
|
||||
let connector = t!(builder.build());
|
||||
let connector = tokio_tls::TlsConnector::from(connector);
|
||||
let res = connector
|
||||
.connect("rust-lang.org", socket)
|
||||
.await
|
||||
.map_err(native2io);
|
||||
|
||||
assert!(res.is_err());
|
||||
assert_bad_hostname_error(&res.err().unwrap());
|
||||
}
|
||||
@@ -1,629 +0,0 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use env_logger;
|
||||
use futures::join;
|
||||
use native_tls;
|
||||
use native_tls::{Identity, TlsAcceptor, TlsConnector};
|
||||
use std::io::Write;
|
||||
use std::marker::Unpin;
|
||||
use std::process::Command;
|
||||
use std::ptr;
|
||||
use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt, Error, ErrorKind};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio_tls;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Keys {
|
||||
cert_der: Vec<u8>,
|
||||
pkey_der: Vec<u8>,
|
||||
pkcs12_der: Vec<u8>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn openssl_keys() -> &'static Keys {
|
||||
static INIT: Once = Once::new();
|
||||
static mut KEYS: *mut Keys = ptr::null_mut();
|
||||
|
||||
INIT.call_once(|| {
|
||||
let path = t!(env::current_exe());
|
||||
let path = path.parent().unwrap();
|
||||
let keyfile = path.join("test.key");
|
||||
let certfile = path.join("test.crt");
|
||||
let config = path.join("openssl.config");
|
||||
|
||||
File::create(&config)
|
||||
.unwrap()
|
||||
.write_all(
|
||||
b"\
|
||||
[req]\n\
|
||||
distinguished_name=dn\n\
|
||||
[ dn ]\n\
|
||||
CN=localhost\n\
|
||||
[ ext ]\n\
|
||||
basicConstraints=CA:FALSE,pathlen:0\n\
|
||||
subjectAltName = @alt_names
|
||||
extendedKeyUsage=serverAuth,clientAuth
|
||||
[alt_names]
|
||||
DNS.1 = localhost
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let subj = "/C=US/ST=Denial/L=Sprintfield/O=Dis/CN=localhost";
|
||||
let output = t!(Command::new("openssl")
|
||||
.arg("req")
|
||||
.arg("-nodes")
|
||||
.arg("-x509")
|
||||
.arg("-newkey")
|
||||
.arg("rsa:2048")
|
||||
.arg("-config")
|
||||
.arg(&config)
|
||||
.arg("-extensions")
|
||||
.arg("ext")
|
||||
.arg("-subj")
|
||||
.arg(subj)
|
||||
.arg("-keyout")
|
||||
.arg(&keyfile)
|
||||
.arg("-out")
|
||||
.arg(&certfile)
|
||||
.arg("-days")
|
||||
.arg("1")
|
||||
.output());
|
||||
assert!(output.status.success());
|
||||
|
||||
let crtout = t!(Command::new("openssl")
|
||||
.arg("x509")
|
||||
.arg("-outform")
|
||||
.arg("der")
|
||||
.arg("-in")
|
||||
.arg(&certfile)
|
||||
.output());
|
||||
assert!(crtout.status.success());
|
||||
let keyout = t!(Command::new("openssl")
|
||||
.arg("rsa")
|
||||
.arg("-outform")
|
||||
.arg("der")
|
||||
.arg("-in")
|
||||
.arg(&keyfile)
|
||||
.output());
|
||||
assert!(keyout.status.success());
|
||||
|
||||
let pkcs12out = t!(Command::new("openssl")
|
||||
.arg("pkcs12")
|
||||
.arg("-export")
|
||||
.arg("-nodes")
|
||||
.arg("-inkey")
|
||||
.arg(&keyfile)
|
||||
.arg("-in")
|
||||
.arg(&certfile)
|
||||
.arg("-password")
|
||||
.arg("pass:foobar")
|
||||
.output());
|
||||
assert!(pkcs12out.status.success());
|
||||
|
||||
let keys = Box::new(Keys {
|
||||
cert_der: crtout.stdout,
|
||||
pkey_der: keyout.stdout,
|
||||
pkcs12_der: pkcs12out.stdout,
|
||||
});
|
||||
unsafe {
|
||||
KEYS = Box::into_raw(keys);
|
||||
}
|
||||
});
|
||||
unsafe { &*KEYS }
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "rustls")] {
|
||||
use webpki;
|
||||
use untrusted;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::process::Command;
|
||||
use std::sync::Once;
|
||||
|
||||
use untrusted::Input;
|
||||
use webpki::trust_anchor_util;
|
||||
|
||||
fn server_cx() -> io::Result<ServerContext> {
|
||||
let mut cx = ServerContext::new();
|
||||
|
||||
let (cert, key) = keys();
|
||||
cx.config_mut()
|
||||
.set_single_cert(vec![cert.to_vec()], key.to_vec());
|
||||
|
||||
Ok(cx)
|
||||
}
|
||||
|
||||
fn configure_client(cx: &mut ClientContext) {
|
||||
let (cert, _key) = keys();
|
||||
let cert = Input::from(cert);
|
||||
let anchor = trust_anchor_util::cert_der_as_trust_anchor(cert).unwrap();
|
||||
cx.config_mut().root_store.add_trust_anchors(&[anchor]);
|
||||
}
|
||||
|
||||
// Like OpenSSL we generate certificates on the fly, but for OSX we
|
||||
// also have to put them into a specific keychain. We put both the
|
||||
// certificates and the keychain next to our binary.
|
||||
//
|
||||
// Right now I don't know of a way to programmatically create a
|
||||
// self-signed certificate, so we just fork out to the `openssl` binary.
|
||||
fn keys() -> (&'static [u8], &'static [u8]) {
|
||||
static INIT: Once = Once::new();
|
||||
static mut KEYS: *mut (Vec<u8>, Vec<u8>) = ptr::null_mut();
|
||||
|
||||
INIT.call_once(|| {
|
||||
let (key, cert) = openssl_keys();
|
||||
let path = t!(env::current_exe());
|
||||
let path = path.parent().unwrap();
|
||||
let keyfile = path.join("test.key");
|
||||
let certfile = path.join("test.crt");
|
||||
let config = path.join("openssl.config");
|
||||
|
||||
File::create(&config).unwrap().write_all(b"\
|
||||
[req]\n\
|
||||
distinguished_name=dn\n\
|
||||
[ dn ]\n\
|
||||
CN=localhost\n\
|
||||
[ ext ]\n\
|
||||
basicConstraints=CA:FALSE,pathlen:0\n\
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
DNS.1 = localhost
|
||||
").unwrap();
|
||||
|
||||
let subj = "/C=US/ST=Denial/L=Sprintfield/O=Dis/CN=localhost";
|
||||
let output = t!(Command::new("openssl")
|
||||
.arg("req")
|
||||
.arg("-nodes")
|
||||
.arg("-x509")
|
||||
.arg("-newkey").arg("rsa:2048")
|
||||
.arg("-config").arg(&config)
|
||||
.arg("-extensions").arg("ext")
|
||||
.arg("-subj").arg(subj)
|
||||
.arg("-keyout").arg(&keyfile)
|
||||
.arg("-out").arg(&certfile)
|
||||
.arg("-days").arg("1")
|
||||
.output());
|
||||
assert!(output.status.success());
|
||||
|
||||
let crtout = t!(Command::new("openssl")
|
||||
.arg("x509")
|
||||
.arg("-outform").arg("der")
|
||||
.arg("-in").arg(&certfile)
|
||||
.output());
|
||||
assert!(crtout.status.success());
|
||||
let keyout = t!(Command::new("openssl")
|
||||
.arg("rsa")
|
||||
.arg("-outform").arg("der")
|
||||
.arg("-in").arg(&keyfile)
|
||||
.output());
|
||||
assert!(keyout.status.success());
|
||||
|
||||
let cert = crtout.stdout;
|
||||
let key = keyout.stdout;
|
||||
unsafe {
|
||||
KEYS = Box::into_raw(Box::new((cert, key)));
|
||||
}
|
||||
});
|
||||
unsafe {
|
||||
(&(*KEYS).0, &(*KEYS).1)
|
||||
}
|
||||
}
|
||||
} else if #[cfg(any(feature = "force-openssl",
|
||||
all(not(target_os = "macos"),
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "ios"))))] {
|
||||
use std::fs::File;
|
||||
use std::env;
|
||||
use std::sync::Once;
|
||||
|
||||
fn contexts() -> (tokio_tls::TlsAcceptor, tokio_tls::TlsConnector) {
|
||||
let keys = openssl_keys();
|
||||
|
||||
let pkcs12 = t!(Identity::from_pkcs12(&keys.pkcs12_der, "foobar"));
|
||||
let srv = TlsAcceptor::builder(pkcs12);
|
||||
|
||||
let cert = t!(native_tls::Certificate::from_der(&keys.cert_der));
|
||||
|
||||
let mut client = TlsConnector::builder();
|
||||
t!(client.add_root_certificate(cert).build());
|
||||
|
||||
(t!(srv.build()).into(), t!(client.build()).into())
|
||||
}
|
||||
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::sync::Once;
|
||||
|
||||
fn contexts() -> (tokio_tls::TlsAcceptor, tokio_tls::TlsConnector) {
|
||||
let keys = openssl_keys();
|
||||
|
||||
let pkcs12 = t!(Identity::from_pkcs12(&keys.pkcs12_der, "foobar"));
|
||||
let srv = TlsAcceptor::builder(pkcs12);
|
||||
|
||||
let cert = native_tls::Certificate::from_der(&keys.cert_der).unwrap();
|
||||
let mut client = TlsConnector::builder();
|
||||
client.add_root_certificate(cert);
|
||||
|
||||
(t!(srv.build()).into(), t!(client.build()).into())
|
||||
}
|
||||
} else {
|
||||
use schannel;
|
||||
use winapi;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::sync::Once;
|
||||
|
||||
use schannel::cert_context::CertContext;
|
||||
use schannel::cert_store::{CertStore, CertAdd, Memory};
|
||||
use winapi::shared::basetsd::*;
|
||||
use winapi::shared::lmcons::*;
|
||||
use winapi::shared::minwindef::*;
|
||||
use winapi::shared::ntdef::WCHAR;
|
||||
use winapi::um::minwinbase::*;
|
||||
use winapi::um::sysinfoapi::*;
|
||||
use winapi::um::timezoneapi::*;
|
||||
use winapi::um::wincrypt::*;
|
||||
|
||||
const FRIENDLY_NAME: &str = "tokio-tls localhost testing cert";
|
||||
|
||||
fn contexts() -> (tokio_tls::TlsAcceptor, tokio_tls::TlsConnector) {
|
||||
let cert = localhost_cert();
|
||||
let mut store = t!(Memory::new()).into_store();
|
||||
t!(store.add_cert(&cert, CertAdd::Always));
|
||||
let pkcs12_der = t!(store.export_pkcs12("foobar"));
|
||||
let pkcs12 = t!(Identity::from_pkcs12(&pkcs12_der, "foobar"));
|
||||
|
||||
let srv = TlsAcceptor::builder(pkcs12);
|
||||
let client = TlsConnector::builder();
|
||||
(t!(srv.build()).into(), t!(client.build()).into())
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Magic!
|
||||
//
|
||||
// Lots of magic is happening here to wrangle certificates for running
|
||||
// these tests on Windows. For more information see the test suite
|
||||
// in the schannel-rs crate as this is just coyping that.
|
||||
//
|
||||
// The general gist of this though is that the only way to add custom
|
||||
// trusted certificates is to add it to the system store of trust. To
|
||||
// do that we go through the whole rigamarole here to generate a new
|
||||
// self-signed certificate and then insert that into the system store.
|
||||
//
|
||||
// This generates some dialogs, so we print what we're doing sometimes,
|
||||
// and otherwise we just manage the ephemeral certificates. Because
|
||||
// they're in the system store we always ensure that they're only valid
|
||||
// for a small period of time (e.g. 1 day).
|
||||
|
||||
fn localhost_cert() -> CertContext {
|
||||
static INIT: Once = Once::new();
|
||||
INIT.call_once(|| {
|
||||
for cert in local_root_store().certs() {
|
||||
let name = match cert.friendly_name() {
|
||||
Ok(name) => name,
|
||||
Err(_) => continue,
|
||||
};
|
||||
if name != FRIENDLY_NAME {
|
||||
continue
|
||||
}
|
||||
if !cert.is_time_valid().unwrap() {
|
||||
io::stdout().write_all(br#"
|
||||
|
||||
The tokio-tls test suite is about to delete an old copy of one of its
|
||||
certificates from your root trust store. This certificate was only valid for one
|
||||
day and it is no longer needed. The host should be "localhost" and the
|
||||
description should mention "tokio-tls".
|
||||
|
||||
"#).unwrap();
|
||||
cert.delete().unwrap();
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
install_certificate().unwrap();
|
||||
});
|
||||
|
||||
for cert in local_root_store().certs() {
|
||||
let name = match cert.friendly_name() {
|
||||
Ok(name) => name,
|
||||
Err(_) => continue,
|
||||
};
|
||||
if name == FRIENDLY_NAME {
|
||||
return cert
|
||||
}
|
||||
}
|
||||
|
||||
panic!("couldn't find a cert");
|
||||
}
|
||||
|
||||
fn local_root_store() -> CertStore {
|
||||
if env::var("CI").is_ok() {
|
||||
CertStore::open_local_machine("Root").unwrap()
|
||||
} else {
|
||||
CertStore::open_current_user("Root").unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn install_certificate() -> io::Result<CertContext> {
|
||||
unsafe {
|
||||
let mut provider = 0;
|
||||
let mut hkey = 0;
|
||||
|
||||
let mut buffer = "tokio-tls test suite".encode_utf16()
|
||||
.chain(Some(0))
|
||||
.collect::<Vec<_>>();
|
||||
let res = CryptAcquireContextW(&mut provider,
|
||||
buffer.as_ptr(),
|
||||
ptr::null_mut(),
|
||||
PROV_RSA_FULL,
|
||||
CRYPT_MACHINE_KEYSET);
|
||||
if res != TRUE {
|
||||
// create a new key container (since it does not exist)
|
||||
let res = CryptAcquireContextW(&mut provider,
|
||||
buffer.as_ptr(),
|
||||
ptr::null_mut(),
|
||||
PROV_RSA_FULL,
|
||||
CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET);
|
||||
if res != TRUE {
|
||||
return Err(Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
// create a new keypair (RSA-2048)
|
||||
let res = CryptGenKey(provider,
|
||||
AT_SIGNATURE,
|
||||
0x0800<<16 | CRYPT_EXPORTABLE,
|
||||
&mut hkey);
|
||||
if res != TRUE {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
// start creating the certificate
|
||||
let name = "CN=localhost,O=tokio-tls,OU=tokio-tls,\
|
||||
G=tokio_tls".encode_utf16()
|
||||
.chain(Some(0))
|
||||
.collect::<Vec<_>>();
|
||||
let mut cname_buffer: [WCHAR; UNLEN as usize + 1] = mem::zeroed();
|
||||
let mut cname_len = cname_buffer.len() as DWORD;
|
||||
let res = CertStrToNameW(X509_ASN_ENCODING,
|
||||
name.as_ptr(),
|
||||
CERT_X500_NAME_STR,
|
||||
ptr::null_mut(),
|
||||
cname_buffer.as_mut_ptr() as *mut u8,
|
||||
&mut cname_len,
|
||||
ptr::null_mut());
|
||||
if res != TRUE {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
let mut subject_issuer = CERT_NAME_BLOB {
|
||||
cbData: cname_len,
|
||||
pbData: cname_buffer.as_ptr() as *mut u8,
|
||||
};
|
||||
let mut key_provider = CRYPT_KEY_PROV_INFO {
|
||||
pwszContainerName: buffer.as_mut_ptr(),
|
||||
pwszProvName: ptr::null_mut(),
|
||||
dwProvType: PROV_RSA_FULL,
|
||||
dwFlags: CRYPT_MACHINE_KEYSET,
|
||||
cProvParam: 0,
|
||||
rgProvParam: ptr::null_mut(),
|
||||
dwKeySpec: AT_SIGNATURE,
|
||||
};
|
||||
let mut sig_algorithm = CRYPT_ALGORITHM_IDENTIFIER {
|
||||
pszObjId: szOID_RSA_SHA256RSA.as_ptr() as *mut _,
|
||||
Parameters: mem::zeroed(),
|
||||
};
|
||||
let mut expiration_date: SYSTEMTIME = mem::zeroed();
|
||||
GetSystemTime(&mut expiration_date);
|
||||
let mut file_time: FILETIME = mem::zeroed();
|
||||
let res = SystemTimeToFileTime(&expiration_date,
|
||||
&mut file_time);
|
||||
if res != TRUE {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
let mut timestamp: u64 = file_time.dwLowDateTime as u64 |
|
||||
(file_time.dwHighDateTime as u64) << 32;
|
||||
// one day, timestamp unit is in 100 nanosecond intervals
|
||||
timestamp += (1E9 as u64) / 100 * (60 * 60 * 24);
|
||||
file_time.dwLowDateTime = timestamp as u32;
|
||||
file_time.dwHighDateTime = (timestamp >> 32) as u32;
|
||||
let res = FileTimeToSystemTime(&file_time,
|
||||
&mut expiration_date);
|
||||
if res != TRUE {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
// create a self signed certificate
|
||||
let cert_context = CertCreateSelfSignCertificate(
|
||||
0 as ULONG_PTR,
|
||||
&mut subject_issuer,
|
||||
0,
|
||||
&mut key_provider,
|
||||
&mut sig_algorithm,
|
||||
ptr::null_mut(),
|
||||
&mut expiration_date,
|
||||
ptr::null_mut());
|
||||
if cert_context.is_null() {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
// TODO: this is.. a terrible hack. Right now `schannel`
|
||||
// doesn't provide a public method to go from a raw
|
||||
// cert context pointer to the `CertContext` structure it
|
||||
// has, so we just fake it here with a transmute. This'll
|
||||
// probably break at some point, but hopefully by then
|
||||
// it'll have a method to do this!
|
||||
struct MyCertContext<T>(T);
|
||||
impl<T> Drop for MyCertContext<T> {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
let cert_context = MyCertContext(cert_context);
|
||||
let cert_context: CertContext = mem::transmute(cert_context);
|
||||
|
||||
cert_context.set_friendly_name(FRIENDLY_NAME)?;
|
||||
|
||||
// install the certificate to the machine's local store
|
||||
io::stdout().write_all(br#"
|
||||
|
||||
The tokio-tls test suite is about to add a certificate to your set of root
|
||||
and trusted certificates. This certificate should be for the domain "localhost"
|
||||
with the description related to "tokio-tls". This certificate is only valid
|
||||
for one day and will be automatically deleted if you re-run the tokio-tls
|
||||
test suite later.
|
||||
|
||||
"#).unwrap();
|
||||
local_root_store().add_cert(&cert_context,
|
||||
CertAdd::ReplaceExisting)?;
|
||||
Ok(cert_context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AMT: usize = 128 * 1024;
|
||||
|
||||
async fn copy_data<W: AsyncWrite + Unpin>(mut w: W) -> Result<usize, Error> {
|
||||
let mut data = vec![9; AMT as usize];
|
||||
let mut amt = 0;
|
||||
while !data.is_empty() {
|
||||
let written = w.write(&data).await?;
|
||||
if written <= data.len() {
|
||||
amt += written;
|
||||
data.resize(data.len() - written, 0);
|
||||
} else {
|
||||
w.write_all(&data).await?;
|
||||
amt += data.len();
|
||||
break;
|
||||
}
|
||||
|
||||
println!("remaining: {}", data.len());
|
||||
}
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn client_to_server() {
|
||||
drop(env_logger::try_init());
|
||||
|
||||
// Create a server listening on a port, then figure out what that port is
|
||||
let mut srv = t!(TcpListener::bind("127.0.0.1:0").await);
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let (server_cx, client_cx) = contexts();
|
||||
|
||||
// Create a future to accept one socket, connect the ssl stream, and then
|
||||
// read all the data from it.
|
||||
let server = async move {
|
||||
let mut incoming = srv.incoming();
|
||||
let socket = t!(incoming.next().await.unwrap());
|
||||
let mut socket = t!(server_cx.accept(socket).await);
|
||||
let mut data = Vec::new();
|
||||
t!(socket.read_to_end(&mut data).await);
|
||||
data
|
||||
};
|
||||
|
||||
// Create a future to connect to our server, connect the ssl stream, and
|
||||
// then write a bunch of data to it.
|
||||
let client = async move {
|
||||
let socket = t!(TcpStream::connect(&addr).await);
|
||||
let socket = t!(client_cx.connect("localhost", socket).await);
|
||||
copy_data(socket).await
|
||||
};
|
||||
|
||||
// Finally, run everything!
|
||||
let (data, _) = join!(server, client);
|
||||
// assert_eq!(amt, AMT);
|
||||
assert!(data == vec![9; AMT]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn server_to_client() {
|
||||
drop(env_logger::try_init());
|
||||
|
||||
// Create a server listening on a port, then figure out what that port is
|
||||
let mut srv = t!(TcpListener::bind("127.0.0.1:0").await);
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let (server_cx, client_cx) = contexts();
|
||||
|
||||
let server = async move {
|
||||
let mut incoming = srv.incoming();
|
||||
let socket = t!(incoming.next().await.unwrap());
|
||||
let socket = t!(server_cx.accept(socket).await);
|
||||
copy_data(socket).await
|
||||
};
|
||||
|
||||
let client = async move {
|
||||
let socket = t!(TcpStream::connect(&addr).await);
|
||||
let mut socket = t!(client_cx.connect("localhost", socket).await);
|
||||
let mut data = Vec::new();
|
||||
t!(socket.read_to_end(&mut data).await);
|
||||
data
|
||||
};
|
||||
|
||||
// Finally, run everything!
|
||||
let (_, data) = join!(server, client);
|
||||
// assert_eq!(amt, AMT);
|
||||
assert!(data == vec![9; AMT]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn one_byte_at_a_time() {
|
||||
const AMT: usize = 1024;
|
||||
drop(env_logger::try_init());
|
||||
|
||||
let mut srv = t!(TcpListener::bind("127.0.0.1:0").await);
|
||||
let addr = t!(srv.local_addr());
|
||||
|
||||
let (server_cx, client_cx) = contexts();
|
||||
|
||||
let server = async move {
|
||||
let mut incoming = srv.incoming();
|
||||
let socket = t!(incoming.next().await.unwrap());
|
||||
let mut socket = t!(server_cx.accept(socket).await);
|
||||
let mut amt = 0;
|
||||
for b in std::iter::repeat(9).take(AMT) {
|
||||
let data = [b as u8];
|
||||
t!(socket.write_all(&data).await);
|
||||
amt += 1;
|
||||
}
|
||||
amt
|
||||
};
|
||||
|
||||
let client = async move {
|
||||
let socket = t!(TcpStream::connect(&addr).await);
|
||||
let mut socket = t!(client_cx.connect("localhost", socket).await);
|
||||
let mut data = Vec::new();
|
||||
loop {
|
||||
let mut buf = [0; 1];
|
||||
match socket.read_exact(&mut buf).await {
|
||||
Ok(_) => data.extend_from_slice(&buf),
|
||||
Err(ref err) if err.kind() == ErrorKind::UnexpectedEof => break,
|
||||
Err(err) => panic!(err),
|
||||
}
|
||||
}
|
||||
data
|
||||
};
|
||||
|
||||
let (amt, data) = join!(server, client);
|
||||
assert_eq!(amt, AMT);
|
||||
assert!(data == vec![9; AMT as usize]);
|
||||
}
|
||||
+11
-5
@@ -3,24 +3,30 @@
|
||||
### Fixed
|
||||
|
||||
- Adjust minimum-supported Tokio version to v0.2.5 to account for an internal
|
||||
dependency on features in that version of Tokio. (#2326)
|
||||
dependency on features in that version of Tokio. ([#2326])
|
||||
|
||||
# 0.3.0 (March 4, 2020)
|
||||
|
||||
### Changed
|
||||
|
||||
- **Breaking Change**: Change `Encoder` trait to take a generic `Item` parameter, which allows
|
||||
codec writers to pass references into `Framed` and `FramedWrite` types. (#1746)
|
||||
codec writers to pass references into `Framed` and `FramedWrite` types. ([#1746])
|
||||
|
||||
### Added
|
||||
|
||||
- Add futures-io/tokio::io compatibility layer. (#2117)
|
||||
- Add `Framed::with_capacity`. (#2215)
|
||||
- Add futures-io/tokio::io compatibility layer. ([#2117])
|
||||
- Add `Framed::with_capacity`. ([#2215])
|
||||
|
||||
### Fixed
|
||||
|
||||
- Use advance over split_to when data is not needed. (#2198)
|
||||
- Use advance over split_to when data is not needed. ([#2198])
|
||||
|
||||
# 0.2.0 (November 26, 2019)
|
||||
|
||||
- Initial release
|
||||
|
||||
[#2326]: https://github.com/tokio-rs/tokio/pull/2326
|
||||
[#2215]: https://github.com/tokio-rs/tokio/pull/2215
|
||||
[#2198]: https://github.com/tokio-rs/tokio/pull/2198
|
||||
[#2117]: https://github.com/tokio-rs/tokio/pull/2117
|
||||
[#1746]: https://github.com/tokio-rs/tokio/pull/1746
|
||||
|
||||
@@ -29,6 +29,7 @@ full = ["codec", "udp", "compat"]
|
||||
compat = ["futures-io",]
|
||||
codec = ["tokio/stream"]
|
||||
udp = ["tokio/udp"]
|
||||
rt = ["tokio/rt-core"]
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.2.5", path = "../tokio" }
|
||||
@@ -37,7 +38,7 @@ bytes = "0.5.0"
|
||||
futures-core = "0.3.0"
|
||||
futures-sink = "0.3.0"
|
||||
futures-io = { version = "0.3.0", optional = true }
|
||||
log = "0.4"
|
||||
log = "0.4.6"
|
||||
pin-project-lite = "0.1.4"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -18,6 +18,16 @@ macro_rules! cfg_compat {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_rt {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_udp {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
|
||||
+53
-161
@@ -1,10 +1,9 @@
|
||||
use crate::codec::decoder::Decoder;
|
||||
use crate::codec::encoder::Encoder;
|
||||
use crate::codec::framed_read::{framed_read2, framed_read2_with_buffer, FramedRead2};
|
||||
use crate::codec::framed_write::{framed_write2, framed_write2_with_buffer, FramedWrite2};
|
||||
use crate::codec::framed_impl::{FramedImpl, RWFrames, ReadFrame, WriteFrame};
|
||||
|
||||
use tokio::{
|
||||
io::{AsyncBufRead, AsyncRead, AsyncWrite},
|
||||
io::{AsyncRead, AsyncWrite},
|
||||
stream::Stream,
|
||||
};
|
||||
|
||||
@@ -12,8 +11,7 @@ use bytes::BytesMut;
|
||||
use futures_sink::Sink;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::fmt;
|
||||
use std::io::{self, BufRead, Read, Write};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
@@ -30,37 +28,7 @@ pin_project! {
|
||||
/// [`Decoder::framed`]: crate::codec::Decoder::framed()
|
||||
pub struct Framed<T, U> {
|
||||
#[pin]
|
||||
inner: FramedRead2<FramedWrite2<Fuse<T, U>>>,
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
pub(crate) struct Fuse<T, U> {
|
||||
#[pin]
|
||||
pub(crate) io: T,
|
||||
pub(crate) codec: U,
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstracts over `FramedRead2` being either `FramedRead2<FramedWrite2<Fuse<T, U>>>` or
|
||||
/// `FramedRead2<Fuse<T, U>>` and lets the io and codec parts be extracted in either case.
|
||||
pub(crate) trait ProjectFuse {
|
||||
type Io;
|
||||
type Codec;
|
||||
|
||||
fn project(self: Pin<&mut Self>) -> Fuse<Pin<&mut Self::Io>, &mut Self::Codec>;
|
||||
}
|
||||
|
||||
impl<T, U> ProjectFuse for Fuse<T, U> {
|
||||
type Io = T;
|
||||
type Codec = U;
|
||||
|
||||
fn project(self: Pin<&mut Self>) -> Fuse<Pin<&mut Self::Io>, &mut Self::Codec> {
|
||||
let self_ = self.project();
|
||||
Fuse {
|
||||
io: self_.io,
|
||||
codec: self_.codec,
|
||||
}
|
||||
inner: FramedImpl<T, U, RWFrames>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +61,11 @@ where
|
||||
/// [`split`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.split
|
||||
pub fn new(inner: T, codec: U) -> Framed<T, U> {
|
||||
Framed {
|
||||
inner: framed_read2(framed_write2(Fuse { io: inner, codec })),
|
||||
inner: FramedImpl {
|
||||
inner,
|
||||
codec,
|
||||
state: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,10 +95,18 @@ where
|
||||
/// [`split`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.split
|
||||
pub fn with_capacity(inner: T, codec: U, capacity: usize) -> Framed<T, U> {
|
||||
Framed {
|
||||
inner: framed_read2_with_buffer(
|
||||
framed_write2(Fuse { io: inner, codec }),
|
||||
BytesMut::with_capacity(capacity),
|
||||
),
|
||||
inner: FramedImpl {
|
||||
inner,
|
||||
codec,
|
||||
state: RWFrames {
|
||||
read: ReadFrame {
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(capacity),
|
||||
},
|
||||
write: WriteFrame::default(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,16 +141,14 @@ impl<T, U> Framed<T, U> {
|
||||
/// [`split`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.split
|
||||
pub fn from_parts(parts: FramedParts<T, U>) -> Framed<T, U> {
|
||||
Framed {
|
||||
inner: framed_read2_with_buffer(
|
||||
framed_write2_with_buffer(
|
||||
Fuse {
|
||||
io: parts.io,
|
||||
codec: parts.codec,
|
||||
},
|
||||
parts.write_buf,
|
||||
),
|
||||
parts.read_buf,
|
||||
),
|
||||
inner: FramedImpl {
|
||||
inner: parts.io,
|
||||
codec: parts.codec,
|
||||
state: RWFrames {
|
||||
read: parts.read_buf.into(),
|
||||
write: parts.write_buf.into(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +159,7 @@ impl<T, U> Framed<T, U> {
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.get_ref().get_ref().io
|
||||
&self.inner.inner
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
@@ -191,7 +169,7 @@ impl<T, U> Framed<T, U> {
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.get_mut().get_mut().io
|
||||
&mut self.inner.inner
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying codec wrapped by
|
||||
@@ -200,7 +178,7 @@ impl<T, U> Framed<T, U> {
|
||||
/// Note that care should be taken to not tamper with the underlying codec
|
||||
/// as it may corrupt the stream of frames otherwise being worked with.
|
||||
pub fn codec(&self) -> &U {
|
||||
&self.inner.get_ref().get_ref().codec
|
||||
&self.inner.codec
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying codec wrapped by
|
||||
@@ -209,12 +187,17 @@ impl<T, U> Framed<T, U> {
|
||||
/// Note that care should be taken to not tamper with the underlying codec
|
||||
/// as it may corrupt the stream of frames otherwise being worked with.
|
||||
pub fn codec_mut(&mut self) -> &mut U {
|
||||
&mut self.inner.get_mut().get_mut().codec
|
||||
&mut self.inner.codec
|
||||
}
|
||||
|
||||
/// Returns a reference to the read buffer.
|
||||
pub fn read_buffer(&self) -> &BytesMut {
|
||||
self.inner.buffer()
|
||||
&self.inner.state.read.buffer
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the read buffer.
|
||||
pub fn read_buffer_mut(&mut self) -> &mut BytesMut {
|
||||
&mut self.inner.state.read.buffer
|
||||
}
|
||||
|
||||
/// Consumes the `Framed`, returning its underlying I/O stream.
|
||||
@@ -223,7 +206,7 @@ impl<T, U> Framed<T, U> {
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.into_inner().into_inner().io
|
||||
self.inner.inner
|
||||
}
|
||||
|
||||
/// Consumes the `Framed`, returning its underlying I/O stream, the buffer
|
||||
@@ -233,19 +216,17 @@ impl<T, U> Framed<T, U> {
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_parts(self) -> FramedParts<T, U> {
|
||||
let (inner, read_buf) = self.inner.into_parts();
|
||||
let (inner, write_buf) = inner.into_parts();
|
||||
|
||||
FramedParts {
|
||||
io: inner.io,
|
||||
codec: inner.codec,
|
||||
read_buf,
|
||||
write_buf,
|
||||
io: self.inner.inner,
|
||||
codec: self.inner.codec,
|
||||
read_buf: self.inner.state.read.buffer,
|
||||
write_buf: self.inner.state.write.buffer,
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This impl just defers to the underlying FramedImpl
|
||||
impl<T, U> Stream for Framed<T, U>
|
||||
where
|
||||
T: AsyncRead,
|
||||
@@ -258,6 +239,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// This impl just defers to the underlying FramedImpl
|
||||
impl<T, I, U> Sink<I> for Framed<T, U>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
@@ -267,19 +249,19 @@ where
|
||||
type Error = U::Error;
|
||||
|
||||
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.project().inner.get_pin_mut().poll_ready(cx)
|
||||
self.project().inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
|
||||
self.project().inner.get_pin_mut().start_send(item)
|
||||
self.project().inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.project().inner.get_pin_mut().poll_flush(cx)
|
||||
self.project().inner.poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.project().inner.get_pin_mut().poll_close(cx)
|
||||
self.project().inner.poll_close(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,109 +272,19 @@ where
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Framed")
|
||||
.field("io", &self.inner.get_ref().get_ref().io)
|
||||
.field("codec", &self.inner.get_ref().get_ref().codec)
|
||||
.field("io", self.get_ref())
|
||||
.field("codec", self.codec())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Fuse =====
|
||||
|
||||
impl<T: Read, U> Read for Fuse<T, U> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.io.read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BufRead, U> BufRead for Fuse<T, U> {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
self.io.fill_buf()
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.io.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead, U> AsyncRead for Fuse<T, U> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [MaybeUninit<u8>]) -> bool {
|
||||
self.io.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
self.project().io.poll_read(cx, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncBufRead, U> AsyncBufRead for Fuse<T, U> {
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
||||
self.project().io.poll_fill_buf(cx)
|
||||
}
|
||||
|
||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
||||
self.project().io.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write, U> Write for Fuse<T, U> {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
self.io.write(src)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.io.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite, U> AsyncWrite for Fuse<T, U> {
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
self.project().io.poll_write(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
self.project().io.poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
self.project().io.poll_shutdown(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: Decoder> Decoder for Fuse<T, U> {
|
||||
type Item = U::Item;
|
||||
type Error = U::Error;
|
||||
|
||||
fn decode(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.codec.decode(buffer)
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.codec.decode_eof(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, U: Encoder<I>> Encoder<I> for Fuse<T, U> {
|
||||
type Error = U::Error;
|
||||
|
||||
fn encode(&mut self, item: I, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
self.codec.encode(item, dst)
|
||||
}
|
||||
}
|
||||
|
||||
/// `FramedParts` contains an export of the data of a Framed transport.
|
||||
/// It can be used to construct a new [`Framed`] with a different codec.
|
||||
/// It contains all current buffers and the inner transport.
|
||||
///
|
||||
/// [`Framed`]: crate::codec::Framed
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::manual_non_exhaustive)]
|
||||
pub struct FramedParts<T, U> {
|
||||
/// The inner transport used to read bytes to and write bytes to
|
||||
pub io: T,
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
use crate::codec::decoder::Decoder;
|
||||
use crate::codec::encoder::Encoder;
|
||||
|
||||
use tokio::{
|
||||
io::{AsyncRead, AsyncWrite},
|
||||
stream::Stream,
|
||||
};
|
||||
|
||||
use bytes::{Buf, BytesMut};
|
||||
use futures_core::ready;
|
||||
use futures_sink::Sink;
|
||||
use log::trace;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pin_project! {
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FramedImpl<T, U, State> {
|
||||
#[pin]
|
||||
pub(crate) inner: T,
|
||||
pub(crate) state: State,
|
||||
pub(crate) codec: U,
|
||||
}
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
|
||||
|
||||
pub(crate) struct ReadFrame {
|
||||
pub(crate) eof: bool,
|
||||
pub(crate) is_readable: bool,
|
||||
pub(crate) buffer: BytesMut,
|
||||
}
|
||||
|
||||
pub(crate) struct WriteFrame {
|
||||
pub(crate) buffer: BytesMut,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct RWFrames {
|
||||
pub(crate) read: ReadFrame,
|
||||
pub(crate) write: WriteFrame,
|
||||
}
|
||||
|
||||
impl Default for ReadFrame {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WriteFrame {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BytesMut> for ReadFrame {
|
||||
fn from(mut buffer: BytesMut) -> Self {
|
||||
let size = buffer.capacity();
|
||||
if size < INITIAL_CAPACITY {
|
||||
buffer.reserve(INITIAL_CAPACITY - size);
|
||||
}
|
||||
|
||||
Self {
|
||||
buffer,
|
||||
is_readable: size > 0,
|
||||
eof: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BytesMut> for WriteFrame {
|
||||
fn from(mut buffer: BytesMut) -> Self {
|
||||
let size = buffer.capacity();
|
||||
if size < INITIAL_CAPACITY {
|
||||
buffer.reserve(INITIAL_CAPACITY - size);
|
||||
}
|
||||
|
||||
Self { buffer }
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<ReadFrame> for RWFrames {
|
||||
fn borrow(&self) -> &ReadFrame {
|
||||
&self.read
|
||||
}
|
||||
}
|
||||
impl BorrowMut<ReadFrame> for RWFrames {
|
||||
fn borrow_mut(&mut self) -> &mut ReadFrame {
|
||||
&mut self.read
|
||||
}
|
||||
}
|
||||
impl Borrow<WriteFrame> for RWFrames {
|
||||
fn borrow(&self) -> &WriteFrame {
|
||||
&self.write
|
||||
}
|
||||
}
|
||||
impl BorrowMut<WriteFrame> for RWFrames {
|
||||
fn borrow_mut(&mut self) -> &mut WriteFrame {
|
||||
&mut self.write
|
||||
}
|
||||
}
|
||||
impl<T, U, R> Stream for FramedImpl<T, U, R>
|
||||
where
|
||||
T: AsyncRead,
|
||||
U: Decoder,
|
||||
R: BorrowMut<ReadFrame>,
|
||||
{
|
||||
type Item = Result<U::Item, U::Error>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let mut pinned = self.project();
|
||||
let state: &mut ReadFrame = pinned.state.borrow_mut();
|
||||
loop {
|
||||
// Repeatedly call `decode` or `decode_eof` as long as it is
|
||||
// "readable". Readable is defined as not having returned `None`. If
|
||||
// the upstream has returned EOF, and the decoder is no longer
|
||||
// readable, it can be assumed that the decoder will never become
|
||||
// readable again, at which point the stream is terminated.
|
||||
if state.is_readable {
|
||||
if state.eof {
|
||||
let frame = pinned.codec.decode_eof(&mut state.buffer)?;
|
||||
return Poll::Ready(frame.map(Ok));
|
||||
}
|
||||
|
||||
trace!("attempting to decode a frame");
|
||||
|
||||
if let Some(frame) = pinned.codec.decode(&mut state.buffer)? {
|
||||
trace!("frame decoded from buffer");
|
||||
return Poll::Ready(Some(Ok(frame)));
|
||||
}
|
||||
|
||||
state.is_readable = false;
|
||||
}
|
||||
|
||||
assert!(!state.eof);
|
||||
|
||||
// Otherwise, try to read more data and try again. Make sure we've
|
||||
// got room for at least one byte to read to ensure that we don't
|
||||
// get a spurious 0 that looks like EOF
|
||||
state.buffer.reserve(1);
|
||||
let bytect = match pinned.inner.as_mut().poll_read_buf(cx, &mut state.buffer)? {
|
||||
Poll::Ready(ct) => ct,
|
||||
Poll::Pending => return Poll::Pending,
|
||||
};
|
||||
if bytect == 0 {
|
||||
state.eof = true;
|
||||
}
|
||||
|
||||
state.is_readable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, U, W> Sink<I> for FramedImpl<T, U, W>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
U: Encoder<I>,
|
||||
U::Error: From<io::Error>,
|
||||
W: BorrowMut<WriteFrame>,
|
||||
{
|
||||
type Error = U::Error;
|
||||
|
||||
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
if self.state.borrow().buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
self.as_mut().poll_flush(cx)
|
||||
} else {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
|
||||
let pinned = self.project();
|
||||
pinned
|
||||
.codec
|
||||
.encode(item, &mut pinned.state.borrow_mut().buffer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
trace!("flushing framed transport");
|
||||
let mut pinned = self.project();
|
||||
|
||||
while !pinned.state.borrow_mut().buffer.is_empty() {
|
||||
let WriteFrame { buffer } = pinned.state.borrow_mut();
|
||||
trace!("writing; remaining={}", buffer.len());
|
||||
|
||||
let buf = &buffer;
|
||||
let n = ready!(pinned.inner.as_mut().poll_write(cx, &buf))?;
|
||||
|
||||
if n == 0 {
|
||||
return Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
"failed to \
|
||||
write frame to transport",
|
||||
)
|
||||
.into()));
|
||||
}
|
||||
|
||||
pinned.state.borrow_mut().buffer.advance(n);
|
||||
}
|
||||
|
||||
// Try flushing the underlying IO
|
||||
ready!(pinned.inner.poll_flush(cx))?;
|
||||
|
||||
trace!("framed transport flushed");
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
ready!(self.as_mut().poll_flush(cx))?;
|
||||
ready!(self.project().inner.poll_shutdown(cx))?;
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
use crate::codec::framed::{Fuse, ProjectFuse};
|
||||
use crate::codec::framed_impl::{FramedImpl, ReadFrame};
|
||||
use crate::codec::Decoder;
|
||||
|
||||
use tokio::{io::AsyncRead, stream::Stream};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use futures_sink::Sink;
|
||||
use log::trace;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::fmt;
|
||||
use std::pin::Pin;
|
||||
@@ -18,22 +17,10 @@ pin_project! {
|
||||
/// [`AsyncRead`]: tokio::io::AsyncRead
|
||||
pub struct FramedRead<T, D> {
|
||||
#[pin]
|
||||
inner: FramedRead2<Fuse<T, D>>,
|
||||
inner: FramedImpl<T, D, ReadFrame>,
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
pub(crate) struct FramedRead2<T> {
|
||||
#[pin]
|
||||
inner: T,
|
||||
eof: bool,
|
||||
is_readable: bool,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
|
||||
// ===== impl FramedRead =====
|
||||
|
||||
impl<T, D> FramedRead<T, D>
|
||||
@@ -44,10 +31,11 @@ where
|
||||
/// Creates a new `FramedRead` with the given `decoder`.
|
||||
pub fn new(inner: T, decoder: D) -> FramedRead<T, D> {
|
||||
FramedRead {
|
||||
inner: framed_read2(Fuse {
|
||||
io: inner,
|
||||
inner: FramedImpl {
|
||||
inner,
|
||||
codec: decoder,
|
||||
}),
|
||||
state: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,13 +43,15 @@ where
|
||||
/// initial size.
|
||||
pub fn with_capacity(inner: T, decoder: D, capacity: usize) -> FramedRead<T, D> {
|
||||
FramedRead {
|
||||
inner: framed_read2_with_buffer(
|
||||
Fuse {
|
||||
io: inner,
|
||||
codec: decoder,
|
||||
inner: FramedImpl {
|
||||
inner,
|
||||
codec: decoder,
|
||||
state: ReadFrame {
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(capacity),
|
||||
},
|
||||
BytesMut::with_capacity(capacity),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +64,7 @@ impl<T, D> FramedRead<T, D> {
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.inner.io
|
||||
&self.inner.inner
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
@@ -84,7 +74,7 @@ impl<T, D> FramedRead<T, D> {
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.inner.io
|
||||
&mut self.inner.inner
|
||||
}
|
||||
|
||||
/// Consumes the `FramedRead`, returning its underlying I/O stream.
|
||||
@@ -93,25 +83,26 @@ impl<T, D> FramedRead<T, D> {
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.inner.io
|
||||
self.inner.inner
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying decoder.
|
||||
pub fn decoder(&self) -> &D {
|
||||
&self.inner.inner.codec
|
||||
&self.inner.codec
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying decoder.
|
||||
pub fn decoder_mut(&mut self) -> &mut D {
|
||||
&mut self.inner.inner.codec
|
||||
&mut self.inner.codec
|
||||
}
|
||||
|
||||
/// Returns a reference to the read buffer.
|
||||
pub fn read_buffer(&self) -> &BytesMut {
|
||||
&self.inner.buffer
|
||||
&self.inner.state.buffer
|
||||
}
|
||||
}
|
||||
|
||||
// This impl just defers to the underlying FramedImpl
|
||||
impl<T, D> Stream for FramedRead<T, D>
|
||||
where
|
||||
T: AsyncRead,
|
||||
@@ -132,43 +123,19 @@ where
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.project()
|
||||
.inner
|
||||
.project()
|
||||
.inner
|
||||
.project()
|
||||
.io
|
||||
.poll_ready(cx)
|
||||
self.project().inner.project().inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
|
||||
self.project()
|
||||
.inner
|
||||
.project()
|
||||
.inner
|
||||
.project()
|
||||
.io
|
||||
.start_send(item)
|
||||
self.project().inner.project().inner.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.project()
|
||||
.inner
|
||||
.project()
|
||||
.inner
|
||||
.project()
|
||||
.io
|
||||
.poll_flush(cx)
|
||||
self.project().inner.project().inner.poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.project()
|
||||
.inner
|
||||
.project()
|
||||
.inner
|
||||
.project()
|
||||
.io
|
||||
.poll_close(cx)
|
||||
self.project().inner.project().inner.poll_close(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,126 +146,11 @@ where
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FramedRead")
|
||||
.field("inner", &self.inner.inner.io)
|
||||
.field("decoder", &self.inner.inner.codec)
|
||||
.field("eof", &self.inner.eof)
|
||||
.field("is_readable", &self.inner.is_readable)
|
||||
.field("buffer", &self.inner.buffer)
|
||||
.field("inner", &self.get_ref())
|
||||
.field("decoder", &self.decoder())
|
||||
.field("eof", &self.inner.state.eof)
|
||||
.field("is_readable", &self.inner.state.is_readable)
|
||||
.field("buffer", &self.read_buffer())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedRead2 =====
|
||||
|
||||
pub(crate) fn framed_read2<T>(inner: T) -> FramedRead2<T> {
|
||||
FramedRead2 {
|
||||
inner,
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn framed_read2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedRead2<T> {
|
||||
if buf.capacity() < INITIAL_CAPACITY {
|
||||
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
|
||||
buf.reserve(bytes_to_reserve);
|
||||
}
|
||||
FramedRead2 {
|
||||
inner,
|
||||
eof: false,
|
||||
is_readable: !buf.is_empty(),
|
||||
buffer: buf,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FramedRead2<T> {
|
||||
pub(crate) fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub(crate) fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub(crate) fn into_parts(self) -> (T, BytesMut) {
|
||||
(self.inner, self.buffer)
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
pub(crate) fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
|
||||
self.project().inner
|
||||
}
|
||||
|
||||
pub(crate) fn buffer(&self) -> &BytesMut {
|
||||
&self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for FramedRead2<T>
|
||||
where
|
||||
T: ProjectFuse + AsyncRead,
|
||||
T::Codec: Decoder,
|
||||
{
|
||||
type Item = Result<<T::Codec as Decoder>::Item, <T::Codec as Decoder>::Error>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let mut pinned = self.project();
|
||||
loop {
|
||||
// Repeatedly call `decode` or `decode_eof` as long as it is
|
||||
// "readable". Readable is defined as not having returned `None`. If
|
||||
// the upstream has returned EOF, and the decoder is no longer
|
||||
// readable, it can be assumed that the decoder will never become
|
||||
// readable again, at which point the stream is terminated.
|
||||
if *pinned.is_readable {
|
||||
if *pinned.eof {
|
||||
let frame = pinned
|
||||
.inner
|
||||
.as_mut()
|
||||
.project()
|
||||
.codec
|
||||
.decode_eof(&mut pinned.buffer)?;
|
||||
return Poll::Ready(frame.map(Ok));
|
||||
}
|
||||
|
||||
trace!("attempting to decode a frame");
|
||||
|
||||
if let Some(frame) = pinned
|
||||
.inner
|
||||
.as_mut()
|
||||
.project()
|
||||
.codec
|
||||
.decode(&mut pinned.buffer)?
|
||||
{
|
||||
trace!("frame decoded from buffer");
|
||||
return Poll::Ready(Some(Ok(frame)));
|
||||
}
|
||||
|
||||
*pinned.is_readable = false;
|
||||
}
|
||||
|
||||
assert!(!*pinned.eof);
|
||||
|
||||
// Otherwise, try to read more data and try again. Make sure we've
|
||||
// got room for at least one byte to read to ensure that we don't
|
||||
// get a spurious 0 that looks like EOF
|
||||
pinned.buffer.reserve(1);
|
||||
let bytect = match pinned
|
||||
.inner
|
||||
.as_mut()
|
||||
.poll_read_buf(cx, &mut pinned.buffer)?
|
||||
{
|
||||
Poll::Ready(ct) => ct,
|
||||
Poll::Pending => return Poll::Pending,
|
||||
};
|
||||
if bytect == 0 {
|
||||
*pinned.eof = true;
|
||||
}
|
||||
|
||||
*pinned.is_readable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
use crate::codec::decoder::Decoder;
|
||||
use crate::codec::encoder::Encoder;
|
||||
use crate::codec::framed::{Fuse, ProjectFuse};
|
||||
use crate::codec::framed_impl::{FramedImpl, WriteFrame};
|
||||
|
||||
use tokio::{
|
||||
io::{AsyncBufRead, AsyncRead, AsyncWrite},
|
||||
stream::Stream,
|
||||
};
|
||||
use tokio::{io::AsyncWrite, stream::Stream};
|
||||
|
||||
use bytes::{Buf, BytesMut};
|
||||
use futures_core::ready;
|
||||
use futures_sink::Sink;
|
||||
use log::trace;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::fmt;
|
||||
use std::io::{self, BufRead, Read};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
@@ -24,21 +16,10 @@ pin_project! {
|
||||
/// [`Sink`]: futures_sink::Sink
|
||||
pub struct FramedWrite<T, E> {
|
||||
#[pin]
|
||||
inner: FramedWrite2<Fuse<T, E>>,
|
||||
inner: FramedImpl<T, E, WriteFrame>,
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
pub(crate) struct FramedWrite2<T> {
|
||||
#[pin]
|
||||
inner: T,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
|
||||
|
||||
impl<T, E> FramedWrite<T, E>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
@@ -46,10 +27,11 @@ where
|
||||
/// Creates a new `FramedWrite` with the given `encoder`.
|
||||
pub fn new(inner: T, encoder: E) -> FramedWrite<T, E> {
|
||||
FramedWrite {
|
||||
inner: framed_write2(Fuse {
|
||||
io: inner,
|
||||
inner: FramedImpl {
|
||||
inner,
|
||||
codec: encoder,
|
||||
}),
|
||||
state: WriteFrame::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +44,7 @@ impl<T, E> FramedWrite<T, E> {
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner.inner.io
|
||||
&self.inner.inner
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying I/O stream wrapped by
|
||||
@@ -72,7 +54,7 @@ impl<T, E> FramedWrite<T, E> {
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.inner.io
|
||||
&mut self.inner.inner
|
||||
}
|
||||
|
||||
/// Consumes the `FramedWrite`, returning its underlying I/O stream.
|
||||
@@ -81,21 +63,21 @@ impl<T, E> FramedWrite<T, E> {
|
||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
||||
/// being worked with.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner.inner.io
|
||||
self.inner.inner
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying decoder.
|
||||
/// Returns a reference to the underlying encoder.
|
||||
pub fn encoder(&self) -> &E {
|
||||
&self.inner.inner.codec
|
||||
&self.inner.codec
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying decoder.
|
||||
/// Returns a mutable reference to the underlying encoder.
|
||||
pub fn encoder_mut(&mut self) -> &mut E {
|
||||
&mut self.inner.inner.codec
|
||||
&mut self.inner.codec
|
||||
}
|
||||
}
|
||||
|
||||
// This impl just defers to the underlying FramedWrite2
|
||||
// This impl just defers to the underlying FramedImpl
|
||||
impl<T, I, E> Sink<I> for FramedWrite<T, E>
|
||||
where
|
||||
T: AsyncWrite,
|
||||
@@ -121,6 +103,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// This impl just defers to the underlying T: Stream
|
||||
impl<T, D> Stream for FramedWrite<T, D>
|
||||
where
|
||||
T: Stream,
|
||||
@@ -128,13 +111,7 @@ where
|
||||
type Item = T::Item;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
self.project()
|
||||
.inner
|
||||
.project()
|
||||
.inner
|
||||
.project()
|
||||
.io
|
||||
.poll_next(cx)
|
||||
self.project().inner.project().inner.poll_next(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,180 +122,9 @@ where
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FramedWrite")
|
||||
.field("inner", &self.inner.get_ref().io)
|
||||
.field("encoder", &self.inner.get_ref().codec)
|
||||
.field("buffer", &self.inner.buffer)
|
||||
.field("inner", &self.get_ref())
|
||||
.field("encoder", &self.encoder())
|
||||
.field("buffer", &self.inner.state.buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FramedWrite2 =====
|
||||
|
||||
pub(crate) fn framed_write2<T>(inner: T) -> FramedWrite2<T> {
|
||||
FramedWrite2 {
|
||||
inner,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn framed_write2_with_buffer<T>(inner: T, mut buf: BytesMut) -> FramedWrite2<T> {
|
||||
if buf.capacity() < INITIAL_CAPACITY {
|
||||
let bytes_to_reserve = INITIAL_CAPACITY - buf.capacity();
|
||||
buf.reserve(bytes_to_reserve);
|
||||
}
|
||||
FramedWrite2 { inner, buffer: buf }
|
||||
}
|
||||
|
||||
impl<T> FramedWrite2<T> {
|
||||
pub(crate) fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub(crate) fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub(crate) fn into_parts(self) -> (T, BytesMut) {
|
||||
(self.inner, self.buffer)
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> Sink<I> for FramedWrite2<T>
|
||||
where
|
||||
T: ProjectFuse + AsyncWrite,
|
||||
T::Codec: Encoder<I>,
|
||||
{
|
||||
type Error = <T::Codec as Encoder<I>>::Error;
|
||||
|
||||
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
// If the buffer is already over 8KiB, then attempt to flush it. If after flushing it's
|
||||
// *still* over 8KiB, then apply backpressure (reject the send).
|
||||
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
match self.as_mut().poll_flush(cx) {
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
|
||||
Poll::Ready(Ok(())) => (),
|
||||
};
|
||||
|
||||
if self.buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
|
||||
let mut pinned = self.project();
|
||||
pinned
|
||||
.inner
|
||||
.project()
|
||||
.codec
|
||||
.encode(item, &mut pinned.buffer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
trace!("flushing framed transport");
|
||||
let mut pinned = self.project();
|
||||
|
||||
while !pinned.buffer.is_empty() {
|
||||
trace!("writing; remaining={}", pinned.buffer.len());
|
||||
|
||||
let buf = &pinned.buffer;
|
||||
let n = ready!(pinned.inner.as_mut().poll_write(cx, &buf))?;
|
||||
|
||||
if n == 0 {
|
||||
return Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
"failed to \
|
||||
write frame to transport",
|
||||
)
|
||||
.into()));
|
||||
}
|
||||
|
||||
pinned.buffer.advance(n);
|
||||
}
|
||||
|
||||
// Try flushing the underlying IO
|
||||
ready!(pinned.inner.poll_flush(cx))?;
|
||||
|
||||
trace!("framed transport flushed");
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
ready!(self.as_mut().poll_flush(cx))?;
|
||||
ready!(self.project().inner.poll_shutdown(cx))?;
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decoder> Decoder for FramedWrite2<T> {
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
|
||||
self.inner.decode(src)
|
||||
}
|
||||
|
||||
fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<T::Item>, T::Error> {
|
||||
self.inner.decode_eof(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read> Read for FramedWrite2<T> {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BufRead> BufRead for FramedWrite2<T> {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
self.inner.fill_buf()
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.inner.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead> AsyncRead for FramedWrite2<T> {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [MaybeUninit<u8>]) -> bool {
|
||||
self.inner.prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
self.project().inner.poll_read(cx, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncBufRead> AsyncBufRead for FramedWrite2<T> {
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
||||
self.project().inner.poll_fill_buf(cx)
|
||||
}
|
||||
|
||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
||||
self.project().inner.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ProjectFuse for FramedWrite2<T>
|
||||
where
|
||||
T: ProjectFuse,
|
||||
{
|
||||
type Io = T::Io;
|
||||
type Codec = T::Codec;
|
||||
|
||||
fn project(self: Pin<&mut Self>) -> Fuse<Pin<&mut Self::Io>, &mut Self::Codec> {
|
||||
self.project().inner.project()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,10 +367,10 @@
|
||||
//! [`LengthDelimitedCodec::new()`]: method@LengthDelimitedCodec::new
|
||||
//! [`FramedRead`]: struct@FramedRead
|
||||
//! [`FramedWrite`]: struct@FramedWrite
|
||||
//! [`AsyncRead`]: ../../trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: ../../trait.AsyncWrite.html
|
||||
//! [`Encoder`]: ../trait.Encoder.html
|
||||
//! [`BytesMut`]: https://docs.rs/bytes/0.4/bytes/struct.BytesMut.html
|
||||
//! [`AsyncRead`]: trait@tokio::io::AsyncRead
|
||||
//! [`AsyncWrite`]: trait@tokio::io::AsyncWrite
|
||||
//! [`Encoder`]: trait@Encoder
|
||||
//! [`BytesMut`]: bytes::BytesMut
|
||||
|
||||
use crate::codec::{Decoder, Encoder, Framed, FramedRead, FramedWrite};
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
//! Utilities for encoding and decoding frames.
|
||||
//! Adaptors from AsyncRead/AsyncWrite to Stream/Sink
|
||||
//!
|
||||
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
|
||||
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
|
||||
//! Framed streams are also known as transports.
|
||||
//! Raw I/O objects work with byte sequences, but higher-level code
|
||||
//! usually wants to batch these into meaningful chunks, called
|
||||
//! "frames".
|
||||
//!
|
||||
//! This module contains adapters to go from streams of bytes,
|
||||
//! [`AsyncRead`] and [`AsyncWrite`], to framed streams implementing
|
||||
//! [`Sink`] and [`Stream`]. Framed streams are also known as
|
||||
//! transports.
|
||||
//!
|
||||
//! [`AsyncRead`]: tokio::io::AsyncRead
|
||||
//! [`AsyncWrite`]: tokio::io::AsyncWrite
|
||||
@@ -18,6 +23,10 @@ pub use self::decoder::Decoder;
|
||||
mod encoder;
|
||||
pub use self::encoder::Encoder;
|
||||
|
||||
mod framed_impl;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use self::framed_impl::{FramedImpl, RWFrames, ReadFrame, WriteFrame};
|
||||
|
||||
mod framed;
|
||||
pub use self::framed::{Framed, FramedParts};
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
//! Tokio context aware futures utilities.
|
||||
//!
|
||||
//! This module includes utilities around integrating tokio with other runtimes
|
||||
//! by allowing the context to be attached to futures. This allows spawning
|
||||
//! futures on other executors while still using tokio to drive them. This
|
||||
//! can be useful if you need to use a tokio based library in an executor/runtime
|
||||
//! that does not provide a tokio context.
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
pin_project! {
|
||||
/// `TokioContext` allows connecting a custom executor with the tokio runtime.
|
||||
///
|
||||
/// It contains a `Handle` to the runtime. A handle to the runtime can be
|
||||
/// obtain by calling the `Runtime::handle()` method.
|
||||
pub struct TokioContext<F> {
|
||||
#[pin]
|
||||
inner: F,
|
||||
handle: Handle,
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Future> Future for TokioContext<F> {
|
||||
type Output = F::Output;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let me = self.project();
|
||||
let handle = me.handle;
|
||||
let fut = me.inner;
|
||||
|
||||
handle.enter(|| fut.poll(cx))
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait extension that simplifies bundling a `Handle` with a `Future`.
|
||||
pub trait HandleExt {
|
||||
/// Convenience method that takes a Future and returns a `TokioContext`.
|
||||
///
|
||||
/// # Example: calling Tokio Runtime from a custom ThreadPool
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio_util::context::HandleExt;
|
||||
/// use tokio::time::{delay_for, Duration};
|
||||
///
|
||||
/// let mut rt = tokio::runtime::Builder::new()
|
||||
/// .threaded_scheduler()
|
||||
/// .enable_all()
|
||||
/// .build().unwrap();
|
||||
///
|
||||
/// let rt2 = tokio::runtime::Builder::new()
|
||||
/// .threaded_scheduler()
|
||||
/// .build().unwrap();
|
||||
///
|
||||
/// let fut = delay_for(Duration::from_millis(2));
|
||||
///
|
||||
/// rt.block_on(
|
||||
/// rt2
|
||||
/// .handle()
|
||||
/// .wrap(async { delay_for(Duration::from_millis(2)).await }),
|
||||
/// );
|
||||
///```
|
||||
fn wrap<F: Future>(&self, fut: F) -> TokioContext<F>;
|
||||
}
|
||||
|
||||
impl HandleExt for Handle {
|
||||
fn wrap<F: Future>(&self, fut: F) -> TokioContext<F> {
|
||||
TokioContext {
|
||||
inner: fut,
|
||||
handle: self.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
@@ -35,3 +35,7 @@ cfg_udp! {
|
||||
cfg_compat! {
|
||||
pub mod compat;
|
||||
}
|
||||
|
||||
cfg_rt! {
|
||||
pub mod context;
|
||||
}
|
||||
|
||||
+36
-19
@@ -6,6 +6,7 @@ use bytes::{BufMut, BytesMut};
|
||||
use futures_core::ready;
|
||||
use futures_sink::Sink;
|
||||
use std::io;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
@@ -36,6 +37,8 @@ pub struct UdpFramed<C> {
|
||||
wr: BytesMut,
|
||||
out_addr: SocketAddr,
|
||||
flushed: bool,
|
||||
is_readable: bool,
|
||||
current_addr: Option<SocketAddr>,
|
||||
}
|
||||
|
||||
impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
|
||||
@@ -46,27 +49,39 @@ impl<C: Decoder + Unpin> Stream for UdpFramed<C> {
|
||||
|
||||
pin.rd.reserve(INITIAL_RD_CAPACITY);
|
||||
|
||||
let (_n, addr) = unsafe {
|
||||
// Read into the buffer without having to initialize the memory.
|
||||
//
|
||||
// safety: we know tokio::net::UdpSocket never reads from the memory
|
||||
// during a recv
|
||||
let res = {
|
||||
let bytes = &mut *(pin.rd.bytes_mut() as *mut _ as *mut [u8]);
|
||||
ready!(Pin::new(&mut pin.socket).poll_recv_from(cx, bytes))
|
||||
loop {
|
||||
// Are there are still bytes left in the read buffer to decode?
|
||||
if pin.is_readable {
|
||||
if let Some(frame) = pin.codec.decode_eof(&mut pin.rd)? {
|
||||
let current_addr = pin
|
||||
.current_addr
|
||||
.expect("will always be set before this line is called");
|
||||
|
||||
return Poll::Ready(Some(Ok((frame, current_addr))));
|
||||
}
|
||||
|
||||
// if this line has been reached then decode has returned `None`.
|
||||
pin.is_readable = false;
|
||||
pin.rd.clear();
|
||||
}
|
||||
|
||||
// We're out of data. Try and fetch more data to decode
|
||||
let addr = unsafe {
|
||||
// Convert `&mut [MaybeUnit<u8>]` to `&mut [u8]` because we will be
|
||||
// writing to it via `poll_recv_from` and therefore initializing the memory.
|
||||
let buf: &mut [u8] =
|
||||
&mut *(pin.rd.bytes_mut() as *mut [MaybeUninit<u8>] as *mut [u8]);
|
||||
|
||||
let res = ready!(Pin::new(&mut pin.socket).poll_recv_from(cx, buf));
|
||||
|
||||
let (n, addr) = res?;
|
||||
pin.rd.advance_mut(n);
|
||||
addr
|
||||
};
|
||||
|
||||
let (n, addr) = res?;
|
||||
pin.rd.advance_mut(n);
|
||||
(n, addr)
|
||||
};
|
||||
|
||||
let frame_res = pin.codec.decode(&mut pin.rd);
|
||||
pin.rd.clear();
|
||||
let frame = frame_res?;
|
||||
let result = frame.map(|frame| Ok((frame, addr))); // frame -> (frame, addr)
|
||||
|
||||
Poll::Ready(result)
|
||||
pin.current_addr = Some(addr);
|
||||
pin.is_readable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +163,8 @@ impl<C> UdpFramed<C> {
|
||||
rd: BytesMut::with_capacity(INITIAL_RD_CAPACITY),
|
||||
wr: BytesMut::with_capacity(INITIAL_WR_CAPACITY),
|
||||
flushed: true,
|
||||
is_readable: false,
|
||||
current_addr: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![cfg(feature = "rt")]
|
||||
|
||||
use tokio::runtime::Builder;
|
||||
use tokio::time::*;
|
||||
use tokio_util::context::HandleExt;
|
||||
|
||||
#[test]
|
||||
fn tokio_context_with_another_runtime() {
|
||||
let mut rt1 = Builder::new()
|
||||
.threaded_scheduler()
|
||||
.core_threads(1)
|
||||
// no timer!
|
||||
.build()
|
||||
.unwrap();
|
||||
let rt2 = Builder::new()
|
||||
.threaded_scheduler()
|
||||
.core_threads(1)
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Without the `HandleExt.wrap()` there would be a panic because there is
|
||||
// no timer running, since it would be referencing runtime r1.
|
||||
let _ = rt1.block_on(
|
||||
rt2.handle()
|
||||
.wrap(async move { delay_for(Duration::from_millis(2)).await }),
|
||||
);
|
||||
}
|
||||
+23
-2
@@ -1,5 +1,5 @@
|
||||
use tokio::{net::UdpSocket, stream::StreamExt};
|
||||
use tokio_util::codec::{Decoder, Encoder};
|
||||
use tokio_util::codec::{Decoder, Encoder, LinesCodec};
|
||||
use tokio_util::udp::UdpFramed;
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
@@ -10,7 +10,7 @@ use std::io;
|
||||
|
||||
#[cfg_attr(any(target_os = "macos", target_os = "ios"), allow(unused_assignments))]
|
||||
#[tokio::test]
|
||||
async fn send_framed() -> std::io::Result<()> {
|
||||
async fn send_framed_byte_codec() -> std::io::Result<()> {
|
||||
let mut a_soc = UdpSocket::bind("127.0.0.1:0").await?;
|
||||
let mut b_soc = UdpSocket::bind("127.0.0.1:0").await?;
|
||||
|
||||
@@ -77,3 +77,24 @@ impl Encoder<&[u8]> for ByteCodec {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn send_framed_lines_codec() -> std::io::Result<()> {
|
||||
let a_soc = UdpSocket::bind("127.0.0.1:0").await?;
|
||||
let b_soc = UdpSocket::bind("127.0.0.1:0").await?;
|
||||
|
||||
let a_addr = a_soc.local_addr()?;
|
||||
let b_addr = b_soc.local_addr()?;
|
||||
|
||||
let mut a = UdpFramed::new(a_soc, ByteCodec);
|
||||
let mut b = UdpFramed::new(b_soc, LinesCodec::new());
|
||||
|
||||
let msg = b"1\r\n2\r\n3\r\n".to_vec();
|
||||
a.send((&msg, b_addr)).await?;
|
||||
|
||||
assert_eq!(b.next().await.unwrap().unwrap(), ("1".to_string(), a_addr));
|
||||
assert_eq!(b.next().await.unwrap().unwrap(), ("2".to_string(), a_addr));
|
||||
assert_eq!(b.next().await.unwrap().unwrap(), ("3".to_string(), a_addr));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+464
-176
@@ -1,3 +1,120 @@
|
||||
# 0.2.25 (January 28, 2021)
|
||||
|
||||
### Changes
|
||||
|
||||
- chore: upgrade mio dependency (#3207)
|
||||
- runtime: update panic messages to include version (#3460)
|
||||
|
||||
### Fixes
|
||||
|
||||
- task: add missing feature flags for `task_local` (#3236)
|
||||
|
||||
# 0.2.24 (December 7, 2020)
|
||||
|
||||
### Fixes
|
||||
|
||||
- sync: fix mpsc bug related to closing the channel (#3215)
|
||||
|
||||
# 0.2.23 (November 12, 2020)
|
||||
|
||||
### Fixes
|
||||
|
||||
- time: report correct error for timers that exceed max duration (#2023)
|
||||
- time: fix resetting expired timers causing panics (#2587)
|
||||
- macros: silence `unreachable_code` warning in `select!` (#2678)
|
||||
- rt: fix potential leak during runtime shutdown (#2649)
|
||||
- sync: fix missing notification during mpsc close (#2854)
|
||||
|
||||
### Changes
|
||||
|
||||
- io: always re-export `std::io` (#2606)
|
||||
- dependencies: update `parking_lot` dependency to 0.11.0 (#2676)
|
||||
- io: rewrite `read_to_end` and `read_to_string` (#2560)
|
||||
- coop: reset coop budget when blocking in `block_on` (#2711)
|
||||
- sync: better Debug for Mutex (#2725)
|
||||
- net: make `UnixListener::poll_accept` public (#2880)
|
||||
- dep: raise `lazy_static` to `1.4.0` (#3132)
|
||||
- dep: raise `slab` to `0.4.2` (#3132)
|
||||
|
||||
### Added
|
||||
|
||||
- io: add `io::duplex()` as bidirectional reader/writer (#2661)
|
||||
- net: introduce split and `into_split` on `UnixDatagram` (#2557)
|
||||
- net: ensure that unix sockets have both `split` and `into_split` (#2687)
|
||||
- net: add `try_recv`/`from` & `try_send`/`to` to UnixDatagram (#1677)
|
||||
- net: Add `UdpSocket::{try_send,try_send_to}` methods (#1979)
|
||||
- net: implement `ToSocketAddrs` for `(String, u16)` (#2724)
|
||||
- io: add `ReaderStream` (#2714)
|
||||
- sync: implement map methods (#2771)
|
||||
|
||||
# 0.2.22 (July 21, 2020)
|
||||
|
||||
### Fixes
|
||||
- docs: misc improvements (#2572, #2658, #2663, #2656, #2647, #2630, #2487, #2621,
|
||||
#2624, #2600, #2623, #2622, #2577, #2569, #2589, #2575, #2540, #2564, #2567,
|
||||
#2520, #2521, #2493)
|
||||
- rt: allow calls to `block_on` inside calls to `block_in_place` that are
|
||||
themselves inside `block_on` (#2645)
|
||||
- net: fix non-portable behavior when dropping `TcpStream` `OwnedWriteHalf` (#2597)
|
||||
- io: improve stack usage by allocating large buffers on directly on the heap
|
||||
(#2634)
|
||||
- io: fix unsound pin projection in `AsyncReadExt::read_buf` and
|
||||
`AsyncWriteExt::write_buf` (#2612)
|
||||
- io: fix unnecessary zeroing for `AsyncRead` implementors (#2525)
|
||||
- io: Fix `BufReader` not correctly forwarding `poll_write_buf` (#2654)
|
||||
- io: fix panic in `AsyncReadExt::read_line` (#2541)
|
||||
|
||||
### Changes
|
||||
- coop: returning `Poll::Pending` no longer decrements the task budget (#2549)
|
||||
|
||||
### Added
|
||||
- io: little-endian variants of `AsyncReadExt` and `AsyncWriteExt` methods
|
||||
(#1915)
|
||||
- task: add [`tracing`] instrumentation to spawned tasks (#2655)
|
||||
- sync: allow unsized types in `Mutex` and `RwLock` (via `default` constructors)
|
||||
(#2615)
|
||||
- net: add `ToSocketAddrs` implementation for `&[SocketAddr]` (#2604)
|
||||
- fs: add `OpenOptionsExt` for `OpenOptions` (#2515)
|
||||
- fs: add `DirBuilder` (#2524)
|
||||
|
||||
[`tracing`]: https://crates.io/crates/tracing
|
||||
|
||||
# 0.2.21 (May 13, 2020)
|
||||
|
||||
### Fixes
|
||||
|
||||
- macros: disambiguate built-in `#[test]` attribute in macro expansion (#2503)
|
||||
- rt: `LocalSet` and task budgeting (#2462).
|
||||
- rt: task budgeting with `block_in_place` (#2502).
|
||||
- sync: release `broadcast` channel memory without sending a value (#2509).
|
||||
- time: notify when resetting a `Delay` to a time in the past (#2290)
|
||||
|
||||
### Added
|
||||
- io: `get_mut`, `get_ref`, and `into_inner` to `Lines` (#2450).
|
||||
- io: `mio::Ready` argument to `PollEvented` (#2419).
|
||||
- os: illumos support (#2486).
|
||||
- rt: `Handle::spawn_blocking` (#2501).
|
||||
- sync: `OwnedMutexGuard` for `Arc<Mutex<T>>` (#2455).
|
||||
|
||||
# 0.2.20 (April 28, 2020)
|
||||
|
||||
### Fixes
|
||||
- sync: `broadcast` closing the channel no longer requires capacity (#2448).
|
||||
- rt: regression when configuring runtime with `max_threads` less than number of CPUs (#2457).
|
||||
|
||||
# 0.2.19 (April 24, 2020)
|
||||
|
||||
### Fixes
|
||||
- docs: misc improvements (#2400, #2405, #2414, #2420, #2423, #2426, #2427, #2434, #2436, #2440).
|
||||
- rt: support `block_in_place` in more contexts (#2409, #2410).
|
||||
- stream: no panic in `merge()` and `chain()` when using `size_hint()` (#2430).
|
||||
- task: include visibility modifier when defining a task-local (#2416).
|
||||
|
||||
### Added
|
||||
- rt: `runtime::Handle::block_on` (#2437).
|
||||
- sync: owned `Semaphore` permit (#2421).
|
||||
- tcp: owned split (#2270).
|
||||
|
||||
# 0.2.18 (April 12, 2020)
|
||||
|
||||
### Fixes
|
||||
@@ -7,7 +124,7 @@
|
||||
# 0.2.17 (April 9, 2020)
|
||||
|
||||
### Fixes
|
||||
- rt: bug in work-stealing queue (#2387)
|
||||
- rt: bug in work-stealing queue (#2387)
|
||||
|
||||
### Changes
|
||||
- rt: threadpool uses logical CPU count instead of physical by default (#2391)
|
||||
@@ -17,121 +134,121 @@
|
||||
### Fixes
|
||||
|
||||
- sync: fix a regression where `Mutex`, `Semaphore`, and `RwLock` futures no
|
||||
longer implement `Sync` (#2375)
|
||||
- fs: fix `fs::copy` not copying file permissions (#2354)
|
||||
longer implement `Sync` ([#2375])
|
||||
- fs: fix `fs::copy` not copying file permissions ([#2354])
|
||||
|
||||
### Added
|
||||
|
||||
- time: added `deadline` method to `delay_queue::Expired` (#2300)
|
||||
- io: added `StreamReader` (#2052)
|
||||
- time: added `deadline` method to `delay_queue::Expired` ([#2300])
|
||||
- io: added `StreamReader` ([#2052])
|
||||
|
||||
# 0.2.15 (April 2, 2020)
|
||||
|
||||
### Fixes
|
||||
|
||||
- rt: fix queue regression (#2362).
|
||||
- rt: fix queue regression ([#2362]).
|
||||
|
||||
### Added
|
||||
|
||||
- sync: Add disarm to `mpsc::Sender` (#2358).
|
||||
- sync: Add disarm to `mpsc::Sender` ([#2358]).
|
||||
|
||||
# 0.2.14 (April 1, 2020)
|
||||
|
||||
### Fixes
|
||||
- rt: concurrency bug in scheduler (#2273).
|
||||
- rt: concurrency bug with shell runtime (#2333).
|
||||
- test-util: correct pause/resume of time (#2253).
|
||||
- time: `DelayQueue` correct wakeup after `insert` (#2285).
|
||||
- rt: concurrency bug in scheduler ([#2273]).
|
||||
- rt: concurrency bug with shell runtime ([#2333]).
|
||||
- test-util: correct pause/resume of time ([#2253]).
|
||||
- time: `DelayQueue` correct wakeup after `insert` ([#2285]).
|
||||
|
||||
### Added
|
||||
- io: impl `RawFd`, `AsRawHandle` for std io types (#2335).
|
||||
- io: impl `RawFd`, `AsRawHandle` for std io types ([#2335]).
|
||||
- rt: automatic cooperative task yielding (#2160, #2343, #2349).
|
||||
- sync: `RwLock::into_inner` (#2321).
|
||||
- sync: `RwLock::into_inner` ([#2321]).
|
||||
|
||||
### Changed
|
||||
- sync: semaphore, mutex internals rewritten to avoid allocations (#2325).
|
||||
- sync: semaphore, mutex internals rewritten to avoid allocations ([#2325]).
|
||||
|
||||
# 0.2.13 (February 28, 2020)
|
||||
|
||||
### Fixes
|
||||
- macros: unresolved import in `pin!` (#2281).
|
||||
- macros: unresolved import in `pin!` ([#2281]).
|
||||
|
||||
# 0.2.12 (February 27, 2020)
|
||||
|
||||
### Fixes
|
||||
- net: `UnixStream::poll_shutdown` should call `shutdown(Write)` (#2245).
|
||||
- process: Wake up read and write on `EPOLLERR` (#2218).
|
||||
- net: `UnixStream::poll_shutdown` should call `shutdown(Write)` ([#2245]).
|
||||
- process: Wake up read and write on `EPOLLERR` ([#2218]).
|
||||
- rt: potential deadlock when using `block_in_place` and shutting down the
|
||||
runtime (#2119).
|
||||
- rt: only detect number of CPUs if `core_threads` not specified (#2238).
|
||||
- sync: reduce `watch::Receiver` struct size (#2191).
|
||||
- time: succeed when setting delay of `$MAX-1` (#2184).
|
||||
- time: avoid having to poll `DelayQueue` after inserting new delay (#2217).
|
||||
runtime ([#2119]).
|
||||
- rt: only detect number of CPUs if `core_threads` not specified ([#2238]).
|
||||
- sync: reduce `watch::Receiver` struct size ([#2191]).
|
||||
- time: succeed when setting delay of `$MAX-1` ([#2184]).
|
||||
- time: avoid having to poll `DelayQueue` after inserting new delay ([#2217]).
|
||||
|
||||
### Added
|
||||
- macros: `pin!` variant that assigns to identifier and pins (#2274).
|
||||
- net: impl `Stream` for `Listener` types (#2275).
|
||||
- macros: `pin!` variant that assigns to identifier and pins ([#2274]).
|
||||
- net: impl `Stream` for `Listener` types ([#2275]).
|
||||
- rt: `Runtime::shutdown_timeout` waits for runtime to shutdown for specified
|
||||
duration (#2186).
|
||||
duration ([#2186]).
|
||||
- stream: `StreamMap` merges streams and can insert / remove streams at
|
||||
runtime (#2185).
|
||||
- stream: `StreamExt::skip()` skips a fixed number of items (#2204).
|
||||
- stream: `StreamExt::skip_while()` skips items based on a predicate (#2205).
|
||||
- sync: `Notify` provides basic `async` / `await` task notification (#2210).
|
||||
- sync: `Mutex::into_inner` retrieves guarded data (#2250).
|
||||
runtime ([#2185]).
|
||||
- stream: `StreamExt::skip()` skips a fixed number of items ([#2204]).
|
||||
- stream: `StreamExt::skip_while()` skips items based on a predicate ([#2205]).
|
||||
- sync: `Notify` provides basic `async` / `await` task notification ([#2210]).
|
||||
- sync: `Mutex::into_inner` retrieves guarded data ([#2250]).
|
||||
- sync: `mpsc::Sender::send_timeout` sends, waiting for up to specified duration
|
||||
for channel capacity (#2227).
|
||||
- time: impl `Ord` and `Hash` for `Instant` (#2239).
|
||||
for channel capacity ([#2227]).
|
||||
- time: impl `Ord` and `Hash` for `Instant` ([#2239]).
|
||||
|
||||
# 0.2.11 (January 27, 2020)
|
||||
|
||||
### Fixes
|
||||
- docs: misc fixes and tweaks (#2155, #2103, #2027, #2167, #2175).
|
||||
- macros: handle generics in `#[tokio::main]` method (#2177).
|
||||
- sync: `broadcast` potential lost notifications (#2135).
|
||||
- rt: improve "no runtime" panic messages (#2145).
|
||||
- macros: handle generics in `#[tokio::main]` method ([#2177]).
|
||||
- sync: `broadcast` potential lost notifications ([#2135]).
|
||||
- rt: improve "no runtime" panic messages ([#2145]).
|
||||
|
||||
### Added
|
||||
- optional support for using `parking_lot` internally (#2164).
|
||||
- fs: `fs::copy`, an async version of `std::fs::copy` (#2079).
|
||||
- macros: `select!` waits for the first branch to complete (#2152).
|
||||
- macros: `join!` waits for all branches to complete (#2158).
|
||||
- macros: `try_join!` waits for all branches to complete or the first error (#2169).
|
||||
- macros: `pin!` pins a value to the stack (#2163).
|
||||
- net: `ReadHalf::poll()` and `ReadHalf::poll_peak` (#2151)
|
||||
- stream: `StreamExt::timeout()` sets a per-item max duration (#2149).
|
||||
- stream: `StreamExt::fold()` applies a function, producing a single value. (#2122).
|
||||
- sync: impl `Eq`, `PartialEq` for `oneshot::RecvError` (#2168).
|
||||
- task: methods for inspecting the `JoinError` cause (#2051).
|
||||
- optional support for using `parking_lot` internally ([#2164]).
|
||||
- fs: `fs::copy`, an async version of `std::fs::copy` ([#2079]).
|
||||
- macros: `select!` waits for the first branch to complete ([#2152]).
|
||||
- macros: `join!` waits for all branches to complete ([#2158]).
|
||||
- macros: `try_join!` waits for all branches to complete or the first error ([#2169]).
|
||||
- macros: `pin!` pins a value to the stack ([#2163]).
|
||||
- net: `ReadHalf::poll()` and `ReadHalf::poll_peak` ([#2151])
|
||||
- stream: `StreamExt::timeout()` sets a per-item max duration ([#2149]).
|
||||
- stream: `StreamExt::fold()` applies a function, producing a single value. ([#2122]).
|
||||
- sync: impl `Eq`, `PartialEq` for `oneshot::RecvError` ([#2168]).
|
||||
- task: methods for inspecting the `JoinError` cause ([#2051]).
|
||||
|
||||
# 0.2.10 (January 21, 2020)
|
||||
|
||||
### Fixes
|
||||
- `#[tokio::main]` when `rt-core` feature flag is not enabled (#2139).
|
||||
- remove `AsyncBufRead` from `BufStream` impl block (#2108).
|
||||
- potential undefined behavior when implementing `AsyncRead` incorrectly (#2030).
|
||||
- `#[tokio::main]` when `rt-core` feature flag is not enabled ([#2139]).
|
||||
- remove `AsyncBufRead` from `BufStream` impl block ([#2108]).
|
||||
- potential undefined behavior when implementing `AsyncRead` incorrectly ([#2030]).
|
||||
|
||||
### Added
|
||||
- `BufStream::with_capacity` (#2125).
|
||||
- impl `From` and `Default` for `RwLock` (#2089).
|
||||
- `BufStream::with_capacity` ([#2125]).
|
||||
- impl `From` and `Default` for `RwLock` ([#2089]).
|
||||
- `io::ReadHalf::is_pair_of` checks if provided `WriteHalf` is for the same
|
||||
underlying object (#1762, #2144).
|
||||
- `runtime::Handle::try_current()` returns a handle to the current runtime (#2118).
|
||||
- `stream::empty()` returns an immediately ready empty stream (#2092).
|
||||
- `stream::once(val)` returns a stream that yields a single value: `val` (#2094).
|
||||
- `stream::pending()` returns a stream that never becomes ready (#2092).
|
||||
- `StreamExt::chain()` sequences a second stream after the first completes (#2093).
|
||||
- `StreamExt::collect()` transform a stream into a collection (#2109).
|
||||
- `StreamExt::fuse` ends the stream after the first `None` (#2085).
|
||||
- `StreamExt::merge` combines two streams, yielding values as they become ready (#2091).
|
||||
- Task-local storage (#2126).
|
||||
- `runtime::Handle::try_current()` returns a handle to the current runtime ([#2118]).
|
||||
- `stream::empty()` returns an immediately ready empty stream ([#2092]).
|
||||
- `stream::once(val)` returns a stream that yields a single value: `val` ([#2094]).
|
||||
- `stream::pending()` returns a stream that never becomes ready ([#2092]).
|
||||
- `StreamExt::chain()` sequences a second stream after the first completes ([#2093]).
|
||||
- `StreamExt::collect()` transform a stream into a collection ([#2109]).
|
||||
- `StreamExt::fuse` ends the stream after the first `None` ([#2085]).
|
||||
- `StreamExt::merge` combines two streams, yielding values as they become ready ([#2091]).
|
||||
- Task-local storage ([#2126]).
|
||||
|
||||
# 0.2.9 (January 9, 2020)
|
||||
|
||||
### Fixes
|
||||
- `AsyncSeek` impl for `File` (#1986).
|
||||
- `AsyncSeek` impl for `File` ([#1986]).
|
||||
- rt: shutdown deadlock in `threaded_scheduler` (#2074, #2082).
|
||||
- rt: memory ordering when dropping `JoinHandle` (#2044).
|
||||
- rt: memory ordering when dropping `JoinHandle` ([#2044]).
|
||||
- docs: misc API documentation fixes and improvements.
|
||||
|
||||
# 0.2.8 (January 7, 2020)
|
||||
@@ -143,108 +260,108 @@
|
||||
|
||||
### Fixes
|
||||
- potential deadlock when dropping `basic_scheduler` Runtime.
|
||||
- calling `spawn_blocking` from within a `spawn_blocking` (#2006).
|
||||
- storing a `Runtime` instance in a thread-local (#2011).
|
||||
- calling `spawn_blocking` from within a `spawn_blocking` ([#2006]).
|
||||
- storing a `Runtime` instance in a thread-local ([#2011]).
|
||||
- miscellaneous documentation fixes.
|
||||
- rt: fix `Waker::will_wake` to return true when tasks match (#2045).
|
||||
- test-util: `time::advance` runs pending tasks before changing the time (#2059).
|
||||
- rt: fix `Waker::will_wake` to return true when tasks match ([#2045]).
|
||||
- test-util: `time::advance` runs pending tasks before changing the time ([#2059]).
|
||||
|
||||
### Added
|
||||
- `net::lookup_host` maps a `T: ToSocketAddrs` to a stream of `SocketAddrs` (#1870).
|
||||
- `process::Child` fields are made public to match `std` (#2014).
|
||||
- impl `Stream` for `sync::broadcast::Receiver` (#2012).
|
||||
- `sync::RwLock` provides an asynchonous read-write lock (#1699).
|
||||
- `runtime::Handle::current` returns the handle for the current runtime (#2040).
|
||||
- `StreamExt::filter` filters stream values according to a predicate (#2001).
|
||||
- `StreamExt::filter_map` simultaneously filter and map stream values (#2001).
|
||||
- `StreamExt::try_next` convenience for streams of `Result<T, E>` (#2005).
|
||||
- `StreamExt::take` limits a stream to a specified number of values (#2025).
|
||||
- `StreamExt::take_while` limits a stream based on a predicate (#2029).
|
||||
- `StreamExt::all` tests if every element of the stream matches a predicate (#2035).
|
||||
- `StreamExt::any` tests if any element of the stream matches a predicate (#2034).
|
||||
- `task::LocalSet.await` runs spawned tasks until the set is idle (#1971).
|
||||
- `time::DelayQueue::len` returns the number entries in the queue (#1755).
|
||||
- expose runtime options from the `#[tokio::main]` and `#[tokio::test]` (#2022).
|
||||
- `net::lookup_host` maps a `T: ToSocketAddrs` to a stream of `SocketAddrs` ([#1870]).
|
||||
- `process::Child` fields are made public to match `std` ([#2014]).
|
||||
- impl `Stream` for `sync::broadcast::Receiver` ([#2012]).
|
||||
- `sync::RwLock` provides an asynchonous read-write lock ([#1699]).
|
||||
- `runtime::Handle::current` returns the handle for the current runtime ([#2040]).
|
||||
- `StreamExt::filter` filters stream values according to a predicate ([#2001]).
|
||||
- `StreamExt::filter_map` simultaneously filter and map stream values ([#2001]).
|
||||
- `StreamExt::try_next` convenience for streams of `Result<T, E>` ([#2005]).
|
||||
- `StreamExt::take` limits a stream to a specified number of values ([#2025]).
|
||||
- `StreamExt::take_while` limits a stream based on a predicate ([#2029]).
|
||||
- `StreamExt::all` tests if every element of the stream matches a predicate ([#2035]).
|
||||
- `StreamExt::any` tests if any element of the stream matches a predicate ([#2034]).
|
||||
- `task::LocalSet.await` runs spawned tasks until the set is idle ([#1971]).
|
||||
- `time::DelayQueue::len` returns the number entries in the queue ([#1755]).
|
||||
- expose runtime options from the `#[tokio::main]` and `#[tokio::test]` ([#2022]).
|
||||
|
||||
# 0.2.6 (December 19, 2019)
|
||||
|
||||
### Fixes
|
||||
- `fs::File::seek` API regression (#1991).
|
||||
- `fs::File::seek` API regression ([#1991]).
|
||||
|
||||
# 0.2.5 (December 18, 2019)
|
||||
|
||||
### Added
|
||||
- `io::AsyncSeek` trait (#1924).
|
||||
- `Mutex::try_lock` (#1939)
|
||||
- `mpsc::Receiver::try_recv` and `mpsc::UnboundedReceiver::try_recv` (#1939).
|
||||
- `writev` support for `TcpStream` (#1956).
|
||||
- `time::throttle` for throttling streams (#1949).
|
||||
- implement `Stream` for `time::DelayQueue` (#1975).
|
||||
- `sync::broadcast` provides a fan-out channel (#1943).
|
||||
- `sync::Semaphore` provides an async semaphore (#1973).
|
||||
- `stream::StreamExt` provides stream utilities (#1962).
|
||||
- `io::AsyncSeek` trait ([#1924]).
|
||||
- `Mutex::try_lock` ([#1939])
|
||||
- `mpsc::Receiver::try_recv` and `mpsc::UnboundedReceiver::try_recv` ([#1939]).
|
||||
- `writev` support for `TcpStream` ([#1956]).
|
||||
- `time::throttle` for throttling streams ([#1949]).
|
||||
- implement `Stream` for `time::DelayQueue` ([#1975]).
|
||||
- `sync::broadcast` provides a fan-out channel ([#1943]).
|
||||
- `sync::Semaphore` provides an async semaphore ([#1973]).
|
||||
- `stream::StreamExt` provides stream utilities ([#1962]).
|
||||
|
||||
### Fixes
|
||||
- deadlock risk while shutting down the runtime (#1972).
|
||||
- panic while shutting down the runtime (#1978).
|
||||
- `sync::MutexGuard` debug output (#1961).
|
||||
- deadlock risk while shutting down the runtime ([#1972]).
|
||||
- panic while shutting down the runtime ([#1978]).
|
||||
- `sync::MutexGuard` debug output ([#1961]).
|
||||
- misc doc improvements (#1933, #1934, #1940, #1942).
|
||||
|
||||
### Changes
|
||||
- runtime threads are configured with `runtime::Builder::core_threads` and
|
||||
`runtime::Builder::max_threads`. `runtime::Builder::num_threads` is
|
||||
deprecated (#1977).
|
||||
deprecated ([#1977]).
|
||||
|
||||
# 0.2.4 (December 6, 2019)
|
||||
|
||||
### Fixes
|
||||
- `sync::Mutex` deadlock when `lock()` future is dropped early (#1898).
|
||||
- `sync::Mutex` deadlock when `lock()` future is dropped early ([#1898]).
|
||||
|
||||
# 0.2.3 (December 6, 2019)
|
||||
|
||||
### Added
|
||||
- read / write integers using `AsyncReadExt` and `AsyncWriteExt` (#1863).
|
||||
- `read_buf` / `write_buf` for reading / writing `Buf` / `BufMut` (#1881).
|
||||
- `TcpStream::poll_peek` - pollable API for performing TCP peek (#1864).
|
||||
- read / write integers using `AsyncReadExt` and `AsyncWriteExt` ([#1863]).
|
||||
- `read_buf` / `write_buf` for reading / writing `Buf` / `BufMut` ([#1881]).
|
||||
- `TcpStream::poll_peek` - pollable API for performing TCP peek ([#1864]).
|
||||
- `sync::oneshot::error::TryRecvError` provides variants to detect the error
|
||||
kind (#1874).
|
||||
- `LocalSet::block_on` accepts `!'static` task (#1882).
|
||||
- `task::JoinError` is now `Sync` (#1888).
|
||||
kind ([#1874]).
|
||||
- `LocalSet::block_on` accepts `!'static` task ([#1882]).
|
||||
- `task::JoinError` is now `Sync` ([#1888]).
|
||||
- impl conversions between `tokio::time::Instant` and
|
||||
`std::time::Instant` (#1904).
|
||||
`std::time::Instant` ([#1904]).
|
||||
|
||||
### Fixes
|
||||
- calling `spawn_blocking` after runtime shutdown (#1875).
|
||||
- `LocalSet` drop inifinite loop (#1892).
|
||||
- `LocalSet` hang under load (#1905).
|
||||
- calling `spawn_blocking` after runtime shutdown ([#1875]).
|
||||
- `LocalSet` drop inifinite loop ([#1892]).
|
||||
- `LocalSet` hang under load ([#1905]).
|
||||
- improved documentation (#1865, #1866, #1868, #1874, #1876, #1911).
|
||||
|
||||
# 0.2.2 (November 29, 2019)
|
||||
|
||||
### Fixes
|
||||
- scheduling with `basic_scheduler` (#1861).
|
||||
- update `spawn` panic message to specify that a task scheduler is required (#1839).
|
||||
- API docs example for `runtime::Builder` to include a task scheduler (#1841).
|
||||
- general documentation (#1834).
|
||||
- building on illumos/solaris (#1772).
|
||||
- panic when dropping `LocalSet` (#1843).
|
||||
- API docs mention the required Cargo features for `Builder::{basic, threaded}_scheduler` (#1858).
|
||||
- scheduling with `basic_scheduler` ([#1861]).
|
||||
- update `spawn` panic message to specify that a task scheduler is required ([#1839]).
|
||||
- API docs example for `runtime::Builder` to include a task scheduler ([#1841]).
|
||||
- general documentation ([#1834]).
|
||||
- building on illumos/solaris ([#1772]).
|
||||
- panic when dropping `LocalSet` ([#1843]).
|
||||
- API docs mention the required Cargo features for `Builder::{basic, threaded}_scheduler` ([#1858]).
|
||||
|
||||
### Added
|
||||
- impl `Stream` for `signal::unix::Signal` (#1849).
|
||||
- API docs for platform specific behavior of `signal::ctrl_c` and `signal::unix::Signal` (#1854).
|
||||
- API docs for `signal::unix::Signal::{recv, poll_recv}` and `signal::windows::CtrlBreak::{recv, poll_recv}` (#1854).
|
||||
- `File::into_std` and `File::try_into_std` methods (#1856).
|
||||
- impl `Stream` for `signal::unix::Signal` ([#1849]).
|
||||
- API docs for platform specific behavior of `signal::ctrl_c` and `signal::unix::Signal` ([#1854]).
|
||||
- API docs for `signal::unix::Signal::{recv, poll_recv}` and `signal::windows::CtrlBreak::{recv, poll_recv}` ([#1854]).
|
||||
- `File::into_std` and `File::try_into_std` methods ([#1856]).
|
||||
|
||||
# 0.2.1 (November 26, 2019)
|
||||
|
||||
### Fixes
|
||||
- API docs for `TcpListener::incoming`, `UnixListener::incoming` (#1831).
|
||||
- API docs for `TcpListener::incoming`, `UnixListener::incoming` ([#1831]).
|
||||
|
||||
### Added
|
||||
- `tokio::task::LocalSet` provides a strategy for spawning `!Send` tasks (#1733).
|
||||
- export `tokio::time::Elapsed` (#1826).
|
||||
- impl `AsRawFd`, `AsRawHandle` for `tokio::fs::File` (#1827).
|
||||
- `tokio::task::LocalSet` provides a strategy for spawning `!Send` tasks ([#1733]).
|
||||
- export `tokio::time::Elapsed` ([#1826]).
|
||||
- impl `AsRawFd`, `AsRawHandle` for `tokio::fs::File` ([#1827]).
|
||||
|
||||
# 0.2.0 (November 26, 2019)
|
||||
|
||||
@@ -270,69 +387,69 @@ another. This changelog entry contains a highlight
|
||||
# 0.1.21 (May 30, 2019)
|
||||
|
||||
### Changed
|
||||
- Bump `tokio-trace-core` version to 0.2 (#1111).
|
||||
- Bump `tokio-trace-core` version to 0.2 ([#1111]).
|
||||
|
||||
# 0.1.20 (May 14, 2019)
|
||||
|
||||
### Added
|
||||
- `tokio::runtime::Builder::panic_handler` allows configuring handling
|
||||
panics on the runtime (#1055).
|
||||
panics on the runtime ([#1055]).
|
||||
|
||||
# 0.1.19 (April 22, 2019)
|
||||
|
||||
### Added
|
||||
- Re-export `tokio::sync::Mutex` primitive (#964).
|
||||
- Re-export `tokio::sync::Mutex` primitive ([#964]).
|
||||
|
||||
# 0.1.18 (March 22, 2019)
|
||||
|
||||
### Added
|
||||
- `TypedExecutor` re-export and implementations (#993).
|
||||
- `TypedExecutor` re-export and implementations ([#993]).
|
||||
|
||||
# 0.1.17 (March 13, 2019)
|
||||
|
||||
### Added
|
||||
- Propagate trace subscriber in the runtime (#966).
|
||||
- Propagate trace subscriber in the runtime ([#966]).
|
||||
|
||||
# 0.1.16 (March 1, 2019)
|
||||
|
||||
### Fixed
|
||||
- async-await: track latest nightly changes (#940).
|
||||
- async-await: track latest nightly changes ([#940]).
|
||||
|
||||
### Added
|
||||
- `sync::Watch`, a single value broadcast channel (#922).
|
||||
- Async equivalent of read / write file helpers being added to `std` (#896).
|
||||
- `sync::Watch`, a single value broadcast channel ([#922]).
|
||||
- Async equivalent of read / write file helpers being added to `std` ([#896]).
|
||||
|
||||
# 0.1.15 (January 24, 2019)
|
||||
|
||||
### Added
|
||||
- Re-export tokio-sync APIs (#839).
|
||||
- Stream enumerate combinator (#832).
|
||||
- Re-export tokio-sync APIs ([#839]).
|
||||
- Stream enumerate combinator ([#832]).
|
||||
|
||||
# 0.1.14 (January 6, 2019)
|
||||
|
||||
* Use feature flags to break up the crate, allowing users to pick & choose
|
||||
components (#808).
|
||||
* Export `UnixDatagram` and `UnixDatagramFramed` (#772).
|
||||
components ([#808]).
|
||||
* Export `UnixDatagram` and `UnixDatagramFramed` ([#772]).
|
||||
|
||||
# 0.1.13 (November 21, 2018)
|
||||
|
||||
* Fix `Runtime::reactor()` when no tasks are spawned (#721).
|
||||
* `runtime::Builder` no longer uses deprecated methods (#749).
|
||||
* Fix `Runtime::reactor()` when no tasks are spawned ([#721]).
|
||||
* `runtime::Builder` no longer uses deprecated methods ([#749]).
|
||||
* Provide `after_start` and `before_stop` configuration settings for
|
||||
`Runtime` (#756).
|
||||
* Implement throttle stream combinator (#736).
|
||||
`Runtime` ([#756]).
|
||||
* Implement throttle stream combinator ([#736]).
|
||||
|
||||
# 0.1.12 (October 23, 2018)
|
||||
|
||||
* runtime: expose `keep_alive` on runtime builder (#676).
|
||||
* runtime: create a reactor per worker thread (#660).
|
||||
* codec: fix panic in `LengthDelimitedCodec` (#682).
|
||||
* io: re-export `tokio_io::io::read` function (#689).
|
||||
* runtime: check for executor re-entry in more places (#708).
|
||||
* runtime: expose `keep_alive` on runtime builder ([#676]).
|
||||
* runtime: create a reactor per worker thread ([#660]).
|
||||
* codec: fix panic in `LengthDelimitedCodec` ([#682]).
|
||||
* io: re-export `tokio_io::io::read` function ([#689]).
|
||||
* runtime: check for executor re-entry in more places ([#708]).
|
||||
|
||||
# 0.1.11 (September 28, 2018)
|
||||
|
||||
* Fix `tokio-async-await` dependency (#675).
|
||||
* Fix `tokio-async-await` dependency ([#675]).
|
||||
|
||||
# 0.1.10 (September 27, 2018)
|
||||
|
||||
@@ -340,65 +457,65 @@ another. This changelog entry contains a highlight
|
||||
|
||||
# 0.1.9 (September 27, 2018)
|
||||
|
||||
* Experimental async/await improvements (#661).
|
||||
* Re-export `TaskExecutor` from `tokio-current-thread` (#652).
|
||||
* Improve `Runtime` builder API (#645).
|
||||
* Experimental async/await improvements ([#661]).
|
||||
* Re-export `TaskExecutor` from `tokio-current-thread` ([#652]).
|
||||
* Improve `Runtime` builder API ([#645]).
|
||||
* `tokio::run` panics when called from the context of an executor
|
||||
(#646).
|
||||
* Introduce `StreamExt` with a `timeout` helper (#573).
|
||||
* Move `length_delimited` into `tokio` (#575).
|
||||
* Re-organize `tokio::net` module (#548).
|
||||
([#646]).
|
||||
* Introduce `StreamExt` with a `timeout` helper ([#573]).
|
||||
* Move `length_delimited` into `tokio` ([#575]).
|
||||
* Re-organize `tokio::net` module ([#548]).
|
||||
* Re-export `tokio-current-thread::spawn` in current_thread runtime
|
||||
(#579).
|
||||
([#579]).
|
||||
|
||||
# 0.1.8 (August 23, 2018)
|
||||
|
||||
* Extract tokio::executor::current_thread to a sub crate (#370)
|
||||
* Add `Runtime::block_on` (#398)
|
||||
* Add `runtime::current_thread::block_on_all` (#477)
|
||||
* Misc documentation improvements (#450)
|
||||
* Implement `std::error::Error` for error types (#501)
|
||||
* Extract tokio::executor::current_thread to a sub crate ([#370])
|
||||
* Add `Runtime::block_on` ([#398])
|
||||
* Add `runtime::current_thread::block_on_all` ([#477])
|
||||
* Misc documentation improvements ([#450])
|
||||
* Implement `std::error::Error` for error types ([#501])
|
||||
|
||||
# 0.1.7 (June 6, 2018)
|
||||
|
||||
* Add `Runtime::block_on` for concurrent runtime (#391).
|
||||
* Add `Runtime::block_on` for concurrent runtime ([#391]).
|
||||
* Provide handle to `current_thread::Runtime` that allows spawning tasks from
|
||||
other threads (#340).
|
||||
* Provide `clock::now()`, a configurable source of time (#381).
|
||||
other threads ([#340]).
|
||||
* Provide `clock::now()`, a configurable source of time ([#381]).
|
||||
|
||||
# 0.1.6 (May 2, 2018)
|
||||
|
||||
* Add asynchronous filesystem APIs (#323).
|
||||
* Add "current thread" runtime variant (#308).
|
||||
* Add asynchronous filesystem APIs ([#323]).
|
||||
* Add "current thread" runtime variant ([#308]).
|
||||
* `CurrentThread`: Expose inner `Park` instance.
|
||||
* Improve fairness of `CurrentThread` executor (#313).
|
||||
* Improve fairness of `CurrentThread` executor ([#313]).
|
||||
|
||||
# 0.1.5 (March 30, 2018)
|
||||
|
||||
* Provide timer API (#266)
|
||||
* Provide timer API ([#266])
|
||||
|
||||
# 0.1.4 (March 22, 2018)
|
||||
|
||||
* Fix build on FreeBSD (#218)
|
||||
* Shutdown the Runtime when the handle is dropped (#214)
|
||||
* Set Runtime thread name prefix for worker threads (#232)
|
||||
* Add builder for Runtime (#234)
|
||||
* Extract TCP and UDP types into separate crates (#224)
|
||||
* Fix build on FreeBSD ([#218])
|
||||
* Shutdown the Runtime when the handle is dropped ([#214])
|
||||
* Set Runtime thread name prefix for worker threads ([#232])
|
||||
* Add builder for Runtime ([#234])
|
||||
* Extract TCP and UDP types into separate crates ([#224])
|
||||
* Optionally support futures 0.2.
|
||||
|
||||
# 0.1.3 (March 09, 2018)
|
||||
|
||||
* Fix `CurrentThread::turn` to block on idle (#212).
|
||||
* Fix `CurrentThread::turn` to block on idle ([#212]).
|
||||
|
||||
# 0.1.2 (March 09, 2018)
|
||||
|
||||
* Introduce Tokio Runtime (#141)
|
||||
* Provide `CurrentThread` for more flexible usage of current thread executor (#141).
|
||||
* Add Lio for platforms that support it (#142).
|
||||
* I/O resources now lazily bind to the reactor (#160).
|
||||
* Extract Reactor to dedicated crate (#169)
|
||||
* Add facade to sub crates and add prelude (#166).
|
||||
* Switch TCP/UDP fns to poll_ -> Poll<...> style (#175)
|
||||
* Introduce Tokio Runtime ([#141])
|
||||
* Provide `CurrentThread` for more flexible usage of current thread executor ([#141]).
|
||||
* Add Lio for platforms that support it ([#142]).
|
||||
* I/O resources now lazily bind to the reactor ([#160]).
|
||||
* Extract Reactor to dedicated crate ([#169])
|
||||
* Add facade to sub crates and add prelude ([#166]).
|
||||
* Switch TCP/UDP fns to poll_ -> Poll<...> style ([#175])
|
||||
|
||||
# 0.1.1 (February 09, 2018)
|
||||
|
||||
@@ -407,3 +524,174 @@ another. This changelog entry contains a highlight
|
||||
# 0.1.0 (February 07, 2018)
|
||||
|
||||
* Initial crate released based on [RFC](https://github.com/tokio-rs/tokio-rfcs/pull/3).
|
||||
|
||||
[#2375]: https://github.com/tokio-rs/tokio/pull/2375
|
||||
[#2362]: https://github.com/tokio-rs/tokio/pull/2362
|
||||
[#2358]: https://github.com/tokio-rs/tokio/pull/2358
|
||||
[#2354]: https://github.com/tokio-rs/tokio/pull/2354
|
||||
[#2335]: https://github.com/tokio-rs/tokio/pull/2335
|
||||
[#2333]: https://github.com/tokio-rs/tokio/pull/2333
|
||||
[#2325]: https://github.com/tokio-rs/tokio/pull/2325
|
||||
[#2321]: https://github.com/tokio-rs/tokio/pull/2321
|
||||
[#2300]: https://github.com/tokio-rs/tokio/pull/2300
|
||||
[#2285]: https://github.com/tokio-rs/tokio/pull/2285
|
||||
[#2281]: https://github.com/tokio-rs/tokio/pull/2281
|
||||
[#2275]: https://github.com/tokio-rs/tokio/pull/2275
|
||||
[#2274]: https://github.com/tokio-rs/tokio/pull/2274
|
||||
[#2273]: https://github.com/tokio-rs/tokio/pull/2273
|
||||
[#2253]: https://github.com/tokio-rs/tokio/pull/2253
|
||||
[#2250]: https://github.com/tokio-rs/tokio/pull/2250
|
||||
[#2245]: https://github.com/tokio-rs/tokio/pull/2245
|
||||
[#2239]: https://github.com/tokio-rs/tokio/pull/2239
|
||||
[#2238]: https://github.com/tokio-rs/tokio/pull/2238
|
||||
[#2227]: https://github.com/tokio-rs/tokio/pull/2227
|
||||
[#2218]: https://github.com/tokio-rs/tokio/pull/2218
|
||||
[#2217]: https://github.com/tokio-rs/tokio/pull/2217
|
||||
[#2210]: https://github.com/tokio-rs/tokio/pull/2210
|
||||
[#2205]: https://github.com/tokio-rs/tokio/pull/2205
|
||||
[#2204]: https://github.com/tokio-rs/tokio/pull/2204
|
||||
[#2191]: https://github.com/tokio-rs/tokio/pull/2191
|
||||
[#2186]: https://github.com/tokio-rs/tokio/pull/2186
|
||||
[#2185]: https://github.com/tokio-rs/tokio/pull/2185
|
||||
[#2184]: https://github.com/tokio-rs/tokio/pull/2184
|
||||
[#2177]: https://github.com/tokio-rs/tokio/pull/2177
|
||||
[#2169]: https://github.com/tokio-rs/tokio/pull/2169
|
||||
[#2168]: https://github.com/tokio-rs/tokio/pull/2168
|
||||
[#2164]: https://github.com/tokio-rs/tokio/pull/2164
|
||||
[#2163]: https://github.com/tokio-rs/tokio/pull/2163
|
||||
[#2158]: https://github.com/tokio-rs/tokio/pull/2158
|
||||
[#2152]: https://github.com/tokio-rs/tokio/pull/2152
|
||||
[#2151]: https://github.com/tokio-rs/tokio/pull/2151
|
||||
[#2149]: https://github.com/tokio-rs/tokio/pull/2149
|
||||
[#2145]: https://github.com/tokio-rs/tokio/pull/2145
|
||||
[#2139]: https://github.com/tokio-rs/tokio/pull/2139
|
||||
[#2135]: https://github.com/tokio-rs/tokio/pull/2135
|
||||
[#2126]: https://github.com/tokio-rs/tokio/pull/2126
|
||||
[#2125]: https://github.com/tokio-rs/tokio/pull/2125
|
||||
[#2122]: https://github.com/tokio-rs/tokio/pull/2122
|
||||
[#2119]: https://github.com/tokio-rs/tokio/pull/2119
|
||||
[#2118]: https://github.com/tokio-rs/tokio/pull/2118
|
||||
[#2109]: https://github.com/tokio-rs/tokio/pull/2109
|
||||
[#2108]: https://github.com/tokio-rs/tokio/pull/2108
|
||||
[#2094]: https://github.com/tokio-rs/tokio/pull/2094
|
||||
[#2093]: https://github.com/tokio-rs/tokio/pull/2093
|
||||
[#2092]: https://github.com/tokio-rs/tokio/pull/2092
|
||||
[#2091]: https://github.com/tokio-rs/tokio/pull/2091
|
||||
[#2089]: https://github.com/tokio-rs/tokio/pull/2089
|
||||
[#2085]: https://github.com/tokio-rs/tokio/pull/2085
|
||||
[#2079]: https://github.com/tokio-rs/tokio/pull/2079
|
||||
[#2059]: https://github.com/tokio-rs/tokio/pull/2059
|
||||
[#2052]: https://github.com/tokio-rs/tokio/pull/2052
|
||||
[#2051]: https://github.com/tokio-rs/tokio/pull/2051
|
||||
[#2045]: https://github.com/tokio-rs/tokio/pull/2045
|
||||
[#2044]: https://github.com/tokio-rs/tokio/pull/2044
|
||||
[#2040]: https://github.com/tokio-rs/tokio/pull/2040
|
||||
[#2035]: https://github.com/tokio-rs/tokio/pull/2035
|
||||
[#2034]: https://github.com/tokio-rs/tokio/pull/2034
|
||||
[#2030]: https://github.com/tokio-rs/tokio/pull/2030
|
||||
[#2029]: https://github.com/tokio-rs/tokio/pull/2029
|
||||
[#2025]: https://github.com/tokio-rs/tokio/pull/2025
|
||||
[#2022]: https://github.com/tokio-rs/tokio/pull/2022
|
||||
[#2014]: https://github.com/tokio-rs/tokio/pull/2014
|
||||
[#2012]: https://github.com/tokio-rs/tokio/pull/2012
|
||||
[#2011]: https://github.com/tokio-rs/tokio/pull/2011
|
||||
[#2006]: https://github.com/tokio-rs/tokio/pull/2006
|
||||
[#2005]: https://github.com/tokio-rs/tokio/pull/2005
|
||||
[#2001]: https://github.com/tokio-rs/tokio/pull/2001
|
||||
[#1991]: https://github.com/tokio-rs/tokio/pull/1991
|
||||
[#1986]: https://github.com/tokio-rs/tokio/pull/1986
|
||||
[#1978]: https://github.com/tokio-rs/tokio/pull/1978
|
||||
[#1977]: https://github.com/tokio-rs/tokio/pull/1977
|
||||
[#1975]: https://github.com/tokio-rs/tokio/pull/1975
|
||||
[#1973]: https://github.com/tokio-rs/tokio/pull/1973
|
||||
[#1972]: https://github.com/tokio-rs/tokio/pull/1972
|
||||
[#1971]: https://github.com/tokio-rs/tokio/pull/1971
|
||||
[#1962]: https://github.com/tokio-rs/tokio/pull/1962
|
||||
[#1961]: https://github.com/tokio-rs/tokio/pull/1961
|
||||
[#1956]: https://github.com/tokio-rs/tokio/pull/1956
|
||||
[#1949]: https://github.com/tokio-rs/tokio/pull/1949
|
||||
[#1943]: https://github.com/tokio-rs/tokio/pull/1943
|
||||
[#1939]: https://github.com/tokio-rs/tokio/pull/1939
|
||||
[#1924]: https://github.com/tokio-rs/tokio/pull/1924
|
||||
[#1905]: https://github.com/tokio-rs/tokio/pull/1905
|
||||
[#1904]: https://github.com/tokio-rs/tokio/pull/1904
|
||||
[#1898]: https://github.com/tokio-rs/tokio/pull/1898
|
||||
[#1892]: https://github.com/tokio-rs/tokio/pull/1892
|
||||
[#1888]: https://github.com/tokio-rs/tokio/pull/1888
|
||||
[#1882]: https://github.com/tokio-rs/tokio/pull/1882
|
||||
[#1881]: https://github.com/tokio-rs/tokio/pull/1881
|
||||
[#1875]: https://github.com/tokio-rs/tokio/pull/1875
|
||||
[#1874]: https://github.com/tokio-rs/tokio/pull/1874
|
||||
[#1870]: https://github.com/tokio-rs/tokio/pull/1870
|
||||
[#1864]: https://github.com/tokio-rs/tokio/pull/1864
|
||||
[#1863]: https://github.com/tokio-rs/tokio/pull/1863
|
||||
[#1861]: https://github.com/tokio-rs/tokio/pull/1861
|
||||
[#1858]: https://github.com/tokio-rs/tokio/pull/1858
|
||||
[#1856]: https://github.com/tokio-rs/tokio/pull/1856
|
||||
[#1854]: https://github.com/tokio-rs/tokio/pull/1854
|
||||
[#1849]: https://github.com/tokio-rs/tokio/pull/1849
|
||||
[#1843]: https://github.com/tokio-rs/tokio/pull/1843
|
||||
[#1841]: https://github.com/tokio-rs/tokio/pull/1841
|
||||
[#1839]: https://github.com/tokio-rs/tokio/pull/1839
|
||||
[#1834]: https://github.com/tokio-rs/tokio/pull/1834
|
||||
[#1831]: https://github.com/tokio-rs/tokio/pull/1831
|
||||
[#1827]: https://github.com/tokio-rs/tokio/pull/1827
|
||||
[#1826]: https://github.com/tokio-rs/tokio/pull/1826
|
||||
[#1772]: https://github.com/tokio-rs/tokio/pull/1772
|
||||
[#1755]: https://github.com/tokio-rs/tokio/pull/1755
|
||||
[#1733]: https://github.com/tokio-rs/tokio/pull/1733
|
||||
[#1699]: https://github.com/tokio-rs/tokio/pull/1699
|
||||
[#1111]: https://github.com/tokio-rs/tokio/pull/1111
|
||||
[#1055]: https://github.com/tokio-rs/tokio/pull/1055
|
||||
[#993]: https://github.com/tokio-rs/tokio/pull/993
|
||||
[#966]: https://github.com/tokio-rs/tokio/pull/966
|
||||
[#964]: https://github.com/tokio-rs/tokio/pull/964
|
||||
[#940]: https://github.com/tokio-rs/tokio/pull/940
|
||||
[#922]: https://github.com/tokio-rs/tokio/pull/922
|
||||
[#896]: https://github.com/tokio-rs/tokio/pull/896
|
||||
[#839]: https://github.com/tokio-rs/tokio/pull/839
|
||||
[#832]: https://github.com/tokio-rs/tokio/pull/832
|
||||
[#808]: https://github.com/tokio-rs/tokio/pull/808
|
||||
[#772]: https://github.com/tokio-rs/tokio/pull/772
|
||||
[#756]: https://github.com/tokio-rs/tokio/pull/756
|
||||
[#749]: https://github.com/tokio-rs/tokio/pull/749
|
||||
[#736]: https://github.com/tokio-rs/tokio/pull/736
|
||||
[#721]: https://github.com/tokio-rs/tokio/pull/721
|
||||
[#708]: https://github.com/tokio-rs/tokio/pull/708
|
||||
[#689]: https://github.com/tokio-rs/tokio/pull/689
|
||||
[#682]: https://github.com/tokio-rs/tokio/pull/682
|
||||
[#676]: https://github.com/tokio-rs/tokio/pull/676
|
||||
[#675]: https://github.com/tokio-rs/tokio/pull/675
|
||||
[#661]: https://github.com/tokio-rs/tokio/pull/661
|
||||
[#660]: https://github.com/tokio-rs/tokio/pull/660
|
||||
[#652]: https://github.com/tokio-rs/tokio/pull/652
|
||||
[#646]: https://github.com/tokio-rs/tokio/pull/646
|
||||
[#645]: https://github.com/tokio-rs/tokio/pull/645
|
||||
[#579]: https://github.com/tokio-rs/tokio/pull/579
|
||||
[#575]: https://github.com/tokio-rs/tokio/pull/575
|
||||
[#573]: https://github.com/tokio-rs/tokio/pull/573
|
||||
[#548]: https://github.com/tokio-rs/tokio/pull/548
|
||||
[#501]: https://github.com/tokio-rs/tokio/pull/501
|
||||
[#477]: https://github.com/tokio-rs/tokio/pull/477
|
||||
[#450]: https://github.com/tokio-rs/tokio/pull/450
|
||||
[#398]: https://github.com/tokio-rs/tokio/pull/398
|
||||
[#391]: https://github.com/tokio-rs/tokio/pull/391
|
||||
[#381]: https://github.com/tokio-rs/tokio/pull/381
|
||||
[#370]: https://github.com/tokio-rs/tokio/pull/370
|
||||
[#340]: https://github.com/tokio-rs/tokio/pull/340
|
||||
[#323]: https://github.com/tokio-rs/tokio/pull/323
|
||||
[#313]: https://github.com/tokio-rs/tokio/pull/313
|
||||
[#308]: https://github.com/tokio-rs/tokio/pull/308
|
||||
[#266]: https://github.com/tokio-rs/tokio/pull/266
|
||||
[#234]: https://github.com/tokio-rs/tokio/pull/234
|
||||
[#232]: https://github.com/tokio-rs/tokio/pull/232
|
||||
[#224]: https://github.com/tokio-rs/tokio/pull/224
|
||||
[#218]: https://github.com/tokio-rs/tokio/pull/218
|
||||
[#214]: https://github.com/tokio-rs/tokio/pull/214
|
||||
[#212]: https://github.com/tokio-rs/tokio/pull/212
|
||||
[#175]: https://github.com/tokio-rs/tokio/pull/175
|
||||
[#169]: https://github.com/tokio-rs/tokio/pull/169
|
||||
[#166]: https://github.com/tokio-rs/tokio/pull/166
|
||||
[#160]: https://github.com/tokio-rs/tokio/pull/160
|
||||
[#142]: https://github.com/tokio-rs/tokio/pull/142
|
||||
[#141]: https://github.com/tokio-rs/tokio/pull/141
|
||||
|
||||
+13
-13
@@ -8,12 +8,12 @@ name = "tokio"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.2.x" git tag.
|
||||
version = "0.2.18"
|
||||
version = "0.2.25"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio/0.2.18/tokio/"
|
||||
documentation = "https://docs.rs/tokio/0.2.25/tokio/"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
@@ -67,7 +67,7 @@ process = [
|
||||
"winapi/winerror",
|
||||
]
|
||||
# Includes basic task execution capabilities
|
||||
rt-core = []
|
||||
rt-core = ["slab"]
|
||||
rt-util = []
|
||||
rt-threaded = [
|
||||
"num_cpus",
|
||||
@@ -91,7 +91,7 @@ udp = ["io-driver"]
|
||||
uds = ["io-driver", "mio-uds", "libc"]
|
||||
|
||||
[dependencies]
|
||||
tokio-macros = { version = "0.2.4", path = "../tokio-macros", optional = true }
|
||||
tokio-macros = { version = "0.2.6", optional = true }
|
||||
|
||||
bytes = "0.5.0"
|
||||
pin-project-lite = "0.1.1"
|
||||
@@ -99,13 +99,14 @@ pin-project-lite = "0.1.1"
|
||||
# Everything else is optional...
|
||||
fnv = { version = "1.0.6", optional = true }
|
||||
futures-core = { version = "0.3.0", optional = true }
|
||||
lazy_static = { version = "1.0.2", optional = true }
|
||||
lazy_static = { version = "1.4.0", optional = true }
|
||||
memchr = { version = "2.2", optional = true }
|
||||
mio = { version = "0.6.20", optional = true }
|
||||
mio = { version = "0.6.23", optional = true }
|
||||
iovec = { version = "0.1.4", optional = true }
|
||||
num_cpus = { version = "1.8.0", optional = true }
|
||||
parking_lot = { version = "0.10.0", optional = true } # Not in full
|
||||
slab = { version = "0.4.1", optional = true } # Backs `DelayQueue`
|
||||
parking_lot = { version = "0.11.0", optional = true } # Not in full
|
||||
slab = { version = "0.4.2", optional = true } # Backs `DelayQueue`
|
||||
tracing = { version = "0.1.16", default-features = false, features = ["std"], optional = true } # Not in full
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
mio-uds = { version = "0.6.5", optional = true }
|
||||
@@ -121,15 +122,14 @@ default-features = false
|
||||
optional = true
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = { version = "0.2.0" }
|
||||
tokio-test = { version = "0.2.0", path = "../tokio-test" }
|
||||
futures = { version = "0.3.0", features = ["async-await"] }
|
||||
futures-test = "0.3.0"
|
||||
proptest = "0.9.4"
|
||||
tempfile = "3.1.0"
|
||||
|
||||
# loom is currently not compiling on windows.
|
||||
# See: https://github.com/Xudong-Huang/generator-rs/issues/19
|
||||
[target.'cfg(not(windows))'.dev-dependencies]
|
||||
loom = { version = "0.3.1", features = ["futures", "checkpoint"] }
|
||||
[target.'cfg(loom)'.dev-dependencies]
|
||||
loom = { version = "0.3.5", features = ["futures", "checkpoint"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
+47
-29
@@ -20,16 +20,17 @@ the Rust programming language. It is:
|
||||
[crates-badge]: https://img.shields.io/crates/v/tokio.svg
|
||||
[crates-url]: https://crates.io/crates/tokio
|
||||
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[mit-url]: LICENSE
|
||||
[mit-url]: https://github.com/tokio-rs/tokio/blob/master/LICENSE
|
||||
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.tokio?branchName=master
|
||||
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=1&branchName=master
|
||||
[discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord&style=flat-square
|
||||
[discord-url]: https://discord.gg/6yGkFeN
|
||||
[discord-url]: https://discord.gg/tokio
|
||||
|
||||
[Website](https://tokio.rs) |
|
||||
[Guides](https://tokio.rs/docs/) |
|
||||
[API Docs](https://docs.rs/tokio/0.2/tokio) |
|
||||
[Chat](https://discord.gg/6yGkFeN)
|
||||
[Guides](https://tokio.rs/tokio/tutorial) |
|
||||
[API Docs](https://docs.rs/tokio/latest/tokio) |
|
||||
[Roadmap](https://github.com/tokio-rs/tokio/blob/master/ROADMAP.md) |
|
||||
[Chat](https://discord.gg/tokio)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -45,20 +46,11 @@ level, it provides a few major components:
|
||||
These components provide the runtime components necessary for building
|
||||
an asynchronous application.
|
||||
|
||||
[net]: https://docs.rs/tokio/0.2/tokio/net/index.html
|
||||
[scheduler]: https://docs.rs/tokio/0.2/tokio/runtime/index.html
|
||||
[net]: https://docs.rs/tokio/latest/tokio/net/index.html
|
||||
[scheduler]: https://docs.rs/tokio/latest/tokio/runtime/index.html
|
||||
|
||||
## Example
|
||||
|
||||
To get started, add the following to `Cargo.toml`.
|
||||
|
||||
```toml
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
```
|
||||
|
||||
Tokio requires components to be explicitly enabled using feature flags. As a
|
||||
shorthand, the `full` feature enables all components.
|
||||
|
||||
A basic TCP echo server with Tokio:
|
||||
|
||||
```rust,no_run
|
||||
@@ -98,19 +90,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
```
|
||||
|
||||
More examples can be found [here](../examples).
|
||||
More examples can be found [here][examples]. For a larger "real world" example, see the
|
||||
[mini-redis] repository.
|
||||
|
||||
[examples]: https://github.com/tokio-rs/tokio/tree/master/examples
|
||||
[mini-redis]: https://github.com/tokio-rs/mini-redis/
|
||||
|
||||
To see a list of the available features flags that can be enabled, check our
|
||||
[docs][feature-flag-docs].
|
||||
|
||||
## Getting Help
|
||||
|
||||
First, see if the answer to your question can be found in the [Guides] or the
|
||||
[API documentation]. If the answer is not there, there is an active community in
|
||||
the [Tokio Discord server][chat]. We would be happy to try to answer your
|
||||
question. Last, if that doesn't work, try opening an [issue] with the question.
|
||||
question. You can also ask your question on [the discussions page][discussions].
|
||||
|
||||
[Guides]: https://tokio.rs/docs/
|
||||
[API documentation]: https://docs.rs/tokio/0.2
|
||||
[chat]: https://discord.gg/6yGkFeN
|
||||
[issue]: https://github.com/tokio-rs/tokio/issues/new
|
||||
[Guides]: https://tokio.rs/tokio/tutorial
|
||||
[API documentation]: https://docs.rs/tokio/latest/tokio
|
||||
[chat]: https://discord.gg/tokio
|
||||
[discussions]: https://github.com/tokio-rs/tokio/discussions
|
||||
[feature-flag-docs]: https://docs.rs/tokio/#feature-flags
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -118,36 +118,54 @@ question. Last, if that doesn't work, try opening an [issue] with the question.
|
||||
you! We have a [contributing guide][guide] to help you get involved in the Tokio
|
||||
project.
|
||||
|
||||
[guide]: CONTRIBUTING.md
|
||||
[guide]: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md
|
||||
|
||||
## Related Projects
|
||||
|
||||
In addition to the crates in this repository, the Tokio project also maintains
|
||||
several other libraries, including:
|
||||
|
||||
* [`hyper`]: A fast and correct HTTP/1.1 and HTTP/2 implementation for Rust.
|
||||
|
||||
* [`tonic`]: A gRPC over HTTP/2 implementation focused on high performance, interoperability, and flexibility.
|
||||
|
||||
* [`warp`]: A super-easy, composable, web server framework for warp speeds.
|
||||
|
||||
* [`tower`]: A library of modular and reusable components for building robust networking clients and servers.
|
||||
|
||||
* [`tracing`] (formerly `tokio-trace`): A framework for application-level
|
||||
tracing and async-aware diagnostics.
|
||||
|
||||
* [`rdbc`]: A Rust database connectivity library for MySQL, Postgres and SQLite.
|
||||
|
||||
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers
|
||||
`tokio`.
|
||||
|
||||
* [`bytes`]: Utilities for working with bytes, including efficient byte buffers.
|
||||
|
||||
* [`loom`]: A testing tool for concurrent Rust code
|
||||
|
||||
[`warp`]: https://github.com/seanmonstar/warp
|
||||
[`hyper`]: https://github.com/hyperium/hyper
|
||||
[`tonic`]: https://github.com/hyperium/tonic
|
||||
[`tower`]: https://github.com/tower-rs/tower
|
||||
[`loom`]: https://github.com/tokio-rs/loom
|
||||
[`rdbc`]: https://github.com/tokio-rs/rdbc
|
||||
[`tracing`]: https://github.com/tokio-rs/tracing
|
||||
[`mio`]: https://github.com/tokio-rs/mio
|
||||
[`bytes`]: https://github.com/tokio-rs/bytes
|
||||
|
||||
## Supported Rust Versions
|
||||
|
||||
Tokio is built against the latest stable, nightly, and beta Rust releases. The
|
||||
minimum version supported is the stable release from three months before the
|
||||
current stable release version. For example, if the latest stable Rust is 1.29,
|
||||
the minimum version supported is 1.26. The current Tokio version is not
|
||||
guaranteed to build on Rust versions earlier than the minimum supported version.
|
||||
Tokio is built against the latest stable release. The minimum supported version is 1.39.
|
||||
The current Tokio version is not guaranteed to build on Rust versions earlier than the
|
||||
minimum supported version.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
This project is licensed under the [MIT license].
|
||||
|
||||
[MIT license]: https://github.com/tokio-rs/tokio/blob/master/LICENSE
|
||||
|
||||
### Contribution
|
||||
|
||||
|
||||
+224
-302
@@ -1,11 +1,12 @@
|
||||
//! Opt-in yield points for improved cooperative scheduling.
|
||||
//!
|
||||
//! A single call to [`poll`] on a top-level task may potentially do a lot of work before it
|
||||
//! returns `Poll::Pending`. If a task runs for a long period of time without yielding back to the
|
||||
//! executor, it can starve other tasks waiting on that executor to execute them, or drive
|
||||
//! underlying resources. Since Rust does not have a runtime, it is difficult to forcibly preempt a
|
||||
//! long-running task. Instead, this module provides an opt-in mechanism for futures to collaborate
|
||||
//! with the executor to avoid starvation.
|
||||
//! A single call to [`poll`] on a top-level task may potentially do a lot of
|
||||
//! work before it returns `Poll::Pending`. If a task runs for a long period of
|
||||
//! time without yielding back to the executor, it can starve other tasks
|
||||
//! waiting on that executor to execute them, or drive underlying resources.
|
||||
//! Since Rust does not have a runtime, it is difficult to forcibly preempt a
|
||||
//! long-running task. Instead, this module provides an opt-in mechanism for
|
||||
//! futures to collaborate with the executor to avoid starvation.
|
||||
//!
|
||||
//! Consider a future like this one:
|
||||
//!
|
||||
@@ -16,9 +17,10 @@
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! It may look harmless, but consider what happens under heavy load if the input stream is
|
||||
//! _always_ ready. If we spawn `drop_all`, the task will never yield, and will starve other tasks
|
||||
//! and resources on the same executor. With opt-in yield points, this problem is alleviated:
|
||||
//! It may look harmless, but consider what happens under heavy load if the
|
||||
//! input stream is _always_ ready. If we spawn `drop_all`, the task will never
|
||||
//! yield, and will starve other tasks and resources on the same executor. With
|
||||
//! opt-in yield points, this problem is alleviated:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! # use tokio::stream::{Stream, StreamExt};
|
||||
@@ -29,71 +31,99 @@
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The `proceed` future will coordinate with the executor to make sure that every so often control
|
||||
//! is yielded back to the executor so it can run other tasks.
|
||||
//! The `proceed` future will coordinate with the executor to make sure that
|
||||
//! every so often control is yielded back to the executor so it can run other
|
||||
//! tasks.
|
||||
//!
|
||||
//! # Placing yield points
|
||||
//!
|
||||
//! Voluntary yield points should be placed _after_ at least some work has been done. If they are
|
||||
//! not, a future sufficiently deep in the task hierarchy may end up _never_ getting to run because
|
||||
//! of the number of yield points that inevitably appear before it is reached. In general, you will
|
||||
//! want yield points to only appear in "leaf" futures -- those that do not themselves poll other
|
||||
//! futures. By doing this, you avoid double-counting each iteration of the outer future against
|
||||
//! the cooperating budget.
|
||||
//! Voluntary yield points should be placed _after_ at least some work has been
|
||||
//! done. If they are not, a future sufficiently deep in the task hierarchy may
|
||||
//! end up _never_ getting to run because of the number of yield points that
|
||||
//! inevitably appear before it is reached. In general, you will want yield
|
||||
//! points to only appear in "leaf" futures -- those that do not themselves poll
|
||||
//! other futures. By doing this, you avoid double-counting each iteration of
|
||||
//! the outer future against the cooperating budget.
|
||||
//!
|
||||
//! [`poll`]: https://doc.rust-lang.org/std/future/trait.Future.html#tymethod.poll
|
||||
//! [`poll`]: method@std::future::Future::poll
|
||||
|
||||
// NOTE: The doctests in this module are ignored since the whole module is (currently) private.
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Constant used to determine how much "work" a task is allowed to do without yielding.
|
||||
///
|
||||
/// The value itself is chosen somewhat arbitrarily. It needs to be high enough to amortize wakeup
|
||||
/// and scheduling costs, but low enough that we do not starve other tasks for too long. The value
|
||||
/// also needs to be high enough that particularly deep tasks are able to do at least some useful
|
||||
/// work at all.
|
||||
///
|
||||
/// Note that as more yield points are added in the ecosystem, this value will probably also have
|
||||
/// to be raised.
|
||||
const BUDGET: usize = 128;
|
||||
|
||||
/// Constant used to determine if budgeting has been disabled.
|
||||
const UNCONSTRAINED: usize = usize::max_value();
|
||||
|
||||
thread_local! {
|
||||
static HITS: Cell<usize> = Cell::new(UNCONSTRAINED);
|
||||
static CURRENT: Cell<Budget> = Cell::new(Budget::unconstrained());
|
||||
}
|
||||
|
||||
/// Run the given closure with a cooperative task budget.
|
||||
///
|
||||
/// Enabling budgeting when it is already enabled is a no-op.
|
||||
/// Opaque type tracking the amount of "work" a task may still do before
|
||||
/// yielding back to the scheduler.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct Budget(Option<u8>);
|
||||
|
||||
impl Budget {
|
||||
/// Budget assigned to a task on each poll.
|
||||
///
|
||||
/// The value itself is chosen somewhat arbitrarily. It needs to be high
|
||||
/// enough to amortize wakeup and scheduling costs, but low enough that we
|
||||
/// do not starve other tasks for too long. The value also needs to be high
|
||||
/// enough that particularly deep tasks are able to do at least some useful
|
||||
/// work at all.
|
||||
///
|
||||
/// Note that as more yield points are added in the ecosystem, this value
|
||||
/// will probably also have to be raised.
|
||||
const fn initial() -> Budget {
|
||||
Budget(Some(128))
|
||||
}
|
||||
|
||||
/// Returns an unconstrained budget. Operations will not be limited.
|
||||
const fn unconstrained() -> Budget {
|
||||
Budget(None)
|
||||
}
|
||||
}
|
||||
|
||||
cfg_rt_threaded! {
|
||||
impl Budget {
|
||||
fn has_remaining(self) -> bool {
|
||||
self.0.map(|budget| budget > 0).unwrap_or(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the given closure with a cooperative task budget. When the function
|
||||
/// returns, the budget is reset to the value prior to calling the function.
|
||||
#[inline(always)]
|
||||
pub(crate) fn budget<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
HITS.with(move |hits| {
|
||||
if hits.get() != UNCONSTRAINED {
|
||||
// We are already being budgeted.
|
||||
//
|
||||
// Arguably this should be an error, but it can happen "correctly"
|
||||
// such as with block_on + LocalSet, so we make it a no-op.
|
||||
return f();
|
||||
}
|
||||
pub(crate) fn budget<R>(f: impl FnOnce() -> R) -> R {
|
||||
with_budget(Budget::initial(), f)
|
||||
}
|
||||
|
||||
struct Guard<'a>(&'a Cell<usize>);
|
||||
impl<'a> Drop for Guard<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.0.set(UNCONSTRAINED);
|
||||
}
|
||||
}
|
||||
cfg_rt_threaded! {
|
||||
/// Set the current task's budget
|
||||
#[cfg(feature = "blocking")]
|
||||
pub(crate) fn set(budget: Budget) {
|
||||
CURRENT.with(|cell| cell.set(budget))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn with_budget<R>(budget: Budget, f: impl FnOnce() -> R) -> R {
|
||||
struct ResetGuard<'a> {
|
||||
cell: &'a Cell<Budget>,
|
||||
prev: Budget,
|
||||
}
|
||||
|
||||
impl<'a> Drop for ResetGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.cell.set(self.prev);
|
||||
}
|
||||
}
|
||||
|
||||
CURRENT.with(move |cell| {
|
||||
let prev = cell.get();
|
||||
|
||||
cell.set(budget);
|
||||
|
||||
let _guard = ResetGuard { cell, prev };
|
||||
|
||||
hits.set(BUDGET);
|
||||
let _guard = Guard(hits);
|
||||
f()
|
||||
})
|
||||
}
|
||||
@@ -101,279 +131,171 @@ where
|
||||
cfg_rt_threaded! {
|
||||
#[inline(always)]
|
||||
pub(crate) fn has_budget_remaining() -> bool {
|
||||
HITS.with(|hits| hits.get() > 0)
|
||||
CURRENT.with(|cell| cell.get().has_remaining())
|
||||
}
|
||||
}
|
||||
|
||||
cfg_blocking_impl! {
|
||||
/// Forcibly remove the budgeting constraints early.
|
||||
pub(crate) fn stop() {
|
||||
HITS.with(|hits| {
|
||||
hits.set(UNCONSTRAINED);
|
||||
});
|
||||
///
|
||||
/// Returns the remaining budget
|
||||
pub(crate) fn stop() -> Budget {
|
||||
CURRENT.with(|cell| {
|
||||
let prev = cell.get();
|
||||
cell.set(Budget::unconstrained());
|
||||
prev
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke `f` with a subset of the remaining budget.
|
||||
///
|
||||
/// This is useful if you have sub-futures that you need to poll, but that you want to restrict
|
||||
/// from using up your entire budget. For example, imagine the following future:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::{future::Future, pin::Pin, task::{Context, Poll}};
|
||||
/// use futures::stream::FuturesUnordered;
|
||||
/// struct MyFuture<F1, F2> {
|
||||
/// big: FuturesUnordered<F1>,
|
||||
/// small: F2,
|
||||
/// }
|
||||
///
|
||||
/// use tokio::stream::Stream;
|
||||
/// impl<F1, F2> Future for MyFuture<F1, F2>
|
||||
/// where F1: Future, F2: Future
|
||||
/// # , F1: Unpin, F2: Unpin
|
||||
/// {
|
||||
/// type Output = F2::Output;
|
||||
///
|
||||
/// // fn poll(...)
|
||||
/// # fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F2::Output> {
|
||||
/// # let this = &mut *self;
|
||||
/// let mut big = // something to pin self.big
|
||||
/// # Pin::new(&mut this.big);
|
||||
/// let small = // something to pin self.small
|
||||
/// # Pin::new(&mut this.small);
|
||||
///
|
||||
/// // see if any of the big futures have finished
|
||||
/// while let Some(e) = futures::ready!(big.as_mut().poll_next(cx)) {
|
||||
/// // do something with e
|
||||
/// # let _ = e;
|
||||
/// }
|
||||
///
|
||||
/// // see if the small future has finished
|
||||
/// small.poll(cx)
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// It could be that every time `poll` gets called, `big` ends up spending the entire budget, and
|
||||
/// `small` never gets polled. That would be sad. If you want to stick up for the little future,
|
||||
/// that's what `limit` is for. It lets you portion out a smaller part of the yield budget to a
|
||||
/// particular segment of your code. In the code above, you would write
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// # use std::{future::Future, pin::Pin, task::{Context, Poll}};
|
||||
/// # use futures::stream::FuturesUnordered;
|
||||
/// # struct MyFuture<F1, F2> {
|
||||
/// # big: FuturesUnordered<F1>,
|
||||
/// # small: F2,
|
||||
/// # }
|
||||
/// #
|
||||
/// # use tokio::stream::Stream;
|
||||
/// # impl<F1, F2> Future for MyFuture<F1, F2>
|
||||
/// # where F1: Future, F2: Future
|
||||
/// # , F1: Unpin, F2: Unpin
|
||||
/// # {
|
||||
/// # type Output = F2::Output;
|
||||
/// # fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F2::Output> {
|
||||
/// # let this = &mut *self;
|
||||
/// # let mut big = Pin::new(&mut this.big);
|
||||
/// # let small = Pin::new(&mut this.small);
|
||||
/// #
|
||||
/// // see if any of the big futures have finished
|
||||
/// while let Some(e) = futures::ready!(tokio::coop::limit(64, || big.as_mut().poll_next(cx))) {
|
||||
/// # // do something with e
|
||||
/// # let _ = e;
|
||||
/// # }
|
||||
/// # small.poll(cx)
|
||||
/// # }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Now, even if `big` spends its entire budget, `small` will likely be left with some budget left
|
||||
/// to also do useful work. In particular, if the remaining budget was `N` at the start of `poll`,
|
||||
/// `small` will have at least a budget of `N - 64`. It may be more if `big` did not spend its
|
||||
/// entire budget.
|
||||
///
|
||||
/// Note that you cannot _increase_ your budget by calling `limit`. The budget provided to the code
|
||||
/// inside the buget is the _minimum_ of the _current_ budget and the bound.
|
||||
///
|
||||
#[allow(unreachable_pub, dead_code)]
|
||||
pub fn limit<R>(bound: usize, f: impl FnOnce() -> R) -> R {
|
||||
HITS.with(|hits| {
|
||||
let budget = hits.get();
|
||||
// with_bound cannot _increase_ the remaining budget
|
||||
let bound = std::cmp::min(budget, bound);
|
||||
// When f() exits, how much should we add to what is left?
|
||||
let floor = budget.saturating_sub(bound);
|
||||
// Make sure we restore the remaining budget even on panic
|
||||
struct RestoreBudget<'a>(&'a Cell<usize>, usize);
|
||||
impl<'a> Drop for RestoreBudget<'a> {
|
||||
fn drop(&mut self) {
|
||||
let left = self.0.get();
|
||||
self.0.set(self.1 + left);
|
||||
cfg_coop! {
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
#[must_use]
|
||||
pub(crate) struct RestoreOnPending(Cell<Budget>);
|
||||
|
||||
impl RestoreOnPending {
|
||||
pub(crate) fn made_progress(&self) {
|
||||
self.0.set(Budget::unconstrained());
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RestoreOnPending {
|
||||
fn drop(&mut self) {
|
||||
// Don't reset if budget was unconstrained or if we made progress.
|
||||
// They are both represented as the remembered budget being unconstrained.
|
||||
let budget = self.0.get();
|
||||
if !budget.is_unconstrained() {
|
||||
CURRENT.with(|cell| {
|
||||
cell.set(budget);
|
||||
});
|
||||
}
|
||||
}
|
||||
// Time to restrict!
|
||||
hits.set(bound);
|
||||
let _restore = RestoreBudget(&hits, floor);
|
||||
f()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Poll::Pending` if the current task has exceeded its budget and should yield.
|
||||
#[allow(unreachable_pub, dead_code)]
|
||||
#[inline]
|
||||
pub fn poll_proceed(cx: &mut Context<'_>) -> Poll<()> {
|
||||
HITS.with(|hits| {
|
||||
let n = hits.get();
|
||||
if n == UNCONSTRAINED {
|
||||
// opted out of budgeting
|
||||
Poll::Ready(())
|
||||
} else if n == 0 {
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
} else {
|
||||
hits.set(n.saturating_sub(1));
|
||||
Poll::Ready(())
|
||||
/// Returns `Poll::Pending` if the current task has exceeded its budget and should yield.
|
||||
///
|
||||
/// When you call this method, the current budget is decremented. However, to ensure that
|
||||
/// progress is made every time a task is polled, the budget is automatically restored to its
|
||||
/// former value if the returned `RestoreOnPending` is dropped. It is the caller's
|
||||
/// responsibility to call `RestoreOnPending::made_progress` if it made progress, to ensure
|
||||
/// that the budget empties appropriately.
|
||||
///
|
||||
/// Note that `RestoreOnPending` restores the budget **as it was before `poll_proceed`**.
|
||||
/// Therefore, if the budget is _further_ adjusted between when `poll_proceed` returns and
|
||||
/// `RestRestoreOnPending` is dropped, those adjustments are erased unless the caller indicates
|
||||
/// that progress was made.
|
||||
#[inline]
|
||||
pub(crate) fn poll_proceed(cx: &mut Context<'_>) -> Poll<RestoreOnPending> {
|
||||
CURRENT.with(|cell| {
|
||||
let mut budget = cell.get();
|
||||
|
||||
if budget.decrement() {
|
||||
let restore = RestoreOnPending(Cell::new(cell.get()));
|
||||
cell.set(budget);
|
||||
Poll::Ready(restore)
|
||||
} else {
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
impl Budget {
|
||||
/// Decrement the budget. Returns `true` if successful. Decrementing fails
|
||||
/// when there is not enough remaining budget.
|
||||
fn decrement(&mut self) -> bool {
|
||||
if let Some(num) = &mut self.0 {
|
||||
if *num > 0 {
|
||||
*num -= 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolves immediately unless the current task has already exceeded its budget.
|
||||
///
|
||||
/// This should be placed after at least some work has been done. Otherwise a future sufficiently
|
||||
/// deep in the task hierarchy may end up never getting to run because of the number of yield
|
||||
/// points that inevitably appear before it is even reached. For example:
|
||||
///
|
||||
/// ```ignore
|
||||
/// # use tokio::stream::{Stream, StreamExt};
|
||||
/// async fn drop_all<I: Stream + Unpin>(mut input: I) {
|
||||
/// while let Some(_) = input.next().await {
|
||||
/// tokio::coop::proceed().await;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(unreachable_pub, dead_code)]
|
||||
#[inline]
|
||||
pub async fn proceed() {
|
||||
use crate::future::poll_fn;
|
||||
poll_fn(|cx| poll_proceed(cx)).await;
|
||||
}
|
||||
|
||||
pin_project_lite::pin_project! {
|
||||
/// A future that cooperatively yields to the task scheduler when polling,
|
||||
/// if the task's budget is exhausted.
|
||||
///
|
||||
/// Internally, this is simply a future combinator which calls
|
||||
/// [`poll_proceed`] in its `poll` implementation before polling the wrapped
|
||||
/// future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// use tokio::coop::CoopFutureExt;
|
||||
///
|
||||
/// async { /* ... */ }
|
||||
/// .cooperate()
|
||||
/// .await;
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`poll_proceed`]: fn@poll_proceed
|
||||
#[derive(Debug)]
|
||||
#[allow(unreachable_pub, dead_code)]
|
||||
pub struct CoopFuture<F> {
|
||||
#[pin]
|
||||
future: F,
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Future> Future for CoopFuture<F> {
|
||||
type Output = F::Output;
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
ready!(poll_proceed(cx));
|
||||
self.project().future.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Future> CoopFuture<F> {
|
||||
/// Returns a new `CoopFuture` wrapping the given future.
|
||||
///
|
||||
#[allow(unreachable_pub, dead_code)]
|
||||
pub fn new(future: F) -> Self {
|
||||
Self { future }
|
||||
}
|
||||
}
|
||||
|
||||
// Currently only used by `tokio::sync`; and if we make this combinator public,
|
||||
// it should probably be on the `FutureExt` trait instead.
|
||||
cfg_sync! {
|
||||
/// Extension trait providing `Future::cooperate` extension method.
|
||||
///
|
||||
/// Note: if/when the co-op API becomes public, this method should probably be
|
||||
/// provided by `FutureExt`, instead.
|
||||
pub(crate) trait CoopFutureExt: Future {
|
||||
/// Wrap `self` to cooperatively yield to the scheduler when polling, if the
|
||||
/// task's budget is exhausted.
|
||||
fn cooperate(self) -> CoopFuture<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
CoopFuture::new(self)
|
||||
fn is_unconstrained(self) -> bool {
|
||||
self.0.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> CoopFutureExt for F where F: Future {}
|
||||
}
|
||||
|
||||
#[cfg(all(test, not(loom)))]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn get() -> usize {
|
||||
HITS.with(|hits| hits.get())
|
||||
fn get() -> Budget {
|
||||
CURRENT.with(|cell| cell.get())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bugeting() {
|
||||
use futures::future::poll_fn;
|
||||
use tokio_test::*;
|
||||
|
||||
assert_eq!(get(), UNCONSTRAINED);
|
||||
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get(), UNCONSTRAINED);
|
||||
budget(|| {
|
||||
assert_eq!(get(), BUDGET);
|
||||
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get(), BUDGET - 1);
|
||||
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get(), BUDGET - 2);
|
||||
});
|
||||
assert_eq!(get(), UNCONSTRAINED);
|
||||
assert!(get().0.is_none());
|
||||
|
||||
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
|
||||
assert!(get().0.is_none());
|
||||
drop(coop);
|
||||
assert!(get().0.is_none());
|
||||
|
||||
budget(|| {
|
||||
limit(3, || {
|
||||
assert_eq!(get(), 3);
|
||||
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get(), 2);
|
||||
limit(4, || {
|
||||
assert_eq!(get(), 2);
|
||||
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get(), 1);
|
||||
});
|
||||
assert_eq!(get(), 1);
|
||||
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get(), 0);
|
||||
assert_pending!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get(), 0);
|
||||
assert_pending!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get(), 0);
|
||||
assert_eq!(get().0, Budget::initial().0);
|
||||
|
||||
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 1);
|
||||
drop(coop);
|
||||
// we didn't make progress
|
||||
assert_eq!(get().0, Budget::initial().0);
|
||||
|
||||
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 1);
|
||||
coop.made_progress();
|
||||
drop(coop);
|
||||
// we _did_ make progress
|
||||
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 1);
|
||||
|
||||
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 2);
|
||||
coop.made_progress();
|
||||
drop(coop);
|
||||
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 2);
|
||||
|
||||
budget(|| {
|
||||
assert_eq!(get().0, Budget::initial().0);
|
||||
|
||||
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 1);
|
||||
coop.made_progress();
|
||||
drop(coop);
|
||||
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 1);
|
||||
});
|
||||
assert_eq!(get(), BUDGET - 3);
|
||||
assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
assert_eq!(get(), BUDGET - 4);
|
||||
assert_ready!(task::spawn(proceed()).poll());
|
||||
assert_eq!(get(), BUDGET - 5);
|
||||
|
||||
assert_eq!(get().0.unwrap(), Budget::initial().0.unwrap() - 2);
|
||||
});
|
||||
|
||||
assert!(get().0.is_none());
|
||||
|
||||
budget(|| {
|
||||
let n = get().0.unwrap();
|
||||
|
||||
for _ in 0..n {
|
||||
let coop = assert_ready!(task::spawn(()).enter(|cx, _| poll_proceed(cx)));
|
||||
coop.made_progress();
|
||||
}
|
||||
|
||||
let mut task = task::spawn(poll_fn(|cx| {
|
||||
let coop = ready!(poll_proceed(cx));
|
||||
coop.made_progress();
|
||||
Poll::Ready(())
|
||||
}));
|
||||
|
||||
assert_pending!(task.poll());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
use crate::fs::asyncify;
|
||||
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// A builder for creating directories in various manners.
|
||||
///
|
||||
/// Additional Unix-specific options are available via importing the
|
||||
/// [`DirBuilderExt`] trait.
|
||||
///
|
||||
/// This is a specialized version of [`std::fs::DirBuilder`] for usage on
|
||||
/// the Tokio runtime.
|
||||
///
|
||||
/// [std::fs::DirBuilder]: std::fs::DirBuilder
|
||||
/// [`DirBuilderExt`]: crate::fs::os::unix::DirBuilderExt
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DirBuilder {
|
||||
/// Indicates whether to create parent directories if they are missing.
|
||||
recursive: bool,
|
||||
|
||||
/// Set the Unix mode for newly created directories.
|
||||
#[cfg(unix)]
|
||||
pub(super) mode: Option<u32>,
|
||||
}
|
||||
|
||||
impl DirBuilder {
|
||||
/// Creates a new set of options with default mode/security settings for all
|
||||
/// platforms and also non-recursive.
|
||||
///
|
||||
/// This is an async version of [`std::fs::DirBuilder::new`][std]
|
||||
///
|
||||
/// [std]: std::fs::DirBuilder::new
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::DirBuilder;
|
||||
///
|
||||
/// let builder = DirBuilder::new();
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Indicates whether to create directories recursively (including all parent directories).
|
||||
/// Parents that do not exist are created with the same security and permissions settings.
|
||||
///
|
||||
/// This option defaults to `false`.
|
||||
///
|
||||
/// This is an async version of [`std::fs::DirBuilder::recursive`][std]
|
||||
///
|
||||
/// [std]: std::fs::DirBuilder::recursive
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::DirBuilder;
|
||||
///
|
||||
/// let mut builder = DirBuilder::new();
|
||||
/// builder.recursive(true);
|
||||
/// ```
|
||||
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
|
||||
self.recursive = recursive;
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates the specified directory with the configured options.
|
||||
///
|
||||
/// It is considered an error if the directory already exists unless
|
||||
/// recursive mode is enabled.
|
||||
///
|
||||
/// This is an async version of [`std::fs::DirBuilder::create`][std]
|
||||
///
|
||||
/// [std]: std::fs::DirBuilder::create
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An error will be returned under the following circumstances:
|
||||
///
|
||||
/// * Path already points to an existing file.
|
||||
/// * Path already points to an existing directory and the mode is
|
||||
/// non-recursive.
|
||||
/// * The calling process doesn't have permissions to create the directory
|
||||
/// or its missing parents.
|
||||
/// * Other I/O error occurred.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::DirBuilder;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// DirBuilder::new()
|
||||
/// .recursive(true)
|
||||
/// .create("/tmp/foo/bar/baz")
|
||||
/// .await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let mut builder = std::fs::DirBuilder::new();
|
||||
builder.recursive(self.recursive);
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if let Some(mode) = self.mode {
|
||||
std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode);
|
||||
}
|
||||
}
|
||||
|
||||
asyncify(move || builder.create(path)).await
|
||||
}
|
||||
}
|
||||
+57
-6
@@ -24,12 +24,28 @@ use std::task::Poll::*;
|
||||
/// Tokio runtime.
|
||||
///
|
||||
/// An instance of a `File` can be read and/or written depending on what options
|
||||
/// it was opened with. Files also implement Seek to alter the logical cursor
|
||||
/// that the file contains internally.
|
||||
/// it was opened with. Files also implement [`AsyncSeek`] to alter the logical
|
||||
/// cursor that the file contains internally.
|
||||
///
|
||||
/// Files are automatically closed when they go out of scope.
|
||||
/// A file will not be closed immediately when it goes out of scope if there
|
||||
/// are any IO operations that have not yet completed. To ensure that a file is
|
||||
/// closed immediately when it is dropped, you should call [`flush`] before
|
||||
/// dropping it. Note that this does not ensure that the file has been fully
|
||||
/// written to disk; the operating system might keep the changes around in an
|
||||
/// in-memory buffer. See the [`sync_all`] method for telling the OS to write
|
||||
/// the data to disk.
|
||||
///
|
||||
/// [std]: std::fs::File
|
||||
/// Reading and writing to a `File` is usually done using the convenience
|
||||
/// methods found on the [`AsyncReadExt`] and [`AsyncWriteExt`] traits. Examples
|
||||
/// import these traits through [the prelude].
|
||||
///
|
||||
/// [std]: struct@std::fs::File
|
||||
/// [`AsyncSeek`]: trait@crate::io::AsyncSeek
|
||||
/// [`flush`]: fn@crate::io::AsyncWriteExt::flush
|
||||
/// [`sync_all`]: fn@crate::fs::File::sync_all
|
||||
/// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt
|
||||
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
||||
/// [the prelude]: crate::prelude
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -37,7 +53,7 @@ use std::task::Poll::*;
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::File;
|
||||
/// use tokio::prelude::*;
|
||||
/// use tokio::prelude::*; // for write_all()
|
||||
///
|
||||
/// # async fn dox() -> std::io::Result<()> {
|
||||
/// let mut file = File::create("foo.txt").await?;
|
||||
@@ -50,7 +66,7 @@ use std::task::Poll::*;
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::File;
|
||||
/// use tokio::prelude::*;
|
||||
/// use tokio::prelude::*; // for read_to_end()
|
||||
///
|
||||
/// # async fn dox() -> std::io::Result<()> {
|
||||
/// let mut file = File::open("foo.txt").await?;
|
||||
@@ -114,6 +130,11 @@ impl File {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// The [`read_to_end`] method is defined on the [`AsyncReadExt`] trait.
|
||||
///
|
||||
/// [`read_to_end`]: fn@crate::io::AsyncReadExt::read_to_end
|
||||
/// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt
|
||||
pub async fn open(path: impl AsRef<Path>) -> io::Result<File> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let std = asyncify(|| sys::File::open(path)).await?;
|
||||
@@ -149,6 +170,11 @@ impl File {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
|
||||
///
|
||||
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
|
||||
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
||||
pub async fn create(path: impl AsRef<Path>) -> io::Result<File> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let std_file = asyncify(move || sys::File::create(path)).await?;
|
||||
@@ -195,6 +221,11 @@ impl File {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// The [`read_exact`] method is defined on the [`AsyncReadExt`] trait.
|
||||
///
|
||||
/// [`read_exact`]: fn@crate::io::AsyncReadExt::read_exact
|
||||
/// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt
|
||||
pub async fn seek(&mut self, mut pos: SeekFrom) -> io::Result<u64> {
|
||||
self.complete_inflight().await;
|
||||
|
||||
@@ -251,6 +282,11 @@ impl File {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
|
||||
///
|
||||
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
|
||||
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
||||
pub async fn sync_all(&mut self) -> io::Result<()> {
|
||||
self.complete_inflight().await;
|
||||
|
||||
@@ -280,6 +316,11 @@ impl File {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
|
||||
///
|
||||
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
|
||||
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
||||
pub async fn sync_data(&mut self) -> io::Result<()> {
|
||||
self.complete_inflight().await;
|
||||
|
||||
@@ -312,6 +353,11 @@ impl File {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
|
||||
///
|
||||
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
|
||||
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
||||
pub async fn set_len(&mut self, size: u64) -> io::Result<()> {
|
||||
self.complete_inflight().await;
|
||||
|
||||
@@ -491,6 +537,11 @@ impl File {
|
||||
}
|
||||
|
||||
impl AsyncRead for File {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [std::mem::MaybeUninit<u8>]) -> bool {
|
||||
// https://github.com/rust-lang/rust/blob/09c817eeb29e764cfc12d0a8d94841e3ffe34023/src/libstd/fs.rs#L668
|
||||
false
|
||||
}
|
||||
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
|
||||
@@ -33,6 +33,9 @@ pub use self::create_dir::create_dir;
|
||||
mod create_dir_all;
|
||||
pub use self::create_dir_all::create_dir_all;
|
||||
|
||||
mod dir_builder;
|
||||
pub use self::dir_builder::DirBuilder;
|
||||
|
||||
mod file;
|
||||
pub use self::file::File;
|
||||
|
||||
|
||||
@@ -382,6 +382,12 @@ impl OpenOptions {
|
||||
let std = asyncify(move || opts.open(path)).await?;
|
||||
Ok(File::from_std(std))
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the the underlying std::fs::OpenOptions
|
||||
#[cfg(unix)]
|
||||
pub(super) fn as_inner_mut(&mut self) -> &mut std::fs::OpenOptions {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::fs::OpenOptions> for OpenOptions {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
use crate::fs::dir_builder::DirBuilder;
|
||||
|
||||
/// Unix-specific extensions to [`DirBuilder`].
|
||||
///
|
||||
/// [`DirBuilder`]: crate::fs::DirBuilder
|
||||
pub trait DirBuilderExt {
|
||||
/// Sets the mode to create new directories with.
|
||||
///
|
||||
/// This option defaults to 0o777.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::DirBuilder;
|
||||
/// use tokio::fs::os::unix::DirBuilderExt;
|
||||
///
|
||||
/// let mut builder = DirBuilder::new();
|
||||
/// builder.mode(0o775);
|
||||
/// ```
|
||||
fn mode(&mut self, mode: u32) -> &mut Self;
|
||||
}
|
||||
|
||||
impl DirBuilderExt for DirBuilder {
|
||||
fn mode(&mut self, mode: u32) -> &mut Self {
|
||||
self.mode = Some(mode);
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -2,3 +2,9 @@
|
||||
|
||||
mod symlink;
|
||||
pub use self::symlink::symlink;
|
||||
|
||||
mod open_options_ext;
|
||||
pub use self::open_options_ext::OpenOptionsExt;
|
||||
|
||||
mod dir_builder_ext;
|
||||
pub use self::dir_builder_ext::DirBuilderExt;
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
use crate::fs::open_options::OpenOptions;
|
||||
use std::os::unix::fs::OpenOptionsExt as StdOpenOptionsExt;
|
||||
|
||||
/// Unix-specific extensions to [`fs::OpenOptions`].
|
||||
///
|
||||
/// This mirrors the definition of [`std::os::unix::fs::OpenOptionsExt`].
|
||||
///
|
||||
///
|
||||
/// [`fs::OpenOptions`]: crate::fs::OpenOptions
|
||||
/// [`std::os::unix::fs::OpenOptionsExt`]: std::os::unix::fs::OpenOptionsExt
|
||||
pub trait OpenOptionsExt {
|
||||
/// Sets the mode bits that a new file will be created with.
|
||||
///
|
||||
/// If a new file is created as part of an `OpenOptions::open` call then this
|
||||
/// specified `mode` will be used as the permission bits for the new file.
|
||||
/// If no `mode` is set, the default of `0o666` will be used.
|
||||
/// The operating system masks out bits with the system's `umask`, to produce
|
||||
/// the final permissions.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use tokio::fs::os::unix::OpenOptionsExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut options = OpenOptions::new();
|
||||
/// options.mode(0o644); // Give read/write for owner and read for others.
|
||||
/// let file = options.open("foo.txt").await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn mode(&mut self, mode: u32) -> &mut Self;
|
||||
|
||||
/// Pass custom flags to the `flags` argument of `open`.
|
||||
///
|
||||
/// The bits that define the access mode are masked out with `O_ACCMODE`, to
|
||||
/// ensure they do not interfere with the access mode set by Rusts options.
|
||||
///
|
||||
/// Custom flags can only set flags, not remove flags set by Rusts options.
|
||||
/// This options overwrites any previously set custom flags.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use libc;
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use tokio::fs::os::unix::OpenOptionsExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut options = OpenOptions::new();
|
||||
/// options.write(true);
|
||||
/// if cfg!(unix) {
|
||||
/// options.custom_flags(libc::O_NOFOLLOW);
|
||||
/// }
|
||||
/// let file = options.open("foo.txt").await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn custom_flags(&mut self, flags: i32) -> &mut Self;
|
||||
}
|
||||
|
||||
impl OpenOptionsExt for OpenOptions {
|
||||
fn mode(&mut self, mode: u32) -> &mut OpenOptions {
|
||||
self.as_inner_mut().mode(mode);
|
||||
self
|
||||
}
|
||||
|
||||
fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
|
||||
self.as_inner_mut().custom_flags(flags);
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::fs::remove_dir_all`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.remove_dir_all.html
|
||||
/// [std]: fn@std::fs::remove_dir_all
|
||||
pub async fn remove_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
let path = path.as_ref().to_owned();
|
||||
asyncify(move || std::fs::remove_dir_all(path)).await
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::fs::set_permissions`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.set_permissions.html
|
||||
/// [std]: fn@std::fs::set_permissions
|
||||
pub async fn set_permissions(path: impl AsRef<Path>, perm: Permissions) -> io::Result<()> {
|
||||
let path = path.as_ref().to_owned();
|
||||
asyncify(|| std::fs::set_permissions(path, perm)).await
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::fs::symlink_metadata`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html
|
||||
/// [std]: fn@std::fs::symlink_metadata
|
||||
pub async fn symlink_metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
|
||||
let path = path.as_ref().to_owned();
|
||||
asyncify(|| std::fs::symlink_metadata(path)).await
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Future for the [`ready`](ready()) function.
|
||||
/// Future for the [`ok`](ok()) function.
|
||||
///
|
||||
/// `pub` in order to use the future as an associated type in a sealed trait.
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -7,14 +7,18 @@ use std::task::{Context, Poll};
|
||||
|
||||
/// Reads bytes asynchronously.
|
||||
///
|
||||
/// This trait inherits from [`std::io::BufRead`] and indicates that an I/O object is
|
||||
/// **non-blocking**. All non-blocking I/O objects must return an error when
|
||||
/// bytes are unavailable instead of blocking the current thread.
|
||||
/// This trait is analogous to [`std::io::BufRead`], but integrates with
|
||||
/// the asynchronous task system. In particular, the [`poll_fill_buf`] method,
|
||||
/// unlike [`BufRead::fill_buf`], will automatically queue the current task for wakeup
|
||||
/// and return if data is not yet available, rather than blocking the calling
|
||||
/// thread.
|
||||
///
|
||||
/// Utilities for working with `AsyncBufRead` values are provided by
|
||||
/// [`AsyncBufReadExt`].
|
||||
///
|
||||
/// [`std::io::BufRead`]: std::io::BufRead
|
||||
/// [`poll_fill_buf`]: AsyncBufRead::poll_fill_buf
|
||||
/// [`BufRead::fill_buf`]: std::io::BufRead::fill_buf
|
||||
/// [`AsyncBufReadExt`]: crate::io::AsyncBufReadExt
|
||||
pub trait AsyncBufRead: AsyncRead {
|
||||
/// Attempts to return the contents of the internal buffer, filling it with more data
|
||||
@@ -60,16 +64,14 @@ pub trait AsyncBufRead: AsyncRead {
|
||||
|
||||
macro_rules! deref_async_buf_read {
|
||||
() => {
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>)
|
||||
-> Poll<io::Result<&[u8]>>
|
||||
{
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
||||
Pin::new(&mut **self.get_mut()).poll_fill_buf(cx)
|
||||
}
|
||||
|
||||
fn consume(mut self: Pin<&mut Self>, amt: usize) {
|
||||
Pin::new(&mut **self).consume(amt)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for Box<T> {
|
||||
|
||||
@@ -73,10 +73,10 @@ pub trait AsyncRead {
|
||||
/// that they did not write to.
|
||||
///
|
||||
/// [`io::Read`]: std::io::Read
|
||||
/// [`poll_read_buf`]: #method.poll_read_buf
|
||||
/// [`poll_read_buf`]: method@Self::poll_read_buf
|
||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [MaybeUninit<u8>]) -> bool {
|
||||
for x in buf {
|
||||
*x.as_mut_ptr() = 0;
|
||||
*x = MaybeUninit::new(0);
|
||||
}
|
||||
|
||||
true
|
||||
@@ -140,12 +140,14 @@ macro_rules! deref_async_read {
|
||||
(**self).prepare_uninitialized_buffer(buf)
|
||||
}
|
||||
|
||||
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8])
|
||||
-> Poll<io::Result<usize>>
|
||||
{
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
Pin::new(&mut **self).poll_read(cx, buf)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncRead + Unpin> AsyncRead for Box<T> {
|
||||
|
||||
@@ -55,13 +55,10 @@ macro_rules! deref_async_seek {
|
||||
Pin::new(&mut **self).start_seek(cx, pos)
|
||||
}
|
||||
|
||||
fn poll_complete(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<io::Result<u64>> {
|
||||
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
||||
Pin::new(&mut **self).poll_complete(cx)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncSeek + Unpin> AsyncSeek for Box<T> {
|
||||
|
||||
@@ -51,7 +51,7 @@ pub trait AsyncWrite {
|
||||
/// If the object is not ready for writing, the method returns
|
||||
/// `Poll::Pending` and arranges for the current task (via
|
||||
/// `cx.waker()`) to receive a notification when the object becomes
|
||||
/// readable or is closed.
|
||||
/// writable or is closed.
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
@@ -153,9 +153,11 @@ pub trait AsyncWrite {
|
||||
|
||||
macro_rules! deref_async_write {
|
||||
() => {
|
||||
fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8])
|
||||
-> Poll<io::Result<usize>>
|
||||
{
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
Pin::new(&mut **self).poll_write(cx, buf)
|
||||
}
|
||||
|
||||
@@ -166,7 +168,7 @@ macro_rules! deref_async_write {
|
||||
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Pin::new(&mut **self).poll_shutdown(cx)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> {
|
||||
|
||||
+19
-12
@@ -181,6 +181,8 @@ impl Park for Driver {
|
||||
self.turn(Some(duration))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn shutdown(&mut self) {}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Driver {
|
||||
@@ -198,8 +200,9 @@ impl Handle {
|
||||
///
|
||||
/// This function panics if there is no current reactor set.
|
||||
pub(super) fn current() -> Self {
|
||||
context::io_handle()
|
||||
.expect("there is no reactor running, must be called from the context of Tokio runtime")
|
||||
context::io_handle().expect(
|
||||
"there is no reactor running, must be called from the context of a Tokio 0.2.x runtime",
|
||||
)
|
||||
}
|
||||
|
||||
/// Forces a reactor blocked in a call to `turn` to wakeup, or otherwise
|
||||
@@ -237,10 +240,14 @@ impl fmt::Debug for Handle {
|
||||
// ===== impl Inner =====
|
||||
|
||||
impl Inner {
|
||||
/// Registers an I/O resource with the reactor.
|
||||
/// Registers an I/O resource with the reactor for a given `mio::Ready` state.
|
||||
///
|
||||
/// The registration token is returned.
|
||||
pub(super) fn add_source(&self, source: &dyn Evented) -> io::Result<Address> {
|
||||
pub(super) fn add_source(
|
||||
&self,
|
||||
source: &dyn Evented,
|
||||
ready: mio::Ready,
|
||||
) -> io::Result<Address> {
|
||||
let address = self.io_dispatch.alloc().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
@@ -253,7 +260,7 @@ impl Inner {
|
||||
self.io.register(
|
||||
source,
|
||||
mio::Token(address.to_usize()),
|
||||
mio::Ready::all(),
|
||||
ready,
|
||||
mio::PollOpt::edge(),
|
||||
)?;
|
||||
|
||||
@@ -339,12 +346,12 @@ mod tests {
|
||||
let inner = reactor.inner;
|
||||
let inner2 = inner.clone();
|
||||
|
||||
let token_1 = inner.add_source(&NotEvented).unwrap();
|
||||
let token_1 = inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
|
||||
let thread = thread::spawn(move || {
|
||||
inner2.drop_source(token_1);
|
||||
});
|
||||
|
||||
let token_2 = inner.add_source(&NotEvented).unwrap();
|
||||
let token_2 = inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
|
||||
thread.join().unwrap();
|
||||
|
||||
assert!(token_1 != token_2);
|
||||
@@ -360,15 +367,15 @@ mod tests {
|
||||
// add sources to fill up the first page so that the dropped index
|
||||
// may be reused.
|
||||
for _ in 0..31 {
|
||||
inner.add_source(&NotEvented).unwrap();
|
||||
inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
|
||||
}
|
||||
|
||||
let token_1 = inner.add_source(&NotEvented).unwrap();
|
||||
let token_1 = inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
|
||||
let thread = thread::spawn(move || {
|
||||
inner2.drop_source(token_1);
|
||||
});
|
||||
|
||||
let token_2 = inner.add_source(&NotEvented).unwrap();
|
||||
let token_2 = inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
|
||||
thread.join().unwrap();
|
||||
|
||||
assert!(token_1 != token_2);
|
||||
@@ -383,11 +390,11 @@ mod tests {
|
||||
let inner2 = inner.clone();
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
let token_2 = inner2.add_source(&NotEvented).unwrap();
|
||||
let token_2 = inner2.add_source(&NotEvented, mio::Ready::all()).unwrap();
|
||||
token_2
|
||||
});
|
||||
|
||||
let token_1 = inner.add_source(&NotEvented).unwrap();
|
||||
let token_1 = inner.add_source(&NotEvented, mio::Ready::all()).unwrap();
|
||||
let token_2 = thread.join().unwrap();
|
||||
|
||||
assert!(token_1 != token_2);
|
||||
|
||||
+48
-21
@@ -15,19 +15,19 @@
|
||||
//! type will _yield_ to the Tokio scheduler when IO is not ready, rather than
|
||||
//! blocking. This allows other tasks to run while waiting on IO.
|
||||
//!
|
||||
//! Another difference is that [`AsyncRead`] and [`AsyncWrite`] only contain
|
||||
//! Another difference is that `AsyncRead` and `AsyncWrite` only contain
|
||||
//! core methods needed to provide asynchronous reading and writing
|
||||
//! functionality. Instead, utility methods are defined in the [`AsyncReadExt`]
|
||||
//! and [`AsyncWriteExt`] extension traits. These traits are automatically
|
||||
//! implemented for all values that implement [`AsyncRead`] and [`AsyncWrite`]
|
||||
//! implemented for all values that implement `AsyncRead` and `AsyncWrite`
|
||||
//! respectively.
|
||||
//!
|
||||
//! End users will rarely interact directly with [`AsyncRead`] and
|
||||
//! [`AsyncWrite`]. Instead, they will use the async functions defined in the
|
||||
//! extension traits. Library authors are expected to implement [`AsyncRead`]
|
||||
//! and [`AsyncWrite`] in order to provide types that behave like byte streams.
|
||||
//! End users will rarely interact directly with `AsyncRead` and
|
||||
//! `AsyncWrite`. Instead, they will use the async functions defined in the
|
||||
//! extension traits. Library authors are expected to implement `AsyncRead`
|
||||
//! and `AsyncWrite` in order to provide types that behave like byte streams.
|
||||
//!
|
||||
//! Even with these differences, Tokio's [`AsyncRead`] and [`AsyncWrite`] traits
|
||||
//! Even with these differences, Tokio's `AsyncRead` and `AsyncWrite` traits
|
||||
//! can be used in almost exactly the same manner as the standard library's
|
||||
//! `Read` and `Write`. Most types in the standard library that implement `Read`
|
||||
//! and `Write` have asynchronous equivalents in `tokio` that implement
|
||||
@@ -57,7 +57,7 @@
|
||||
//! [`File`]: crate::fs::File
|
||||
//! [`TcpStream`]: crate::net::TcpStream
|
||||
//! [`std::fs::File`]: std::fs::File
|
||||
//! [std_example]: https://doc.rust-lang.org/std/io/index.html#read-and-write
|
||||
//! [std_example]: std::io#read-and-write
|
||||
//!
|
||||
//! ## Buffered Readers and Writers
|
||||
//!
|
||||
@@ -93,7 +93,8 @@
|
||||
//! ```
|
||||
//!
|
||||
//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call
|
||||
//! to [`write`](crate::io::AsyncWriteExt::write):
|
||||
//! to [`write`](crate::io::AsyncWriteExt::write). However, you **must** flush
|
||||
//! [`BufWriter`] to ensure that any buffered data is written.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use tokio::io::{self, BufWriter, AsyncWriteExt};
|
||||
@@ -105,16 +106,19 @@
|
||||
//! {
|
||||
//! let mut writer = BufWriter::new(f);
|
||||
//!
|
||||
//! // write a byte to the buffer
|
||||
//! // Write a byte to the buffer.
|
||||
//! writer.write(&[42u8]).await?;
|
||||
//!
|
||||
//! } // the buffer is flushed once writer goes out of scope
|
||||
//! // Flush the buffer before it goes out of scope.
|
||||
//! writer.flush().await?;
|
||||
//!
|
||||
//! } // Unless flushed or shut down, the contents of the buffer is discarded on drop.
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [stdbuf]: https://doc.rust-lang.org/std/io/index.html#bufreader-and-bufwriter
|
||||
//! [stdbuf]: std::io#bufreader-and-bufwriter
|
||||
//! [`std::io::BufRead`]: std::io::BufRead
|
||||
//! [`AsyncBufRead`]: crate::io::AsyncBufRead
|
||||
//! [`BufReader`]: crate::io::BufReader
|
||||
@@ -122,12 +126,26 @@
|
||||
//!
|
||||
//! ## Implementing AsyncRead and AsyncWrite
|
||||
//!
|
||||
//! Because they are traits, we can implement `AsyncRead` and `AsyncWrite` for
|
||||
//! Because they are traits, we can implement [`AsyncRead`] and [`AsyncWrite`] for
|
||||
//! our own types, as well. Note that these traits must only be implemented for
|
||||
//! non-blocking I/O types that integrate with the futures type system. In
|
||||
//! other words, these types must never block the thread, and instead the
|
||||
//! current task is notified when the I/O resource is ready.
|
||||
//!
|
||||
//! ## Conversion to and from Sink/Stream
|
||||
//!
|
||||
//! It is often convenient to encapsulate the reading and writing of
|
||||
//! bytes and instead work with a [`Sink`] or [`Stream`] of some data
|
||||
//! type that is encoded as bytes and/or decoded from bytes. Tokio
|
||||
//! provides some utility traits in the [tokio-util] crate that
|
||||
//! abstract the asynchronous buffering that is required and allows
|
||||
//! you to write [`Encoder`] and [`Decoder`] functions working with a
|
||||
//! buffer of bytes, and then use that ["codec"] to transform anything
|
||||
//! that implements [`AsyncRead`] and [`AsyncWrite`] into a `Sink`/`Stream` of
|
||||
//! your structured data.
|
||||
//!
|
||||
//! [tokio-util]: https://docs.rs/tokio-util/0.3/tokio_util/codec/index.html
|
||||
//!
|
||||
//! # Standard input and output
|
||||
//!
|
||||
//! Tokio provides asynchronous APIs to standard [input], [output], and [error].
|
||||
@@ -144,15 +162,23 @@
|
||||
//!
|
||||
//! # `std` re-exports
|
||||
//!
|
||||
//! Additionally, [`Error`], [`ErrorKind`], and [`Result`] are re-exported
|
||||
//! from `std::io` for ease of use.
|
||||
//! Additionally, [`Error`], [`ErrorKind`], [`Result`], and [`SeekFrom`] are
|
||||
//! re-exported from `std::io` for ease of use.
|
||||
//!
|
||||
//! [`AsyncRead`]: trait@AsyncRead
|
||||
//! [`AsyncWrite`]: trait@AsyncWrite
|
||||
//! [`AsyncReadExt`]: trait@AsyncReadExt
|
||||
//! [`AsyncWriteExt`]: trait@AsyncWriteExt
|
||||
//! ["codec"]: https://docs.rs/tokio-util/0.3/tokio_util/codec/index.html
|
||||
//! [`Encoder`]: https://docs.rs/tokio-util/0.3/tokio_util/codec/trait.Encoder.html
|
||||
//! [`Decoder`]: https://docs.rs/tokio-util/0.3/tokio_util/codec/trait.Decoder.html
|
||||
//! [`Error`]: struct@Error
|
||||
//! [`ErrorKind`]: enum@ErrorKind
|
||||
//! [`Result`]: type@Result
|
||||
//! [`Read`]: std::io::Read
|
||||
//! [`SeekFrom`]: enum@SeekFrom
|
||||
//! [`Sink`]: https://docs.rs/futures/0.3/futures/sink/trait.Sink.html
|
||||
//! [`Stream`]: crate::stream::Stream
|
||||
//! [`Write`]: std::io::Write
|
||||
cfg_io_blocking! {
|
||||
pub(crate) mod blocking;
|
||||
@@ -170,6 +196,10 @@ pub use self::async_seek::AsyncSeek;
|
||||
mod async_write;
|
||||
pub use self::async_write::AsyncWrite;
|
||||
|
||||
// Re-export some types from `std::io` so that users don't have to deal
|
||||
// with conflicts when `use`ing `tokio::io` and `std::io`.
|
||||
pub use std::io::{Error, ErrorKind, Result, SeekFrom};
|
||||
|
||||
cfg_io_driver! {
|
||||
pub(crate) mod driver;
|
||||
|
||||
@@ -200,17 +230,14 @@ cfg_io_util! {
|
||||
|
||||
pub(crate) mod util;
|
||||
pub use util::{
|
||||
copy, empty, repeat, sink, AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt,
|
||||
BufReader, BufStream, BufWriter, Copy, Empty, Lines, Repeat, Sink, Split, Take,
|
||||
copy, duplex, empty, repeat, sink, AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt,
|
||||
BufReader, BufStream, BufWriter, DuplexStream, Copy, Empty, Lines, Repeat, Sink, Split, Take,
|
||||
};
|
||||
|
||||
cfg_stream! {
|
||||
pub use util::{stream_reader, StreamReader};
|
||||
pub use util::{reader_stream, ReaderStream};
|
||||
}
|
||||
|
||||
// Re-export io::Error so that users don't have to deal with conflicts when
|
||||
// `use`ing `tokio::io` and `std::io`.
|
||||
pub use std::io::{Error, ErrorKind, Result};
|
||||
}
|
||||
|
||||
cfg_not_io_util! {
|
||||
|
||||
@@ -90,17 +90,17 @@ cfg_io_driver! {
|
||||
/// These events are included as part of the read readiness event stream. The
|
||||
/// write readiness event stream is only for `Ready::writable()` events.
|
||||
///
|
||||
/// [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
||||
/// [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
|
||||
/// [`AsyncRead`]: ../io/trait.AsyncRead.html
|
||||
/// [`AsyncWrite`]: ../io/trait.AsyncWrite.html
|
||||
/// [`mio::Evented`]: https://docs.rs/mio/0.6/mio/trait.Evented.html
|
||||
/// [`std::io::Read`]: trait@std::io::Read
|
||||
/// [`std::io::Write`]: trait@std::io::Write
|
||||
/// [`AsyncRead`]: trait@AsyncRead
|
||||
/// [`AsyncWrite`]: trait@AsyncWrite
|
||||
/// [`mio::Evented`]: trait@mio::Evented
|
||||
/// [`Registration`]: struct@Registration
|
||||
/// [`TcpListener`]: ../net/struct.TcpListener.html
|
||||
/// [`clear_read_ready`]: #method.clear_read_ready
|
||||
/// [`clear_write_ready`]: #method.clear_write_ready
|
||||
/// [`poll_read_ready`]: #method.poll_read_ready
|
||||
/// [`poll_write_ready`]: #method.poll_write_ready
|
||||
/// [`TcpListener`]: struct@crate::net::TcpListener
|
||||
/// [`clear_read_ready`]: method@Self::clear_read_ready
|
||||
/// [`clear_write_ready`]: method@Self::clear_write_ready
|
||||
/// [`poll_read_ready`]: method@Self::poll_read_ready
|
||||
/// [`poll_write_ready`]: method@Self::poll_write_ready
|
||||
pub struct PollEvented<E: Evented> {
|
||||
io: Option<E>,
|
||||
inner: Inner,
|
||||
@@ -175,7 +175,35 @@ where
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
|
||||
pub fn new(io: E) -> io::Result<Self> {
|
||||
let registration = Registration::new(&io)?;
|
||||
PollEvented::new_with_ready(io, mio::Ready::all())
|
||||
}
|
||||
|
||||
/// Creates a new `PollEvented` associated with the default reactor, for specific `mio::Ready`
|
||||
/// state. `new_with_ready` should be used over `new` when you need control over the readiness
|
||||
/// state, such as when a file descriptor only allows reads. This does not add `hup` or `error`
|
||||
/// so if you are interested in those states, you will need to add them to the readiness state
|
||||
/// passed to this function.
|
||||
///
|
||||
/// An example to listen to read only
|
||||
///
|
||||
/// ```rust
|
||||
/// ##[cfg(unix)]
|
||||
/// mio::Ready::from_usize(
|
||||
/// mio::Ready::readable().as_usize()
|
||||
/// | mio::unix::UnixReady::error().as_usize()
|
||||
/// | mio::unix::UnixReady::hup().as_usize()
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
|
||||
pub fn new_with_ready(io: E, ready: mio::Ready) -> io::Result<Self> {
|
||||
let registration = Registration::new_with_ready(&io, ready)?;
|
||||
Ok(Self {
|
||||
io: Some(io),
|
||||
inner: Inner {
|
||||
@@ -225,7 +253,7 @@ where
|
||||
/// The I/O resource will remain in a read-ready state until readiness is
|
||||
/// cleared by calling [`clear_read_ready`].
|
||||
///
|
||||
/// [`clear_read_ready`]: #method.clear_read_ready
|
||||
/// [`clear_read_ready`]: method@Self::clear_read_ready
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
@@ -233,6 +261,11 @@ where
|
||||
///
|
||||
/// * `ready` includes writable.
|
||||
/// * called from outside of a task context.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This method may not be called concurrently. It takes `&self` to allow
|
||||
/// calling it concurrently with `poll_write_ready`.
|
||||
pub fn poll_read_ready(
|
||||
&self,
|
||||
cx: &mut Context<'_>,
|
||||
@@ -291,7 +324,7 @@ where
|
||||
/// The I/O resource will remain in a write-ready state until readiness is
|
||||
/// cleared by calling [`clear_write_ready`].
|
||||
///
|
||||
/// [`clear_write_ready`]: #method.clear_write_ready
|
||||
/// [`clear_write_ready`]: method@Self::clear_write_ready
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
@@ -299,6 +332,11 @@ where
|
||||
///
|
||||
/// * `ready` contains bits besides `writable` and `hup`.
|
||||
/// * called from outside of a task context.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This method may not be called concurrently. It takes `&self` to allow
|
||||
/// calling it concurrently with `poll_read_ready`.
|
||||
pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<mio::Ready>> {
|
||||
poll_ready!(
|
||||
self,
|
||||
|
||||
@@ -34,9 +34,9 @@ cfg_io_driver! {
|
||||
/// stream. The write readiness event stream is only for `Ready::writable()`
|
||||
/// events.
|
||||
///
|
||||
/// [`new`]: #method.new
|
||||
/// [`poll_read_ready`]: #method.poll_read_ready`]
|
||||
/// [`poll_write_ready`]: #method.poll_write_ready`]
|
||||
/// [`new`]: method@Self::new
|
||||
/// [`poll_read_ready`]: method@Self::poll_read_ready`
|
||||
/// [`poll_write_ready`]: method@Self::poll_write_ready`
|
||||
#[derive(Debug)]
|
||||
pub struct Registration {
|
||||
handle: Handle,
|
||||
@@ -63,12 +63,49 @@ impl Registration {
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
|
||||
pub fn new<T>(io: &T) -> io::Result<Registration>
|
||||
where
|
||||
T: Evented,
|
||||
{
|
||||
Registration::new_with_ready(io, mio::Ready::all())
|
||||
}
|
||||
|
||||
/// Registers the I/O resource with the default reactor, for a specific `mio::Ready` state.
|
||||
/// `new_with_ready` should be used over `new` when you need control over the readiness state,
|
||||
/// such as when a file descriptor only allows reads. This does not add `hup` or `error` so if
|
||||
/// you are interested in those states, you will need to add them to the readiness state passed
|
||||
/// to this function.
|
||||
///
|
||||
/// An example to listen to read only
|
||||
///
|
||||
/// ```rust
|
||||
/// ##[cfg(unix)]
|
||||
/// mio::Ready::from_usize(
|
||||
/// mio::Ready::readable().as_usize()
|
||||
/// | mio::unix::UnixReady::error().as_usize()
|
||||
/// | mio::unix::UnixReady::hup().as_usize()
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// - `Ok` if the registration happened successfully
|
||||
/// - `Err` if an error was encountered during registration
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
|
||||
pub fn new_with_ready<T>(io: &T, ready: mio::Ready) -> io::Result<Registration>
|
||||
where
|
||||
T: Evented,
|
||||
{
|
||||
let handle = Handle::current();
|
||||
let address = if let Some(inner) = handle.inner() {
|
||||
inner.add_source(io)?
|
||||
inner.add_source(io, ready)?
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
@@ -116,8 +153,6 @@ impl Registration {
|
||||
/// the function will always return `Ready(HUP)`. This should be treated as
|
||||
/// the end of the readiness stream.
|
||||
///
|
||||
/// Ensure that [`register`] has been called first.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// There are several possible return values:
|
||||
@@ -129,22 +164,26 @@ impl Registration {
|
||||
/// since the last call to `poll_read_ready`.
|
||||
///
|
||||
/// * `Poll::Ready(Err(err))` means that the registration has encountered an
|
||||
/// error. This error either represents a permanent internal error **or**
|
||||
/// the fact that [`register`] was not called first.
|
||||
/// error. This could represent a permanent internal error for example.
|
||||
///
|
||||
/// [`register`]: #method.register
|
||||
/// [edge-triggered]: https://docs.rs/mio/0.6/mio/struct.Poll.html#edge-triggered-and-level-triggered
|
||||
/// [edge-triggered]: struct@mio::Poll#edge-triggered-and-level-triggered
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if called from outside of a task context.
|
||||
pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<mio::Ready>> {
|
||||
// Keep track of task budget
|
||||
ready!(crate::coop::poll_proceed(cx));
|
||||
let coop = ready!(crate::coop::poll_proceed(cx));
|
||||
|
||||
let v = self.poll_ready(Direction::Read, Some(cx))?;
|
||||
let v = self.poll_ready(Direction::Read, Some(cx)).map_err(|e| {
|
||||
coop.made_progress();
|
||||
e
|
||||
})?;
|
||||
match v {
|
||||
Some(v) => Poll::Ready(Ok(v)),
|
||||
Some(v) => {
|
||||
coop.made_progress();
|
||||
Poll::Ready(Ok(v))
|
||||
}
|
||||
None => Poll::Pending,
|
||||
}
|
||||
}
|
||||
@@ -155,7 +194,7 @@ impl Registration {
|
||||
/// will not notify the current task when a new event is received. As such,
|
||||
/// it is safe to call this function from outside of a task context.
|
||||
///
|
||||
/// [`poll_read_ready`]: #method.poll_read_ready
|
||||
/// [`poll_read_ready`]: method@Self::poll_read_ready
|
||||
pub fn take_read_ready(&self) -> io::Result<Option<mio::Ready>> {
|
||||
self.poll_ready(Direction::Read, None)
|
||||
}
|
||||
@@ -170,8 +209,6 @@ impl Registration {
|
||||
/// the function will always return `Ready(HUP)`. This should be treated as
|
||||
/// the end of the readiness stream.
|
||||
///
|
||||
/// Ensure that [`register`] has been called first.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// There are several possible return values:
|
||||
@@ -183,22 +220,26 @@ impl Registration {
|
||||
/// since the last call to `poll_write_ready`.
|
||||
///
|
||||
/// * `Poll::Ready(Err(err))` means that the registration has encountered an
|
||||
/// error. This error either represents a permanent internal error **or**
|
||||
/// the fact that [`register`] was not called first.
|
||||
/// error. This could represent a permanent internal error for example.
|
||||
///
|
||||
/// [`register`]: #method.register
|
||||
/// [edge-triggered]: https://docs.rs/mio/0.6/mio/struct.Poll.html#edge-triggered-and-level-triggered
|
||||
/// [edge-triggered]: struct@mio::Poll#edge-triggered-and-level-triggered
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if called from outside of a task context.
|
||||
pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<mio::Ready>> {
|
||||
// Keep track of task budget
|
||||
ready!(crate::coop::poll_proceed(cx));
|
||||
let coop = ready!(crate::coop::poll_proceed(cx));
|
||||
|
||||
let v = self.poll_ready(Direction::Write, Some(cx))?;
|
||||
let v = self.poll_ready(Direction::Write, Some(cx)).map_err(|e| {
|
||||
coop.made_progress();
|
||||
e
|
||||
})?;
|
||||
match v {
|
||||
Some(v) => Poll::Ready(Ok(v)),
|
||||
Some(v) => {
|
||||
coop.made_progress();
|
||||
Poll::Ready(Ok(v))
|
||||
}
|
||||
None => Poll::Pending,
|
||||
}
|
||||
}
|
||||
@@ -209,7 +250,7 @@ impl Registration {
|
||||
/// will not notify the current task when a new event is received. As such,
|
||||
/// it is safe to call this function from outside of a task context.
|
||||
///
|
||||
/// [`poll_write_ready`]: #method.poll_write_ready
|
||||
/// [`poll_write_ready`]: method@Self::poll_write_ready
|
||||
pub fn take_write_ready(&self) -> io::Result<Option<mio::Ready>> {
|
||||
self.poll_ready(Direction::Write, None)
|
||||
}
|
||||
|
||||
+20
-12
@@ -12,11 +12,14 @@ cfg_io_std! {
|
||||
/// The handle implements the [`AsyncRead`] trait, but beware that concurrent
|
||||
/// reads of `Stdin` must be executed with care.
|
||||
///
|
||||
/// As an additional caveat, reading from the handle may block the calling
|
||||
/// future indefinitely if there is not enough data available. This makes this
|
||||
/// handle unsuitable for use in any circumstance where immediate reaction to
|
||||
/// available data is required, e.g. interactive use or when implementing a
|
||||
/// subprocess driven by requests on the standard input.
|
||||
/// This handle is best used for non-interactive uses, such as when a file
|
||||
/// is piped into the application. For technical reasons, `stdin` is
|
||||
/// implemented by using an ordinary blocking read on a separate thread, and
|
||||
/// it is impossible to cancel that read. This can make shutdown of the
|
||||
/// runtime hang until the user presses enter.
|
||||
///
|
||||
/// For interactive uses, it is recommended to spawn a thread dedicated to
|
||||
/// user input and use blocking IO directly in that thread.
|
||||
///
|
||||
/// Created by the [`stdin`] function.
|
||||
///
|
||||
@@ -29,14 +32,14 @@ cfg_io_std! {
|
||||
|
||||
/// Constructs a new handle to the standard input of the current process.
|
||||
///
|
||||
/// The returned handle allows reading from standard input from the within the
|
||||
/// Tokio runtime.
|
||||
/// This handle is best used for non-interactive uses, such as when a file
|
||||
/// is piped into the application. For technical reasons, `stdin` is
|
||||
/// implemented by using an ordinary blocking read on a separate thread, and
|
||||
/// it is impossible to cancel that read. This can make shutdown of the
|
||||
/// runtime hang until the user presses enter.
|
||||
///
|
||||
/// As an additional caveat, reading from the handle may block the calling
|
||||
/// future indefinitely if there is not enough data available. This makes this
|
||||
/// handle unsuitable for use in any circumstance where immediate reaction to
|
||||
/// available data is required, e.g. interactive use or when implementing a
|
||||
/// subprocess driven by requests on the standard input.
|
||||
/// For interactive uses, it is recommended to spawn a thread dedicated to
|
||||
/// user input and use blocking IO directly in that thread.
|
||||
pub fn stdin() -> Stdin {
|
||||
let std = io::stdin();
|
||||
Stdin {
|
||||
@@ -60,6 +63,11 @@ impl std::os::windows::io::AsRawHandle for Stdin {
|
||||
}
|
||||
|
||||
impl AsyncRead for Stdin {
|
||||
unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [std::mem::MaybeUninit<u8>]) -> bool {
|
||||
// https://github.com/rust-lang/rust/blob/09c817eeb29e764cfc12d0a8d94841e3ffe34023/src/libstd/io/stdio.rs#L97
|
||||
false
|
||||
}
|
||||
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
|
||||
@@ -2,8 +2,12 @@ use crate::io::util::chain::{chain, Chain};
|
||||
use crate::io::util::read::{read, Read};
|
||||
use crate::io::util::read_buf::{read_buf, ReadBuf};
|
||||
use crate::io::util::read_exact::{read_exact, ReadExact};
|
||||
use crate::io::util::read_int::{ReadI128, ReadI16, ReadI32, ReadI64, ReadI8};
|
||||
use crate::io::util::read_int::{ReadU128, ReadU16, ReadU32, ReadU64, ReadU8};
|
||||
use crate::io::util::read_int::{
|
||||
ReadI128, ReadI128Le, ReadI16, ReadI16Le, ReadI32, ReadI32Le, ReadI64, ReadI64Le, ReadI8,
|
||||
};
|
||||
use crate::io::util::read_int::{
|
||||
ReadU128, ReadU128Le, ReadU16, ReadU16Le, ReadU32, ReadU32Le, ReadU64, ReadU64Le, ReadU8,
|
||||
};
|
||||
use crate::io::util::read_to_end::{read_to_end, ReadToEnd};
|
||||
use crate::io::util::read_to_string::{read_to_string, ReadToString};
|
||||
use crate::io::util::take::{take, Take};
|
||||
@@ -221,7 +225,7 @@ cfg_io_util! {
|
||||
/// ```
|
||||
fn read_buf<'a, B>(&'a mut self, buf: &'a mut B) -> ReadBuf<'a, Self, B>
|
||||
where
|
||||
Self: Sized,
|
||||
Self: Sized + Unpin,
|
||||
B: BufMut,
|
||||
{
|
||||
read_buf(self, buf)
|
||||
@@ -238,12 +242,6 @@ cfg_io_util! {
|
||||
/// This function reads as many bytes as necessary to completely fill
|
||||
/// the specified buffer `buf`.
|
||||
///
|
||||
/// No guarantees are provided about the contents of `buf` when this
|
||||
/// function is called, implementations cannot rely on any property of
|
||||
/// the contents of `buf` being `true`. It is recommended that
|
||||
/// implementations only write data to `buf` instead of reading its
|
||||
/// contents.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the operation encounters an "end of file" before completely
|
||||
@@ -669,6 +667,313 @@ cfg_io_util! {
|
||||
/// }
|
||||
/// ```
|
||||
fn read_i128(&mut self) -> ReadI128;
|
||||
|
||||
/// Reads an unsigned 16-bit integer in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_u16_le(&mut self) -> io::Result<u16>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read unsigned 16 bit little-endian integers from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![2, 5, 3, 0]);
|
||||
///
|
||||
/// assert_eq!(1282, reader.read_u16_le().await?);
|
||||
/// assert_eq!(3, reader.read_u16_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_u16_le(&mut self) -> ReadU16Le;
|
||||
|
||||
/// Reads a signed 16-bit integer in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_i16_le(&mut self) -> io::Result<i16>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read signed 16 bit little-endian integers from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![0x00, 0xc1, 0xff, 0x7c]);
|
||||
///
|
||||
/// assert_eq!(-16128, reader.read_i16_le().await?);
|
||||
/// assert_eq!(31999, reader.read_i16_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_i16_le(&mut self) -> ReadI16Le;
|
||||
|
||||
/// Reads an unsigned 32-bit integer in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_u32_le(&mut self) -> io::Result<u32>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read unsigned 32-bit little-endian integers from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![0x00, 0x00, 0x01, 0x0b]);
|
||||
///
|
||||
/// assert_eq!(184614912, reader.read_u32_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_u32_le(&mut self) -> ReadU32Le;
|
||||
|
||||
/// Reads a signed 32-bit integer in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_i32_le(&mut self) -> io::Result<i32>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read signed 32-bit little-endian integers from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![0xff, 0xff, 0x7a, 0x33]);
|
||||
///
|
||||
/// assert_eq!(863698943, reader.read_i32_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_i32_le(&mut self) -> ReadI32Le;
|
||||
|
||||
/// Reads an unsigned 64-bit integer in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_u64_le(&mut self) -> io::Result<u64>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read unsigned 64-bit little-endian integers from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![
|
||||
/// 0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83
|
||||
/// ]);
|
||||
///
|
||||
/// assert_eq!(9477368352180732672, reader.read_u64_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_u64_le(&mut self) -> ReadU64Le;
|
||||
|
||||
/// Reads an signed 64-bit integer in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_i64_le(&mut self) -> io::Result<i64>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read signed 64-bit little-endian integers from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![0x80, 0, 0, 0, 0, 0, 0, 0]);
|
||||
///
|
||||
/// assert_eq!(128, reader.read_i64_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_i64_le(&mut self) -> ReadI64Le;
|
||||
|
||||
/// Reads an unsigned 128-bit integer in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_u128_le(&mut self) -> io::Result<u128>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read unsigned 128-bit little-endian integers from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![
|
||||
/// 0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83,
|
||||
/// 0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83
|
||||
/// ]);
|
||||
///
|
||||
/// assert_eq!(174826588484952389081207917399662330624, reader.read_u128_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_u128_le(&mut self) -> ReadU128Le;
|
||||
|
||||
/// Reads an signed 128-bit integer in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_i128_le(&mut self) -> io::Result<i128>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read signed 128-bit little-endian integers from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![
|
||||
/// 0x80, 0, 0, 0, 0, 0, 0, 0,
|
||||
/// 0, 0, 0, 0, 0, 0, 0, 0
|
||||
/// ]);
|
||||
///
|
||||
/// assert_eq!(128, reader.read_i128_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_i128_le(&mut self) -> ReadI128Le;
|
||||
}
|
||||
|
||||
/// Reads all bytes until EOF in this source, placing them into `buf`.
|
||||
@@ -681,10 +986,12 @@ cfg_io_util! {
|
||||
///
|
||||
/// All bytes read from this source will be appended to the specified
|
||||
/// buffer `buf`. This function will continuously call [`read()`] to
|
||||
/// append more data to `buf` until [`read()`][read] returns `Ok(0)`.
|
||||
/// append more data to `buf` until [`read()`] returns `Ok(0)`.
|
||||
///
|
||||
/// If successful, the total number of bytes read is returned.
|
||||
///
|
||||
/// [`read()`]: AsyncReadExt::read
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If a read error is encountered then the `read_to_end` operation
|
||||
@@ -713,7 +1020,7 @@ cfg_io_util! {
|
||||
/// (See also the [`tokio::fs::read`] convenience function for reading from a
|
||||
/// file.)
|
||||
///
|
||||
/// [`tokio::fs::read`]: crate::fs::read::read
|
||||
/// [`tokio::fs::read`]: fn@crate::fs::read
|
||||
fn read_to_end<'a>(&'a mut self, buf: &'a mut Vec<u8>) -> ReadToEnd<'a, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
@@ -760,7 +1067,7 @@ cfg_io_util! {
|
||||
/// (See also the [`crate::fs::read_to_string`] convenience function for
|
||||
/// reading from a file.)
|
||||
///
|
||||
/// [`crate::fs::read_to_string`]: crate::fs::read_to_string::read_to_string
|
||||
/// [`crate::fs::read_to_string`]: fn@crate::fs::read_to_string
|
||||
fn read_to_string<'a>(&'a mut self, dst: &'a mut String) -> ReadToString<'a, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
@@ -773,7 +1080,11 @@ cfg_io_util! {
|
||||
/// This function returns a new instance of `AsyncRead` which will read
|
||||
/// at most `limit` bytes, after which it will always return EOF
|
||||
/// (`Ok(0)`). Any read errors will not count towards the number of
|
||||
/// bytes read and future calls to [`read()`][read] may succeed.
|
||||
/// bytes read and future calls to [`read()`] may succeed.
|
||||
///
|
||||
/// [`read()`]: fn@crate::io::AsyncReadExt::read
|
||||
///
|
||||
/// [read]: AsyncReadExt::read
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user