summaryrefslogtreecommitdiffstats
path: root/third_party/rust/http-body/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/http-body/src
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/http-body/src')
-rw-r--r--third_party/rust/http-body/src/combinators/box_body.rs134
-rw-r--r--third_party/rust/http-body/src/combinators/map_data.rs94
-rw-r--r--third_party/rust/http-body/src/combinators/map_err.rs97
-rw-r--r--third_party/rust/http-body/src/combinators/mod.rs11
-rw-r--r--third_party/rust/http-body/src/empty.rs75
-rw-r--r--third_party/rust/http-body/src/full.rs151
-rw-r--r--third_party/rust/http-body/src/lib.rs324
-rw-r--r--third_party/rust/http-body/src/limited.rs299
-rw-r--r--third_party/rust/http-body/src/next.rs31
-rw-r--r--third_party/rust/http-body/src/size_hint.rs86
10 files changed, 1302 insertions, 0 deletions
diff --git a/third_party/rust/http-body/src/combinators/box_body.rs b/third_party/rust/http-body/src/combinators/box_body.rs
new file mode 100644
index 0000000000..97c8313fd7
--- /dev/null
+++ b/third_party/rust/http-body/src/combinators/box_body.rs
@@ -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 {}))
+ }
+}
diff --git a/third_party/rust/http-body/src/combinators/map_data.rs b/third_party/rust/http-body/src/combinators/map_data.rs
new file mode 100644
index 0000000000..6d9c5a8964
--- /dev/null
+++ b/third_party/rust/http-body/src/combinators/map_data.rs
@@ -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()
+ }
+}
diff --git a/third_party/rust/http-body/src/combinators/map_err.rs b/third_party/rust/http-body/src/combinators/map_err.rs
new file mode 100644
index 0000000000..c77168deb5
--- /dev/null
+++ b/third_party/rust/http-body/src/combinators/map_err.rs
@@ -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()
+ }
+}
diff --git a/third_party/rust/http-body/src/combinators/mod.rs b/third_party/rust/http-body/src/combinators/mod.rs
new file mode 100644
index 0000000000..c52f367554
--- /dev/null
+++ b/third_party/rust/http-body/src/combinators/mod.rs
@@ -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,
+};
diff --git a/third_party/rust/http-body/src/empty.rs b/third_party/rust/http-body/src/empty.rs
new file mode 100644
index 0000000000..7d63ceb054
--- /dev/null
+++ b/third_party/rust/http-body/src/empty.rs
@@ -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> {}
diff --git a/third_party/rust/http-body/src/full.rs b/third_party/rust/http-body/src/full.rs
new file mode 100644
index 0000000000..f1d063b0ae
--- /dev/null
+++ b/third_party/rust/http-body/src/full.rs
@@ -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());
+ }
+}
diff --git a/third_party/rust/http-body/src/lib.rs b/third_party/rust/http-body/src/lib.rs
new file mode 100644
index 0000000000..84efd9169f
--- /dev/null
+++ b/third_party/rust/http-body/src/lib.rs
@@ -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>) {}
+}
diff --git a/third_party/rust/http-body/src/limited.rs b/third_party/rust/http-body/src/limited.rs
new file mode 100644
index 0000000000..a40add91f9
--- /dev/null
+++ b/third_party/rust/http-body/src/limited.rs
@@ -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)
+ ));
+ }
+}
diff --git a/third_party/rust/http-body/src/next.rs b/third_party/rust/http-body/src/next.rs
new file mode 100644
index 0000000000..fc87ffcf01
--- /dev/null
+++ b/third_party/rust/http-body/src/next.rs
@@ -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)
+ }
+}
diff --git a/third_party/rust/http-body/src/size_hint.rs b/third_party/rust/http-body/src/size_hint.rs
new file mode 100644
index 0000000000..00a8f19177
--- /dev/null
+++ b/third_party/rust/http-body/src/size_hint.rs
@@ -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);
+ }
+}