use std::net::SocketAddr;
use std::num::NonZeroUsize;
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicUsize, Ordering};
use std::sync::{Arc, Weak};
use std::task::{Context, Poll};
use std::time::Duration;
use crate::clock::Instant;
pub(crate) struct ConnectionMetrics {
pub(crate) requests_served: AtomicU32,
pub(crate) bytes_sent: AtomicU64,
pub(crate) bytes_received: AtomicU64,
}
impl ConnectionMetrics {
fn new() -> Self {
Self {
requests_served: AtomicU32::new(0),
bytes_sent: AtomicU64::new(0),
bytes_received: AtomicU64::new(0),
}
}
}
pub(crate) enum HttpConnection<B> {
H1(hyper::client::conn::http1::SendRequest<B>),
H2(hyper::client::conn::http2::SendRequest<B>),
#[cfg(all(feature = "http3", feature = "rustls"))]
H3(crate::h3_transport::H3Connection),
}
pub(crate) struct PooledConnection<B> {
pub(crate) conn: HttpConnection<B>,
pub(crate) remote_addr: Option<SocketAddr>,
pub(crate) tls_info: Option<crate::tls::TlsInfo>,
pub(crate) tls_handshake_duration: Option<Duration>,
pub(crate) sans: Arc<[String]>,
pub(crate) created_at: Instant,
pub(crate) metrics: Arc<ConnectionMetrics>,
pub(crate) is_multiplex_clone: bool,
active_streams: Option<Arc<AtomicUsize>>,
multiplex_retired: Arc<AtomicBool>,
_active_stream_permit: Option<ActiveStreamPermit>,
pub(crate) upgrade_handle_local: Option<crate::upgrade::UpgradeHandleLocal>,
pub(crate) pool: Weak<std::sync::Mutex<super::PoolInner<B>>>,
pub(crate) key: Option<super::PoolKey>,
}
pub(crate) struct ActiveStreamPermit {
active: Arc<AtomicUsize>,
retired: Arc<AtomicBool>,
}
impl ActiveStreamPermit {
pub(crate) fn retire_transport(&self) {
self.retired.store(true, Ordering::Release);
}
}
impl Drop for ActiveStreamPermit {
fn drop(&mut self) {
self.active.fetch_sub(1, Ordering::AcqRel);
}
}
impl<B> PooledConnection<B> {
pub(crate) fn version(&self) -> http::Version {
match &self.conn {
HttpConnection::H1(_) => http::Version::HTTP_11,
HttpConnection::H2(_) => http::Version::HTTP_2,
#[cfg(all(feature = "http3", feature = "rustls"))]
HttpConnection::H3(_) => http::Version::HTTP_3,
}
}
pub(crate) fn retire_multiplex_transport(&mut self) {
if !self.is_h2_or_h3() {
return;
}
self.multiplex_retired.store(true, Ordering::Release);
if let (Some(pool), Some(key)) = (self.pool.upgrade(), self.key.as_ref())
&& let Ok(mut inner) = pool.lock()
{
inner.idle.remove(key);
}
self.pool = Weak::new();
self.key = None;
}
pub(crate) fn new_h1(sender: hyper::client::conn::http1::SendRequest<B>) -> Self {
Self {
conn: HttpConnection::H1(sender),
remote_addr: None,
tls_info: None,
tls_handshake_duration: None,
sans: Arc::from([]),
created_at: Instant::now(),
metrics: Arc::new(ConnectionMetrics::new()),
is_multiplex_clone: false,
active_streams: None,
multiplex_retired: Arc::new(AtomicBool::new(false)),
_active_stream_permit: None,
upgrade_handle_local: None,
pool: Weak::new(),
key: None,
}
}
pub(crate) fn new_h2(sender: hyper::client::conn::http2::SendRequest<B>) -> Self {
Self {
conn: HttpConnection::H2(sender),
remote_addr: None,
tls_info: None,
tls_handshake_duration: None,
sans: Arc::from([]),
created_at: Instant::now(),
metrics: Arc::new(ConnectionMetrics::new()),
is_multiplex_clone: false,
active_streams: Some(Arc::new(AtomicUsize::new(0))),
multiplex_retired: Arc::new(AtomicBool::new(false)),
_active_stream_permit: None,
upgrade_handle_local: None,
pool: Weak::new(),
key: None,
}
}
#[cfg(all(feature = "http3", feature = "rustls"))]
pub(crate) fn new_h3(sender: crate::h3_transport::H3Connection) -> Self {
Self {
conn: HttpConnection::H3(sender),
remote_addr: None,
tls_info: None,
tls_handshake_duration: None,
sans: Arc::from([]),
created_at: Instant::now(),
metrics: Arc::new(ConnectionMetrics::new()),
is_multiplex_clone: false,
active_streams: Some(Arc::new(AtomicUsize::new(0))),
multiplex_retired: Arc::new(AtomicBool::new(false)),
_active_stream_permit: None,
upgrade_handle_local: None,
pool: Weak::new(),
key: None,
}
}
pub(crate) fn is_ready(&self) -> bool {
if self.multiplex_retired.load(Ordering::Acquire) {
return false;
}
match &self.conn {
HttpConnection::H1(s) => s.is_ready(),
HttpConnection::H2(s) => s.is_ready(),
#[cfg(all(feature = "http3", feature = "rustls"))]
HttpConnection::H3(s) => s.is_ready(),
}
}
pub(crate) fn is_h1(&self) -> bool {
matches!(&self.conn, HttpConnection::H1(_))
}
#[cfg(all(feature = "http3", feature = "rustls"))]
pub(crate) fn is_h3(&self) -> bool {
matches!(&self.conn, HttpConnection::H3(_))
}
pub(crate) fn supports_unsent_request_recovery(&self) -> bool {
match &self.conn {
HttpConnection::H1(_) | HttpConnection::H2(_) => true,
#[cfg(all(feature = "http3", feature = "rustls"))]
HttpConnection::H3(_) => false,
}
}
pub(crate) fn is_h2_or_h3(&self) -> bool {
match &self.conn {
HttpConnection::H1(_) => false,
HttpConnection::H2(_) => true,
#[cfg(all(feature = "http3", feature = "rustls"))]
HttpConnection::H3(_) => true,
}
}
pub(crate) fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<bool> {
match &mut self.conn {
HttpConnection::H1(s) => match s.poll_ready(cx) {
Poll::Ready(Ok(())) => Poll::Ready(true),
Poll::Ready(Err(_)) => Poll::Ready(false),
Poll::Pending => Poll::Pending,
},
HttpConnection::H2(s) => {
let _ = s;
Poll::Ready(true)
}
#[cfg(all(feature = "http3", feature = "rustls"))]
HttpConnection::H3(_) => Poll::Ready(true),
}
}
fn acquire_multiplex_permit(
&self,
max_active: Option<NonZeroUsize>,
) -> Option<ActiveStreamPermit> {
let active = self.active_streams.as_ref()?.clone();
if let Some(max_active) = max_active {
let max = max_active.get();
let mut current = active.load(Ordering::Acquire);
loop {
if current >= max {
return None;
}
match active.compare_exchange_weak(
current,
current + 1,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => {
return Some(ActiveStreamPermit {
active,
retired: self.multiplex_retired.clone(),
});
}
Err(observed) => current = observed,
}
}
}
active.fetch_add(1, Ordering::AcqRel);
Some(ActiveStreamPermit {
active,
retired: self.multiplex_retired.clone(),
})
}
#[cfg(all(test, feature = "tokio"))]
pub(crate) fn active_multiplex_streams(&self) -> Option<usize> {
self.active_streams
.as_ref()
.map(|active| active.load(Ordering::Acquire))
}
pub(crate) fn record_request(&self, body_size: u64) {
self.metrics
.bytes_sent
.fetch_add(body_size, Ordering::Relaxed);
self.metrics.requests_served.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn record_bytes_received(&self, len: u64) {
self.metrics
.bytes_received
.fetch_add(len, Ordering::Relaxed);
}
pub(crate) fn take_active_stream_permit(&mut self) -> Option<ActiveStreamPermit> {
self._active_stream_permit.take()
}
pub(crate) fn requests_served(&self) -> u32 {
self.metrics.requests_served.load(Ordering::Relaxed)
}
pub(crate) fn bytes_sent(&self) -> u64 {
self.metrics.bytes_sent.load(Ordering::Relaxed)
}
pub(crate) fn bytes_received(&self) -> u64 {
self.metrics.bytes_received.load(Ordering::Relaxed)
}
}
impl<B> Drop for PooledConnection<B> {
fn drop(&mut self) {
if let Some(ref key) = self.key
&& let Some(pool_inner) = self.pool.upgrade()
&& let Ok(mut inner) = pool_inner.lock()
{
super::decrement_active(&mut inner, key);
}
}
}
impl<B: 'static> PooledConnection<B> {
#[cfg(all(test, feature = "tokio"))]
pub(crate) fn clone_for_multiplex(&self) -> Option<Self> {
self.clone_for_multiplex_with_limit(None)
}
pub(crate) fn clone_for_multiplex_with_limit(
&self,
max_active: Option<NonZeroUsize>,
) -> Option<Self> {
if self.multiplex_retired.load(Ordering::Acquire) {
return None;
}
let active_stream_permit = self.acquire_multiplex_permit(max_active)?;
let conn = match &self.conn {
HttpConnection::H1(_) => return None,
HttpConnection::H2(s) => HttpConnection::H2(s.clone()),
#[cfg(all(feature = "http3", feature = "rustls"))]
HttpConnection::H3(s) => HttpConnection::H3(s.clone()),
};
Some(Self {
conn,
remote_addr: self.remote_addr,
tls_info: self.tls_info.clone(),
tls_handshake_duration: self.tls_handshake_duration,
sans: self.sans.clone(),
created_at: self.created_at,
metrics: Arc::clone(&self.metrics),
is_multiplex_clone: true,
active_streams: self.active_streams.clone(),
multiplex_retired: self.multiplex_retired.clone(),
_active_stream_permit: Some(active_stream_permit),
upgrade_handle_local: None,
pool: self.pool.clone(),
key: self.key.clone(),
})
}
}