use std::cell::RefCell;
use std::future::Future;
use std::rc::Rc;
use futures::channel::oneshot;
use futures::future::LocalBoxFuture;
use futures::FutureExt;
use crate::connection::{H2Connection, RequestInit, Response};
use crate::errors::H2Error;
pub trait PoolConnection {
fn is_closed(&self) -> bool;
fn can_open_stream(&self) -> bool;
fn request(&self, init: RequestInit) -> LocalBoxFuture<'static, Result<Response, H2Error>>;
fn close(&self);
}
impl PoolConnection for H2Connection {
fn is_closed(&self) -> bool {
H2Connection::is_closed(self)
}
fn can_open_stream(&self) -> bool {
H2Connection::can_open_stream(self)
}
fn request(&self, init: RequestInit) -> LocalBoxFuture<'static, Result<Response, H2Error>> {
let conn = self.clone();
async move { conn.request(init).await }.boxed_local()
}
fn close(&self) {
H2Connection::close(self)
}
}
type Factory = Rc<dyn Fn() -> LocalBoxFuture<'static, Result<Rc<dyn PoolConnection>, H2Error>>>;
struct PoolState {
conns: Vec<Rc<dyn PoolConnection>>,
opening: bool,
open_waiters: Vec<oneshot::Sender<()>>,
}
#[derive(Clone)]
pub struct H2Pool {
shared: Rc<RefCell<PoolState>>,
factory: Factory,
max_connections: usize,
}
impl H2Pool {
pub fn new<F, Fut>(factory: F, max_connections: usize) -> Self
where
F: Fn() -> Fut + 'static,
Fut: Future<Output = Result<Rc<dyn PoolConnection>, H2Error>> + 'static,
{
Self {
shared: Rc::new(RefCell::new(PoolState {
conns: Vec::new(),
opening: false,
open_waiters: Vec::new(),
})),
factory: Rc::new(move || factory().boxed_local()),
max_connections,
}
}
pub async fn request(&self, init: RequestInit) -> Result<Response, H2Error> {
let mut init = Some(init);
loop {
let chosen: Option<Rc<dyn PoolConnection>> = {
let mut st = self.shared.borrow_mut();
st.conns.retain(|c| !c.is_closed());
if let Some(c) = st.conns.iter().find(|c| c.can_open_stream()) {
Some(c.clone())
} else if !st.conns.is_empty() && st.conns.len() >= self.max_connections {
st.conns.first().cloned()
} else {
None
}
};
if let Some(conn) = chosen {
return conn.request(init.take().expect("request issued once")).await;
}
self.open_one().await?;
let fresh = self.shared.borrow().conns.last().cloned();
if let Some(conn) = fresh {
if !conn.is_closed() && !conn.can_open_stream() {
return conn.request(init.take().expect("request issued once")).await;
}
}
}
}
async fn open_one(&self) -> Result<(), H2Error> {
let wait = {
let mut st = self.shared.borrow_mut();
if st.opening {
let (tx, rx) = oneshot::channel();
st.open_waiters.push(tx);
Some(rx)
} else {
st.opening = true;
None
}
};
if let Some(rx) = wait {
let _ = rx.await; return Ok(());
}
let result = (self.factory)().await;
let mut st = self.shared.borrow_mut();
st.opening = false;
for tx in st.open_waiters.drain(..) {
let _ = tx.send(());
}
let conn = result?;
st.conns.push(conn);
Ok(())
}
pub fn connections(&self) -> usize {
self.shared
.borrow()
.conns
.iter()
.filter(|c| !c.is_closed())
.count()
}
pub fn close(&self) {
for conn in self.shared.borrow_mut().conns.drain(..) {
conn.close();
}
}
}