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: async_channel::Sender<EarlyHintsMessage>,
13}
14
15impl EarlyHints {
16 #[inline]
17 pub(super) fn new(inner: async_channel::Sender<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}