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;
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 {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
BodyHandle::new().into()
}
fn handle(&mut self) -> &mut BodyHandle {
self.reader.get_mut()
}
pub fn into_handle(mut self) -> BodyHandle {
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()
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/buffers-body.md")
)]
pub fn into_bytes(self) -> Vec<u8> {
self.into_handle().into_bytes()
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/buffers-body.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/panics-body-utf8.md")
)]
pub fn into_string(self) -> String {
self.into_handle().into_string()
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/body-append-constant-time.md")
)]
pub fn append(&mut self, other: Body) {
self.writer.flush().expect("fastly_http_body::write failed");
self.handle().append(other.into_handle())
}
pub fn write_bytes(&mut self, bytes: &[u8]) -> usize {
self.writer
.write(bytes)
.expect("fastly_http_body::write failed")
}
pub fn write_str(&mut self, string: &str) -> usize {
self.write_bytes(string.as_ref())
}
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)),
}
})
}
}
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 {
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()
}
}