http-body-0.4.6/.cargo_vcs_info.json0000644000000001360000000000100127550ustar { "git": { "sha1": "930f1a256dc823ccfd85eebbb7307018a84e112d" }, "path_in_vcs": "" }http-body-0.4.6/.github/workflows/CI.yml000064400000000000000000000012201046102023000161530ustar 00000000000000name: 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 http-body-0.4.6/.gitignore000064400000000000000000000000221046102023000135270ustar 00000000000000target Cargo.lock http-body-0.4.6/CHANGELOG.md000064400000000000000000000024761046102023000133670ustar 00000000000000# 0.4.6 (December 8, 2023) - Add `Collect` combinator (backported from http-body-util). # 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` and `http::Response`. # 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 http-body-0.4.6/Cargo.toml0000644000000022120000000000100107500ustar # 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.6" authors = [ "Carl Lerche ", "Lucio Franco ", "Sean McArthur ", ] 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", ] http-body-0.4.6/Cargo.toml.orig000064400000000000000000000014651046102023000144420ustar 00000000000000[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.6" authors = [ "Carl Lerche ", "Lucio Franco ", "Sean McArthur ", ] 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"] } http-body-0.4.6/LICENSE000064400000000000000000000020461046102023000125540ustar 00000000000000Copyright (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. http-body-0.4.6/README.md000064400000000000000000000016511046102023000130270ustar 00000000000000# 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. http-body-0.4.6/src/collect.rs000064400000000000000000000133501046102023000143310ustar 00000000000000use std::{ collections::VecDeque, future::Future, pin::Pin, task::{Context, Poll}, }; use super::Body; use bytes::{Buf, Bytes}; use http::HeaderMap; use pin_project_lite::pin_project; pin_project! { /// Future that resolves into a [`Collected`]. pub struct Collect where T: Body, { #[pin] body: T, collected: Option>, is_data_done: bool, } } impl Collect { pub(crate) fn new(body: T) -> Self { Self { body, collected: Some(Collected::default()), is_data_done: false, } } } impl Future for Collect { type Output = Result, T::Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut me = self.project(); loop { if !*me.is_data_done { match me.body.as_mut().poll_data(cx) { Poll::Ready(Some(Ok(data))) => { me.collected.as_mut().unwrap().push_data(data); } Poll::Ready(Some(Err(err))) => { return Poll::Ready(Err(err)); } Poll::Ready(None) => { *me.is_data_done = true; } Poll::Pending => return Poll::Pending, } } else { match me.body.as_mut().poll_trailers(cx) { Poll::Ready(Ok(Some(trailers))) => { me.collected.as_mut().unwrap().push_trailers(trailers); break; } Poll::Ready(Err(err)) => { return Poll::Ready(Err(err)); } Poll::Ready(Ok(None)) => break, Poll::Pending => return Poll::Pending, } } } Poll::Ready(Ok(me.collected.take().expect("polled after complete"))) } } /// A collected body produced by [`Body::collect`] which collects all the DATA frames /// and trailers. #[derive(Debug)] pub struct Collected { bufs: BufList, trailers: Option, } impl Collected { /// 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()) } fn push_data(&mut self, data: B) { // 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); } } fn push_trailers(&mut self, trailers: HeaderMap) { if let Some(current) = &mut self.trailers { current.extend(trailers); } else { self.trailers = Some(trailers); } } } impl Default for Collected { fn default() -> Self { Self { bufs: BufList::default(), trailers: None, } } } impl Unpin for Collected {} #[derive(Debug)] struct BufList { bufs: VecDeque, } impl BufList { #[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 { self.bufs.pop_front() } */ } impl Buf for BufList { #[inline] fn remaining(&self) -> usize { self.bufs.iter().map(|buf| buf.remaining()).sum() } #[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 [std::io::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 { use bytes::{BufMut, BytesMut}; // 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), _ => { assert!(len <= self.remaining(), "`len` greater than remaining"); let mut bm = BytesMut::with_capacity(len); bm.put(self.take(len)); bm.freeze() } } } } impl Default for BufList { fn default() -> Self { BufList { bufs: VecDeque::new(), } } } http-body-0.4.6/src/combinators/box_body.rs000064400000000000000000000055441046102023000170370ustar 00000000000000use crate::Body; use bytes::Buf; use std::{ fmt, pin::Pin, task::{Context, Poll}, }; /// A boxed [`Body`] trait object. pub struct BoxBody { inner: Pin + Send + Sync + 'static>>, } /// A boxed [`Body`] trait object that is !Sync. pub struct UnsyncBoxBody { inner: Pin + Send + 'static>>, } impl BoxBody { /// Create a new `BoxBody`. pub fn new(body: B) -> Self where B: Body + Send + Sync + 'static, D: Buf, { Self { inner: Box::pin(body), } } } impl fmt::Debug for BoxBody { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("BoxBody").finish() } } impl Body for BoxBody where D: Buf, { type Data = D; type Error = E; fn poll_data( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { self.inner.as_mut().poll_data(cx) } fn poll_trailers( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, 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 Default for BoxBody where D: Buf + 'static, { fn default() -> Self { BoxBody::new(crate::Empty::new().map_err(|err| match err {})) } } // === UnsyncBoxBody === impl UnsyncBoxBody { /// Create a new `BoxBody`. pub fn new(body: B) -> Self where B: Body + Send + 'static, D: Buf, { Self { inner: Box::pin(body), } } } impl fmt::Debug for UnsyncBoxBody { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("UnsyncBoxBody").finish() } } impl Body for UnsyncBoxBody where D: Buf, { type Data = D; type Error = E; fn poll_data( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { self.inner.as_mut().poll_data(cx) } fn poll_trailers( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, 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 Default for UnsyncBoxBody where D: Buf + 'static, { fn default() -> Self { UnsyncBoxBody::new(crate::Empty::new().map_err(|err| match err {})) } } http-body-0.4.6/src/combinators/map_data.rs000064400000000000000000000042251046102023000167730ustar 00000000000000use 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 { #[pin] inner: B, f: F } } impl MapData { #[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 Body for MapData 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>> { 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, Self::Error>> { self.project().inner.poll_trailers(cx) } fn is_end_stream(&self) -> bool { self.inner.is_end_stream() } } impl fmt::Debug for MapData 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::()) .finish() } } http-body-0.4.6/src/combinators/map_err.rs000064400000000000000000000043511046102023000166520ustar 00000000000000use 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 { #[pin] inner: B, f: F } } impl MapErr { #[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 Body for MapErr 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>> { 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, 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 fmt::Debug for MapErr 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::()) .finish() } } http-body-0.4.6/src/combinators/mod.rs000064400000000000000000000002701046102023000160000ustar 00000000000000//! 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, }; http-body-0.4.6/src/empty.rs000064400000000000000000000025761046102023000140520ustar 00000000000000use 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 { _marker: PhantomData D>, } impl Empty { /// Create a new `Empty`. pub fn new() -> Self { Self::default() } } impl Body for Empty { type Data = D; type Error = Infallible; #[inline] fn poll_data( self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { Poll::Ready(None) } #[inline] fn poll_trailers( self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll, Self::Error>> { Poll::Ready(Ok(None)) } fn is_end_stream(&self) -> bool { true } fn size_hint(&self) -> SizeHint { SizeHint::with_exact(0) } } impl fmt::Debug for Empty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Empty").finish() } } impl Default for Empty { fn default() -> Self { Self { _marker: PhantomData, } } } impl Clone for Empty { fn clone(&self) -> Self { Self { _marker: PhantomData, } } } impl Copy for Empty {} http-body-0.4.6/src/full.rs000064400000000000000000000061071046102023000136500ustar 00000000000000use 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 { data: Option, } } impl Full 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 Body for Full where D: Buf, { type Data = D; type Error = Infallible; fn poll_data( mut self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { Poll::Ready(self.data.take().map(Ok)) } fn poll_trailers( self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll, 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 Default for Full where D: Buf, { /// Create an empty `Full`. fn default() -> Self { Full { data: None } } } impl From for Full where D: Buf + From, { fn from(bytes: Bytes) -> Self { Full::new(D::from(bytes)) } } impl From> for Full where D: Buf + From>, { fn from(vec: Vec) -> Self { Full::new(D::from(vec)) } } impl From<&'static [u8]> for Full where D: Buf + From<&'static [u8]>, { fn from(slice: &'static [u8]) -> Self { Full::new(D::from(slice)) } } impl From> for Full where D: Buf + From<&'static B> + From, 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 From for Full where D: Buf + From, { fn from(s: String) -> Self { Full::new(D::from(s)) } } impl From<&'static str> for Full 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()); } } http-body-0.4.6/src/lib.rs000064400000000000000000000206361046102023000134570ustar 00000000000000#![doc(html_root_url = "https://docs.rs/http-body/0.4.6")] #![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 collect; mod empty; mod full; mod limited; mod next; mod size_hint; pub mod combinators; pub use self::collect::Collected; 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>>; /// 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, 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(self, f: F) -> MapData 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(self, f: F) -> MapErr where Self: Sized, F: FnMut(Self::Error) -> E, { MapErr::new(self, f) } /// Turn this body into [`Collected`] body which will collect all the DATA frames /// and trailers. fn collect(self) -> crate::collect::Collect where Self: Sized, { collect::Collect::new(self) } /// Turn this body into a boxed trait object. fn boxed(self) -> BoxBody 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 where Self: Sized + Send + 'static, { UnsyncBoxBody::new(self) } } impl Body for &mut T { type Data = T::Data; type Error = T::Error; fn poll_data( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { Pin::new(&mut **self).poll_data(cx) } fn poll_trailers( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, 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

Body for Pin

where P: Unpin + ops::DerefMut, P::Target: Body, { type Data = <

::Target as Body>::Data; type Error = <

::Target as Body>::Error; fn poll_data( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { Pin::get_mut(self).as_mut().poll_data(cx) } fn poll_trailers( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, 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 Body for Box { type Data = T::Data; type Error = T::Error; fn poll_data( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { Pin::new(&mut **self).poll_data(cx) } fn poll_trailers( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, 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 Body for http::Request { type Data = B::Data; type Error = B::Error; fn poll_data( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { unsafe { self.map_unchecked_mut(http::Request::body_mut) .poll_data(cx) } } fn poll_trailers( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, 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 Body for http::Response { type Data = B::Data; type Error = B::Error; fn poll_data( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { unsafe { self.map_unchecked_mut(http::Response::body_mut) .poll_data(cx) } } fn poll_trailers( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, 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>> { 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, 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>, Error = std::io::Error>) {} } http-body-0.4.6/src/limited.rs000064400000000000000000000212371046102023000143360ustar 00000000000000use 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 { remaining: usize, #[pin] inner: B, } } impl Limited { /// Create a new `Limited`. pub fn new(inner: B, limit: usize) -> Self { Self { remaining: limit, inner, } } } impl Body for Limited where B: Body, B::Error: Into>, { type Data = B::Data; type Error = Box; fn poll_data( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { 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, 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>> { 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, 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>> { Poll::Ready(Some(Err(ErrorBodyError::Data))) } fn poll_trailers( self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll, 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) )); } } http-body-0.4.6/src/next.rs000064400000000000000000000017601046102023000136640ustar 00000000000000use 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>; fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll { 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, T::Error>; fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll { Pin::new(&mut self.0).poll_trailers(ctx) } } http-body-0.4.6/src/size_hint.rs000064400000000000000000000041041046102023000146750ustar 00000000000000use 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, } 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 { 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 { 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); } } http-body-0.4.6/tests/is_end_stream.rs000064400000000000000000000033171046102023000160750ustar 00000000000000use 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>; type Error = (); fn poll_data( self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { Poll::Ready(None) } fn poll_trailers( self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll, 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() ); }