fastly 0.6.0-beta2

Fastly Compute@Edge API
Documentation
//! HTTP bodies.

pub(crate) mod handle;
pub(crate) mod streaming;

use self::handle::BodyHandle;
use crate::abi;
use fastly_shared::BodyWriteEnd;
use std::fmt::Debug;
use std::io::{BufRead, BufReader, BufWriter, Read, Write};

pub use streaming::StreamingBody;

/// An HTTP body that can be read from, written to, or appended to another body.
///
/// The most efficient ways to read from and write to the body are through the [`Read`],
/// [`BufRead`], and [`Write`] implementations.
///
/// Read and write operations to a [`Body`] are automatically buffered, though you can take direct
/// control over aspects of the buffering using the [`BufRead`] methods and [`Write::flush()`].
pub struct Body {
    reader: BufReader<BodyHandle>,
    writer: BufWriter<BodyHandle>,
}

impl Debug for Body {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "<opaque Body>")
    }
}

impl Body {
    /// Get a new, empty HTTP body.
    #[allow(clippy::new_without_default)]
    pub fn new() -> Self {
        BodyHandle::new().into()
    }

    // this is not exported, since misuse can lead to data getting dropped or appearing out of order
    fn handle(&mut self) -> &mut BodyHandle {
        self.reader.get_mut()
    }

    /// Convert a [`Body`] into the low-level [`BodyHandle`] interface.
    pub fn into_handle(mut self) -> BodyHandle {
        // we need to make sure that any currently buffered data in either the reader or writer is
        // put (back) in the host body
        let read_buf = self.reader.buffer();
        if !read_buf.is_empty() {
            let mut nwritten = 0;
            let status = unsafe {
                abi::fastly_http_body::write(
                    self.reader.get_ref().as_u32(),
                    read_buf.as_ptr(),
                    read_buf.len(),
                    BodyWriteEnd::Front,
                    &mut nwritten,
                )
            };
            assert!(status.is_ok(), "fastly_http_body::write_front failed");
            assert!(
                nwritten == read_buf.len(),
                "fastly_http_body::write_front didn't fully write"
            );
        }
        self.writer.flush().expect("fastly_http_body::write failed");
        self.reader.into_inner()
    }

    /// Read the entirety of the body into a byte vector.
    ///
    #[cfg_attr(
        feature = "unstable-doc",
        doc(include = "../../docs/snippets/buffers-body.md")
    )]
    pub fn into_bytes(self) -> Vec<u8> {
        self.into_handle().into_bytes()
    }

    /// Read the entirety of the body into a `String`, interpreting the bytes as UTF-8.
    ///
    #[cfg_attr(
        feature = "unstable-doc",
        doc(include = "../../docs/snippets/buffers-body.md")
    )]
    ///
    /// # Panics
    ///
    #[cfg_attr(
        feature = "unstable-doc",
        doc(include = "../../docs/snippets/panics-body-utf8.md")
    )]
    pub fn into_string(self) -> String {
        self.into_handle().into_string()
    }

    /// Append another body onto the end of this body.
    ///
    #[cfg_attr(
        feature = "unstable-doc",
        doc(include = "../../docs/snippets/body-append-constant-time.md")
    )]
    pub fn append(&mut self, other: Body) {
        // flush the write buffer of the destination body, so that we can use the append method on
        // the underlying handles. Unwrap, as `BodyHandle::flush` won't return actionable errors.
        self.writer.flush().expect("fastly_http_body::write failed");
        self.handle().append(other.into_handle())
    }

    /// Write a slice of bytes to the end of this body, and return the number of bytes written.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # let mut body = fastly::Body::new();
    /// body.write_bytes(&[0, 1, 2, 3]);
    /// ```
    pub fn write_bytes(&mut self, bytes: &[u8]) -> usize {
        self.writer
            .write(bytes)
            .expect("fastly_http_body::write failed")
    }

    /// Write a string slice to the end of this body, and return the number of bytes written.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # let mut body = fastly::Body::new();
    /// body.write_str("woof woof");
    /// ```
    pub fn write_str(&mut self, string: &str) -> usize {
        self.write_bytes(string.as_ref())
    }

    /// Return an iterator that reads the body in chunks of at most the given number of bytes.
    ///
    /// If `chunk_size` does not evenly divide the length of the body, then the last chunk will not
    /// have length `chunk_size`.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use fastly::Body;
    /// fn remove_0s(body: &mut Body) {
    ///     let mut no_0s = Body::new();
    ///     for chunk in body.read_chunks(4096) {
    ///         let mut chunk = chunk.unwrap();
    ///         chunk.retain(|b| *b != 0);
    ///         no_0s.write_bytes(&chunk);
    ///     }
    ///     *body = no_0s;
    /// }
    /// ```
    pub fn read_chunks<'a>(
        &'a mut self,
        chunk_size: usize,
    ) -> impl Iterator<Item = Result<Vec<u8>, std::io::Error>> + 'a {
        std::iter::from_fn(move || {
            let mut chunk = Vec::with_capacity(chunk_size);
            match self.read(&mut chunk) {
                Ok(0) => None,
                Ok(nread) => {
                    chunk.truncate(nread);
                    Some(Ok(chunk))
                }
                Err(e) => Some(Err(e)),
            }
        })
    }
}

// For these trait implementations we only implement the methods that the underlying buffered
// adaptors implement; the default implementations for the others will behave the same.
//
// The main bit of caution we must use here is that any read should be preceded by flushing the
// write buffer. `BufWriter` doesn't make any calls if its buffer is empty, so this isn't very
// expensive and could prevent unexpected results if a program is trying to read and write from the
// same body.
impl Read for Body {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.writer.flush()?;
        self.reader.read(buf)
    }

    fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut]) -> std::io::Result<usize> {
        self.writer.flush()?;
        self.reader.read_vectored(bufs)
    }
}

impl BufRead for Body {
    fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
        self.writer.flush()?;
        self.reader.fill_buf()
    }

    fn consume(&mut self, amt: usize) {
        self.reader.consume(amt)
    }
}

impl Write for Body {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.writer.write(buf)
    }

    fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
        self.writer.write_vectored(bufs)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.writer.flush()
    }
}

impl From<BodyHandle> for Body {
    fn from(handle: BodyHandle) -> Self {
        // we clone the handle here in order to have an owned type for the reader and writer, but
        // this means we have to be careful that we don't make the aliasing observable from the
        // public interface
        let handle2 = unsafe { BodyHandle::from_u32(handle.as_u32()) };
        Self {
            reader: BufReader::new(handle),
            writer: BufWriter::new(handle2),
        }
    }
}

impl From<&str> for Body {
    fn from(s: &str) -> Self {
        BodyHandle::from(s).into()
    }
}

impl From<String> for Body {
    fn from(s: String) -> Self {
        BodyHandle::from(s).into()
    }
}

impl From<&[u8]> for Body {
    fn from(s: &[u8]) -> Self {
        BodyHandle::from(s).into()
    }
}

impl From<Vec<u8>> for Body {
    fn from(s: Vec<u8>) -> Self {
        BodyHandle::from(s).into()
    }
}