chore: checkpoint before Python removal

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

View File

@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"b440668a177aa8c05ff746df0d5ba5b9c3e79192e974051b8a0f432d070e7ca1","CHANGELOG.md":"f901f548e4ed5a928bf6db34b1e4a6262d26fff405e73e4c39d7d9203572c592","Cargo.lock":"e5610aa93c7490e5b3dcb20420625c78b060d1d6cf219441c62cba6024c95105","Cargo.toml":"f2405b0b650acf66f387e91530f459beb56935efb8b4680d3e6ababd7616837d","Cargo.toml.orig":"dd200bab9fd48b9c98469f51df2b5ad11b50560e45c82b76388499dc27e00775","LICENSE":"b843fb7430efdf9732c834b6814beda44fccf3f0ddf7c9e030b39da17f6b159c","README.md":"0f90f61ee419eefd4104005ef6900445fafce9a710dd1989463f3cebaf0fafe8","src/channel.rs":"c2ff39f314712ffbb5f725a7cd619d55e23d48fe7318e4ae8a1d91bd9e2fcd35","src/collected.rs":"23d193d5c44cc80b14cc2f7c469a13634acd1fe577b985bcb476010ed7f8c6fb","src/combinators/box_body.rs":"9dbd5e4fe5f7d07f8f087886438c5ea11b74f21a33eae4d0b655bd56ddeaee31","src/combinators/collect.rs":"3fa5f2bfa7f9a6d460ebc307857454d96730869c3cab62a30b453402ed3b24bb","src/combinators/frame.rs":"29f0af03a02321e16e34b335ae3e2e270dbbf34ce6f7364b515608626199642f","src/combinators/map_err.rs":"825954afe9fd843e86afa3b8fa6fb50005291d647ea2413f9538dde9f48bcb19","src/combinators/map_frame.rs":"8eb64fe3a4077c36351ed41c59704209e4b64552fa9b4bcc79ec86c8a929905c","src/combinators/mod.rs":"3d03db85c5d1b1d7a66c13c7a814bd6cb170739d46cb74f454ce341ee20828fc","src/combinators/with_trailers.rs":"c8fdc73fef609f5c444502f26caf8527ad275e7588743eeaf7d20251ab08de17","src/either.rs":"bb9507a54cc4ef0a7cb02bf4e3a4c03f94fbc41bd02d48f7d3933e95dcb4b435","src/empty.rs":"e47f062d02b5e57daf418659c67095be3f63cdc7f498c049383ae89a421de5ad","src/full.rs":"7b249decc34dbad3c7c5561f7484cc792370eb3946a6f0a178d5170fe629b8fc","src/lib.rs":"017df148d039e6872bbd21b128c52cb2faa6314ec23b9a16eae81dcc6bd4a875","src/limited.rs":"c12ee827d8e09935a96f7ff9bdc9ccc6a5f0bbdc061faa4a3519fa738c49491e","src/stream.rs":"09e9ea1380a306dfa96a98e742113022a76a16d707aae3bfde91f7af60e383d0","src/util.rs":"d23ff3fb96171fb2ff9bf9db1c176f862fca3f40f5c0b4ca26d0137103ecb780"},"package":"b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"}

View File

@@ -0,0 +1,7 @@
{
"git": {
"sha1": "ad4a9c9450596ee3f41390448205432a8179d80c",
"dirty": true
},
"path_in_vcs": "http-body-util"
}

30
vendor/http-body-util/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,30 @@
# v0.1.3
- Add `Channel`, a body type backed by an async channel.
- Make `Empty::new()` to be `const fn`.
# v0.1.2
- Add `BodyDataStream` type to convert a body to a stream of its data.
# v0.1.1
- Add `BodyExt::with_trailers()` combinator.
- Improve performance of `BodyExt::collect().to_bytes()`.
# v0.1.0
- Update `http` to 1.0.
- Update `http-body` to 1.0.
# v0.1.0-rc.3
- Fix `BodyExt::collect()` from panicking on an empty frame.
# v0.1.0-rc.2
- Update to `http-body` rc.2.
# v0.1.0-rc.1
- Initial release, split from http-body 0.4.5.

217
vendor/http-body-util/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,217 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bytes"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1f8e949d755f9d79112b5bb46938e0ef9d3804a0b16dfab13aafcaa5f0fa72"
[[package]]
name = "fnv"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "hermit-abi"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "http"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
]
[[package]]
name = "http-body-util"
version = "0.1.3"
dependencies = [
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"pin-project-lite",
"tokio",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "libc"
version = "0.2.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "socket2"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89797afd69d206ccd11fb0ea560a44bbb87731d020670e79416d442919257d42"
dependencies = [
"autocfg",
"num_cpus",
"once_cell",
"pin-project-lite",
"socket2",
"tokio-macros",
]
[[package]]
name = "tokio-macros"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

79
vendor/http-body-util/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,79 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
rust-version = "1.61"
name = "http-body-util"
version = "0.1.3"
authors = [
"Carl Lerche <me@carllerche.com>",
"Lucio Franco <luciofranco14@gmail.com>",
"Sean McArthur <sean@seanmonstar.com>",
]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = """
Combinators and adapters for HTTP request or response bodies.
"""
documentation = "https://docs.rs/http-body-util"
readme = "README.md"
keywords = ["http"]
categories = ["web-programming"]
license = "MIT"
repository = "https://github.com/hyperium/http-body"
[features]
channel = ["dep:tokio"]
default = []
full = ["channel"]
[lib]
name = "http_body_util"
path = "src/lib.rs"
[dependencies.bytes]
version = "1"
[dependencies.futures-core]
version = "0.3"
default-features = false
[dependencies.http]
version = "1"
[dependencies.http-body]
version = "1"
[dependencies.pin-project-lite]
version = "0.2"
[dependencies.tokio]
version = "1"
features = ["sync"]
optional = true
[dev-dependencies.futures-util]
version = "0.3"
default-features = false
[dev-dependencies.tokio]
version = "1"
features = [
"macros",
"rt",
"sync",
"rt-multi-thread",
]

25
vendor/http-body-util/LICENSE vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2019-2025 Sean McArthur & Hyper 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.

27
vendor/http-body-util/README.md vendored Normal file
View File

@@ -0,0 +1,27 @@
# HTTP Body
A trait representing asynchronous operations on an HTTP body.
[![crates.io][crates-badge]][crates-url]
[![documentation][docs-badge]][docs-url]
[![MIT License][mit-badge]][mit-url]
[![CI Status][ci-badge]][ci-url]
[crates-badge]: https://img.shields.io/crates/v/http-body.svg
[crates-url]: https://crates.io/crates/http-body
[docs-badge]: https://docs.rs/http-body/badge.svg
[docs-url]: https://docs.rs/http-body
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
[mit-url]: LICENSE
[ci-badge]: https://github.com/hyperium/http-body/workflows/CI/badge.svg
[ci-url]: https://github.com/hyperium/http-body/actions?query=workflow%3ACI
## License
This project is licensed under the [MIT license](LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in `http-body` by you, shall be licensed as MIT, without any additional
terms or conditions.

359
vendor/http-body-util/src/channel.rs vendored Normal file
View File

@@ -0,0 +1,359 @@
//! A body backed by a channel.
use std::{
fmt::Display,
pin::Pin,
task::{Context, Poll},
};
use bytes::Buf;
use http::HeaderMap;
use http_body::{Body, Frame};
use pin_project_lite::pin_project;
use tokio::sync::{mpsc, oneshot};
pin_project! {
/// A body backed by a channel.
pub struct Channel<D, E = std::convert::Infallible> {
rx_frame: mpsc::Receiver<Frame<D>>,
#[pin]
rx_error: oneshot::Receiver<E>,
}
}
impl<D, E> Channel<D, E> {
/// Create a new channel body.
///
/// The channel will buffer up to the provided number of messages. Once the buffer is full,
/// attempts to send new messages will wait until a message is received from the channel. The
/// provided buffer capacity must be at least 1.
pub fn new(buffer: usize) -> (Sender<D, E>, Self) {
let (tx_frame, rx_frame) = mpsc::channel(buffer);
let (tx_error, rx_error) = oneshot::channel();
(Sender { tx_frame, tx_error }, Self { rx_frame, rx_error })
}
}
impl<D, E> Body for Channel<D, E>
where
D: Buf,
{
type Data = D;
type Error = E;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
let this = self.project();
match this.rx_frame.poll_recv(cx) {
Poll::Ready(frame @ Some(_)) => return Poll::Ready(frame.map(Ok)),
Poll::Ready(None) | Poll::Pending => {}
}
use core::future::Future;
match this.rx_error.poll(cx) {
Poll::Ready(Ok(error)) => return Poll::Ready(Some(Err(error))),
Poll::Ready(Err(_)) => return Poll::Ready(None),
Poll::Pending => {}
}
Poll::Pending
}
}
impl<D, E: std::fmt::Debug> std::fmt::Debug for Channel<D, E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Channel")
.field("rx_frame", &self.rx_frame)
.field("rx_error", &self.rx_error)
.finish()
}
}
/// A sender half created through [`Channel::new`].
pub struct Sender<D, E = std::convert::Infallible> {
tx_frame: mpsc::Sender<Frame<D>>,
tx_error: oneshot::Sender<E>,
}
impl<D, E> Sender<D, E> {
/// Send a frame on the channel.
pub async fn send(&mut self, frame: Frame<D>) -> Result<(), SendError> {
self.tx_frame.send(frame).await.map_err(|_| SendError)
}
/// Send data on data channel.
pub async fn send_data(&mut self, buf: D) -> Result<(), SendError> {
self.send(Frame::data(buf)).await
}
/// Send trailers on trailers channel.
pub async fn send_trailers(&mut self, trailers: HeaderMap) -> Result<(), SendError> {
self.send(Frame::trailers(trailers)).await
}
/// Attempts to send a frame on this channel.
///
/// This function returns the unsent frame back as an `Err(_)` if the channel could not
/// (currently) accept another frame.
///
/// # Note
///
/// This is mostly useful for when trying to send a frame from outside of an asynchronous
/// context. If in an async context, prefer [`Sender::send_data()`] instead.
pub fn try_send(&mut self, frame: Frame<D>) -> Result<(), Frame<D>> {
let Self {
tx_frame,
tx_error: _,
} = self;
tx_frame
.try_send(frame)
.map_err(tokio::sync::mpsc::error::TrySendError::into_inner)
}
/// Returns the current capacity of the channel.
///
/// The capacity goes down when [`Frame<T>`]s are sent. The capacity goes up when these frames
/// are received by the corresponding [`Channel<D, E>`]. This is distinct from
/// [`max_capacity()`][Self::max_capacity], which always returns the buffer capacity initially
/// specified when [`Channel::new()`][Channel::new] was called.
///
/// # Examples
///
/// ```
/// use bytes::Bytes;
/// use http_body_util::{BodyExt, channel::Channel};
/// use std::convert::Infallible;
///
/// #[tokio::main]
/// async fn main() {
/// let (mut tx, mut body) = Channel::<Bytes, Infallible>::new(4);
/// assert_eq!(tx.capacity(), 4);
///
/// // Sending a value decreases the available capacity.
/// tx.send_data(Bytes::from("Hel")).await.unwrap();
/// assert_eq!(tx.capacity(), 3);
///
/// // Reading a value increases the available capacity.
/// let _ = body.frame().await;
/// assert_eq!(tx.capacity(), 4);
/// }
/// ```
pub fn capacity(&mut self) -> usize {
self.tx_frame.capacity()
}
/// Returns the maximum capacity of the channel.
///
/// This function always returns the buffer capacity initially specified when
/// [`Channel::new()`][Channel::new] was called. This is distinct from
/// [`capacity()`][Self::capacity], which returns the currently available capacity.
///
/// # Examples
///
/// ```
/// use bytes::Bytes;
/// use http_body_util::{BodyExt, channel::Channel};
/// use std::convert::Infallible;
///
/// #[tokio::main]
/// async fn main() {
/// let (mut tx, mut body) = Channel::<Bytes, Infallible>::new(4);
/// assert_eq!(tx.max_capacity(), 4);
///
/// // Sending a value buffers it, but does not affect the maximum capacity reported.
/// tx.send_data(Bytes::from("Hel")).await.unwrap();
/// assert_eq!(tx.max_capacity(), 4);
/// }
/// ```
pub fn max_capacity(&mut self) -> usize {
self.tx_frame.max_capacity()
}
/// Aborts the body in an abnormal fashion.
pub fn abort(self, error: E) {
self.tx_error.send(error).ok();
}
}
impl<D, E: std::fmt::Debug> std::fmt::Debug for Sender<D, E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Sender")
.field("tx_frame", &self.tx_frame)
.field("tx_error", &self.tx_error)
.finish()
}
}
/// The error returned if [`Sender`] fails to send because the receiver is closed.
#[derive(Debug)]
#[non_exhaustive]
pub struct SendError;
impl Display for SendError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "failed to send frame")
}
}
impl std::error::Error for SendError {}
#[cfg(test)]
mod tests {
use bytes::Bytes;
use http::{HeaderName, HeaderValue};
use crate::BodyExt;
use super::*;
#[tokio::test]
async fn empty() {
let (tx, body) = Channel::<Bytes>::new(1024);
drop(tx);
let collected = body.collect().await.unwrap();
assert!(collected.trailers().is_none());
assert!(collected.to_bytes().is_empty());
}
#[tokio::test]
async fn can_send_data() {
let (mut tx, body) = Channel::<Bytes>::new(1024);
tokio::spawn(async move {
tx.send_data(Bytes::from("Hel")).await.unwrap();
tx.send_data(Bytes::from("lo!")).await.unwrap();
});
let collected = body.collect().await.unwrap();
assert!(collected.trailers().is_none());
assert_eq!(collected.to_bytes(), "Hello!");
}
#[tokio::test]
async fn can_send_trailers() {
let (mut tx, body) = Channel::<Bytes>::new(1024);
tokio::spawn(async move {
let mut trailers = HeaderMap::new();
trailers.insert(
HeaderName::from_static("foo"),
HeaderValue::from_static("bar"),
);
tx.send_trailers(trailers).await.unwrap();
});
let collected = body.collect().await.unwrap();
assert_eq!(collected.trailers().unwrap()["foo"], "bar");
assert!(collected.to_bytes().is_empty());
}
#[tokio::test]
async fn can_send_both_data_and_trailers() {
let (mut tx, body) = Channel::<Bytes>::new(1024);
tokio::spawn(async move {
tx.send_data(Bytes::from("Hel")).await.unwrap();
tx.send_data(Bytes::from("lo!")).await.unwrap();
let mut trailers = HeaderMap::new();
trailers.insert(
HeaderName::from_static("foo"),
HeaderValue::from_static("bar"),
);
tx.send_trailers(trailers).await.unwrap();
});
let collected = body.collect().await.unwrap();
assert_eq!(collected.trailers().unwrap()["foo"], "bar");
assert_eq!(collected.to_bytes(), "Hello!");
}
#[tokio::test]
async fn try_send_works() {
let (mut tx, mut body) = Channel::<Bytes>::new(2);
// Send two messages, filling the channel's buffer.
tx.try_send(Frame::data(Bytes::from("one")))
.expect("can send one message");
tx.try_send(Frame::data(Bytes::from("two")))
.expect("can send two messages");
// Sending a value to a full channel should return it back to us.
match tx.try_send(Frame::data(Bytes::from("three"))) {
Err(frame) => assert_eq!(frame.into_data().unwrap(), "three"),
Ok(()) => panic!("synchronously sending a value to a full channel should fail"),
};
// Read the messages out of the body.
assert_eq!(
body.frame()
.await
.expect("yields result")
.expect("yields frame")
.into_data()
.expect("yields data"),
"one"
);
assert_eq!(
body.frame()
.await
.expect("yields result")
.expect("yields frame")
.into_data()
.expect("yields data"),
"two"
);
// Drop the body.
drop(body);
// Sending a value to a closed channel should return it back to us.
match tx.try_send(Frame::data(Bytes::from("closed"))) {
Err(frame) => assert_eq!(frame.into_data().unwrap(), "closed"),
Ok(()) => panic!("synchronously sending a value to a closed channel should fail"),
};
}
/// A stand-in for an error type, for unit tests.
type Error = &'static str;
/// An example error message.
const MSG: Error = "oh no";
#[tokio::test]
async fn aborts_before_trailers() {
let (mut tx, body) = Channel::<Bytes, Error>::new(1024);
tokio::spawn(async move {
tx.send_data(Bytes::from("Hel")).await.unwrap();
tx.send_data(Bytes::from("lo!")).await.unwrap();
tx.abort(MSG);
});
let err = body.collect().await.unwrap_err();
assert_eq!(err, MSG);
}
#[tokio::test]
async fn aborts_after_trailers() {
let (mut tx, body) = Channel::<Bytes, Error>::new(1024);
tokio::spawn(async move {
tx.send_data(Bytes::from("Hel")).await.unwrap();
tx.send_data(Bytes::from("lo!")).await.unwrap();
let mut trailers = HeaderMap::new();
trailers.insert(
HeaderName::from_static("foo"),
HeaderValue::from_static("bar"),
);
tx.send_trailers(trailers).await.unwrap();
tx.abort(MSG);
});
let err = body.collect().await.unwrap_err();
assert_eq!(err, MSG);
}
}

178
vendor/http-body-util/src/collected.rs vendored Normal file
View File

@@ -0,0 +1,178 @@
use std::{
convert::Infallible,
pin::Pin,
task::{Context, Poll},
};
use bytes::{Buf, Bytes};
use http::HeaderMap;
use http_body::{Body, Frame};
use crate::util::BufList;
/// A collected body produced by [`BodyExt::collect`] which collects all the DATA frames
/// and trailers.
///
/// [`BodyExt::collect`]: crate::BodyExt::collect
#[derive(Debug)]
pub struct Collected<B> {
bufs: BufList<B>,
trailers: Option<HeaderMap>,
}
impl<B: Buf> Collected<B> {
/// If there is a trailers frame buffered, returns a reference to it.
///
/// Returns `None` if the body contained no trailers.
pub fn trailers(&self) -> Option<&HeaderMap> {
self.trailers.as_ref()
}
/// Aggregate this buffered into a [`Buf`].
pub fn aggregate(self) -> impl Buf {
self.bufs
}
/// Convert this body into a [`Bytes`].
pub fn to_bytes(mut self) -> Bytes {
self.bufs.copy_to_bytes(self.bufs.remaining())
}
pub(crate) fn push_frame(&mut self, frame: Frame<B>) {
let frame = match frame.into_data() {
Ok(data) => {
// Only push this frame if it has some data in it, to avoid crashing on
// `BufList::push`.
if data.has_remaining() {
self.bufs.push(data);
}
return;
}
Err(frame) => frame,
};
if let Ok(trailers) = frame.into_trailers() {
if let Some(current) = &mut self.trailers {
current.extend(trailers);
} else {
self.trailers = Some(trailers);
}
};
}
}
impl<B: Buf> Body for Collected<B> {
type Data = B;
type Error = Infallible;
fn poll_frame(
mut self: Pin<&mut Self>,
_: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
let frame = if let Some(data) = self.bufs.pop() {
Frame::data(data)
} else if let Some(trailers) = self.trailers.take() {
Frame::trailers(trailers)
} else {
return Poll::Ready(None);
};
Poll::Ready(Some(Ok(frame)))
}
}
impl<B> Default for Collected<B> {
fn default() -> Self {
Self {
bufs: BufList::default(),
trailers: None,
}
}
}
impl<B> Unpin for Collected<B> {}
#[cfg(test)]
mod tests {
use std::convert::TryInto;
use futures_util::stream;
use crate::{BodyExt, Full, StreamBody};
use super::*;
#[tokio::test]
async fn full_body() {
let body = Full::new(&b"hello"[..]);
let buffered = body.collect().await.unwrap();
let mut buf = buffered.to_bytes();
assert_eq!(&buf.copy_to_bytes(buf.remaining())[..], &b"hello"[..]);
}
#[tokio::test]
async fn segmented_body() {
let bufs = [&b"hello"[..], &b"world"[..], &b"!"[..]];
let body = StreamBody::new(stream::iter(bufs.map(Frame::data).map(Ok::<_, Infallible>)));
let buffered = body.collect().await.unwrap();
let mut buf = buffered.to_bytes();
assert_eq!(&buf.copy_to_bytes(buf.remaining())[..], b"helloworld!");
}
#[tokio::test]
async fn delayed_segments() {
let one = stream::once(async { Ok::<_, Infallible>(Frame::data(&b"hello "[..])) });
let two = stream::once(async {
// a yield just so its not ready immediately
tokio::task::yield_now().await;
Ok::<_, Infallible>(Frame::data(&b"world!"[..]))
});
let stream = futures_util::StreamExt::chain(one, two);
let body = StreamBody::new(stream);
let buffered = body.collect().await.unwrap();
let mut buf = buffered.to_bytes();
assert_eq!(&buf.copy_to_bytes(buf.remaining())[..], b"hello world!");
}
#[tokio::test]
async fn trailers() {
let mut trailers = HeaderMap::new();
trailers.insert("this", "a trailer".try_into().unwrap());
let bufs = [
Frame::data(&b"hello"[..]),
Frame::data(&b"world!"[..]),
Frame::trailers(trailers.clone()),
];
let body = StreamBody::new(stream::iter(bufs.map(Ok::<_, Infallible>)));
let buffered = body.collect().await.unwrap();
assert_eq!(&trailers, buffered.trailers().unwrap());
let mut buf = buffered.to_bytes();
assert_eq!(&buf.copy_to_bytes(buf.remaining())[..], b"helloworld!");
}
/// Test for issue [#88](https://github.com/hyperium/http-body/issues/88).
#[tokio::test]
async fn empty_frame() {
let bufs: [&[u8]; 1] = [&[]];
let body = StreamBody::new(stream::iter(bufs.map(Frame::data).map(Ok::<_, Infallible>)));
let buffered = body.collect().await.unwrap();
assert_eq!(buffered.to_bytes().len(), 0);
}
}

View File

@@ -0,0 +1,122 @@
use crate::BodyExt as _;
use bytes::Buf;
use http_body::{Body, Frame, SizeHint};
use std::{
fmt,
pin::Pin,
task::{Context, Poll},
};
/// A boxed [`Body`] trait object.
pub struct BoxBody<D, E> {
inner: Pin<Box<dyn Body<Data = D, Error = E> + Send + Sync + 'static>>,
}
/// A boxed [`Body`] trait object that is !Sync.
pub struct UnsyncBoxBody<D, E> {
inner: Pin<Box<dyn Body<Data = D, Error = E> + Send + 'static>>,
}
impl<D, E> BoxBody<D, E> {
/// Create a new `BoxBody`.
pub fn new<B>(body: B) -> Self
where
B: Body<Data = D, Error = E> + Send + Sync + 'static,
D: Buf,
{
Self {
inner: Box::pin(body),
}
}
}
impl<D, E> fmt::Debug for BoxBody<D, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BoxBody").finish()
}
}
impl<D, E> Body for BoxBody<D, E>
where
D: Buf,
{
type Data = D;
type Error = E;
fn poll_frame(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
self.inner.as_mut().poll_frame(cx)
}
fn is_end_stream(&self) -> bool {
self.inner.is_end_stream()
}
fn size_hint(&self) -> SizeHint {
self.inner.size_hint()
}
}
impl<D, E> Default for BoxBody<D, E>
where
D: Buf + 'static,
{
fn default() -> Self {
BoxBody::new(crate::Empty::new().map_err(|err| match err {}))
}
}
// === UnsyncBoxBody ===
impl<D, E> UnsyncBoxBody<D, E> {
/// Create a new `UnsyncBoxBody`.
pub fn new<B>(body: B) -> Self
where
B: Body<Data = D, Error = E> + Send + 'static,
D: Buf,
{
Self {
inner: Box::pin(body),
}
}
}
impl<D, E> fmt::Debug for UnsyncBoxBody<D, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UnsyncBoxBody").finish()
}
}
impl<D, E> Body for UnsyncBoxBody<D, E>
where
D: Buf,
{
type Data = D;
type Error = E;
fn poll_frame(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
self.inner.as_mut().poll_frame(cx)
}
fn is_end_stream(&self) -> bool {
self.inner.is_end_stream()
}
fn size_hint(&self) -> SizeHint {
self.inner.size_hint()
}
}
impl<D, E> Default for UnsyncBoxBody<D, E>
where
D: Buf + 'static,
{
fn default() -> Self {
UnsyncBoxBody::new(crate::Empty::new().map_err(|err| match err {}))
}
}

View File

@@ -0,0 +1,44 @@
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use futures_core::ready;
use http_body::Body;
use pin_project_lite::pin_project;
pin_project! {
/// Future that resolves into a [`Collected`].
///
/// [`Collected`]: crate::Collected
pub struct Collect<T>
where
T: Body,
T: ?Sized,
{
pub(crate) collected: Option<crate::Collected<T::Data>>,
#[pin]
pub(crate) body: T,
}
}
impl<T: Body + ?Sized> Future for Collect<T> {
type Output = Result<crate::Collected<T::Data>, T::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> std::task::Poll<Self::Output> {
let mut me = self.project();
loop {
let frame = ready!(me.body.as_mut().poll_frame(cx));
let frame = if let Some(frame) = frame {
frame?
} else {
return Poll::Ready(Ok(me.collected.take().expect("polled after complete")));
};
me.collected.as_mut().unwrap().push_frame(frame);
}
}
}

View File

@@ -0,0 +1,18 @@
use http_body::Body;
use core::future::Future;
use core::pin::Pin;
use core::task;
#[must_use = "futures don't do anything unless polled"]
#[derive(Debug)]
/// Future that resolves to the next frame from a [`Body`].
pub struct Frame<'a, T: ?Sized>(pub(crate) &'a mut T);
impl<T: Body + Unpin + ?Sized> Future for Frame<'_, T> {
type Output = Option<Result<http_body::Frame<T::Data>, T::Error>>;
fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
Pin::new(&mut self.0).poll_frame(ctx)
}
}

View File

@@ -0,0 +1,89 @@
use http_body::{Body, Frame, SizeHint};
use pin_project_lite::pin_project;
use std::{
any::type_name,
fmt,
pin::Pin,
task::{Context, Poll},
};
pin_project! {
/// Body returned by the [`map_err`] combinator.
///
/// [`map_err`]: crate::BodyExt::map_err
#[derive(Clone, Copy)]
pub struct MapErr<B, F> {
#[pin]
inner: B,
f: F
}
}
impl<B, F> MapErr<B, F> {
#[inline]
pub(crate) fn new(body: B, f: F) -> Self {
Self { inner: body, f }
}
/// Get a reference to the inner body
pub fn get_ref(&self) -> &B {
&self.inner
}
/// Get a mutable reference to the inner body
pub fn get_mut(&mut self) -> &mut B {
&mut self.inner
}
/// Get a pinned mutable reference to the inner body
pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut B> {
self.project().inner
}
/// Consume `self`, returning the inner body
pub fn into_inner(self) -> B {
self.inner
}
}
impl<B, F, E> Body for MapErr<B, F>
where
B: Body,
F: FnMut(B::Error) -> E,
{
type Data = B::Data;
type Error = E;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
let this = self.project();
match this.inner.poll_frame(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(None) => Poll::Ready(None),
Poll::Ready(Some(Ok(frame))) => Poll::Ready(Some(Ok(frame))),
Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err((this.f)(err)))),
}
}
fn is_end_stream(&self) -> bool {
self.inner.is_end_stream()
}
fn size_hint(&self) -> SizeHint {
self.inner.size_hint()
}
}
impl<B, F> fmt::Debug for MapErr<B, F>
where
B: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("MapErr")
.field("inner", &self.inner)
.field("f", &type_name::<F>())
.finish()
}
}

View File

@@ -0,0 +1,87 @@
use bytes::Buf;
use http_body::{Body, Frame};
use pin_project_lite::pin_project;
use std::{
any::type_name,
fmt,
pin::Pin,
task::{Context, Poll},
};
pin_project! {
/// Body returned by the [`map_frame`] combinator.
///
/// [`map_frame`]: crate::BodyExt::map_frame
#[derive(Clone, Copy)]
pub struct MapFrame<B, F> {
#[pin]
inner: B,
f: F
}
}
impl<B, F> MapFrame<B, F> {
#[inline]
pub(crate) fn new(body: B, f: F) -> Self {
Self { inner: body, f }
}
/// Get a reference to the inner body
pub fn get_ref(&self) -> &B {
&self.inner
}
/// Get a mutable reference to the inner body
pub fn get_mut(&mut self) -> &mut B {
&mut self.inner
}
/// Get a pinned mutable reference to the inner body
pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut B> {
self.project().inner
}
/// Consume `self`, returning the inner body
pub fn into_inner(self) -> B {
self.inner
}
}
impl<B, F, B2> Body for MapFrame<B, F>
where
B: Body,
F: FnMut(Frame<B::Data>) -> Frame<B2>,
B2: Buf,
{
type Data = B2;
type Error = B::Error;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
let this = self.project();
match this.inner.poll_frame(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(None) => Poll::Ready(None),
Poll::Ready(Some(Ok(frame))) => Poll::Ready(Some(Ok((this.f)(frame)))),
Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(err))),
}
}
fn is_end_stream(&self) -> bool {
self.inner.is_end_stream()
}
}
impl<B, F> fmt::Debug for MapFrame<B, F>
where
B: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("MapFrame")
.field("inner", &self.inner)
.field("f", &type_name::<F>())
.finish()
}
}

View File

@@ -0,0 +1,17 @@
//! Combinators for the `Body` trait.
mod box_body;
mod collect;
mod frame;
mod map_err;
mod map_frame;
mod with_trailers;
pub use self::{
box_body::{BoxBody, UnsyncBoxBody},
collect::Collect,
frame::Frame,
map_err::MapErr,
map_frame::MapFrame,
with_trailers::WithTrailers,
};

View File

@@ -0,0 +1,213 @@
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use futures_core::ready;
use http::HeaderMap;
use http_body::{Body, Frame};
use pin_project_lite::pin_project;
pin_project! {
/// Adds trailers to a body.
///
/// See [`BodyExt::with_trailers`] for more details.
pub struct WithTrailers<T, F> {
#[pin]
state: State<T, F>,
}
}
impl<T, F> WithTrailers<T, F> {
pub(crate) fn new(body: T, trailers: F) -> Self {
Self {
state: State::PollBody {
body,
trailers: Some(trailers),
},
}
}
}
pin_project! {
#[project = StateProj]
enum State<T, F> {
PollBody {
#[pin]
body: T,
trailers: Option<F>,
},
PollTrailers {
#[pin]
trailers: F,
prev_trailers: Option<HeaderMap>,
},
Done,
}
}
impl<T, F> Body for WithTrailers<T, F>
where
T: Body,
F: Future<Output = Option<Result<HeaderMap, T::Error>>>,
{
type Data = T::Data;
type Error = T::Error;
fn poll_frame(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
loop {
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
StateProj::PollBody { body, trailers } => match ready!(body.poll_frame(cx)?) {
Some(frame) => match frame.into_trailers() {
Ok(prev_trailers) => {
let trailers = trailers.take().unwrap();
this.state.set(State::PollTrailers {
trailers,
prev_trailers: Some(prev_trailers),
});
}
Err(frame) => {
return Poll::Ready(Some(Ok(frame)));
}
},
None => {
let trailers = trailers.take().unwrap();
this.state.set(State::PollTrailers {
trailers,
prev_trailers: None,
});
}
},
StateProj::PollTrailers {
trailers,
prev_trailers,
} => {
let trailers = ready!(trailers.poll(cx)?);
match (trailers, prev_trailers.take()) {
(None, None) => return Poll::Ready(None),
(None, Some(trailers)) | (Some(trailers), None) => {
this.state.set(State::Done);
return Poll::Ready(Some(Ok(Frame::trailers(trailers))));
}
(Some(new_trailers), Some(mut prev_trailers)) => {
prev_trailers.extend(new_trailers);
this.state.set(State::Done);
return Poll::Ready(Some(Ok(Frame::trailers(prev_trailers))));
}
}
}
StateProj::Done => {
return Poll::Ready(None);
}
}
}
}
#[inline]
fn size_hint(&self) -> http_body::SizeHint {
match &self.state {
State::PollBody { body, .. } => body.size_hint(),
State::PollTrailers { .. } | State::Done => Default::default(),
}
}
}
#[cfg(test)]
mod tests {
use std::convert::Infallible;
use bytes::Bytes;
use http::{HeaderName, HeaderValue};
use crate::{BodyExt, Empty, Full};
#[allow(unused_imports)]
use super::*;
#[tokio::test]
async fn works() {
let mut trailers = HeaderMap::new();
trailers.insert(
HeaderName::from_static("foo"),
HeaderValue::from_static("bar"),
);
let body =
Full::<Bytes>::from("hello").with_trailers(std::future::ready(Some(
Ok::<_, Infallible>(trailers.clone()),
)));
futures_util::pin_mut!(body);
let waker = futures_util::task::noop_waker();
let mut cx = Context::from_waker(&waker);
let data = unwrap_ready(body.as_mut().poll_frame(&mut cx))
.unwrap()
.unwrap()
.into_data()
.unwrap();
assert_eq!(data, "hello");
let body_trailers = unwrap_ready(body.as_mut().poll_frame(&mut cx))
.unwrap()
.unwrap()
.into_trailers()
.unwrap();
assert_eq!(body_trailers, trailers);
assert!(unwrap_ready(body.as_mut().poll_frame(&mut cx)).is_none());
}
#[tokio::test]
async fn merges_trailers() {
let mut trailers_1 = HeaderMap::new();
trailers_1.insert(
HeaderName::from_static("foo"),
HeaderValue::from_static("bar"),
);
let mut trailers_2 = HeaderMap::new();
trailers_2.insert(
HeaderName::from_static("baz"),
HeaderValue::from_static("qux"),
);
let body = Empty::<Bytes>::new()
.with_trailers(std::future::ready(Some(Ok::<_, Infallible>(
trailers_1.clone(),
))))
.with_trailers(std::future::ready(Some(Ok::<_, Infallible>(
trailers_2.clone(),
))));
futures_util::pin_mut!(body);
let waker = futures_util::task::noop_waker();
let mut cx = Context::from_waker(&waker);
let body_trailers = unwrap_ready(body.as_mut().poll_frame(&mut cx))
.unwrap()
.unwrap()
.into_trailers()
.unwrap();
let mut all_trailers = HeaderMap::new();
all_trailers.extend(trailers_1);
all_trailers.extend(trailers_2);
assert_eq!(body_trailers, all_trailers);
assert!(unwrap_ready(body.as_mut().poll_frame(&mut cx)).is_none());
}
fn unwrap_ready<T>(poll: Poll<T>) -> T {
match poll {
Poll::Ready(t) => t,
Poll::Pending => panic!("pending"),
}
}
}

186
vendor/http-body-util/src/either.rs vendored Normal file
View File

@@ -0,0 +1,186 @@
use std::error::Error;
use std::fmt::Debug;
use std::pin::Pin;
use std::task::{Context, Poll};
use bytes::Buf;
use http_body::{Body, Frame, SizeHint};
use proj::EitherProj;
/// Sum type with two cases: [`Left`] and [`Right`], used if a body can be one of
/// two distinct types.
///
/// [`Left`]: Either::Left
/// [`Right`]: Either::Right
#[derive(Debug, Clone, Copy)]
pub enum Either<L, R> {
/// A value of type `L`
Left(L),
/// A value of type `R`
Right(R),
}
impl<L, R> Either<L, R> {
/// This function is part of the generated code from `pin-project-lite`,
/// for a more in depth explanation and the rest of the generated code refer
/// to the [`proj`] module.
pub(crate) fn project(self: Pin<&mut Self>) -> EitherProj<L, R> {
unsafe {
match self.get_unchecked_mut() {
Self::Left(left) => EitherProj::Left(Pin::new_unchecked(left)),
Self::Right(right) => EitherProj::Right(Pin::new_unchecked(right)),
}
}
}
}
impl<L> Either<L, L> {
/// Convert [`Either`] into the inner type, if both `Left` and `Right` are
/// of the same type.
pub fn into_inner(self) -> L {
match self {
Either::Left(left) => left,
Either::Right(right) => right,
}
}
}
impl<L, R, Data> Body for Either<L, R>
where
L: Body<Data = Data>,
R: Body<Data = Data>,
L::Error: Into<Box<dyn Error + Send + Sync>>,
R::Error: Into<Box<dyn Error + Send + Sync>>,
Data: Buf,
{
type Data = Data;
type Error = Box<dyn Error + Send + Sync>;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
match self.project() {
EitherProj::Left(left) => left
.poll_frame(cx)
.map(|poll| poll.map(|opt| opt.map_err(Into::into))),
EitherProj::Right(right) => right
.poll_frame(cx)
.map(|poll| poll.map(|opt| opt.map_err(Into::into))),
}
}
fn is_end_stream(&self) -> bool {
match self {
Either::Left(left) => left.is_end_stream(),
Either::Right(right) => right.is_end_stream(),
}
}
fn size_hint(&self) -> SizeHint {
match self {
Either::Left(left) => left.size_hint(),
Either::Right(right) => right.size_hint(),
}
}
}
pub(crate) mod proj {
//! This code is the (cleaned output) generated by [pin-project-lite], as it
//! does not support tuple variants.
//!
//! This is the altered expansion from the following snippet, expanded by
//! `cargo-expand`:
//!
//! ```rust
//! use pin_project_lite::pin_project;
//!
//! pin_project! {
//! #[project = EitherProj]
//! pub enum Either<L, R> {
//! Left {#[pin] left: L},
//! Right {#[pin] right: R}
//! }
//! }
//! ```
//!
//! [pin-project-lite]: https://docs.rs/pin-project-lite/latest/pin_project_lite/
use std::marker::PhantomData;
use std::pin::Pin;
use super::Either;
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(unknown_lints)]
#[allow(clippy::mut_mut)]
#[allow(clippy::redundant_pub_crate)]
#[allow(clippy::ref_option_ref)]
#[allow(clippy::type_repetition_in_bounds)]
pub(crate) enum EitherProj<'__pin, L, R>
where
Either<L, R>: '__pin,
{
Left(Pin<&'__pin mut L>),
Right(Pin<&'__pin mut R>),
}
#[allow(single_use_lifetimes)]
#[allow(unknown_lints)]
#[allow(clippy::used_underscore_binding)]
#[allow(missing_debug_implementations)]
const _: () = {
#[allow(non_snake_case)]
pub struct __Origin<'__pin, L, R> {
__dummy_lifetime: PhantomData<&'__pin ()>,
_Left: L,
_Right: R,
}
impl<'__pin, L, R> Unpin for Either<L, R> where __Origin<'__pin, L, R>: Unpin {}
trait MustNotImplDrop {}
#[allow(drop_bounds)]
impl<T: Drop> MustNotImplDrop for T {}
impl<L, R> MustNotImplDrop for Either<L, R> {}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{BodyExt, Empty, Full};
#[tokio::test]
async fn data_left() {
let full = Full::new(&b"hello"[..]);
let mut value: Either<_, Empty<&[u8]>> = Either::Left(full);
assert_eq!(value.size_hint().exact(), Some(b"hello".len() as u64));
assert_eq!(
value.frame().await.unwrap().unwrap().into_data().unwrap(),
&b"hello"[..]
);
assert!(value.frame().await.is_none());
}
#[tokio::test]
async fn data_right() {
let full = Full::new(&b"hello!"[..]);
let mut value: Either<Empty<&[u8]>, _> = Either::Right(full);
assert_eq!(value.size_hint().exact(), Some(b"hello!".len() as u64));
assert_eq!(
value.frame().await.unwrap().unwrap().into_data().unwrap(),
&b"hello!"[..]
);
assert!(value.frame().await.is_none());
}
#[test]
fn into_inner() {
let a = Either::<i32, i32>::Left(2);
assert_eq!(a.into_inner(), 2)
}
}

64
vendor/http-body-util/src/empty.rs vendored Normal file
View File

@@ -0,0 +1,64 @@
use bytes::Buf;
use http_body::{Body, Frame, SizeHint};
use std::{
convert::Infallible,
fmt,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
/// A body that is always empty.
pub struct Empty<D> {
_marker: PhantomData<fn() -> D>,
}
impl<D> Empty<D> {
/// Create a new `Empty`.
pub const fn new() -> Self {
Self {
_marker: PhantomData,
}
}
}
impl<D: Buf> Body for Empty<D> {
type Data = D;
type Error = Infallible;
#[inline]
fn poll_frame(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
Poll::Ready(None)
}
fn is_end_stream(&self) -> bool {
true
}
fn size_hint(&self) -> SizeHint {
SizeHint::with_exact(0)
}
}
impl<D> fmt::Debug for Empty<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Empty").finish()
}
}
impl<D> Default for Empty<D> {
fn default() -> Self {
Self::new()
}
}
impl<D> Clone for Empty<D> {
fn clone(&self) -> Self {
*self
}
}
impl<D> Copy for Empty<D> {}

147
vendor/http-body-util/src/full.rs vendored Normal file
View File

@@ -0,0 +1,147 @@
use bytes::{Buf, Bytes};
use http_body::{Body, Frame, SizeHint};
use pin_project_lite::pin_project;
use std::borrow::Cow;
use std::convert::{Infallible, TryFrom};
use std::pin::Pin;
use std::task::{Context, Poll};
pin_project! {
/// A body that consists of a single chunk.
#[derive(Clone, Copy, Debug)]
pub struct Full<D> {
data: Option<D>,
}
}
impl<D> Full<D>
where
D: Buf,
{
/// Create a new `Full`.
pub fn new(data: D) -> Self {
let data = if data.has_remaining() {
Some(data)
} else {
None
};
Full { data }
}
}
impl<D> Body for Full<D>
where
D: Buf,
{
type Data = D;
type Error = Infallible;
fn poll_frame(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<D>, Self::Error>>> {
Poll::Ready(self.data.take().map(|d| Ok(Frame::data(d))))
}
fn is_end_stream(&self) -> bool {
self.data.is_none()
}
fn size_hint(&self) -> SizeHint {
self.data
.as_ref()
.map(|data| SizeHint::with_exact(u64::try_from(data.remaining()).unwrap()))
.unwrap_or_else(|| SizeHint::with_exact(0))
}
}
impl<D> Default for Full<D>
where
D: Buf,
{
/// Create an empty `Full`.
fn default() -> Self {
Full { data: None }
}
}
impl<D> From<Bytes> for Full<D>
where
D: Buf + From<Bytes>,
{
fn from(bytes: Bytes) -> Self {
Full::new(D::from(bytes))
}
}
impl<D> From<Vec<u8>> for Full<D>
where
D: Buf + From<Vec<u8>>,
{
fn from(vec: Vec<u8>) -> Self {
Full::new(D::from(vec))
}
}
impl<D> From<&'static [u8]> for Full<D>
where
D: Buf + From<&'static [u8]>,
{
fn from(slice: &'static [u8]) -> Self {
Full::new(D::from(slice))
}
}
impl<D, B> From<Cow<'static, B>> for Full<D>
where
D: Buf + From<&'static B> + From<B::Owned>,
B: ToOwned + ?Sized,
{
fn from(cow: Cow<'static, B>) -> Self {
match cow {
Cow::Borrowed(b) => Full::new(D::from(b)),
Cow::Owned(o) => Full::new(D::from(o)),
}
}
}
impl<D> From<String> for Full<D>
where
D: Buf + From<String>,
{
fn from(s: String) -> Self {
Full::new(D::from(s))
}
}
impl<D> From<&'static str> for Full<D>
where
D: Buf + From<&'static str>,
{
fn from(slice: &'static str) -> Self {
Full::new(D::from(slice))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::BodyExt;
#[tokio::test]
async fn full_returns_some() {
let mut full = Full::new(&b"hello"[..]);
assert_eq!(full.size_hint().exact(), Some(b"hello".len() as u64));
assert_eq!(
full.frame().await.unwrap().unwrap().into_data().unwrap(),
&b"hello"[..]
);
assert!(full.frame().await.is_none());
}
#[tokio::test]
async fn empty_full_returns_none() {
assert!(Full::<&[u8]>::default().frame().await.is_none());
assert!(Full::new(&b""[..]).frame().await.is_none());
}
}

147
vendor/http-body-util/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,147 @@
#![deny(missing_debug_implementations, missing_docs, unreachable_pub)]
#![cfg_attr(test, deny(warnings))]
//! Utilities for [`http_body::Body`].
//!
//! [`BodyExt`] adds extensions to the common trait.
//!
//! [`Empty`] and [`Full`] provide simple implementations.
mod collected;
pub mod combinators;
mod either;
mod empty;
mod full;
mod limited;
mod stream;
#[cfg(feature = "channel")]
pub mod channel;
mod util;
use self::combinators::{BoxBody, MapErr, MapFrame, UnsyncBoxBody};
pub use self::collected::Collected;
pub use self::either::Either;
pub use self::empty::Empty;
pub use self::full::Full;
pub use self::limited::{LengthLimitError, Limited};
pub use self::stream::{BodyDataStream, BodyStream, StreamBody};
#[cfg(feature = "channel")]
pub use self::channel::Channel;
/// An extension trait for [`http_body::Body`] adding various combinators and adapters
pub trait BodyExt: http_body::Body {
/// Returns a future that resolves to the next [`Frame`], if any.
///
/// [`Frame`]: combinators::Frame
fn frame(&mut self) -> combinators::Frame<'_, Self>
where
Self: Unpin,
{
combinators::Frame(self)
}
/// Maps this body's frame to a different kind.
fn map_frame<F, B>(self, f: F) -> MapFrame<Self, F>
where
Self: Sized,
F: FnMut(http_body::Frame<Self::Data>) -> http_body::Frame<B>,
B: bytes::Buf,
{
MapFrame::new(self, f)
}
/// Maps this body's error value to a different value.
fn map_err<F, E>(self, f: F) -> MapErr<Self, F>
where
Self: Sized,
F: FnMut(Self::Error) -> E,
{
MapErr::new(self, f)
}
/// Turn this body into a boxed trait object.
fn boxed(self) -> BoxBody<Self::Data, Self::Error>
where
Self: Sized + Send + Sync + 'static,
{
BoxBody::new(self)
}
/// Turn this body into a boxed trait object that is !Sync.
fn boxed_unsync(self) -> UnsyncBoxBody<Self::Data, Self::Error>
where
Self: Sized + Send + 'static,
{
UnsyncBoxBody::new(self)
}
/// Turn this body into [`Collected`] body which will collect all the DATA frames
/// and trailers.
fn collect(self) -> combinators::Collect<Self>
where
Self: Sized,
{
combinators::Collect {
body: self,
collected: Some(crate::Collected::default()),
}
}
/// Add trailers to the body.
///
/// The trailers will be sent when all previous frames have been sent and the `trailers` future
/// resolves.
///
/// # Example
///
/// ```
/// use http::HeaderMap;
/// use http_body_util::{Full, BodyExt};
/// use bytes::Bytes;
///
/// # #[tokio::main]
/// async fn main() {
/// let (tx, rx) = tokio::sync::oneshot::channel::<HeaderMap>();
///
/// let body = Full::<Bytes>::from("Hello, World!")
/// // add trailers via a future
/// .with_trailers(async move {
/// match rx.await {
/// Ok(trailers) => Some(Ok(trailers)),
/// Err(_err) => None,
/// }
/// });
///
/// // compute the trailers in the background
/// tokio::spawn(async move {
/// let _ = tx.send(compute_trailers().await);
/// });
///
/// async fn compute_trailers() -> HeaderMap {
/// // ...
/// # unimplemented!()
/// }
/// # }
/// ```
fn with_trailers<F>(self, trailers: F) -> combinators::WithTrailers<Self, F>
where
Self: Sized,
F: std::future::Future<Output = Option<Result<http::HeaderMap, Self::Error>>>,
{
combinators::WithTrailers::new(self, trailers)
}
/// Turn this body into [`BodyDataStream`].
fn into_data_stream(self) -> BodyDataStream<Self>
where
Self: Sized,
{
BodyDataStream::new(self)
}
}
impl<T: ?Sized> BodyExt for T where T: http_body::Body {}

265
vendor/http-body-util/src/limited.rs vendored Normal file
View File

@@ -0,0 +1,265 @@
use bytes::Buf;
use http_body::{Body, Frame, SizeHint};
use pin_project_lite::pin_project;
use std::error::Error;
use std::fmt;
use std::pin::Pin;
use std::task::{Context, Poll};
pin_project! {
/// A length limited body.
///
/// This body will return an error if more than the configured number
/// of bytes are returned on polling the wrapped body.
#[derive(Clone, Copy, Debug)]
pub struct Limited<B> {
remaining: usize,
#[pin]
inner: B,
}
}
impl<B> Limited<B> {
/// Create a new `Limited`.
pub fn new(inner: B, limit: usize) -> Self {
Self {
remaining: limit,
inner,
}
}
}
impl<B> Body for Limited<B>
where
B: Body,
B::Error: Into<Box<dyn Error + Send + Sync>>,
{
type Data = B::Data;
type Error = Box<dyn Error + Send + Sync>;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
let this = self.project();
let res = match this.inner.poll_frame(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(None) => None,
Poll::Ready(Some(Ok(frame))) => {
if let Some(data) = frame.data_ref() {
if data.remaining() > *this.remaining {
*this.remaining = 0;
Some(Err(LengthLimitError.into()))
} else {
*this.remaining -= data.remaining();
Some(Ok(frame))
}
} else {
Some(Ok(frame))
}
}
Poll::Ready(Some(Err(err))) => Some(Err(err.into())),
};
Poll::Ready(res)
}
fn is_end_stream(&self) -> bool {
self.inner.is_end_stream()
}
fn size_hint(&self) -> SizeHint {
use std::convert::TryFrom;
match u64::try_from(self.remaining) {
Ok(n) => {
let mut hint = self.inner.size_hint();
if hint.lower() >= n {
hint.set_exact(n)
} else if let Some(max) = hint.upper() {
hint.set_upper(n.min(max))
} else {
hint.set_upper(n)
}
hint
}
Err(_) => self.inner.size_hint(),
}
}
}
/// An error returned when body length exceeds the configured limit.
#[derive(Debug)]
#[non_exhaustive]
pub struct LengthLimitError;
impl fmt::Display for LengthLimitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("length limit exceeded")
}
}
impl Error for LengthLimitError {}
#[cfg(test)]
mod tests {
use super::*;
use crate::{BodyExt, Full, StreamBody};
use bytes::Bytes;
use std::convert::Infallible;
#[tokio::test]
async fn read_for_body_under_limit_returns_data() {
const DATA: &[u8] = b"testing";
let inner = Full::new(Bytes::from(DATA));
let body = &mut Limited::new(inner, 8);
let mut hint = SizeHint::new();
hint.set_upper(7);
assert_eq!(body.size_hint().upper(), hint.upper());
let data = body.frame().await.unwrap().unwrap().into_data().unwrap();
assert_eq!(data, DATA);
hint.set_upper(0);
assert_eq!(body.size_hint().upper(), hint.upper());
assert!(body.frame().await.is_none());
}
#[tokio::test]
async fn read_for_body_over_limit_returns_error() {
const DATA: &[u8] = b"testing a string that is too long";
let inner = Full::new(Bytes::from(DATA));
let body = &mut Limited::new(inner, 8);
let mut hint = SizeHint::new();
hint.set_upper(8);
assert_eq!(body.size_hint().upper(), hint.upper());
let error = body.frame().await.unwrap().unwrap_err();
assert!(matches!(error.downcast_ref(), Some(LengthLimitError)));
}
fn body_from_iter<I>(into_iter: I) -> impl Body<Data = Bytes, Error = Infallible>
where
I: IntoIterator,
I::Item: Into<Bytes> + 'static,
I::IntoIter: Send + 'static,
{
let iter = into_iter
.into_iter()
.map(|it| Frame::data(it.into()))
.map(Ok::<_, Infallible>);
StreamBody::new(futures_util::stream::iter(iter))
}
#[tokio::test]
async fn read_for_chunked_body_around_limit_returns_first_chunk_but_returns_error_on_over_limit_chunk(
) {
const DATA: [&[u8]; 2] = [b"testing ", b"a string that is too long"];
let inner = body_from_iter(DATA);
let body = &mut Limited::new(inner, 8);
let mut hint = SizeHint::new();
hint.set_upper(8);
assert_eq!(body.size_hint().upper(), hint.upper());
let data = body.frame().await.unwrap().unwrap().into_data().unwrap();
assert_eq!(data, DATA[0]);
hint.set_upper(0);
assert_eq!(body.size_hint().upper(), hint.upper());
let error = body.frame().await.unwrap().unwrap_err();
assert!(matches!(error.downcast_ref(), Some(LengthLimitError)));
}
#[tokio::test]
async fn read_for_chunked_body_over_limit_on_first_chunk_returns_error() {
const DATA: [&[u8]; 2] = [b"testing a string", b" that is too long"];
let inner = body_from_iter(DATA);
let body = &mut Limited::new(inner, 8);
let mut hint = SizeHint::new();
hint.set_upper(8);
assert_eq!(body.size_hint().upper(), hint.upper());
let error = body.frame().await.unwrap().unwrap_err();
assert!(matches!(error.downcast_ref(), Some(LengthLimitError)));
}
#[tokio::test]
async fn read_for_chunked_body_under_limit_is_okay() {
const DATA: [&[u8]; 2] = [b"test", b"ing!"];
let inner = body_from_iter(DATA);
let body = &mut Limited::new(inner, 8);
let mut hint = SizeHint::new();
hint.set_upper(8);
assert_eq!(body.size_hint().upper(), hint.upper());
let data = body.frame().await.unwrap().unwrap().into_data().unwrap();
assert_eq!(data, DATA[0]);
hint.set_upper(4);
assert_eq!(body.size_hint().upper(), hint.upper());
let data = body.frame().await.unwrap().unwrap().into_data().unwrap();
assert_eq!(data, DATA[1]);
hint.set_upper(0);
assert_eq!(body.size_hint().upper(), hint.upper());
assert!(body.frame().await.is_none());
}
struct SomeTrailers;
impl Body for SomeTrailers {
type Data = Bytes;
type Error = Infallible;
fn poll_frame(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
Poll::Ready(Some(Ok(Frame::trailers(http::HeaderMap::new()))))
}
}
#[tokio::test]
async fn read_for_trailers_propagates_inner_trailers() {
let body = &mut Limited::new(SomeTrailers, 8);
let frame = body.frame().await.unwrap().unwrap();
assert!(frame.is_trailers());
}
#[derive(Debug)]
struct ErrorBodyError;
impl fmt::Display for ErrorBodyError {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}
impl Error for ErrorBodyError {}
struct ErrorBody;
impl Body for ErrorBody {
type Data = &'static [u8];
type Error = ErrorBodyError;
fn poll_frame(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
Poll::Ready(Some(Err(ErrorBodyError)))
}
}
#[tokio::test]
async fn read_for_body_returning_error_propagates_error() {
let body = &mut Limited::new(ErrorBody, 8);
let error = body.frame().await.unwrap().unwrap_err();
assert!(matches!(error.downcast_ref(), Some(ErrorBodyError)));
}
}

240
vendor/http-body-util/src/stream.rs vendored Normal file
View File

@@ -0,0 +1,240 @@
use bytes::Buf;
use futures_core::{ready, stream::Stream};
use http_body::{Body, Frame};
use pin_project_lite::pin_project;
use std::{
pin::Pin,
task::{Context, Poll},
};
pin_project! {
/// A body created from a [`Stream`].
#[derive(Clone, Copy, Debug)]
pub struct StreamBody<S> {
#[pin]
stream: S,
}
}
impl<S> StreamBody<S> {
/// Create a new `StreamBody`.
pub fn new(stream: S) -> Self {
Self { stream }
}
}
impl<S, D, E> Body for StreamBody<S>
where
S: Stream<Item = Result<Frame<D>, E>>,
D: Buf,
{
type Data = D;
type Error = E;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
match self.project().stream.poll_next(cx) {
Poll::Ready(Some(result)) => Poll::Ready(Some(result)),
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
}
}
}
impl<S: Stream> Stream for StreamBody<S> {
type Item = S::Item;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project().stream.poll_next(cx)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.stream.size_hint()
}
}
pin_project! {
/// A stream created from a [`Body`].
#[derive(Clone, Copy, Debug)]
pub struct BodyStream<B> {
#[pin]
body: B,
}
}
impl<B> BodyStream<B> {
/// Create a new `BodyStream`.
pub fn new(body: B) -> Self {
Self { body }
}
}
impl<B> Body for BodyStream<B>
where
B: Body,
{
type Data = B::Data;
type Error = B::Error;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
self.project().body.poll_frame(cx)
}
}
impl<B> Stream for BodyStream<B>
where
B: Body,
{
type Item = Result<Frame<B::Data>, B::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
match self.project().body.poll_frame(cx) {
Poll::Ready(Some(frame)) => Poll::Ready(Some(frame)),
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
}
}
}
pin_project! {
/// A data stream created from a [`Body`].
#[derive(Clone, Copy, Debug)]
pub struct BodyDataStream<B> {
#[pin]
body: B,
}
}
impl<B> BodyDataStream<B> {
/// Create a new `BodyDataStream`
pub fn new(body: B) -> Self {
Self { body }
}
}
impl<B> Stream for BodyDataStream<B>
where
B: Body,
{
type Item = Result<B::Data, B::Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
loop {
return match ready!(self.as_mut().project().body.poll_frame(cx)) {
Some(Ok(frame)) => match frame.into_data() {
Ok(bytes) => Poll::Ready(Some(Ok(bytes))),
Err(_) => continue,
},
Some(Err(err)) => Poll::Ready(Some(Err(err))),
None => Poll::Ready(None),
};
}
}
}
#[cfg(test)]
mod tests {
use crate::{BodyExt, BodyStream, StreamBody};
use bytes::Bytes;
use futures_util::StreamExt;
use http_body::Frame;
use std::convert::Infallible;
#[tokio::test]
async fn body_from_stream() {
let chunks: Vec<Result<_, Infallible>> = vec![
Ok(Frame::data(Bytes::from(vec![1]))),
Ok(Frame::data(Bytes::from(vec![2]))),
Ok(Frame::data(Bytes::from(vec![3]))),
];
let stream = futures_util::stream::iter(chunks);
let mut body = StreamBody::new(stream);
assert_eq!(
body.frame()
.await
.unwrap()
.unwrap()
.into_data()
.unwrap()
.as_ref(),
[1]
);
assert_eq!(
body.frame()
.await
.unwrap()
.unwrap()
.into_data()
.unwrap()
.as_ref(),
[2]
);
assert_eq!(
body.frame()
.await
.unwrap()
.unwrap()
.into_data()
.unwrap()
.as_ref(),
[3]
);
assert!(body.frame().await.is_none());
}
#[tokio::test]
async fn stream_from_body() {
let chunks: Vec<Result<_, Infallible>> = vec![
Ok(Frame::data(Bytes::from(vec![1]))),
Ok(Frame::data(Bytes::from(vec![2]))),
Ok(Frame::data(Bytes::from(vec![3]))),
];
let stream = futures_util::stream::iter(chunks);
let body = StreamBody::new(stream);
let mut stream = BodyStream::new(body);
assert_eq!(
stream
.next()
.await
.unwrap()
.unwrap()
.into_data()
.unwrap()
.as_ref(),
[1]
);
assert_eq!(
stream
.next()
.await
.unwrap()
.unwrap()
.into_data()
.unwrap()
.as_ref(),
[2]
);
assert_eq!(
stream
.next()
.await
.unwrap()
.unwrap()
.into_data()
.unwrap()
.as_ref(),
[3]
);
assert!(stream.next().await.is_none());
}
}

164
vendor/http-body-util/src/util.rs vendored Normal file
View File

@@ -0,0 +1,164 @@
use std::collections::VecDeque;
use std::io::IoSlice;
use bytes::{Buf, BufMut, Bytes, BytesMut};
#[derive(Debug)]
pub(crate) struct BufList<T> {
bufs: VecDeque<T>,
}
impl<T: Buf> BufList<T> {
#[inline]
pub(crate) fn push(&mut self, buf: T) {
debug_assert!(buf.has_remaining());
self.bufs.push_back(buf);
}
#[inline]
pub(crate) fn pop(&mut self) -> Option<T> {
self.bufs.pop_front()
}
}
impl<T: Buf> Buf for BufList<T> {
#[inline]
fn remaining(&self) -> usize {
self.bufs.iter().map(|buf| buf.remaining()).sum()
}
#[inline]
fn has_remaining(&self) -> bool {
self.bufs.iter().any(|buf| buf.has_remaining())
}
#[inline]
fn chunk(&self) -> &[u8] {
self.bufs.front().map(Buf::chunk).unwrap_or_default()
}
#[inline]
fn advance(&mut self, mut cnt: usize) {
while cnt > 0 {
{
let front = &mut self.bufs[0];
let rem = front.remaining();
if rem > cnt {
front.advance(cnt);
return;
} else {
front.advance(rem);
cnt -= rem;
}
}
self.bufs.pop_front();
}
}
#[inline]
fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize {
if dst.is_empty() {
return 0;
}
let mut vecs = 0;
for buf in &self.bufs {
vecs += buf.chunks_vectored(&mut dst[vecs..]);
if vecs == dst.len() {
break;
}
}
vecs
}
#[inline]
fn copy_to_bytes(&mut self, len: usize) -> Bytes {
// Our inner buffer may have an optimized version of copy_to_bytes, and if the whole
// request can be fulfilled by the front buffer, we can take advantage.
match self.bufs.front_mut() {
Some(front) if front.remaining() == len => {
let b = front.copy_to_bytes(len);
self.bufs.pop_front();
b
}
Some(front) if front.remaining() > len => front.copy_to_bytes(len),
_ => {
let rem = self.remaining();
assert!(len <= rem, "`len` greater than remaining");
let mut bm = BytesMut::with_capacity(len);
if rem == len {
// .take() costs a lot more, so skip it if we don't need it
bm.put(self);
} else {
bm.put(self.take(len));
}
bm.freeze()
}
}
}
}
impl<T> Default for BufList<T> {
fn default() -> Self {
BufList {
bufs: VecDeque::new(),
}
}
}
#[cfg(test)]
mod tests {
use std::ptr;
use super::*;
fn hello_world_buf() -> BufList<Bytes> {
BufList {
bufs: vec![Bytes::from("Hello"), Bytes::from(" "), Bytes::from("World")].into(),
}
}
#[test]
fn to_bytes_shorter() {
let mut bufs = hello_world_buf();
let old_ptr = bufs.chunk().as_ptr();
let start = bufs.copy_to_bytes(4);
assert_eq!(start, "Hell");
assert!(ptr::eq(old_ptr, start.as_ptr()));
assert_eq!(bufs.chunk(), b"o");
assert!(ptr::eq(old_ptr.wrapping_add(4), bufs.chunk().as_ptr()));
assert_eq!(bufs.remaining(), 7);
}
#[test]
fn to_bytes_eq() {
let mut bufs = hello_world_buf();
let old_ptr = bufs.chunk().as_ptr();
let start = bufs.copy_to_bytes(5);
assert_eq!(start, "Hello");
assert!(ptr::eq(old_ptr, start.as_ptr()));
assert_eq!(bufs.chunk(), b" ");
assert_eq!(bufs.remaining(), 6);
}
#[test]
fn to_bytes_longer() {
let mut bufs = hello_world_buf();
let start = bufs.copy_to_bytes(7);
assert_eq!(start, "Hello W");
assert_eq!(bufs.remaining(), 4);
}
#[test]
fn one_long_buf_to_bytes() {
let mut buf = BufList::default();
buf.push(b"Hello World" as &[_]);
assert_eq!(buf.copy_to_bytes(5), "Hello");
assert_eq!(buf.chunk(), b" World");
}
#[test]
#[should_panic(expected = "`len` greater than remaining")]
fn buf_to_bytes_too_many() {
hello_world_buf().copy_to_bytes(42);
}
}