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}