Skip to main content

trillium_http/
http_context.rs

1use crate::{
2    Conn, ConnectionStatus, HttpConfig, Result, TypeSet, Upgrade, headers::qpack::HeaderObserver,
3};
4use fieldwork::Fieldwork;
5use futures_lite::{AsyncRead, AsyncWrite};
6use std::{future::Future, sync::Arc};
7use swansong::{ShutdownCompletion, Swansong};
8/// This struct represents the shared configuration and context for a http server.
9///
10/// This currently contains tunable parameters in a [`HttpConfig`], the [`Swansong`] graceful
11/// shutdown control interface, and a shared [`TypeSet`] that contains application-specific
12/// information about the running server
13#[derive(Default, Debug, Fieldwork)]
14#[fieldwork(get, set, get_mut, with)]
15pub struct HttpContext {
16    /// [`HttpConfig`] performance and security parameters
17    pub(crate) config: HttpConfig,
18
19    /// [`Swansong`] graceful shutdown interface
20    pub(crate) swansong: Swansong,
21
22    /// [`TypeSet`] shared state
23    pub(crate) shared_state: TypeSet,
24
25    /// Per-listener QPACK header-frequency observer. Shared by `Arc` across all connections
26    /// a given listener accepts; runtime adapters isolate it per hop-and-direction via
27    /// [`__isolate_qpack_observer`](Self::__isolate_qpack_observer).
28    #[cfg_attr(not(feature = "unstable"), field = false)]
29    pub(crate) observer: Arc<HeaderObserver>,
30}
31impl AsRef<TypeSet> for HttpContext {
32    fn as_ref(&self) -> &TypeSet {
33        &self.shared_state
34    }
35}
36
37impl AsMut<TypeSet> for HttpContext {
38    fn as_mut(&mut self) -> &mut TypeSet {
39        &mut self.shared_state
40    }
41}
42
43impl AsRef<Swansong> for HttpContext {
44    fn as_ref(&self) -> &Swansong {
45        &self.swansong
46    }
47}
48
49impl AsRef<HttpConfig> for HttpContext {
50    fn as_ref(&self) -> &HttpConfig {
51        &self.config
52    }
53}
54
55impl HttpContext {
56    /// Construct a new `HttpContext`
57    pub fn new() -> Self {
58        Self::default()
59    }
60
61    /// Perform HTTP on the provided transport, applying the provided `async Conn -> Conn` handler
62    /// function for every distinct http request-response.
63    ///
64    /// For any given invocation of `HttpContext::run`, the handler function may run any number of
65    /// times, depending on whether the connection is reused by the client.
66    ///
67    /// This can only be called on an `Arc<HttpContext>` because an arc clone is moved into the
68    /// Conn.
69    ///
70    /// # Errors
71    ///
72    /// This function will return an [`Error`](crate::Error) if any of the http requests is
73    /// irrecoverably malformed or otherwise noncompliant.
74    pub async fn run<Transport, Handler, Fut>(
75        self: Arc<Self>,
76        transport: Transport,
77        mut handler: Handler,
78    ) -> Result<Option<Upgrade<Transport>>>
79    where
80        Transport: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static,
81        Handler: FnMut(Conn<Transport>) -> Fut,
82        Fut: Future<Output = Conn<Transport>>,
83    {
84        let _guard = self.swansong.guard();
85        let buffer = Vec::with_capacity(self.config.request_buffer_initial_len).into();
86
87        let mut conn = Conn::new_internal(self, transport, buffer).await?;
88
89        loop {
90            conn = match handler(conn).await.send().await? {
91                ConnectionStatus::Upgrade(upgrade) => return Ok(Some(upgrade)),
92                ConnectionStatus::Close => return Ok(None),
93                ConnectionStatus::Conn(next) => next,
94            }
95        }
96    }
97
98    /// Attempt graceful shutdown of this server.
99    ///
100    /// The returned [`ShutdownCompletion`] type can
101    /// either be awaited in an async context or blocked on with [`ShutdownCompletion::block`] in a
102    /// blocking context
103    pub fn shut_down(&self) -> ShutdownCompletion {
104        self.swansong.shut_down()
105    }
106
107    /// Replace this context's QPACK header observer with a fresh, empty one.
108    ///
109    /// Runtime adapters (trillium-server-common, trillium-client) call this during listener
110    /// setup so that each hop-and-direction pair in a deployment gets its own observer. A
111    /// reverse proxy's inbound server observer is distinct from its outbound client observer
112    /// by construction, so header values one hop forwards (e.g. `authorization`, `cookie`)
113    /// cannot reach the QPACK state of unrelated clients on the other hop.
114    ///
115    /// Not part of the stable public API; exposed only so adapter crates can call it.
116    #[doc(hidden)]
117    pub fn __isolate_qpack_observer(&mut self) -> &mut Self {
118        self.observer = Arc::new(HeaderObserver::default());
119        log::trace!(
120            target: "qpack_metrics",
121            "isolated fresh QPACK observer for this context (ptr={:p})",
122            Arc::as_ptr(&self.observer),
123        );
124        self
125    }
126}