use std::pin::Pin; use std::sync::mpsc as std_mpsc; use std::task::{Context, Poll}; use std::time::Instant; use bytes::Bytes; use hyper::body::{Body, Frame}; use crate::ProxyEvent; /// A body wrapper that emits `ResponseBodyChunk` per frame and /// `ResponseBodyComplete` when the stream finishes. pub struct MeasuredBody { inner: B, request_id: u64, bytes_count: u64, chunks: Vec, event_tx: std_mpsc::Sender, start: Instant, finished: bool, } impl MeasuredBody { pub fn new( inner: B, request_id: u64, start: Instant, event_tx: std_mpsc::Sender, ) -> Self { Self { inner, request_id, bytes_count: 0, chunks: Vec::new(), event_tx, start, finished: false, } } fn send_complete(&mut self) { if !self.finished { self.finished = true; let body = if self.chunks.is_empty() { None } else { let mut buf = Vec::with_capacity(self.bytes_count as usize); for chunk in self.chunks.drain(..) { buf.extend_from_slice(&chunk); } Some(buf) }; let _ = self.event_tx.send(ProxyEvent::ResponseBodyComplete { id: self.request_id, body, size: self.bytes_count, elapsed_ms: self.start.elapsed().as_millis() as u64, }); } } } impl Body for MeasuredBody where B: Body + Unpin, B::Error: std::error::Error + Send + Sync + 'static, { type Data = Bytes; type Error = B::Error; fn poll_frame( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>>> { let inner = Pin::new(&mut self.inner); match inner.poll_frame(cx) { Poll::Ready(Some(Ok(frame))) => { if let Some(data) = frame.data_ref() { let len = data.len(); self.bytes_count += len as u64; self.chunks.push(data.clone()); let _ = self .event_tx .send(ProxyEvent::ResponseBodyChunk { id: self.request_id, bytes: len }); } Poll::Ready(Some(Ok(frame))) } Poll::Ready(Some(Err(e))) => { self.send_complete(); Poll::Ready(Some(Err(e))) } Poll::Ready(None) => { self.send_complete(); Poll::Ready(None) } Poll::Pending => Poll::Pending, } } fn is_end_stream(&self) -> bool { self.inner.is_end_stream() } fn size_hint(&self) -> hyper::body::SizeHint { self.inner.size_hint() } } impl Drop for MeasuredBody { fn drop(&mut self) { self.send_complete(); } }