Skip to main content

hpx_util/tower/delay/
layer.rs

1use std::time::Duration;
2
3use tower::Layer;
4
5use super::service::{Delay, DelayWith, JitterDelay, JitterDelayWith};
6
7/// A Tower [`Layer`] that introduces a fixed delay before each request.
8#[derive(Clone, Debug)]
9pub struct DelayLayer {
10    delay: Duration,
11}
12
13/// Conditional delay [`Layer`], applies delay based on a predicate.
14///
15/// Created via [`DelayLayer::when`].
16#[derive(Clone, Debug)]
17pub struct DelayLayerWith<P> {
18    delay: Duration,
19    predicate: P,
20}
21
22/// A Tower [`Layer`] that introduces a jittered delay before each request.
23///
24/// The actual delay for each request will be randomly chosen within the range
25/// `[base - base*pct, base + base*pct]`.
26///
27/// # Example
28///
29/// ```no_run
30/// use std::time::Duration;
31///
32/// use hpx::Client;
33/// use hpx_util::tower::delay::JitterDelayLayer;
34///
35/// // Creates delays in range [0.8s, 1.2s] (1s ± 20%)
36/// let client = Client::builder()
37///     .layer(JitterDelayLayer::new(Duration::from_secs(1), 0.2))
38///     .build()?;
39/// # Ok::<(), hpx::Error>(())
40/// ```
41#[derive(Clone, Debug)]
42pub struct JitterDelayLayer {
43    base: Duration,
44    pct: f64,
45}
46
47/// Conditional jitter delay [`Layer`], applies delay based on a predicate.
48///
49/// Created via [`JitterDelayLayer::when`].
50#[derive(Clone, Debug)]
51pub struct JitterDelayLayerWith<P> {
52    base: Duration,
53    pct: f64,
54    predicate: P,
55}
56
57// ===== impl DelayLayer =====
58
59impl DelayLayer {
60    /// Create a new [`DelayLayer`] with the given delay duration.
61    #[inline]
62    pub const fn new(delay: Duration) -> Self {
63        DelayLayer { delay }
64    }
65
66    /// Apply delay only to requests that satisfy a predicate.
67    ///
68    /// Requests that don't match the predicate will pass through without delay.
69    ///
70    /// # Example
71    ///
72    /// ```ignore
73    /// use std::time::Duration;
74    /// use http::Request;
75    /// use hpx_util::tower::delay::DelayLayer;
76    ///
77    /// // Only delay POST requests
78    /// let layer = DelayLayer::new(Duration::from_secs(1))
79    ///     .when(|req: &Request<_>| req.method() == http::Method::POST);
80    /// ```
81    pub fn when<P, Req>(self, predicate: P) -> DelayLayerWith<P>
82    where
83        P: Fn(&Req) -> bool + Clone,
84    {
85        DelayLayerWith::new(self.delay, predicate)
86    }
87}
88
89impl<S> Layer<S> for DelayLayer {
90    type Service = Delay<S>;
91
92    #[inline]
93    fn layer(&self, service: S) -> Self::Service {
94        Delay::new(service, self.delay)
95    }
96}
97
98// ===== impl DelayLayerWith =====
99
100impl<P> DelayLayerWith<P> {
101    /// Creates a new [`DelayLayerWith`].
102    #[inline]
103    pub fn new(delay: Duration, predicate: P) -> Self {
104        Self { delay, predicate }
105    }
106}
107
108impl<P, S> Layer<S> for DelayLayerWith<P>
109where
110    P: Clone,
111{
112    type Service = DelayWith<S, P>;
113
114    #[inline]
115    fn layer(&self, inner: S) -> Self::Service {
116        DelayWith::new(inner, self.delay, self.predicate.clone())
117    }
118}
119
120// ===== impl JitterDelayLayer =====
121
122impl JitterDelayLayer {
123    /// Create a new [`JitterDelayLayer`] with the given base delay and jitter percentage.
124    ///
125    /// # Arguments
126    /// * `base` - The base delay duration
127    /// * `pct` - The jitter percentage (0.0 to 1.0), representing ±pct deviation
128    ///
129    /// # Example
130    ///
131    /// ```
132    /// use std::time::Duration;
133    ///
134    /// use hpx_util::tower::delay::JitterDelayLayer;
135    ///
136    /// // Creates delays in range [800ms, 1200ms]
137    /// let layer = JitterDelayLayer::new(Duration::from_secs(1), 0.2);
138    /// ```
139    #[inline]
140    pub fn new(base: Duration, pct: f64) -> Self {
141        Self {
142            base,
143            pct: pct.clamp(0.0, 1.0),
144        }
145    }
146
147    /// Apply jitter delay only to requests that satisfy a predicate.
148    ///
149    /// Requests that don't match the predicate will pass through without delay.
150    ///
151    /// # Example
152    ///
153    /// ```ignore
154    /// use std::time::Duration;
155    /// use http::Request;
156    /// use hpx_util::tower::delay::JitterDelayLayer;
157    ///
158    /// // Only delay requests to paths starting with "/slow"
159    /// let layer = JitterDelayLayer::new(Duration::from_secs(1), 0.2)
160    ///     .when(|req: &Request<_>| req.uri().path().starts_with("/slow"));
161    /// ```
162    pub fn when<P, Req>(self, predicate: P) -> JitterDelayLayerWith<P>
163    where
164        P: Fn(&Req) -> bool + Clone,
165    {
166        JitterDelayLayerWith::new(self.base, self.pct, predicate)
167    }
168}
169
170impl<S> Layer<S> for JitterDelayLayer {
171    type Service = JitterDelay<S>;
172
173    #[inline]
174    fn layer(&self, inner: S) -> Self::Service {
175        JitterDelay::new(inner, self.base, self.pct)
176    }
177}
178
179// ===== impl JitterDelayLayerWith =====
180
181impl<P> JitterDelayLayerWith<P> {
182    /// Creates a new [`JitterDelayLayerWith`].
183    #[inline]
184    pub fn new(base: Duration, pct: f64, predicate: P) -> Self {
185        Self {
186            base,
187            pct: pct.clamp(0.0, 1.0),
188            predicate,
189        }
190    }
191}
192
193impl<P, S> Layer<S> for JitterDelayLayerWith<P>
194where
195    P: Clone,
196{
197    type Service = JitterDelayWith<S, P>;
198
199    #[inline]
200    fn layer(&self, inner: S) -> Self::Service {
201        JitterDelayWith::new(inner, self.base, self.pct, self.predicate.clone())
202    }
203}