use crate::abi;
use fastly_shared::BodyWriteEnd;
use std::io::{BufReader, Read, Write};
use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct BodyHandle {
pub(super) handle: u32,
}
pub(crate) static GOT_CLIENT_BODY: AtomicBool = AtomicBool::new(false);
impl BodyHandle {
pub const INVALID: Self = Self {
handle: fastly_shared::INVALID_BODY_HANDLE,
};
pub fn is_valid(&self) -> bool {
!self.is_invalid()
}
pub fn is_invalid(&self) -> bool {
self.handle == Self::INVALID.handle
}
pub(crate) fn from_u32(handle: u32) -> Self {
Self { handle }
}
pub(crate) unsafe fn as_u32(&self) -> u32 {
self.handle
}
pub(crate) fn as_u32_mut(&mut self) -> &mut u32 {
&mut self.handle
}
pub(crate) fn set_got_client() {
if GOT_CLIENT_BODY.swap(true, Ordering::SeqCst) {
panic!("cannot get more than one handle to the client body per execution");
}
}
pub fn from_client() -> Self {
Self::set_got_client();
let mut handle = BodyHandle::INVALID;
let status = unsafe {
abi::fastly_http_req::body_downstream_get(std::ptr::null_mut(), handle.as_u32_mut())
};
match status.result().map(|_| handle) {
Ok(h) if h.is_valid() => h,
_ => panic!("fastly_http_req::body_downstream_get failed"),
}
}
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
let mut handle = BodyHandle::INVALID;
let status = unsafe { abi::fastly_http_body::new(handle.as_u32_mut()) };
match status.result().map(|_| handle) {
Ok(h) if h.is_valid() => h,
_ => panic!("fastly_http_body::new failed"),
}
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../../docs/snippets/body-append-constant-time.md")
)]
pub fn append(&mut self, other: BodyHandle) {
unsafe { abi::fastly_http_body::append(self.as_u32(), other.as_u32()) }
.result()
.expect("fastly_http_body::append failed")
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../../docs/snippets/buffers-body-handle.md")
)]
pub fn into_bytes(self) -> Vec<u8> {
let mut body = vec![];
let mut bufread = BufReader::new(self);
bufread
.read_to_end(&mut body)
.expect("fastly_http_body::read failed");
body
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../../docs/snippets/buffers-body-handle.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../../docs/snippets/panics-body-utf8.md")
)]
pub fn into_string(self) -> String {
let mut body = String::new();
let mut bufread = BufReader::new(self);
bufread
.read_to_string(&mut body)
.expect("fastly_http_body::read failed");
body
}
pub fn write_bytes(&mut self, bytes: &[u8]) -> usize {
let mut nwritten = 0;
let status = unsafe {
abi::fastly_http_body::write(
self.as_u32(),
bytes.as_ptr(),
bytes.len(),
BodyWriteEnd::Back,
&mut nwritten,
)
};
status
.result()
.map(|_| nwritten)
.expect("fastly_http_body::write failed")
}
pub fn write_str(&mut self, string: &str) -> usize {
self.write_bytes(string.as_bytes())
}
pub(crate) fn write_front(&mut self, bytes: &[u8]) -> usize {
let mut nwritten = 0;
let status = unsafe {
abi::fastly_http_body::write(
self.as_u32(),
bytes.as_ptr(),
bytes.len(),
BodyWriteEnd::Front,
&mut nwritten,
)
};
assert!(status.is_ok(), "fastly_http_body::write_front failed");
assert!(
nwritten == bytes.len(),
"fastly_http_body::write_front didn't fully write"
);
nwritten
}
}
impl Read for BodyHandle {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
use std::io::{Error, ErrorKind};
let mut nread = 0;
let status = unsafe {
abi::fastly_http_body::read(self.as_u32(), buf.as_mut_ptr(), buf.len(), &mut nread)
};
if status.is_err() {
Err(Error::new(
ErrorKind::Other,
"fastly_http_body::read failed",
))
} else {
Ok(nread)
}
}
}
impl Write for BodyHandle {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Ok(self.write_bytes(buf))
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl From<&str> for BodyHandle {
fn from(s: &str) -> Self {
let mut handle = Self::new();
handle.write_str(s);
handle
}
}
impl From<String> for BodyHandle {
fn from(s: String) -> Self {
Self::from(s.as_str())
}
}
impl From<&[u8]> for BodyHandle {
fn from(s: &[u8]) -> Self {
let mut handle = Self::new();
handle.write_bytes(s);
handle
}
}
impl From<Vec<u8>> for BodyHandle {
fn from(s: Vec<u8>) -> Self {
Self::from(s.as_slice())
}
}