Skip to main content

hitbox_http/predicates/version/
predicate.rs

1//! HTTP version predicate implementation.
2
3use async_trait::async_trait;
4use hitbox::Neutral;
5use hitbox::predicate::{Predicate, PredicateResult};
6use http::Version;
7
8use super::operation::Operation;
9
10/// A predicate that matches subjects by HTTP protocol version.
11///
12/// Works with any subject implementing [`HasVersion`], including both
13/// requests and responses.
14///
15/// # Type Parameters
16///
17/// * `P` - The inner predicate to chain with. Use [`HttpVersion::new`] to start
18///   a new predicate chain (uses [`Neutral`] internally), or use the
19///   [`VersionPredicate`] extension trait to chain onto an existing predicate.
20///
21/// # Examples
22///
23/// ```
24/// use hitbox_http::predicates::version::{HttpVersion, Operation};
25/// use http::Version;
26///
27/// # use bytes::Bytes;
28/// # use http_body_util::Empty;
29/// # use hitbox::Neutral;
30/// # use hitbox_http::CacheableHttpRequest;
31/// # type Subject = CacheableHttpRequest<Empty<Bytes>>;
32/// // Cache only HTTP/2 requests
33/// let predicate = HttpVersion::new(Operation::Eq(Version::HTTP_2));
34/// # let _: &HttpVersion<Neutral<Subject>> = &predicate;
35/// ```
36#[derive(Debug)]
37pub struct HttpVersion<P> {
38    pub(crate) operation: Operation,
39    pub(crate) inner: P,
40}
41
42impl<S> HttpVersion<Neutral<S>> {
43    /// Creates a version predicate that matches the HTTP protocol version.
44    ///
45    /// Returns [`Cacheable`](hitbox::predicate::PredicateResult::Cacheable) when
46    /// the version satisfies the operation, [`NonCacheable`](hitbox::predicate::PredicateResult::NonCacheable) otherwise.
47    pub fn new(operation: Operation) -> Self {
48        Self {
49            operation,
50            inner: Neutral::new(),
51        }
52    }
53}
54
55/// Extension trait for adding version matching to a predicate chain.
56///
57/// # For Callers
58///
59/// Chain this to match requests or responses by their HTTP protocol version.
60/// Useful for applying different caching strategies based on HTTP/1.1 vs HTTP/2.
61///
62/// # For Implementors
63///
64/// This trait is automatically implemented for all [`Predicate`](hitbox::predicate::Predicate)
65/// types. You don't need to implement it manually.
66pub trait VersionPredicate: Sized {
67    /// Adds a version match to this predicate chain.
68    fn version(self, operation: Operation) -> HttpVersion<Self>;
69}
70
71impl<P> VersionPredicate for P
72where
73    P: Predicate,
74{
75    fn version(self, operation: Operation) -> HttpVersion<Self> {
76        HttpVersion {
77            operation,
78            inner: self,
79        }
80    }
81}
82
83/// Trait for types that provide access to the HTTP protocol version.
84///
85/// Implement this trait to enable version predicates on custom types.
86/// Both [`CacheableHttpRequest`](crate::CacheableHttpRequest) and
87/// [`CacheableHttpResponse`](crate::CacheableHttpResponse) implement this trait.
88pub trait HasVersion {
89    /// Returns the HTTP protocol version.
90    fn http_version(&self) -> Version;
91}
92
93// Generic implementation for any subject that has an HTTP version
94#[async_trait]
95impl<P> Predicate for HttpVersion<P>
96where
97    P: Predicate + Send + Sync,
98    P::Subject: HasVersion + Send,
99{
100    type Subject = P::Subject;
101
102    async fn check(&self, subject: Self::Subject) -> PredicateResult<Self::Subject> {
103        match self.inner.check(subject).await {
104            PredicateResult::Cacheable(subject) => {
105                if self.operation.check(subject.http_version()) {
106                    PredicateResult::Cacheable(subject)
107                } else {
108                    PredicateResult::NonCacheable(subject)
109                }
110            }
111            PredicateResult::NonCacheable(subject) => PredicateResult::NonCacheable(subject),
112        }
113    }
114}