use crate::{
Buffer, Conn, ConnectionStatus, HttpConfig, Result, TypeSet, Upgrade,
headers::header_observer::HeaderObserver,
};
use fieldwork::Fieldwork;
use futures_lite::{AsyncRead, AsyncWrite};
use std::{future::Future, sync::Arc};
use swansong::{ShutdownCompletion, Swansong};
#[derive(Default, Debug, Fieldwork)]
#[fieldwork(get, set, get_mut, with)]
pub struct HttpContext {
pub(crate) config: HttpConfig,
pub(crate) swansong: Swansong,
pub(crate) shared_state: TypeSet,
#[cfg_attr(not(feature = "unstable"), field = false)]
pub(crate) observer: Arc<HeaderObserver>,
}
impl AsRef<TypeSet> for HttpContext {
fn as_ref(&self) -> &TypeSet {
&self.shared_state
}
}
impl AsMut<TypeSet> for HttpContext {
fn as_mut(&mut self) -> &mut TypeSet {
&mut self.shared_state
}
}
impl AsRef<Swansong> for HttpContext {
fn as_ref(&self) -> &Swansong {
&self.swansong
}
}
impl AsRef<HttpConfig> for HttpContext {
fn as_ref(&self) -> &HttpConfig {
&self.config
}
}
impl HttpContext {
pub fn new() -> Self {
Self::default()
}
pub async fn run<Transport, Handler, Fut>(
self: Arc<Self>,
transport: Transport,
handler: Handler,
) -> Result<Option<Upgrade<Transport>>>
where
Transport: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static,
Handler: FnMut(Conn<Transport>) -> Fut,
Fut: Future<Output = Conn<Transport>>,
{
let initial_bytes = Vec::with_capacity(self.config.request_buffer_initial_len);
run_with_initial_bytes(self, transport, initial_bytes, handler).await
}
pub fn shut_down(&self) -> ShutdownCompletion {
self.swansong.shut_down()
}
#[doc(hidden)]
pub fn __isolate_qpack_observer(&mut self) -> &mut Self {
self.observer = Arc::new(HeaderObserver::default());
log::trace!(
target: "qpack_metrics",
"isolated fresh QPACK observer for this context (ptr={:p})",
Arc::as_ptr(&self.observer),
);
self
}
}
pub async fn run_with_initial_bytes<Transport, Handler, Fut>(
context: Arc<HttpContext>,
transport: Transport,
initial_bytes: Vec<u8>,
mut handler: Handler,
) -> Result<Option<Upgrade<Transport>>>
where
Transport: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static,
Handler: FnMut(Conn<Transport>) -> Fut,
Fut: Future<Output = Conn<Transport>>,
{
let _guard = context.swansong.guard();
let buffer: Buffer = initial_bytes.into();
let mut conn = Conn::new_internal(context, transport, buffer).await?;
loop {
conn = match handler(conn).await.send().await? {
ConnectionStatus::Upgrade(upgrade) => return Ok(Some(upgrade)),
ConnectionStatus::Close => return Ok(None),
ConnectionStatus::Conn(next) => next,
}
}
}