Skip to main content

vibeio_http/
early_hints.rs

1use http::{HeaderMap, Request};
2use http_body::Body;
3
4type EarlyHintsResult = Result<(), std::io::Error>;
5pub(super) type EarlyHintsMessage = (
6    HeaderMap,
7    futures_util::lock::Mutex<oneshot::Sender<EarlyHintsResult>>,
8);
9
10#[derive(Clone)]
11pub(super) struct EarlyHints {
12    inner: kanal::AsyncSender<EarlyHintsMessage>,
13}
14
15impl EarlyHints {
16    #[inline]
17    pub(super) fn new(inner: kanal::AsyncSender<EarlyHintsMessage>) -> Self {
18        Self { inner }
19    }
20
21    #[inline]
22    async fn send(&self, headers: HeaderMap) -> EarlyHintsResult {
23        let (tx, rx) = oneshot::async_channel();
24        self.inner
25            .send((headers, futures_util::lock::Mutex::new(tx)))
26            .await
27            .map_err(std::io::Error::other)?;
28        rx.await.map_err(std::io::Error::other)?
29    }
30}
31
32/// Sends a `103 Early Hints` response for the given request.
33///
34/// Early hints allow the server to push `Link` (or other) headers to the
35/// client before the final response is ready, enabling the browser to begin
36/// preloading resources (stylesheets, scripts, fonts, etc.) while the handler
37/// is still computing the response.
38///
39/// # Requirements
40///
41/// The connection must be HTTP/2 or HTTP/3, or if HTTP/1.1, the server must
42/// have been created with
43/// [`Http1Options::enable_early_hints`](crate::h1::Http1Options::enable_early_hints)
44/// set to `true`. When that option is enabled the handler inserts an
45/// `EarlyHints` token into each request's extensions. If the token is absent
46/// (i.e. early hints were not enabled) this function returns an error.
47///
48/// # Errors
49///
50/// Returns `Err` if:
51/// - Early hints are not enabled on the connection (`"early hints not supported"`).
52/// - The connection handler has already shut down and can no longer accept
53///   interim responses.
54/// - Writing the `103` response to the underlying I/O stream fails.
55///
56/// # Example
57///
58/// ```rust,ignore
59/// use http::HeaderMap;
60///
61/// let mut link_headers = HeaderMap::new();
62/// link_headers.insert(
63///     http::header::LINK,
64///     "</style.css>; rel=preload; as=style".parse().unwrap(),
65/// );
66/// send_early_hints(&mut request, link_headers).await?;
67/// ```
68#[inline]
69pub async fn send_early_hints(
70    req: &mut Request<impl Body>,
71    headers: HeaderMap,
72) -> Result<(), std::io::Error> {
73    req.extensions()
74        .get::<EarlyHints>()
75        .ok_or_else(|| std::io::Error::other("early hints not supported"))?
76        .send(headers)
77        .await
78}