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}