diff --git a/examples/client_json.rs b/examples/client_json.rs index addd7ab57b..3c03bb4882 100644 --- a/examples/client_json.rs +++ b/examples/client_json.rs @@ -1,6 +1,7 @@ #![deny(warnings)] #![warn(rust_2018_idioms)] + use hyper::Body; use hyper::{body::Buf, Request}; use serde::Deserialize; diff --git a/examples/echo.rs b/examples/echo.rs index 097851795d..fc3c82d451 100644 --- a/examples/echo.rs +++ b/examples/echo.rs @@ -46,7 +46,7 @@ async fn echo(req: Request) -> Result, hyper::Error> { // Return the 404 Not Found for other routes. _ => { - let mut not_found = Response::default(); + let mut not_found = Response::new(()); *not_found.status_mut() = StatusCode::NOT_FOUND; Ok(not_found) } diff --git a/src/body/body.rs b/src/body/body.rs index 856aea3e29..f06b745980 100644 --- a/src/body/body.rs +++ b/src/body/body.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::fmt; use bytes::Bytes; @@ -35,7 +34,8 @@ pub struct Body { } enum Kind { - Once(Option), + #[allow(dead_code)] + Empty, Chan { content_length: DecodedLength, want_tx: watch::Sender, @@ -104,21 +104,6 @@ const WANT_PENDING: usize = 1; const WANT_READY: usize = 2; impl Body { - /// Create an empty `Body` stream. - /// - /// # Example - /// - /// ``` - /// use hyper::{Body, Request}; - /// - /// // create a `GET /` request - /// let get = Request::new(Body::empty()); - /// ``` - #[inline] - pub fn empty() -> Body { - Body::new(Kind::Once(None)) - } - /// Create a `Body` stream with an associated sender half. /// /// Useful when wanting to stream chunks from another thread. @@ -176,6 +161,18 @@ impl Body { body } + #[cfg(feature = "ffi")] + #[inline] + pub(crate) fn ffi() -> Self { + Body::new(Kind::Ffi(crate::ffi::UserBody::new())) + } + + #[allow(dead_code)] + #[inline] + pub(crate) fn empty() -> Self { + Body::new(Kind::Empty) + } + #[cfg(any(feature = "http1", feature = "http2"))] #[cfg(feature = "client")] pub(crate) fn delayed_eof(&mut self, fut: DelayEofUntil) { @@ -249,7 +246,7 @@ impl Body { fn poll_inner(&mut self, cx: &mut task::Context<'_>) -> Poll>> { match self.kind { - Kind::Once(ref mut val) => Poll::Ready(val.take().map(Ok)), + Kind::Empty => Poll::Ready(None), Kind::Chan { content_length: ref mut len, ref mut data_rx, @@ -286,23 +283,6 @@ impl Body { Kind::Ffi(ref mut body) => body.poll_data(cx), } } - - #[cfg(feature = "http1")] - pub(super) fn take_full_data(&mut self) -> Option { - if let Kind::Once(ref mut chunk) = self.kind { - chunk.take() - } else { - None - } - } -} - -impl Default for Body { - /// Returns `Body::empty()`. - #[inline] - fn default() -> Body { - Body::empty() - } } impl HttpBody for Body { @@ -321,6 +301,7 @@ impl HttpBody for Body { #[cfg_attr(not(feature = "http2"), allow(unused))] cx: &mut task::Context<'_>, ) -> Poll, Self::Error>> { match self.kind { + Kind::Empty => Poll::Ready(Ok(None)), #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] Kind::H2 { recv: ref mut h2, @@ -342,13 +323,12 @@ impl HttpBody for Body { }, #[cfg(feature = "ffi")] Kind::Ffi(ref mut body) => body.poll_trailers(cx), - _ => Poll::Ready(Ok(None)), } } fn is_end_stream(&self) -> bool { match self.kind { - Kind::Once(ref val) => val.is_none(), + Kind::Empty => true, Kind::Chan { content_length, .. } => content_length == DecodedLength::ZERO, #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] Kind::H2 { recv: ref h2, .. } => h2.is_end_stream(), @@ -371,8 +351,7 @@ impl HttpBody for Body { } match self.kind { - Kind::Once(Some(ref val)) => SizeHint::with_exact(val.len() as u64), - Kind::Once(None) => SizeHint::with_exact(0), + Kind::Empty => SizeHint::with_exact(0), Kind::Chan { content_length, .. } => opt_len!(content_length), #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] Kind::H2 { content_length, .. } => opt_len!(content_length), @@ -388,13 +367,10 @@ impl fmt::Debug for Body { struct Streaming; #[derive(Debug)] struct Empty; - #[derive(Debug)] - struct Full<'a>(&'a Bytes); let mut builder = f.debug_tuple("Body"); match self.kind { - Kind::Once(None) => builder.field(&Empty), - Kind::Once(Some(ref chunk)) => builder.field(&Full(chunk)), + Kind::Empty => builder.field(&Empty), _ => builder.field(&Streaming), }; @@ -402,65 +378,6 @@ impl fmt::Debug for Body { } } -impl From for Body { - #[inline] - fn from(chunk: Bytes) -> Body { - if chunk.is_empty() { - Body::empty() - } else { - Body::new(Kind::Once(Some(chunk))) - } - } -} - -impl From> for Body { - #[inline] - fn from(vec: Vec) -> Body { - Body::from(Bytes::from(vec)) - } -} - -impl From<&'static [u8]> for Body { - #[inline] - fn from(slice: &'static [u8]) -> Body { - Body::from(Bytes::from(slice)) - } -} - -impl From> for Body { - #[inline] - fn from(cow: Cow<'static, [u8]>) -> Body { - match cow { - Cow::Borrowed(b) => Body::from(b), - Cow::Owned(o) => Body::from(o), - } - } -} - -impl From for Body { - #[inline] - fn from(s: String) -> Body { - Body::from(Bytes::from(s.into_bytes())) - } -} - -impl From<&'static str> for Body { - #[inline] - fn from(slice: &'static str) -> Body { - Body::from(Bytes::from(slice.as_bytes())) - } -} - -impl From> for Body { - #[inline] - fn from(cow: Cow<'static, str>) -> Body { - match cow { - Cow::Borrowed(b) => Body::from(b), - Cow::Owned(o) => Body::from(o), - } - } -} - impl Sender { /// Check to see if this `Sender` can send more data. pub fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll> { @@ -595,8 +512,6 @@ mod tests { assert_eq!(a.upper(), b.upper(), "upper for {:?}", note); } - eq(Body::from("Hello"), SizeHint::with_exact(5), "from str"); - eq(Body::empty(), SizeHint::with_exact(0), "empty"); eq(Body::channel().1, SizeHint::new(), "channel"); diff --git a/src/body/mod.rs b/src/body/mod.rs index 5e2181e941..8c6789daa5 100644 --- a/src/body/mod.rs +++ b/src/body/mod.rs @@ -29,33 +29,6 @@ mod body; mod length; mod to_bytes; -/// An optimization to try to take a full body if immediately available. -/// -/// This is currently limited to *only* `hyper::Body`s. -#[cfg(feature = "http1")] -pub(crate) fn take_full_data(body: &mut T) -> Option { - use std::any::{Any, TypeId}; - - // This static type check can be optimized at compile-time. - if TypeId::of::() == TypeId::of::() { - let mut full = (body as &mut dyn Any) - .downcast_mut::() - .expect("must be Body") - .take_full_data(); - // This second cast is required to make the type system happy. - // Without it, the compiler cannot reason that the type is actually - // `T::Data`. Oh wells. - // - // It's still a measurable win! - (&mut full as &mut dyn Any) - .downcast_mut::>() - .expect("must be T::Data") - .take() - } else { - None - } -} - fn _assert_send_sync() { fn _assert_send() {} fn _assert_sync() {} diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs index 21b111bbf2..4934271a35 100644 --- a/src/client/dispatch.rs +++ b/src/client/dispatch.rs @@ -370,7 +370,7 @@ mod tests { let (mut tx, mut rx) = channel::, Response>(); b.iter(move || { - let _ = tx.send(Request::default()).unwrap(); + let _ = tx.send(Request::new(Body::empty())).unwrap(); rt.block_on(async { loop { let poll_once = PollOnce(&mut rx); diff --git a/src/ffi/body.rs b/src/ffi/body.rs index 39ba5beffb..a455dae0e3 100644 --- a/src/ffi/body.rs +++ b/src/ffi/body.rs @@ -33,7 +33,7 @@ ffi_fn! { /// /// If not configured, this body acts as an empty payload. fn hyper_body_new() -> *mut hyper_body { - Box::into_raw(Box::new(hyper_body(Body::empty()))) + Box::into_raw(Box::new(hyper_body(Body::ffi()))) } ?= ptr::null_mut() } diff --git a/src/ffi/http_types.rs b/src/ffi/http_types.rs index ea10f139cb..90eb447081 100644 --- a/src/ffi/http_types.rs +++ b/src/ffi/http_types.rs @@ -39,7 +39,7 @@ type hyper_request_on_informational_callback = extern "C" fn(*mut c_void, *mut h ffi_fn! { /// Construct a new HTTP request. fn hyper_request_new() -> *mut hyper_request { - Box::into_raw(Box::new(hyper_request(Request::new(Body::empty())))) + Box::into_raw(Box::new(hyper_request(Request::new(Body::ffi())))) } ?= std::ptr::null_mut() } @@ -335,7 +335,7 @@ ffi_fn! { /// /// It is safe to free the response even after taking ownership of its body. fn hyper_response_body(resp: *mut hyper_response) -> *mut hyper_body { - let body = std::mem::take(non_null!(&mut *resp ?= std::ptr::null_mut()).0.body_mut()); + let body = std::mem::replace(non_null!(&mut *resp ?= std::ptr::null_mut()).0.body_mut(), crate::Body::empty()); Box::into_raw(Box::new(hyper_body(body))) } ?= std::ptr::null_mut() } diff --git a/src/proto/h1/conn.rs b/src/proto/h1/conn.rs index 37ab380f8b..2db8380c4c 100644 --- a/src/proto/h1/conn.rs +++ b/src/proto/h1/conn.rs @@ -522,24 +522,6 @@ where } } - pub(crate) fn write_full_msg(&mut self, head: MessageHead, body: B) { - if let Some(encoder) = - self.encode_head(head, Some(BodyLength::Known(body.remaining() as u64))) - { - let is_last = encoder.is_last(); - // Make sure we don't write a body if we weren't actually allowed - // to do so, like because its a HEAD request. - if !encoder.is_eof() { - encoder.danger_full_buf(body, self.io.write_buf()); - } - self.state.writing = if is_last { - Writing::Closed - } else { - Writing::KeepAlive - } - } - } - fn encode_head( &mut self, mut head: MessageHead, diff --git a/src/proto/h1/dispatch.rs b/src/proto/h1/dispatch.rs index 677131bfdd..5a5daf6d43 100644 --- a/src/proto/h1/dispatch.rs +++ b/src/proto/h1/dispatch.rs @@ -8,9 +8,7 @@ use tracing::{debug, trace}; use super::{Http1Transaction, Wants}; use crate::body::{Body, DecodedLength, HttpBody}; use crate::common::{task, Future, Pin, Poll, Unpin}; -use crate::proto::{ - BodyLength, Conn, Dispatched, MessageHead, RequestHead, -}; +use crate::proto::{BodyLength, Conn, Dispatched, MessageHead, RequestHead}; use crate::upgrade::OnUpgrade; pub(crate) struct Dispatcher { @@ -295,16 +293,7 @@ where && self.dispatch.should_poll() { if let Some(msg) = ready!(Pin::new(&mut self.dispatch).poll_msg(cx)) { - let (head, mut body) = msg.map_err(crate::Error::new_user_service)?; - - // Check if the body knows its full data immediately. - // - // If so, we can skip a bit of bookkeeping that streaming - // bodies need to do. - if let Some(full) = crate::body::take_full_data(&mut body) { - self.conn.write_full_msg(head, full); - return Poll::Ready(Ok(())); - } + let (head, body) = msg.map_err(crate::Error::new_user_service)?; let body_type = if body.is_end_stream() { self.body_rx.set(None); @@ -708,9 +697,15 @@ mod tests { let dispatcher = Dispatcher::new(Client::new(rx), conn); let _dispatcher = tokio::spawn(async move { dispatcher.await }); + let body = { + let (mut tx, body) = crate::Body::new_channel(DecodedLength::new(4), false); + tx.try_send_data("reee".into()).unwrap(); + body + }; + let req = crate::Request::builder() .method("POST") - .body(crate::Body::from("reee")) + .body(body) .unwrap(); let res = tx.try_send(req).unwrap().await.expect("response"); diff --git a/src/proto/h1/encode.rs b/src/proto/h1/encode.rs index f0aa261a4f..cb4a7841fe 100644 --- a/src/proto/h1/encode.rs +++ b/src/proto/h1/encode.rs @@ -180,39 +180,6 @@ impl Encoder { } } } - - /// Encodes the full body, without verifying the remaining length matches. - /// - /// This is used in conjunction with HttpBody::__hyper_full_data(), which - /// means we can trust that the buf has the correct size (the buf itself - /// was checked to make the headers). - pub(super) fn danger_full_buf(self, msg: B, dst: &mut WriteBuf>) - where - B: Buf, - { - debug_assert!(msg.remaining() > 0, "encode() called with empty buf"); - debug_assert!( - match self.kind { - Kind::Length(len) => len == msg.remaining() as u64, - _ => true, - }, - "danger_full_buf length mismatches" - ); - - match self.kind { - Kind::Chunked => { - let len = msg.remaining(); - trace!("encoding chunked {}B", len); - let buf = ChunkSize::new(len) - .chain(msg) - .chain(b"\r\n0\r\n\r\n" as &'static [u8]); - dst.buffer(buf); - } - _ => { - dst.buffer(msg); - } - } - } } impl Buf for EncodedBuf diff --git a/src/proto/h2/server.rs b/src/proto/h2/server.rs index d24e6bac5f..0a539692de 100644 --- a/src/proto/h2/server.rs +++ b/src/proto/h2/server.rs @@ -503,7 +503,6 @@ where } } - if !body.is_end_stream() { // automatically set Content-Length from body... if let Some(len) = body.size_hint().exact() { diff --git a/src/service/util.rs b/src/service/util.rs index 7cba1206f1..241d685b11 100644 --- a/src/service/util.rs +++ b/src/service/util.rs @@ -11,12 +11,14 @@ use crate::{Request, Response}; /// # Example /// /// ``` +/// use bytes::Bytes; /// use hyper::{Body, Request, Response, Version}; +/// use http_body_util::Full; /// use hyper::service::service_fn; /// /// let service = service_fn(|req: Request| async move { /// if req.version() == Version::HTTP_11 { -/// Ok(Response::new(Body::from("Hello World"))) +/// Ok(Response::new(Full::::from("Hello World"))) /// } else { /// // Note: it's usually better to return a Response /// // with an appropriate StatusCode instead of an Err. diff --git a/tests/client.rs b/tests/client.rs index 3408547822..77fc8de13a 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -3183,7 +3183,7 @@ mod conn { let mut body = req.into_body(); - let mut send_stream = respond.send_response(Response::default(), false).unwrap(); + let mut send_stream = respond.send_response(Response::new(()), false).unwrap(); send_stream.send_data("Bread?".into(), true).unwrap();