use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use std::time::Duration;
use hyper::body::{Body, Frame, Incoming, SizeHint};
use hyper::service::Service;
use hyper::{Request, Response};
use pin_project_lite::pin_project;
use tokio::sync::watch;
pub(crate) struct StreamCounter {
open: watch::Sender<usize>,
}
impl StreamCounter {
pub(crate) fn new() -> Arc<Self> {
Arc::new(Self {
open: watch::channel(0).0,
})
}
fn enter(self: &Arc<Self>) -> StreamGuard {
self.open.send_modify(|n| *n += 1);
StreamGuard(self.clone())
}
pub(crate) fn watch(&self) -> watch::Receiver<usize> {
self.open.subscribe()
}
}
pub(crate) struct StreamGuard(Arc<StreamCounter>);
impl Drop for StreamGuard {
fn drop(&mut self) {
self.0.open.send_modify(|n| *n = n.saturating_sub(1));
}
}
pub(crate) struct TrackedService<Svc> {
inner: Svc,
counter: Arc<StreamCounter>,
}
impl<Svc> TrackedService<Svc> {
pub(crate) fn new(inner: Svc, counter: Arc<StreamCounter>) -> Self {
Self { inner, counter }
}
}
impl<Svc, B> Service<Request<Incoming>> for TrackedService<Svc>
where
Svc: Service<Request<Incoming>, Response = Response<B>>,
Svc::Future: Send + 'static,
B: Send + 'static,
{
type Response = Response<GuardedBody<B>>;
type Error = Svc::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn call(&self, req: Request<Incoming>) -> Self::Future {
let guard = self.counter.enter();
let fut = self.inner.call(req);
Box::pin(async move {
let resp = fut.await?;
Ok(resp.map(|inner| GuardedBody {
inner,
_guard: guard,
}))
})
}
}
pin_project! {
pub(crate) struct GuardedBody<B> {
#[pin]
inner: B,
_guard: StreamGuard,
}
}
impl<B: Body> Body for GuardedBody<B> {
type Data = B::Data;
type Error = B::Error;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
self.project().inner.poll_frame(cx)
}
fn is_end_stream(&self) -> bool {
self.inner.is_end_stream()
}
fn size_hint(&self) -> SizeHint {
self.inner.size_hint()
}
}
pub(crate) async fn wait_idle(mut open: watch::Receiver<usize>, idle: Duration) {
loop {
let count = *open.borrow_and_update();
if count != 0 {
if open.changed().await.is_err() {
return;
}
continue;
}
tokio::select! {
_ = tokio::time::sleep(idle) => return,
r = open.changed() => {
if r.is_err() {
return;
}
}
}
}
}