From 008753f07a30bd5dbc875545f5012075ad88ae0d Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 23 Jan 2022 03:57:08 +0000 Subject: [PATCH] improve body docs --- actix-http/src/body/boxed.rs | 2 +- actix-http/src/body/message_body.rs | 65 ++++++++++++++++++++++++++--- actix-http/src/body/mod.rs | 5 +++ actix-http/src/body/none.rs | 7 +++- 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/actix-http/src/body/boxed.rs b/actix-http/src/body/boxed.rs index d109a6a74..cac6b7eb9 100644 --- a/actix-http/src/body/boxed.rs +++ b/actix-http/src/body/boxed.rs @@ -31,7 +31,7 @@ impl fmt::Debug for BoxBodyInner { } impl BoxBody { - /// Same as `MessageBody::boxed`. + /// Boxes body type, erasing type information. /// /// If the body type to wrap is unknown or generic it is better to use [`MessageBody::boxed`] to /// avoid double boxing. diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 0a605a69a..86ff09fbe 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -14,8 +14,44 @@ use pin_project_lite::pin_project; use super::{BodySize, BoxBody}; -/// An interface types that can converted to bytes and used as response bodies. -// TODO: examples +/// An interface for types that can be used as a response body. +/// +/// It is not usually necessary to create custom body types, this trait is already [implemented for +/// a large number of sensible body types](#foreign-impls) including: +/// - Empty body: `()` +/// - Text-based: `String`, `&'static str`, `ByteString`. +/// - Byte-based: `Bytes`, `BytesMut`, `Vec`, `&'static [u8]`; +/// - Streams: [`BodyStream`](super::BodyStream), [`SizedStream`](super::SizedStream) +/// +/// # Examples +/// ``` +/// # use std::convert::Infallible; +/// # use std::task::{Poll, Context}; +/// # use std::pin::Pin; +/// # use bytes::Bytes; +/// # use actix_http::body::{BodySize, MessageBody}; +/// struct Repeat { +/// chunk: String, +/// n_times: usize, +/// } +/// +/// impl MessageBody for Repeat { +/// type Error = Infallible; +/// +/// fn size(&self) -> BodySize { +/// BodySize::Sized((self.chunk.len() * self.n_times) as u64) +/// } +/// +/// fn poll_next( +/// self: Pin<&mut Self>, +/// _cx: &mut Context<'_>, +/// ) -> Poll>> { +/// let payload_string = self.chunk.repeat(self.n_times); +/// let payload_bytes = Bytes::from(payload_string); +/// Poll::Ready(Some(Ok(payload_bytes))) +/// } +/// } +/// ``` pub trait MessageBody { /// The type of error that will be returned if streaming body fails. /// @@ -29,7 +65,22 @@ pub trait MessageBody { fn size(&self) -> BodySize; /// Attempt to pull out the next chunk of body bytes. - // TODO: expand documentation + /// + /// # Return Value + /// Similar to the `Stream` interface, there are several possible return values, each indicating + /// a distinct state: + /// - `Poll::Pending` means that this body's next chunk is not ready yet. Implementations must + /// ensure that the current task will be notified when the next chunk may be ready. + /// - `Poll::Ready(Some(val))` means that the body has successfully produced a chunk, `val`, + /// and may produce further values on subsequent `poll_next` calls. + /// - `Poll::Ready(None)` means that the body is complete, and `poll_next` should not be + /// invoked again. + /// + /// # Panics + /// Once a body is complete (i.e., `poll_next` returned `Ready(None)`), calling its `poll_next` + /// method again may panic, block forever, or cause other kinds of problems; this trait places + /// no requirements on the effects of such a call. However, as the `poll_next` method is not + /// marked unsafe, Rust’s usual rules apply: calls must never cause UB, regardless of its state. fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -37,7 +88,7 @@ pub trait MessageBody { /// Try to convert into the complete chunk of body bytes. /// - /// Implement this method if the entire body can be trivially extracted. This is useful for + /// Override this method if the complete body can be trivially extracted. This is useful for /// optimizations where `poll_next` calls can be avoided. /// /// Body types with [`BodySize::None`] are allowed to return empty `Bytes`. Although, if calling @@ -54,7 +105,11 @@ pub trait MessageBody { Err(self) } - /// Converts this body into `BoxBody`. + /// Wraps this body into a `BoxBody`. + /// + /// No-op when called on a `BoxBody`, meaning there is no risk of double boxing when calling + /// this on a generic `MessageBody`. Prefer this over [`BoxBody::new`] when a boxed body + /// is required. #[inline] fn boxed(self) -> BoxBody where diff --git a/actix-http/src/body/mod.rs b/actix-http/src/body/mod.rs index af7c4626f..0fb090eb5 100644 --- a/actix-http/src/body/mod.rs +++ b/actix-http/src/body/mod.rs @@ -1,4 +1,9 @@ //! Traits and structures to aid consuming and writing HTTP payloads. +//! +//! "Body" and "payload" are used somewhat interchangeably in this documentation. + +// Though the spec kinda reads like "payload" is the possibly-transfer-encoded part of the message +// and the "body" is the intended possibly-decoded version of that. mod body_stream; mod boxed; diff --git a/actix-http/src/body/none.rs b/actix-http/src/body/none.rs index 0e7bbe5a9..b1d3f7f2a 100644 --- a/actix-http/src/body/none.rs +++ b/actix-http/src/body/none.rs @@ -10,9 +10,12 @@ use super::{BodySize, MessageBody}; /// Body type for responses that forbid payloads. /// -/// Distinct from an empty response which would contain a Content-Length header. -/// +/// This is distinct from an "empty" response which _would_ contain a `Content-Length` header. /// For an "empty" body, use `()` or `Bytes::new()`. +/// +/// For example, the HTTP spec forbids a payload to be sent with a `204 No Content` response. +/// In this case, the payload (or lack thereof) is implicit from the status code, so a +/// `Content-Length` header is not required. #[derive(Debug, Clone, Copy, Default)] #[non_exhaustive] pub struct None;