From 78f3a4488fe3b180f6a999f307294a897f240259 Mon Sep 17 00:00:00 2001 From: Aaron Todd Date: Wed, 17 Jul 2024 07:32:11 -0400 Subject: [PATCH] bootstrap repository setup (#1) --- .cargo-deny-config.toml | 71 +++++ .cargo/config.toml | 3 + .github/workflows/audit.yml | 30 ++ .github/workflows/ci.yml | 270 ++++++++++++++++++ .github/workflows/pr-audit.yml | 32 +++ .gitignore | 63 ++++ CODEOWNERS | 2 + Cargo.toml | 5 + README.md | 61 +++- aws-s3-transfer-manager/README.md | 57 ---- aws-s3-transfer-manager/external-types.toml | 8 +- aws-s3-transfer-manager/src/download/body.rs | 2 +- aws-s3-transfer-manager/src/io/part_reader.rs | 9 +- aws-s3-transfer-manager/src/io/path_body.rs | 4 +- aws-s3-transfer-manager/src/lib.rs | 1 + aws-s3-transfer-manager/src/upload.rs | 1 - aws-s3-transfer-manager/src/upload/request.rs | 1 + 17 files changed, 543 insertions(+), 77 deletions(-) create mode 100644 .cargo-deny-config.toml create mode 100644 .cargo/config.toml create mode 100644 .github/workflows/audit.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pr-audit.yml create mode 100644 CODEOWNERS create mode 100644 Cargo.toml delete mode 100644 aws-s3-transfer-manager/README.md diff --git a/.cargo-deny-config.toml b/.cargo-deny-config.toml new file mode 100644 index 0000000..3a9407f --- /dev/null +++ b/.cargo-deny-config.toml @@ -0,0 +1,71 @@ +# This is the config file for `cargo-deny` used in CI + +# This section is considered when running `cargo deny check licenses` +# More documentation for the licenses section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html +[licenses] +default = "deny" +unlicensed = "deny" +copyleft = "deny" +allow-osi-fsf-free = "neither" +allow = [ + # See https://spdx.org/licenses/ for list of possible licenses + # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. + "Apache-2.0 WITH LLVM-exception", + "Apache-2.0", + "BSD-3-Clause", + "ISC", + "MIT", + "MPL-2.0", + "Unicode-DFS-2016", + "Unicode-3.0", +] +confidence-threshold = 1.0 +exceptions = [ + { allow = ["OpenSSL"], name = "ring", version = "*" }, + { allow = ["OpenSSL"], name = "aws-lc-sys", version = "*" }, + { allow = ["OpenSSL"], name = "aws-lc-fips-sys", version = "*" }, +] + +[[licenses.clarify]] +name = "webpki" +version = "*" +expression = "MIT AND ISC" +license-files = [{ path = "LICENSE", hash = 0x001c7e6c }] + +[[licenses.clarify]] +name = "ring" +expression = "MIT AND ISC AND OpenSSL" +license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] + +[[licenses.clarify]] +name = "webpki" +expression = "ISC" +license-files = [ + { path = "LICENSE", hash = 0x001c7e6c }, +] + +[[licenses.clarify]] +name = "rustls-webpki" +expression = "ISC" +license-files = [ + { path = "LICENSE", hash = 0x001c7e6c }, +] + +# This section is considered when running `cargo deny check bans`. +# More documentation about the 'bans' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html +[bans] +multiple-versions = "allow" +wildcards = "deny" # Don't allow wildcard dependencies +highlight = "all" +deny = [] + +# This section is considered when running `cargo deny check sources`. +# More documentation about the 'sources' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html +[sources] +unknown-registry = "deny" +unknown-git = "deny" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +allow-git = [] diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..040e71a --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,3 @@ +[build] +# Share one `target` directory at the project root for all Cargo projects and workspaces in aws-s3-transfer-manager-rs +target-dir = "target" diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..dcff973 --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,30 @@ +name: Security Audit + +on: + push: + branches: + - main + paths: + - '**/Cargo.toml' + schedule: + - cron: '0 2 * * *' # run at 2 AM UTC + +permissions: + contents: read + +jobs: + security-audit: + permissions: + checks: write # for rustsec/audit-check to create check + contents: read # for actions/checkout to fetch code + issues: write # for rustsec/audit-check to create issues + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, 'ci skip')" + steps: + - uses: actions/checkout@v4 + + - name: Audit Check + # https://github.com/rustsec/audit-check/issues/2 + uses: rustsec/audit-check@master + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..accfd58 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,270 @@ +on: + pull_request: + +name: CI + +# Allow one instance of this workflow per pull request, and cancel older runs when new changes are pushed +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + + +env: + RUSTFLAGS: -Dwarnings + RUST_BACKTRACE: 1 + # Change to specific Rust release to pin + rust_stable: stable + rust_nightly: nightly-2024-07-07 + rust_clippy: '1.77' + # When updating this, also update relevant docs + rust_min: '1.76' + + +defaults: + run: + shell: bash + +permissions: + contents: read + +jobs: + # depends on all actions required for a "successful" CI run + ci-required-checks: + name: Required checks pass + runs-on: ubuntu-24.04 + needs: + - test-hll + - fmt + - clippy + - docs + - minrust + - check-external-types + - check-deny + - sanitizers + - features + steps: + - run: exit 0 + + # Basic actions that must pass before we kick off more expensive tests. + basics: + name: basic checks + runs-on: ubuntu-24.04 + needs: + - fmt + - clippy + - docs + - minrust + steps: + - run: exit 0 + + test-hll: + name: Test S3 transfer manager HLL + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-24.04 + - windows-2022 + - macos-14 + needs: basics + steps: + - uses: actions/checkout@v4 + - name: Install Rust ${{ env.rust_nightly }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.rust_stable }} + - name: Install cargo-nextest + uses: taiki-e/install-action@v2 + with: + tool: cargo-nextest + + - uses: Swatinem/rust-cache@v2 + + - name: test s3-transfer-manager HLL + run: | + cargo nextest run --workspace --all-features + cargo test --doc --workspace --all-features + + fmt: + name: fmt + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust ${{ env.rust_stable }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.rust_stable }} + components: rustfmt + - uses: Swatinem/rust-cache@v2 + # Check fmt + - name: "cargo fmt --check" + # Workaround for rust-lang/cargo#7732 + run: | + if ! cargo fmt --check; then + printf "Please run \`cargo fmt\` to fix rustfmt errors.\nSee CONTRIBUTING.md for more details.\n" >&2 + exit 1 + fi + + clippy: + name: clippy + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust ${{ env.rust_clippy }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.rust_clippy }} + components: clippy + - uses: Swatinem/rust-cache@v2 + - name: "clippy --all" + run: cargo clippy --all --tests --all-features --no-deps + + docs: + name: docs + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust ${{ env.rust_nightly }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.rust_nightly }} + - uses: Swatinem/rust-cache@v2 + - name: "doc --lib --all-features" + run: | + cargo doc --lib --no-deps --all-features --document-private-items + env: + RUSTFLAGS: --cfg docsrs + RUSTDOCFLAGS: --cfg docsrs + + minrust: + name: minrust + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust ${{ env.rust_min }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.rust_min }} + - uses: Swatinem/rust-cache@v2 + - name: "check --workspace --all-features" + run: cargo check --workspace --all-features + env: + RUSTFLAGS: "" # remove -Dwarnings + + check-external-types: + name: check-external-types (${{ matrix.os }}) + needs: basics + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + # FIXME - can't generate docs on windows due to typenum (see https://github.com/paholg/typenum/issues/158) + # - windows-2022 + - ubuntu-24.04 + rust: + # `check-external-types` requires a specific Rust nightly version. See + # the README for details: https://github.com/awslabs/cargo-check-external-types + - nightly-2024-05-01 + steps: + - uses: actions/checkout@v4 + - name: Install Rust ${{ matrix.rust }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-check-external-types + uses: taiki-e/cache-cargo-install-action@v2 + with: + tool: cargo-check-external-types@0.1.12 + - name: check-external-types + run: cargo check-external-types --all-features --config external-types.toml + working-directory: aws-s3-transfer-manager + + check-deny: + name: check deps with cargo-deny + needs: basics + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust ${{ matrix.rust }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.rust_stable }} + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-deny + uses: taiki-e/install-action@v2 + with: + tool: cargo-deny + - name: cargo-deny + run: cargo deny --all-features check --hide-inclusion-graph --config .cargo-deny-config.toml licenses bans sources + + sanitizers: + name: saniters + needs: basics + runs-on: ubuntu-24.04 + # TODO - add additional sanitizers like leak via matrix or other jobs + steps: + - uses: actions/checkout@v4 + - name: Install llvm + # Required to resolve symbols in sanitizer output + run: sudo apt-get install -y llvm + - name: Install Rust ${{ env.rust_nightly }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.rust_nightly }} + - uses: Swatinem/rust-cache@v2 + - name: asan + run: cargo test --workspace --all-features --target x86_64-unknown-linux-gnu --tests -- --test-threads 1 --nocapture + env: + RUSTFLAGS: -Z sanitizer=address + # Ignore `trybuild` errors as they are irrelevant and flaky on nightly + TRYBUILD: overwrite + + semver: + name: semver + needs: basics + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Check semver + uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + rust-toolchain: ${{ env.rust_stable }} + release-type: minor + + features: + name: features + needs: basics + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust ${{ env.rust_nightly }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.rust_nightly }} + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - uses: Swatinem/rust-cache@v2 + - name: check --feature-powerset + run: cargo hack check --all --feature-powerset + + # TODO - get cross check working + # cross-check: + # name: cross-check + # needs: basics + # runs-on: ubuntu-24.04 + # strategy: + # matrix: + # target: + # - powerpc-unknown-linux-gnu + # - powerpc64-unknown-linux-gnu + # - arm-linux-androideabi + # steps: + # - uses: actions/checkout@v4 + # - name: Install Rust ${{ env.rust_stable }} + # uses: dtolnay/rust-toolchain@stable + # with: + # toolchain: ${{ env.rust_stable }} + # target: ${{ matrix.target }} + # - uses: Swatinem/rust-cache@v2 + # - run: cargo check --workspace --all-features --target ${{ matrix.target }} diff --git a/.github/workflows/pr-audit.yml b/.github/workflows/pr-audit.yml new file mode 100644 index 0000000..ce2e81c --- /dev/null +++ b/.github/workflows/pr-audit.yml @@ -0,0 +1,32 @@ +name: Pull Request Security Audit + +on: + push: + paths: + - '**/Cargo.toml' + pull_request: + paths: + - '**/Cargo.toml' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + security-audit: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, 'ci skip')" + steps: + - uses: actions/checkout@v4 + + - name: Install cargo-audit + run: cargo install cargo-audit + + - name: Generate lockfile + run: cargo generate-lockfile + + - name: Audit dependencies + run: cargo audit diff --git a/.gitignore b/.gitignore index 2c96eb1..6fc6a50 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,65 @@ +# attn: Intellij related ignores belong in ~/.gitignore +# Java/Kotlin +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + +# Gradle +.gradle +build/ +**/bin/main/ +**/bin/test/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# MacOS +.DS_Store + +# Rust build artifacts target/ + +# Rust lock files Cargo.lock + +# IDEs +.idea/ +.vscode/ +.project +.settings +.classpath + +# tools +.tool-versions + +# python +__pycache__ diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..23e808d --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +* @awslabs/rust-sdk-s3-hll + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ed295e8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +resolver = "2" +members = [ + "aws-s3-transfer-manager" +] diff --git a/README.md b/README.md index 847260c..56405a8 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,61 @@ -## My Project +# AWS S3 Transfer Manager -TODO: Fill this README out! +A high performance Amazon S3 client for Rust. -Be sure to: -* Change the title in this README -* Edit your repository description on GitHub +## Development + +**Run all tests** + +```sh +cargo test --all-features +``` + +**Run individual test** + +```sh +cargo test --lib download::worker::tests::test_distribute_work +``` + +### Examples + +NOTE: You can use the `profiling` profile from `.cargo/config.toml` to enable release with debug info for any example. + +**Copy** + +See all options: +```sh +cargo run --example cp -- -h +``` + +**Download a file from S3** + +```sh +AWS_PROFILE= RUST_LOG=trace cargo run --example cp s3:/// /local/path/ +``` + +NOTE: To run in release mode add `--release/-r` to the command, see `cargo run -h`. +NOTE: `trace` may be too verbose, you can see just this library's logs with `RUST_LOG=aws_s3_transfer_manager=trace` + +#### Flamegraphs + +See [cargo-flamegraph](https://github.com/flamegraph-rs/flamegraph) for more prerequisites and installation information. + +Generate a flamegraph (default is to output to `flamegraph.svg`): + +```sh +sudo AWS_PROFILE= RUST_LOG=aws_s3_transfer_manager=info cargo flamegraph --profile profiling --example cp -- s3://test-sdk-rust-aaron/mb-128.dat /tmp/mb-128.dat +``` + +#### Using tokio-console + +Examples use [`console-subscriber`](https://crates.io/crates/console-subscriber) which allows you to run them with +[tokio-console](https://github.com/tokio-rs/console) to help debug task execution. + + +Follow installation instructions for [tokio-console](https://github.com/tokio-rs/console) and then run the +example with `tokio-console` running. + ## Security @@ -14,4 +64,3 @@ See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more inform ## License This project is licensed under the Apache-2.0 License. - diff --git a/aws-s3-transfer-manager/README.md b/aws-s3-transfer-manager/README.md deleted file mode 100644 index 282b908..0000000 --- a/aws-s3-transfer-manager/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# AWS S3 Transfer Manager - -A high performance Amazon S3 client. - - -## Development - -**Run all tests** - -```sh -cargo test --all-features -``` - -**Run individual test** - -```sh -cargo test --lib download::worker::tests::test_distribute_work -``` - -### Examples - -NOTE: You can use the `profiling` profile from `.cargo/config.toml` to enable release with debug info for any example. - -**Copy** - -See all options: -```sh -cargo run --example cp -- -h -``` - -**Download a file from S3** - -```sh -AWS_PROFILE= RUST_LOG=trace cargo run --example cp s3:/// /local/path/ -``` - -NOTE: To run in release mode add `--release/-r` to the command, see `cargo run -h`. -NOTE: `trace` may be too verbose, you can see just this library's logs with `RUST_LOG=aws_s3_transfer_manager=trace` - -#### Flamegraphs - -See [cargo-flamegraph](https://github.com/flamegraph-rs/flamegraph) for more prerequisites and installation information. - -Generate a flamegraph (default is to output to `flamegraph.svg`): - -```sh -sudo AWS_PROFILE= RUST_LOG=aws_s3_transfer_manager=info cargo flamegraph --profile profiling --example cp -- s3://test-sdk-rust-aaron/mb-128.dat /tmp/mb-128.dat -``` - -#### Using tokio-console - -Examples use [`console-subscriber`](https://crates.io/crates/console-subscriber) which allows you to run them with -[tokio-console](https://github.com/tokio-rs/console) to help debug task execution. - - -Follow installation instructions for [tokio-console](https://github.com/tokio-rs/console) and then run the -example with `tokio-console` running. diff --git a/aws-s3-transfer-manager/external-types.toml b/aws-s3-transfer-manager/external-types.toml index d0b12ae..25f0f12 100644 --- a/aws-s3-transfer-manager/external-types.toml +++ b/aws-s3-transfer-manager/external-types.toml @@ -1,8 +1,8 @@ allowed_external_types = [ - "aws_sdk_s3::operation::get_object::builders::GetObjectFluentBuilder", - "aws_sdk_s3::operation::get_object::_get_object_input::GetObjectInputBuilder", - "aws_sdk_s3::operation::get_object::GetObjectError", - "aws_sdk_s3::operation::head_object::HeadObjectError", + "aws_sdk_s3::*", + "aws_types::sdk_config::SdkConfig", "aws_smithy_runtime_api::*", "aws_smithy_types::*", + "tokio::runtime::task::error::JoinError", + "bytes::bytes::Bytes", ] diff --git a/aws-s3-transfer-manager/src/download/body.rs b/aws-s3-transfer-manager/src/download/body.rs index 3cb37f0..f759be4 100644 --- a/aws-s3-transfer-manager/src/download/body.rs +++ b/aws-s3-transfer-manager/src/download/body.rs @@ -213,7 +213,7 @@ mod tests { received.push(data); } - let expected: Vec = vec![0, 1, 2].iter().map(|i| format!("chunk {i}")).collect(); + let expected: Vec = [0, 1, 2].iter().map(|i| format!("chunk {i}")).collect(); assert_eq!(expected, received); } diff --git a/aws-s3-transfer-manager/src/io/part_reader.rs b/aws-s3-transfer-manager/src/io/part_reader.rs index 338ea9e..e0ac2e4 100644 --- a/aws-s3-transfer-manager/src/io/part_reader.rs +++ b/aws-s3-transfer-manager/src/io/part_reader.rs @@ -73,7 +73,7 @@ impl ReadPart for PartReader { /// Data for a single part pub(crate) struct PartData { - // 1-indexed + // 1-indexed pub(crate) part_number: u64, pub(crate) data: Bytes, } @@ -258,7 +258,7 @@ mod test { use bytes::{Buf, Bytes}; use tempfile::NamedTempFile; - use crate::io::part_reader::{PartData, Builder, ReadPart}; + use crate::io::part_reader::{Builder, PartData, ReadPart}; use crate::io::InputStream; async fn collect_parts(reader: impl ReadPart) -> Vec { @@ -304,10 +304,7 @@ mod test { let expected = data.chunks(part_size).collect::>(); let stream = builder.build().unwrap(); - let reader = Builder::new() - .part_size(part_size) - .stream(stream) - .build(); + let reader = Builder::new().part_size(part_size).stream(stream).build(); let parts = collect_parts(reader).await; let actual = parts.iter().map(|p| p.data.chunk()).collect::>(); diff --git a/aws-s3-transfer-manager/src/io/path_body.rs b/aws-s3-transfer-manager/src/io/path_body.rs index 0d64d0f..d17ee98 100644 --- a/aws-s3-transfer-manager/src/io/path_body.rs +++ b/aws-s3-transfer-manager/src/io/path_body.rs @@ -19,7 +19,7 @@ pub(super) struct PathBody { pub(super) offset: u64, } -/// Builder for creating [`InputStream`](InputStream) from a file/path. +/// Builder for creating [`InputStream`] from a file/path. /// /// ```no_run /// # { @@ -145,7 +145,7 @@ mod test { #[test] fn test_explicit_content_length() { - let mut tmp = NamedTempFile::new().unwrap(); + let tmp = NamedTempFile::new().unwrap(); let stream = PathBodyBuilder::new() .path(tmp.path()) diff --git a/aws-s3-transfer-manager/src/lib.rs b/aws-s3-transfer-manager/src/lib.rs index 9ba6988..195db77 100644 --- a/aws-s3-transfer-manager/src/lib.rs +++ b/aws-s3-transfer-manager/src/lib.rs @@ -29,6 +29,7 @@ pub mod download; pub mod error; /// Types and helpers for I/O +#[allow(unused)] // FIXME(aws-sdk-rust#1159) - remove when consumed internally by other modules pub mod io; /// Abstractions for downloading objects from Amazon S3 diff --git a/aws-s3-transfer-manager/src/upload.rs b/aws-s3-transfer-manager/src/upload.rs index 8290e5d..aae46a5 100644 --- a/aws-s3-transfer-manager/src/upload.rs +++ b/aws-s3-transfer-manager/src/upload.rs @@ -6,7 +6,6 @@ pub use crate::upload::request::UploadRequest; pub use crate::upload::response::UploadResponse; - /// Request types for uploads to Amazon S3 pub mod request; diff --git a/aws-s3-transfer-manager/src/upload/request.rs b/aws-s3-transfer-manager/src/upload/request.rs index ec57f14..261fcc1 100644 --- a/aws-s3-transfer-manager/src/upload/request.rs +++ b/aws-s3-transfer-manager/src/upload/request.rs @@ -193,6 +193,7 @@ impl UploadRequest { } /// Split the body from the request by taking it and replacing it with the default. + #[allow(dead_code)] // FIXME(aws-sdk-rust#1159) - remove when consumed internally by other modules pub(crate) fn take_body(&mut self) -> InputStream { mem::take(&mut self.body) }