use std::borrow::Cow;
use std::pin::Pin;
use std::rc::Rc;
use std::task::Context;
use std::task::Poll;
use std::task::ready;
use bytes::Bytes;
use bytes::BytesMut;
use deno_core::AsyncRefCell;
use deno_core::AsyncResult;
use deno_core::BufView;
use deno_core::RcRef;
use deno_core::Resource;
use deno_core::futures::Stream;
use deno_core::futures::StreamExt;
use deno_core::futures::TryFutureExt;
use deno_core::futures::stream::Peekable;
use deno_core::futures::task::noop_waker;
use deno_error::JsErrorBox;
use hyper::body::Body;
use hyper::body::Frame;
use hyper::body::Incoming;
use hyper::body::SizeHint;
pub struct BufferedIncoming {
inner: Incoming,
pending: BytesMut,
done: bool,
}
impl BufferedIncoming {
pub fn new(inner: Incoming) -> Self {
Self {
inner,
pending: BytesMut::new(),
done: false,
}
}
pub fn try_take_full(&mut self) -> Option<Vec<u8>> {
let waker = noop_waker();
let mut cx = Context::from_waker(&waker);
loop {
if self.done {
return Some(std::mem::take(&mut self.pending).to_vec());
}
match Pin::new(&mut self.inner).poll_frame(&mut cx) {
Poll::Ready(Some(Ok(frame))) => {
if let Ok(data) = frame.into_data() {
self.pending.extend_from_slice(&data);
}
}
Poll::Ready(Some(Err(_))) => return None,
Poll::Ready(None) => {
self.done = true;
return Some(std::mem::take(&mut self.pending).to_vec());
}
Poll::Pending => return None,
}
}
}
}
impl Body for BufferedIncoming {
type Data = Bytes;
type Error = hyper::Error;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
let this = self.get_mut();
if !this.pending.is_empty() {
let chunk = this.pending.split().freeze();
return Poll::Ready(Some(Ok(Frame::data(chunk))));
}
if this.done {
return Poll::Ready(None);
}
match Pin::new(&mut this.inner).poll_frame(cx) {
Poll::Ready(None) => {
this.done = true;
Poll::Ready(None)
}
other => other,
}
}
fn size_hint(&self) -> SizeHint {
let pending_len = self.pending.len() as u64;
if pending_len == 0 {
return self.inner.size_hint();
}
let inner_hint = self.inner.size_hint();
let mut hint = SizeHint::new();
hint.set_lower(inner_hint.lower().saturating_add(pending_len));
if let Some(upper) = inner_hint.upper() {
hint.set_upper(upper.saturating_add(pending_len));
}
hint
}
fn is_end_stream(&self) -> bool {
self.done && self.pending.is_empty()
}
}
struct ReadFuture(BufferedIncoming);
impl Stream for ReadFuture {
type Item = Result<Bytes, hyper::Error>;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
let res = ready!(Pin::new(&mut this.0).poll_frame(cx));
break match res {
Some(Ok(frame)) => {
if let Ok(data) = frame.into_data() {
if !data.is_empty() {
break Poll::Ready(Some(Ok(data)));
}
}
continue;
}
Some(Err(e)) => Poll::Ready(Some(Err(e))),
None => Poll::Ready(None),
};
}
}
}
pub struct HttpRequestBody(AsyncRefCell<Peekable<ReadFuture>>, SizeHint);
impl HttpRequestBody {
pub fn new(body: BufferedIncoming) -> Self {
let size_hint = body.size_hint();
Self(AsyncRefCell::new(ReadFuture(body).peekable()), size_hint)
}
async fn read(self: Rc<Self>, limit: usize) -> Result<BufView, hyper::Error> {
let peekable = RcRef::map(self, |this| &this.0);
let mut peekable = peekable.borrow_mut().await;
match Pin::new(&mut *peekable).peek_mut().await {
None => Ok(BufView::empty()),
Some(Err(_)) => Err(peekable.next().await.unwrap().err().unwrap()),
Some(Ok(bytes)) => {
if bytes.len() <= limit {
return Ok(BufView::from(peekable.next().await.unwrap()?));
}
let ret = bytes.split_to(limit);
Ok(BufView::from(ret))
}
}
}
}
impl Resource for HttpRequestBody {
fn name(&self) -> Cow<'_, str> {
"requestBody".into()
}
fn read(self: Rc<Self>, limit: usize) -> AsyncResult<BufView> {
Box::pin(
HttpRequestBody::read(self, limit)
.map_err(|e| JsErrorBox::new("Http", e.to_string())),
)
}
fn size_hint(&self) -> (u64, Option<u64>) {
(self.1.lower(), self.1.upper())
}
}