Skip to main content

hitbox_http/extractors/
version.rs

1//! HTTP version extraction for cache keys.
2//!
3//! Provides [`Version`] extractor for including the HTTP protocol version
4//! in cache keys.
5
6use async_trait::async_trait;
7use hitbox::{Extractor, KeyPart, KeyParts};
8
9use super::NeutralExtractor;
10use crate::CacheableHttpRequest;
11
12/// Extracts the HTTP protocol version as a cache key part.
13///
14/// Generates a key part with name `"version"` and value like `"HTTP/1.1"` or `"HTTP/2"`.
15///
16/// # Examples
17///
18/// ```
19/// use hitbox_http::extractors::{Method, version::VersionExtractor};
20///
21/// # use bytes::Bytes;
22/// # use http_body_util::Empty;
23/// # use hitbox_http::extractors::{NeutralExtractor, Version};
24/// // Include version in cache key
25/// let extractor = Method::new().version();
26/// # let _: &Version<Method<NeutralExtractor<Empty<Bytes>>>> = &extractor;
27/// ```
28#[derive(Debug)]
29pub struct Version<E> {
30    inner: E,
31}
32
33impl<S> Version<NeutralExtractor<S>> {
34    /// Creates a version extractor for cache key generation.
35    ///
36    /// Adds a key part with name `"version"` and the HTTP protocol version
37    /// as value (e.g., `"HTTP/1.1"`, `"HTTP/2"`).
38    ///
39    /// Chain onto existing extractors using [`VersionExtractor::version`] instead
40    /// if you already have an extractor chain.
41    pub fn new() -> Self {
42        Self {
43            inner: NeutralExtractor::new(),
44        }
45    }
46}
47
48impl<S> Default for Version<NeutralExtractor<S>> {
49    fn default() -> Self {
50        Self::new()
51    }
52}
53
54/// Extension trait for adding version extraction to an extractor chain.
55///
56/// # For Callers
57///
58/// Chain this to include the HTTP protocol version in your cache key.
59/// The version is added as a key part with name `"version"` and value
60/// like `"HTTP/1.1"` or `"HTTP/2"`.
61///
62/// # For Implementors
63///
64/// This trait is automatically implemented for all [`Extractor`]
65/// types. You don't need to implement it manually.
66pub trait VersionExtractor: Sized {
67    /// Adds HTTP version extraction to this extractor chain.
68    fn version(self) -> Version<Self>;
69}
70
71impl<E> VersionExtractor for E
72where
73    E: Extractor,
74{
75    fn version(self) -> Version<Self> {
76        Version { inner: self }
77    }
78}
79
80#[async_trait]
81impl<ReqBody, E> Extractor for Version<E>
82where
83    ReqBody: hyper::body::Body + Send + 'static,
84    ReqBody::Error: Send,
85    E: Extractor<Subject = CacheableHttpRequest<ReqBody>> + Send + Sync,
86{
87    type Subject = E::Subject;
88
89    async fn get(&self, subject: Self::Subject) -> KeyParts<Self::Subject> {
90        let version = format!("{:?}", subject.parts().version);
91        let mut parts = self.inner.get(subject).await;
92        parts.push(KeyPart::new("version", Some(version)));
93        parts
94    }
95}