Skip to main content

trillium_http/
http_context.rs

1use crate::{
2    Conn, HttpConfig, Result, TypeSet, Upgrade, h1::H1Connection,
3    headers::header_observer::HeaderObserver,
4};
5use fieldwork::Fieldwork;
6use futures_lite::{AsyncRead, AsyncWrite};
7use std::{future::Future, sync::Arc};
8use swansong::{ShutdownCompletion, Swansong};
9/// A connection's own handle on the server's [`HttpContext`].
10///
11/// A conn holds this rather than the server's `Arc` directly. The server's is shared by every
12/// connection on every core, so cloning and dropping it for each request or stream would make that
13/// one refcount contended; clones of a connection's handle stay with the connection.
14pub(crate) type ConnectionContext = Arc<Arc<HttpContext>>;
15
16/// Shared configuration and context for an http server.
17///
18/// Contains tunable parameters in a [`HttpConfig`], the [`Swansong`] graceful shutdown control
19/// interface, and a shared [`TypeSet`] that contains application-specific information about the
20/// running server.
21#[derive(Default, Debug, Fieldwork)]
22#[fieldwork(get, set, get_mut, with)]
23pub struct HttpContext {
24    /// [`HttpConfig`] performance and security parameters
25    pub(crate) config: HttpConfig,
26
27    /// [`Swansong`] graceful shutdown interface
28    pub(crate) swansong: Swansong,
29
30    /// [`TypeSet`] shared state
31    pub(crate) shared_state: TypeSet,
32
33    /// Per-listener QPACK header-frequency observer. Shared by `Arc` across all connections
34    /// a given listener accepts; runtime adapters isolate it per hop-and-direction via
35    /// [`__isolate_qpack_observer`](Self::__isolate_qpack_observer).
36    #[cfg_attr(not(feature = "unstable"), field = false)]
37    pub(crate) observer: Arc<HeaderObserver>,
38}
39impl AsRef<TypeSet> for HttpContext {
40    fn as_ref(&self) -> &TypeSet {
41        &self.shared_state
42    }
43}
44
45impl AsMut<TypeSet> for HttpContext {
46    fn as_mut(&mut self) -> &mut TypeSet {
47        &mut self.shared_state
48    }
49}
50
51impl AsRef<Swansong> for HttpContext {
52    fn as_ref(&self) -> &Swansong {
53        &self.swansong
54    }
55}
56
57impl AsRef<HttpConfig> for HttpContext {
58    fn as_ref(&self) -> &HttpConfig {
59        &self.config
60    }
61}
62
63impl HttpContext {
64    /// Construct a new `HttpContext`
65    pub fn new() -> Self {
66        Self::default()
67    }
68
69    /// Perform HTTP on the provided transport, applying the provided `async Conn -> Conn` handler
70    /// function for every distinct http request-response.
71    ///
72    /// For any given invocation of `HttpContext::run`, the handler function may run any number of
73    /// times, depending on whether the connection is reused by the client.
74    ///
75    /// When the connection closes, the transport is dropped immediately. To close it gracefully
76    /// instead, use [`H1Connection`], which hands back an
77    /// [`H1Close`](crate::h1::H1Close) to await.
78    ///
79    /// # Errors
80    ///
81    /// This function will return an [`Error`](crate::Error) if any of the http requests is
82    /// irrecoverably malformed or otherwise noncompliant.
83    pub async fn run<Transport, Handler, Fut>(
84        self: Arc<Self>,
85        transport: Transport,
86        handler: Handler,
87    ) -> Result<Option<Upgrade<Transport>>>
88    where
89        Transport: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static,
90        Handler: FnMut(Conn<Transport>) -> Fut,
91        Fut: Future<Output = Conn<Transport>>,
92    {
93        H1Connection::new(self, transport)
94            .run(handler)
95            .await
96            .into_upgrade()
97    }
98
99    /// Attempt graceful shutdown of this server.
100    ///
101    /// The returned [`ShutdownCompletion`] type can
102    /// either be awaited in an async context or blocked on with [`ShutdownCompletion::block`] in a
103    /// blocking context
104    pub fn shut_down(&self) -> ShutdownCompletion {
105        self.swansong.shut_down()
106    }
107
108    /// Replace this context's QPACK header observer with a fresh, empty one.
109    ///
110    /// Adapter crates call this during listener setup so each hop-and-direction pair in a
111    /// deployment gets its own observer. A reverse proxy's inbound server observer is distinct
112    /// from its outbound client observer by construction, so header values one hop forwards
113    /// (e.g. `authorization`, `cookie`) cannot reach the QPACK state of unrelated clients on
114    /// the other hop.
115    ///
116    /// Not part of the stable public API; exposed only for adapter crates.
117    #[doc(hidden)]
118    pub fn __isolate_header_observer(&mut self) -> &mut Self {
119        self.observer = Arc::new(HeaderObserver::default());
120        self
121    }
122}
123
124/// Like [`HttpContext::run`], but starts with the supplied bytes pre-filled into the request
125/// buffer.
126///
127/// For adapters that peek the first few bytes off a cleartext TCP stream to decide between
128/// HTTP/1.1 and HTTP/2 prior-knowledge dispatch, then need to hand those bytes into the HTTP/1
129/// parser without re-reading. Bytes already in the buffer are consumed by the parser before
130/// any transport read happens.
131///
132/// # Errors
133///
134/// Same as [`HttpContext::run`] — any irrecoverably malformed or noncompliant HTTP/1 request
135/// surfaces as an [`Error`](crate::Error).
136#[deprecated(
137    since = "1.8.0",
138    note = "use `H1Connection::new(context, \
139            transport).with_initial_bytes(initial_bytes).run(handler)`"
140)]
141pub async fn run_with_initial_bytes<Transport, Handler, Fut>(
142    context: Arc<HttpContext>,
143    transport: Transport,
144    initial_bytes: Vec<u8>,
145    handler: Handler,
146) -> Result<Option<Upgrade<Transport>>>
147where
148    Transport: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static,
149    Handler: FnMut(Conn<Transport>) -> Fut,
150    Fut: Future<Output = Conn<Transport>>,
151{
152    H1Connection::new(context, transport)
153        .with_initial_bytes(initial_bytes)
154        .run(handler)
155        .await
156        .into_upgrade()
157}
158
159/// Parse a single HTTP/1.x request head off `transport` and return the resulting [`Conn`], without
160/// entering the handler/keepalive loop.
161///
162/// The request buffer is seeded at `request_buffer_initial_len` exactly as [`HttpContext::run`]
163/// seeds it, so this exercises the real head-read buffer-growth path. A complete-but-malformed head
164/// still yields a `Conn` (carrying the error status); only an incomplete head or transport IO error
165/// returns `Err`.
166///
167/// Exposed for microbenchmarking the parse path; not part of the stable public API.
168#[cfg(feature = "unstable")]
169#[doc(hidden)]
170pub async fn parse_head_for_bench<Transport>(
171    context: Arc<HttpContext>,
172    transport: Transport,
173) -> Result<Conn<Transport>>
174where
175    Transport: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static,
176{
177    let initial_bytes = Vec::with_capacity(context.config.request_buffer_initial_len);
178    match Conn::new_h1(context, transport, initial_bytes).await {
179        Ok(conn) => Ok(conn),
180        Err(crate::conn::HeadError::BadRequest(bad)) => Ok(bad),
181        Err(crate::conn::HeadError::Fatal(e)) => Err(e),
182    }
183}