fastly 0.6.0-beta1

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 lazy_static::lazy_static;
use std::fmt::Debug;
use std::io::{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 way to read and write the body is through the `Read`, `BufRead`, and `Write`
/// implementations.
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`](struct.Body.html) into a [`BodyHandle`](struct.BodyHandle.html).
    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.
    ///
    /// Note that this will involve copying and buffering the body, and so should only be used for
    /// convenience on small request bodies.
    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.
    ///
    /// Note that this will involve copying and buffering the body, and so should only be used for
    /// convenience on small request bodies.
    pub fn into_string(self) -> String {
        self.into_handle().into_string()
    }

    /// Append another body onto the end of this body.
    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`](struct.Body.html).
    ///
    /// This function returns the number of bytes written.
    ///
    /// ```rust,no_run
    /// # use fastly::Body;
    /// # let mut body = 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`](struct.Body.html).
    ///
    /// This function returns the number of bytes written.
    ///
    /// ```rust,no_run
    /// # use fastly::Body;
    /// # let mut body = Body::new();
    /// body.write_str("woof woof");
    /// ```
    pub fn write_str(&mut self, string: &str) -> usize {
        self.write_bytes(string.as_ref())
    }
}

// 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 std::io::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()
    }
}

lazy_static! {
    pub(crate) static ref EMPTY_BODY: Body = Body::new();
}