titan_http/body.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
use std::{
convert::Infallible,
pin::{pin, Pin},
task::Poll,
};
use bytes::Bytes;
use futures_core::Stream;
use http_body::{Frame, SizeHint};
/// Represents the body of an HTTP response.
///
/// The `Body` enum can either represent a fully-loaded body in memory or a stream of data.
/// It is used as the response body when implementing the `Respondable` trait, allowing
/// handlers to return either a complete body or a body that streams data incrementally.
///
/// # Variants
///
/// - `Full(Box<[u8]>)`:
/// This variant holds the full body of the response in memory as a boxed byte slice. This is typically used
/// when the response body is small enough to be loaded entirely into memory at once.
///
/// - `Stream(Pin<Box<dyn Stream<Item = Vec<u8>> + Send>>)`:
/// This variant represents a streaming body, where the body is returned incrementally in chunks. This is useful
/// when dealing with large responses (e.g., files or large datasets) that should be sent in multiple parts.
/// The stream yields `Vec<u8>` chunks, allowing the receiver to process the data incrementally as it arrives.
///
/// This enum is not usually used of the library user and is quite low-level.
pub enum Body {
Full(Box<[u8]>),
Stream(Pin<Box<dyn Stream<Item = Vec<u8>> + Send>>),
}
impl http_body::Body for Body {
type Data = Bytes;
type Error = Infallible;
fn size_hint(&self) -> http_body::SizeHint {
match self {
Body::Full(value) => SizeHint::with_exact(value.len() as u64),
Body::Stream(body) => {
let (lower, higher) = body.size_hint();
let mut size_hint = SizeHint::default();
size_hint.set_lower(lower as u64);
if let Some(higher) = higher {
size_hint.set_upper(higher as u64);
}
size_hint
}
}
}
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>>
{
match self.get_mut() {
Body::Full(body) => {
Poll::Ready(Some(Ok(Frame::data(Bytes::from(body.clone())))))
}
Body::Stream(body) => {
let value = pin!(body);
let value = match value.poll_next(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(value) => match value {
None => return Poll::Ready(None),
Some(value) => value,
},
};
let frame = Frame::data(Bytes::from(value));
Poll::Ready(Some(Ok(frame)))
}
}
}
fn is_end_stream(&self) -> bool {
// TODO
true
}
}
impl From<String> for Body {
fn from(value: String) -> Self {
Self::Full(value.as_bytes().into())
}
}
impl From<()> for Body {
fn from(_: ()) -> Self {
Self::Full([].into())
}
}
impl<'a> From<&'a str> for Body {
fn from(value: &'a str) -> Self {
Self::Full(value.as_bytes().into())
}
}
impl From<Box<[u8]>> for Body {
fn from(value: Box<[u8]>) -> Self {
Self::Full(value)
}
}
impl From<Vec<u8>> for Body {
fn from(value: Vec<u8>) -> Self {
Self::Full(value.into())
}
}
impl From<&'_ [u8]> for Body {
fn from(value: &'_ [u8]) -> Self {
Self::Full(value.into())
}
}
//impl From<StatusCode> for Body {
// fn from(value: StatusCode) -> Self {
// match value {
// StatusCode::OK => "Ok",
// StatusCode::BAD_REQUEST => "Bad Request",
// _ => panic!("no"),
// }
// .into()
// }
//}
//impl From<Body> for String {
// fn from(value: Body) -> Self {
// match value {
// Body::Full(bytes) => unsafe {
// String::from_utf8_unchecked(bytes.to_vec())
// },
// }
// }
//}
macro_rules! impl_tostring {
($( $type:ident )*) => {
$(impl From<$type> for Body {
fn from(value: $type) -> Self {
let body_str = value.to_string();
Self::Full(body_str.as_bytes().into())
}
})*
};
}
impl_tostring! { usize i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 }