openzeppelin_monitor/utils/
http.rs1use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
2use reqwest_retry::{
3 policies::ExponentialBackoff, Jitter, RetryTransientMiddleware, RetryableStrategy,
4};
5use serde::{Deserialize, Serialize};
6use std::time::Duration;
7
8fn default_max_attempts() -> u32 {
10 3
11}
12
13fn default_initial_backoff() -> Duration {
14 Duration::from_millis(250)
15}
16
17fn default_max_backoff() -> Duration {
18 Duration::from_secs(10)
19}
20
21fn default_base_for_backoff() -> u32 {
22 2
23}
24
25#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
27#[serde(rename_all = "lowercase")]
28enum JitterSetting {
29 None,
31 #[default]
33 Full,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
38pub struct HttpRetryConfig {
39 #[serde(default = "default_max_attempts")]
41 pub max_retries: u32,
42 #[serde(default = "default_base_for_backoff")]
44 pub base_for_backoff: u32,
45 #[serde(default = "default_initial_backoff")]
47 pub initial_backoff: Duration,
48 #[serde(default = "default_max_backoff")]
50 pub max_backoff: Duration,
51 #[serde(default)]
53 jitter: JitterSetting,
54}
55
56impl Default for HttpRetryConfig {
57 fn default() -> Self {
59 Self {
60 max_retries: default_max_attempts(),
61 base_for_backoff: default_base_for_backoff(),
62 initial_backoff: default_initial_backoff(),
63 max_backoff: default_max_backoff(),
64 jitter: JitterSetting::default(),
65 }
66 }
67}
68
69pub fn create_retryable_http_client<S>(
80 config: &HttpRetryConfig,
81 base_client: reqwest::Client,
82 custom_strategy: Option<S>,
83) -> ClientWithMiddleware
84where
85 S: RetryableStrategy + Send + Sync + 'static,
86{
87 let policy_builder = match config.jitter {
89 JitterSetting::None => ExponentialBackoff::builder().jitter(Jitter::None),
90 JitterSetting::Full => ExponentialBackoff::builder().jitter(Jitter::Full),
91 };
92
93 let retry_policy = policy_builder
95 .base(config.base_for_backoff)
96 .retry_bounds(config.initial_backoff, config.max_backoff)
97 .build_with_max_retries(config.max_retries);
98
99 if let Some(strategy) = custom_strategy {
101 ClientBuilder::new(base_client).with(
102 RetryTransientMiddleware::new_with_policy_and_strategy(retry_policy, strategy),
103 )
104 } else {
105 ClientBuilder::new(base_client)
106 .with(RetryTransientMiddleware::new_with_policy(retry_policy))
107 }
108 .build()
109}