Import Upstream version 0.4.5
This commit is contained in:
commit
59a15aae5d
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"git": {
|
||||
"sha1": "6f722a14f7c6488cd53dcc4126a84e6ee201964f"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
style:
|
||||
name: Check Style
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Install Rustfmt
|
||||
run: rustup component add rustfmt
|
||||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
test:
|
||||
name: Test
|
||||
needs: [style]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Install Rust
|
||||
run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
|
||||
- name: Run tests
|
||||
run: cargo test
|
|
@ -0,0 +1,2 @@
|
|||
target
|
||||
Cargo.lock
|
|
@ -0,0 +1,62 @@
|
|||
# Unreleased
|
||||
|
||||
None.
|
||||
|
||||
# 0.4.5 (May 20, 2022)
|
||||
|
||||
- Add `String` impl for `Body`.
|
||||
- Add `Limited` body implementation.
|
||||
|
||||
# 0.4.4 (October 22, 2021)
|
||||
|
||||
- Add `UnsyncBoxBody` and `Body::boxed_unsync`.
|
||||
|
||||
# 0.4.3 (August 8, 2021)
|
||||
|
||||
- Implement `Default` for `BoxBody`.
|
||||
|
||||
# 0.4.2 (May 8, 2021)
|
||||
|
||||
- Correctly override `Body::size_hint` and `Body::is_end_stream` for `Empty`.
|
||||
- Add `Full` which is a body that consists of a single chunk.
|
||||
|
||||
# 0.4.1 (March 18, 2021)
|
||||
|
||||
- Add combinators to `Body`:
|
||||
- `map_data`: Change the `Data` chunks produced by the body.
|
||||
- `map_err`: Change the `Error`s produced by the body.
|
||||
- `boxed`: Convert the `Body` into a boxed trait object.
|
||||
- Add `Empty`.
|
||||
|
||||
# 0.4.0 (December 23, 2020)
|
||||
|
||||
- Update `bytes` to v1.0.
|
||||
|
||||
# 0.3.1 (December 13, 2019)
|
||||
|
||||
- Implement `Body` for `http::Request<impl Body>` and `http::Response<impl Body>`.
|
||||
|
||||
# 0.3.0 (December 4, 2019)
|
||||
|
||||
- Rename `next` combinator to `data`.
|
||||
|
||||
# 0.2.0 (December 3, 2019)
|
||||
|
||||
- Update `http` to v0.2.
|
||||
- Update `bytes` to v0.5.
|
||||
|
||||
# 0.2.0-alpha.3 (October 1, 2019)
|
||||
|
||||
- Fix `Body` to be object-safe.
|
||||
|
||||
# 0.2.0-alpha.2 (October 1, 2019)
|
||||
|
||||
- Add `next` and `trailers` combinator methods.
|
||||
|
||||
# 0.2.0-alpha.1 (August 20, 2019)
|
||||
|
||||
- Update to use `Pin` in `poll_data` and `poll_trailers`.
|
||||
|
||||
# 0.1.0 (May 7, 2019)
|
||||
|
||||
- Initial release
|
|
@ -0,0 +1,45 @@
|
|||
# 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"
|
||||
name = "http-body"
|
||||
version = "0.4.5"
|
||||
authors = [
|
||||
"Carl Lerche <me@carllerche.com>",
|
||||
"Lucio Franco <luciofranco14@gmail.com>",
|
||||
"Sean McArthur <sean@seanmonstar.com>",
|
||||
]
|
||||
description = """
|
||||
Trait representing an asynchronous, streaming, HTTP request or response body.
|
||||
"""
|
||||
documentation = "https://docs.rs/http-body"
|
||||
readme = "README.md"
|
||||
keywords = ["http"]
|
||||
categories = ["web-programming"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/hyperium/http-body"
|
||||
|
||||
[dependencies.bytes]
|
||||
version = "1"
|
||||
|
||||
[dependencies.http]
|
||||
version = "0.2"
|
||||
|
||||
[dependencies.pin-project-lite]
|
||||
version = "0.2"
|
||||
|
||||
[dev-dependencies.tokio]
|
||||
version = "1"
|
||||
features = [
|
||||
"macros",
|
||||
"rt",
|
||||
]
|
|
@ -0,0 +1,34 @@
|
|||
[package]
|
||||
name = "http-body"
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "vx.y.z" git tag.
|
||||
version = "0.4.5"
|
||||
authors = [
|
||||
"Carl Lerche <me@carllerche.com>",
|
||||
"Lucio Franco <luciofranco14@gmail.com>",
|
||||
"Sean McArthur <sean@seanmonstar.com>",
|
||||
]
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/http-body"
|
||||
repository = "https://github.com/hyperium/http-body"
|
||||
license = "MIT"
|
||||
description = """
|
||||
Trait representing an asynchronous, streaming, HTTP request or response body.
|
||||
"""
|
||||
keywords = ["http"]
|
||||
categories = ["web-programming"]
|
||||
|
||||
[dependencies]
|
||||
bytes = "1"
|
||||
http = "0.2"
|
||||
pin-project-lite = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["macros", "rt"] }
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2019 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.
|
|
@ -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.
|
|
@ -0,0 +1 @@
|
|||
{"package":"d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1","files":{}}
|
|
@ -0,0 +1,23 @@
|
|||
rust-http-body (0.4.5-1) unstable; urgency=medium
|
||||
|
||||
* Team upload.
|
||||
* Package http-body 0.4.5 from crates.io using debcargo 2.5.0
|
||||
|
||||
[ Fabian Gruenbichler ]
|
||||
* Package http-body 0.4.4 from crates.io using debcargo 2.5.0
|
||||
|
||||
-- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Sat, 21 May 2022 20:26:34 -0400
|
||||
|
||||
rust-http-body (0.1.0-2) unstable; urgency=medium
|
||||
|
||||
* Team upload.
|
||||
* Package http-body 0.1.0 from crates.io using debcargo 2.2.10
|
||||
* Source upload for migration
|
||||
|
||||
-- Sylvestre Ledru <sylvestre@debian.org> Tue, 06 Aug 2019 18:10:37 +0200
|
||||
|
||||
rust-http-body (0.1.0-1) unstable; urgency=medium
|
||||
|
||||
* Package http-body 0.1.0 from crates.io using debcargo 2.2.10
|
||||
|
||||
-- Robin Krahl <robin.krahl@ireas.org> Sat, 13 Jul 2019 22:14:15 +0200
|
|
@ -0,0 +1 @@
|
|||
12
|
|
@ -0,0 +1,38 @@
|
|||
Source: rust-http-body
|
||||
Section: rust
|
||||
Priority: optional
|
||||
Build-Depends: debhelper (>= 12),
|
||||
dh-cargo (>= 25),
|
||||
cargo:native <!nocheck>,
|
||||
rustc:native <!nocheck>,
|
||||
libstd-rust-dev <!nocheck>,
|
||||
librust-bytes-1+default-dev <!nocheck>,
|
||||
librust-http-0.2+default-dev <!nocheck>,
|
||||
librust-pin-project-lite-0.2+default-dev <!nocheck>
|
||||
Maintainer: Debian Rust Maintainers <pkg-rust-maintainers@alioth-lists.debian.net>
|
||||
Uploaders:
|
||||
Robin Krahl <robin.krahl@ireas.org>
|
||||
Standards-Version: 4.5.1
|
||||
Vcs-Git: https://salsa.debian.org/rust-team/debcargo-conf.git [src/http-body]
|
||||
Vcs-Browser: https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/http-body
|
||||
Rules-Requires-Root: no
|
||||
|
||||
Package: librust-http-body-dev
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends:
|
||||
${misc:Depends},
|
||||
librust-bytes-1+default-dev,
|
||||
librust-http-0.2+default-dev,
|
||||
librust-pin-project-lite-0.2+default-dev
|
||||
Provides:
|
||||
librust-http-body+default-dev (= ${binary:Version}),
|
||||
librust-http-body-0-dev (= ${binary:Version}),
|
||||
librust-http-body-0+default-dev (= ${binary:Version}),
|
||||
librust-http-body-0.4-dev (= ${binary:Version}),
|
||||
librust-http-body-0.4+default-dev (= ${binary:Version}),
|
||||
librust-http-body-0.4.5-dev (= ${binary:Version}),
|
||||
librust-http-body-0.4.5+default-dev (= ${binary:Version})
|
||||
Description: trait representing asynchronous operations on an HTTP body - Rust source code
|
||||
This package contains the source for the Rust http-body crate, packaged by
|
||||
debcargo for use with cargo and dh-cargo.
|
|
@ -0,0 +1,40 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: http-body
|
||||
Upstream-Contact:
|
||||
Carl Lerche <me@carllerche.com>
|
||||
Lucio Franco <luciofranco14@gmail.com>
|
||||
Sean McArthur <sean@seanmonstar.com>
|
||||
Source: https://github.com/hyperium/http-body
|
||||
|
||||
Files: *
|
||||
Copyright:
|
||||
2019 Carl Lerche <me@carllerche.com>
|
||||
2019 Lucio Franco <luciofranco14@gmail.com>
|
||||
2019 Sean McArthur <sean@seanmonstar.com>
|
||||
2019 Hyper Contributors
|
||||
License: MIT
|
||||
|
||||
Files: debian/*
|
||||
Copyright:
|
||||
2019 Debian Rust Maintainers <pkg-rust-maintainers@alioth-lists.debian.net>
|
||||
2019 Robin Krahl <robin.krahl@ireas.org>
|
||||
License: MIT
|
||||
|
||||
License: MIT
|
||||
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.
|
|
@ -0,0 +1,51 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: http-body
|
||||
Upstream-Contact:
|
||||
Carl Lerche <me@carllerche.com>
|
||||
Lucio Franco <luciofranco14@gmail.com>
|
||||
Sean McArthur <sean@seanmonstar.com>
|
||||
Source: https://github.com/hyperium/http-body
|
||||
|
||||
Files: *
|
||||
Copyright:
|
||||
FIXME (overlay) UNKNOWN-YEARS Carl Lerche <me@carllerche.com>
|
||||
FIXME (overlay) UNKNOWN-YEARS Lucio Franco <luciofranco14@gmail.com>
|
||||
FIXME (overlay) UNKNOWN-YEARS Sean McArthur <sean@seanmonstar.com>
|
||||
License: MIT
|
||||
Comment:
|
||||
FIXME (overlay): Since upstream copyright years are not available in
|
||||
Cargo.toml, they were extracted from the upstream Git repository. This may not
|
||||
be correct information so you should review and fix this before uploading to
|
||||
the archive.
|
||||
|
||||
Files: ./LICENSE
|
||||
Copyright: 2019 Hyper Contributors
|
||||
License: UNKNOWN-LICENSE; FIXME (overlay)
|
||||
Comment:
|
||||
FIXME (overlay): These notices are extracted from files. Please review them
|
||||
before uploading to the archive.
|
||||
|
||||
Files: debian/*
|
||||
Copyright:
|
||||
2019-2022 Debian Rust Maintainers <pkg-rust-maintainers@alioth-lists.debian.net>
|
||||
2019-2022 Robin Krahl <robin.krahl@ireas.org>
|
||||
License: MIT
|
||||
|
||||
License: MIT
|
||||
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.
|
|
@ -0,0 +1,3 @@
|
|||
overlay = "."
|
||||
uploaders = ["Robin Krahl <robin.krahl@ireas.org>"]
|
||||
summary = "trait representing asynchronous operations on an HTTP body"
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/make -f
|
||||
%:
|
||||
dh $@ --buildsystem cargo
|
|
@ -0,0 +1 @@
|
|||
3.0 (quilt)
|
|
@ -0,0 +1,14 @@
|
|||
Test-Command: /usr/share/cargo/bin/cargo-auto-test http-body 0.4.5 --all-targets --all-features
|
||||
Features: test-name=rust-http-body:@
|
||||
Depends: dh-cargo (>= 18), librust-tokio-1+default-dev, librust-tokio-1+macros-dev, librust-tokio-1+rt-dev, @
|
||||
Restrictions: allow-stderr, skip-not-installable
|
||||
|
||||
Test-Command: /usr/share/cargo/bin/cargo-auto-test http-body 0.4.5 --all-targets
|
||||
Features: test-name=librust-http-body-dev:default
|
||||
Depends: dh-cargo (>= 18), librust-tokio-1+default-dev, librust-tokio-1+macros-dev, librust-tokio-1+rt-dev, @
|
||||
Restrictions: allow-stderr, skip-not-installable
|
||||
|
||||
Test-Command: /usr/share/cargo/bin/cargo-auto-test http-body 0.4.5 --all-targets --no-default-features
|
||||
Features: test-name=librust-http-body-dev:
|
||||
Depends: dh-cargo (>= 18), librust-tokio-1+default-dev, librust-tokio-1+macros-dev, librust-tokio-1+rt-dev, @
|
||||
Restrictions: allow-stderr, skip-not-installable
|
|
@ -0,0 +1,4 @@
|
|||
version=4
|
||||
opts=filenamemangle=s/.*\/(.*)\/download/http-body-$1\.tar\.gz/g,\
|
||||
uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/ \
|
||||
https://qa.debian.org/cgi-bin/fakeupstream.cgi?upstream=crates.io/http-body .*/crates/http-body/@ANY_VERSION@/download
|
|
@ -0,0 +1,134 @@
|
|||
use crate::Body;
|
||||
use bytes::Buf;
|
||||
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_data(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
self.inner.as_mut().poll_data(cx)
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
|
||||
self.inner.as_mut().poll_trailers(cx)
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
self.inner.is_end_stream()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> crate::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 `BoxBody`.
|
||||
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_data(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
self.inner.as_mut().poll_data(cx)
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
|
||||
self.inner.as_mut().poll_trailers(cx)
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
self.inner.is_end_stream()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> crate::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 {}))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
use crate::Body;
|
||||
use bytes::Buf;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::{
|
||||
any::type_name,
|
||||
fmt,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
pin_project! {
|
||||
/// Body returned by the [`map_data`] combinator.
|
||||
///
|
||||
/// [`map_data`]: crate::util::BodyExt::map_data
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MapData<B, F> {
|
||||
#[pin]
|
||||
inner: B,
|
||||
f: F
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, F> MapData<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 MapData<B, F>
|
||||
where
|
||||
B: Body,
|
||||
F: FnMut(B::Data) -> B2,
|
||||
B2: Buf,
|
||||
{
|
||||
type Data = B2;
|
||||
type Error = B::Error;
|
||||
|
||||
fn poll_data(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
let this = self.project();
|
||||
match this.inner.poll_data(cx) {
|
||||
Poll::Pending => Poll::Pending,
|
||||
Poll::Ready(None) => Poll::Ready(None),
|
||||
Poll::Ready(Some(Ok(data))) => Poll::Ready(Some(Ok((this.f)(data)))),
|
||||
Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(err))),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
|
||||
self.project().inner.poll_trailers(cx)
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
self.inner.is_end_stream()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, F> fmt::Debug for MapData<B, F>
|
||||
where
|
||||
B: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("MapData")
|
||||
.field("inner", &self.inner)
|
||||
.field("f", &type_name::<F>())
|
||||
.finish()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
use crate::Body;
|
||||
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::util::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_data(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
let this = self.project();
|
||||
match this.inner.poll_data(cx) {
|
||||
Poll::Pending => Poll::Pending,
|
||||
Poll::Ready(None) => Poll::Ready(None),
|
||||
Poll::Ready(Some(Ok(data))) => Poll::Ready(Some(Ok(data))),
|
||||
Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err((this.f)(err)))),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
|
||||
let this = self.project();
|
||||
this.inner.poll_trailers(cx).map_err(this.f)
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
self.inner.is_end_stream()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> crate::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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
//! Combinators for the `Body` trait.
|
||||
|
||||
mod box_body;
|
||||
mod map_data;
|
||||
mod map_err;
|
||||
|
||||
pub use self::{
|
||||
box_body::{BoxBody, UnsyncBoxBody},
|
||||
map_data::MapData,
|
||||
map_err::MapErr,
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
use super::{Body, SizeHint};
|
||||
use bytes::Buf;
|
||||
use http::HeaderMap;
|
||||
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 fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Buf> Body for Empty<D> {
|
||||
type Data = D;
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline]
|
||||
fn poll_data(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
Poll::Ready(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
Poll::Ready(Ok(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 {
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Clone for Empty<D> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Copy for Empty<D> {}
|
|
@ -0,0 +1,151 @@
|
|||
use crate::{Body, SizeHint};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
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_data(
|
||||
mut self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<D, Self::Error>>> {
|
||||
Poll::Ready(self.data.take().map(Ok))
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
Poll::Ready(Ok(None))
|
||||
}
|
||||
|
||||
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::*;
|
||||
|
||||
#[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.data().await, Some(Ok(&b"hello"[..])));
|
||||
assert!(full.data().await.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn empty_full_returns_none() {
|
||||
assert!(Full::<&[u8]>::default().data().await.is_none());
|
||||
assert!(Full::new(&b""[..]).data().await.is_none());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
#![doc(html_root_url = "https://docs.rs/http-body/0.4.5")]
|
||||
#![deny(
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
unreachable_pub,
|
||||
broken_intra_doc_links
|
||||
)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! Asynchronous HTTP request or response body.
|
||||
//!
|
||||
//! See [`Body`] for more details.
|
||||
//!
|
||||
//! [`Body`]: trait.Body.html
|
||||
|
||||
mod empty;
|
||||
mod full;
|
||||
mod limited;
|
||||
mod next;
|
||||
mod size_hint;
|
||||
|
||||
pub mod combinators;
|
||||
|
||||
pub use self::empty::Empty;
|
||||
pub use self::full::Full;
|
||||
pub use self::limited::{LengthLimitError, Limited};
|
||||
pub use self::next::{Data, Trailers};
|
||||
pub use self::size_hint::SizeHint;
|
||||
|
||||
use self::combinators::{BoxBody, MapData, MapErr, UnsyncBoxBody};
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
use std::convert::Infallible;
|
||||
use std::ops;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Trait representing a streaming body of a Request or Response.
|
||||
///
|
||||
/// Data is streamed via the `poll_data` function, which asynchronously yields `T: Buf` values. The
|
||||
/// `size_hint` function provides insight into the total number of bytes that will be streamed.
|
||||
///
|
||||
/// The `poll_trailers` function returns an optional set of trailers used to finalize the request /
|
||||
/// response exchange. This is mostly used when using the HTTP/2.0 protocol.
|
||||
///
|
||||
pub trait Body {
|
||||
/// Values yielded by the `Body`.
|
||||
type Data: Buf;
|
||||
|
||||
/// The error type this `Body` might generate.
|
||||
type Error;
|
||||
|
||||
/// Attempt to pull out the next data buffer of this stream.
|
||||
fn poll_data(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>>;
|
||||
|
||||
/// Poll for an optional **single** `HeaderMap` of trailers.
|
||||
///
|
||||
/// This function should only be called once `poll_data` returns `None`.
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>>;
|
||||
|
||||
/// Returns `true` when the end of stream has been reached.
|
||||
///
|
||||
/// An end of stream means that both `poll_data` and `poll_trailers` will
|
||||
/// return `None`.
|
||||
///
|
||||
/// A return value of `false` **does not** guarantee that a value will be
|
||||
/// returned from `poll_stream` or `poll_trailers`.
|
||||
fn is_end_stream(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns the bounds on the remaining length of the stream.
|
||||
///
|
||||
/// When the **exact** remaining length of the stream is known, the upper bound will be set and
|
||||
/// will equal the lower bound.
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
SizeHint::default()
|
||||
}
|
||||
|
||||
/// Returns future that resolves to next data chunk, if any.
|
||||
fn data(&mut self) -> Data<'_, Self>
|
||||
where
|
||||
Self: Unpin + Sized,
|
||||
{
|
||||
Data(self)
|
||||
}
|
||||
|
||||
/// Returns future that resolves to trailers, if any.
|
||||
fn trailers(&mut self) -> Trailers<'_, Self>
|
||||
where
|
||||
Self: Unpin + Sized,
|
||||
{
|
||||
Trailers(self)
|
||||
}
|
||||
|
||||
/// Maps this body's data value to a different value.
|
||||
fn map_data<F, B>(self, f: F) -> MapData<Self, F>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Self::Data) -> B,
|
||||
B: Buf,
|
||||
{
|
||||
MapData::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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Body + Unpin + ?Sized> Body for &mut T {
|
||||
type Data = T::Data;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll_data(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
Pin::new(&mut **self).poll_data(cx)
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
Pin::new(&mut **self).poll_trailers(cx)
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
Pin::new(&**self).is_end_stream()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
Pin::new(&**self).size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Body for Pin<P>
|
||||
where
|
||||
P: Unpin + ops::DerefMut,
|
||||
P::Target: Body,
|
||||
{
|
||||
type Data = <<P as ops::Deref>::Target as Body>::Data;
|
||||
type Error = <<P as ops::Deref>::Target as Body>::Error;
|
||||
|
||||
fn poll_data(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
Pin::get_mut(self).as_mut().poll_data(cx)
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
Pin::get_mut(self).as_mut().poll_trailers(cx)
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
self.as_ref().is_end_stream()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
self.as_ref().size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Body + Unpin + ?Sized> Body for Box<T> {
|
||||
type Data = T::Data;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll_data(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
Pin::new(&mut **self).poll_data(cx)
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
Pin::new(&mut **self).poll_trailers(cx)
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
self.as_ref().is_end_stream()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
self.as_ref().size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Body> Body for http::Request<B> {
|
||||
type Data = B::Data;
|
||||
type Error = B::Error;
|
||||
|
||||
fn poll_data(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
unsafe {
|
||||
self.map_unchecked_mut(http::Request::body_mut)
|
||||
.poll_data(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
unsafe {
|
||||
self.map_unchecked_mut(http::Request::body_mut)
|
||||
.poll_trailers(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
self.body().is_end_stream()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
self.body().size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Body> Body for http::Response<B> {
|
||||
type Data = B::Data;
|
||||
type Error = B::Error;
|
||||
|
||||
fn poll_data(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
unsafe {
|
||||
self.map_unchecked_mut(http::Response::body_mut)
|
||||
.poll_data(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
unsafe {
|
||||
self.map_unchecked_mut(http::Response::body_mut)
|
||||
.poll_trailers(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
self.body().is_end_stream()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
self.body().size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl Body for String {
|
||||
type Data = Bytes;
|
||||
type Error = Infallible;
|
||||
|
||||
fn poll_data(
|
||||
mut self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
if !self.is_empty() {
|
||||
let s = std::mem::take(&mut *self);
|
||||
Poll::Ready(Some(Ok(s.into_bytes().into())))
|
||||
} else {
|
||||
Poll::Ready(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
Poll::Ready(Ok(None))
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
SizeHint::with_exact(self.len() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn _assert_bounds() {
|
||||
fn can_be_trait_object(_: &dyn Body<Data = std::io::Cursor<Vec<u8>>, Error = std::io::Error>) {}
|
||||
}
|
|
@ -0,0 +1,299 @@
|
|||
use crate::{Body, SizeHint};
|
||||
use bytes::Buf;
|
||||
use http::HeaderMap;
|
||||
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_data(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
let this = self.project();
|
||||
let res = match this.inner.poll_data(cx) {
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready(None) => None,
|
||||
Poll::Ready(Some(Ok(data))) => {
|
||||
if data.remaining() > *this.remaining {
|
||||
*this.remaining = 0;
|
||||
Some(Err(LengthLimitError.into()))
|
||||
} else {
|
||||
*this.remaining -= data.remaining();
|
||||
Some(Ok(data))
|
||||
}
|
||||
}
|
||||
Poll::Ready(Some(Err(err))) => Some(Err(err.into())),
|
||||
};
|
||||
|
||||
Poll::Ready(res)
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
let this = self.project();
|
||||
let res = match this.inner.poll_trailers(cx) {
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready(Ok(data)) => Ok(data),
|
||||
Poll::Ready(Err(err)) => 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::Full;
|
||||
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.data().await.unwrap().unwrap();
|
||||
assert_eq!(data, DATA);
|
||||
hint.set_upper(0);
|
||||
assert_eq!(body.size_hint().upper(), hint.upper());
|
||||
|
||||
assert!(matches!(body.data().await, 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.data().await.unwrap().unwrap_err();
|
||||
assert!(matches!(error.downcast_ref(), Some(LengthLimitError)));
|
||||
}
|
||||
|
||||
struct Chunky(&'static [&'static [u8]]);
|
||||
|
||||
impl Body for Chunky {
|
||||
type Data = &'static [u8];
|
||||
type Error = Infallible;
|
||||
|
||||
fn poll_data(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
let mut this = self;
|
||||
match this.0.split_first().map(|(&head, tail)| (Ok(head), tail)) {
|
||||
Some((data, new_tail)) => {
|
||||
this.0 = new_tail;
|
||||
|
||||
Poll::Ready(Some(data))
|
||||
}
|
||||
None => Poll::Ready(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
Poll::Ready(Ok(Some(HeaderMap::new())))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_for_chunked_body_around_limit_returns_first_chunk_but_returns_error_on_over_limit_chunk(
|
||||
) {
|
||||
const DATA: &[&[u8]] = &[b"testing ", b"a string that is too long"];
|
||||
let inner = Chunky(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.data().await.unwrap().unwrap();
|
||||
assert_eq!(data, DATA[0]);
|
||||
hint.set_upper(0);
|
||||
assert_eq!(body.size_hint().upper(), hint.upper());
|
||||
|
||||
let error = body.data().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]] = &[b"testing a string", b" that is too long"];
|
||||
let inner = Chunky(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.data().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]] = &[b"test", b"ing!"];
|
||||
let inner = Chunky(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.data().await.unwrap().unwrap();
|
||||
assert_eq!(data, DATA[0]);
|
||||
hint.set_upper(4);
|
||||
assert_eq!(body.size_hint().upper(), hint.upper());
|
||||
|
||||
let data = body.data().await.unwrap().unwrap();
|
||||
assert_eq!(data, DATA[1]);
|
||||
hint.set_upper(0);
|
||||
assert_eq!(body.size_hint().upper(), hint.upper());
|
||||
|
||||
assert!(matches!(body.data().await, None));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_for_trailers_propagates_inner_trailers() {
|
||||
const DATA: &[&[u8]] = &[b"test", b"ing!"];
|
||||
let inner = Chunky(DATA);
|
||||
let body = &mut Limited::new(inner, 8);
|
||||
let trailers = body.trailers().await.unwrap();
|
||||
assert_eq!(trailers, Some(HeaderMap::new()))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ErrorBodyError {
|
||||
Data,
|
||||
Trailers,
|
||||
}
|
||||
|
||||
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_data(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
Poll::Ready(Some(Err(ErrorBodyError::Data)))
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
Poll::Ready(Err(ErrorBodyError::Trailers))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_for_body_returning_error_propagates_error() {
|
||||
let body = &mut Limited::new(ErrorBody, 8);
|
||||
let error = body.data().await.unwrap().unwrap_err();
|
||||
assert!(matches!(error.downcast_ref(), Some(ErrorBodyError::Data)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn trailers_for_body_returning_error_propagates_error() {
|
||||
let body = &mut Limited::new(ErrorBody, 8);
|
||||
let error = body.trailers().await.unwrap_err();
|
||||
assert!(matches!(
|
||||
error.downcast_ref(),
|
||||
Some(ErrorBodyError::Trailers)
|
||||
));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
use crate::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 data chunk from `Body`
|
||||
pub struct Data<'a, T: ?Sized>(pub(crate) &'a mut T);
|
||||
|
||||
impl<'a, T: Body + Unpin + ?Sized> Future for Data<'a, T> {
|
||||
type Output = Option<Result<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_data(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use = "futures don't do anything unless polled"]
|
||||
#[derive(Debug)]
|
||||
/// Future that resolves to the optional trailers from `Body`
|
||||
pub struct Trailers<'a, T: ?Sized>(pub(crate) &'a mut T);
|
||||
|
||||
impl<'a, T: Body + Unpin + ?Sized> Future for Trailers<'a, T> {
|
||||
type Output = Result<Option<http::HeaderMap>, T::Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
|
||||
Pin::new(&mut self.0).poll_trailers(ctx)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
use std::u64;
|
||||
|
||||
/// A `Body` size hint
|
||||
///
|
||||
/// The default implementation returns:
|
||||
///
|
||||
/// * 0 for `lower`
|
||||
/// * `None` for `upper`.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct SizeHint {
|
||||
lower: u64,
|
||||
upper: Option<u64>,
|
||||
}
|
||||
|
||||
impl SizeHint {
|
||||
/// Returns a new `SizeHint` with default values
|
||||
#[inline]
|
||||
pub fn new() -> SizeHint {
|
||||
SizeHint::default()
|
||||
}
|
||||
|
||||
/// Returns a new `SizeHint` with both upper and lower bounds set to the
|
||||
/// given value.
|
||||
#[inline]
|
||||
pub fn with_exact(value: u64) -> SizeHint {
|
||||
SizeHint {
|
||||
lower: value,
|
||||
upper: Some(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the lower bound of data that the `Body` will yield before
|
||||
/// completing.
|
||||
#[inline]
|
||||
pub fn lower(&self) -> u64 {
|
||||
self.lower
|
||||
}
|
||||
|
||||
/// Set the value of the `lower` hint.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The function panics if `value` is greater than `upper`.
|
||||
#[inline]
|
||||
pub fn set_lower(&mut self, value: u64) {
|
||||
assert!(value <= self.upper.unwrap_or(u64::MAX));
|
||||
self.lower = value;
|
||||
}
|
||||
|
||||
/// Returns the upper bound of data the `Body` will yield before
|
||||
/// completing, or `None` if the value is unknown.
|
||||
#[inline]
|
||||
pub fn upper(&self) -> Option<u64> {
|
||||
self.upper
|
||||
}
|
||||
|
||||
/// Set the value of the `upper` hint value.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if `value` is less than `lower`.
|
||||
#[inline]
|
||||
pub fn set_upper(&mut self, value: u64) {
|
||||
assert!(value >= self.lower, "`value` is less than than `lower`");
|
||||
|
||||
self.upper = Some(value);
|
||||
}
|
||||
|
||||
/// Returns the exact size of data that will be yielded **if** the
|
||||
/// `lower` and `upper` bounds are equal.
|
||||
#[inline]
|
||||
pub fn exact(&self) -> Option<u64> {
|
||||
if Some(self.lower) == self.upper {
|
||||
self.upper
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the value of the `lower` and `upper` bounds to exactly the same.
|
||||
#[inline]
|
||||
pub fn set_exact(&mut self, value: u64) {
|
||||
self.lower = value;
|
||||
self.upper = Some(value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
use http::HeaderMap;
|
||||
use http_body::{Body, SizeHint};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
struct Mock {
|
||||
size_hint: SizeHint,
|
||||
}
|
||||
|
||||
impl Body for Mock {
|
||||
type Data = ::std::io::Cursor<Vec<u8>>;
|
||||
type Error = ();
|
||||
|
||||
fn poll_data(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
||||
Poll::Ready(None)
|
||||
}
|
||||
|
||||
fn poll_trailers(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
|
||||
Poll::Ready(Ok(None))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> SizeHint {
|
||||
self.size_hint.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_end_stream_true() {
|
||||
let combos = [
|
||||
(None, None, false),
|
||||
(Some(123), None, false),
|
||||
(Some(0), Some(123), false),
|
||||
(Some(123), Some(123), false),
|
||||
(Some(0), Some(0), false),
|
||||
];
|
||||
|
||||
for &(lower, upper, is_end_stream) in &combos {
|
||||
let mut size_hint = SizeHint::new();
|
||||
assert_eq!(0, size_hint.lower());
|
||||
assert!(size_hint.upper().is_none());
|
||||
|
||||
if let Some(lower) = lower {
|
||||
size_hint.set_lower(lower);
|
||||
}
|
||||
|
||||
if let Some(upper) = upper {
|
||||
size_hint.set_upper(upper);
|
||||
}
|
||||
|
||||
let mut mock = Mock { size_hint };
|
||||
|
||||
assert_eq!(
|
||||
is_end_stream,
|
||||
Pin::new(&mut mock).is_end_stream(),
|
||||
"size_hint = {:?}",
|
||||
mock.size_hint.clone()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_end_stream_default_false() {
|
||||
let mut mock = Mock {
|
||||
size_hint: SizeHint::default(),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
false,
|
||||
Pin::new(&mut mock).is_end_stream(),
|
||||
"size_hint = {:?}",
|
||||
mock.size_hint.clone()
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue