use core::cell::RefCell;
use core::error::Error;
use core::pin::pin;
use core::time::Duration;
use futures::{FutureExt, StreamExt};
use hyper::body::{Body, Incoming};
use hyper::service::Service;
use hyper::{Request, Response};
use hyper_util::rt::{TokioExecutor, TokioIo};
use hyper_util::server::conn::auto::Builder;
use std::rc::Rc;
use tokio::net::TcpListener;
use tokio::sync::watch;
use tokio::task::JoinHandle;
use tokio_stream::wrappers::TcpListenerStream;
pub type ConnectionsError = Box<dyn Error + Send + Sync>;
pub const DRAIN: Duration = Duration::from_secs(5);
pub struct HyperConnections {
accepting: JoinHandle<Result<(), ConnectionsError>>,
closing: watch::Sender<bool>,
serving: Rc<RefCell<Vec<JoinHandle<()>>>>,
drain: Duration,
}
impl Drop for HyperConnections {
fn drop(&mut self) {
self.accepting.abort();
for connection in self.serving.borrow().iter() {
connection.abort();
}
}
}
impl HyperConnections {
pub fn serve<Answering, ResponseBody>(listener: TcpListener, service: Answering, drain: Duration) -> Self
where
Answering: Service<Request<Incoming>, Response = Response<ResponseBody>> + Clone + 'static,
Answering::Error: Into<ConnectionsError>,
Answering::Future: Send,
ResponseBody: Body + Send + 'static,
ResponseBody::Data: Send,
ResponseBody::Error: Into<ConnectionsError>,
{
let serving = Rc::new(RefCell::new(Vec::<JoinHandle<()>>::new()));
let (closing, closed) = watch::channel(false);
let connections = TcpListenerStream::new(listener).filter_map(|accepted| async move { accepted.ok() });
let accepting = tokio::task::spawn_local(
connections
.for_each({
let serving = Rc::clone(&serving);
move |stream| {
let service = service.clone();
let serving = Rc::clone(&serving);
let mut closed = closed.clone();
async move {
let connection = tokio::task::spawn_local(async move {
let connection = Builder::new(TokioExecutor::new());
let mut connection = pin!(connection.serve_connection(TokioIo::new(stream), service));
tokio::select! {
_ = connection.as_mut() => {}
_ = closed.changed() => {
connection.as_mut().graceful_shutdown();
let _ = connection.await;
}
}
});
let mut serving = serving.borrow_mut();
serving.retain(|connection| !connection.is_finished());
serving.push(connection);
}
}
})
.map(Ok),
);
Self { accepting, closing, serving, drain }
}
pub async fn close(&mut self) {
self.accepting.abort();
if !self.accepting.is_finished() {
let _ = (&mut self.accepting).await;
}
let _ = self.closing.send(true);
}
pub async fn end(&mut self) {
self.close().await;
let mut serving = self.serving.take();
let draining = async {
for connection in &mut serving {
let _ = connection.await;
}
};
let _ = tokio::time::timeout(self.drain, draining).await;
for connection in &serving {
connection.abort();
}
}
}