fetch_ccip/
fetch.rs

1use rand::Rng;
2use rand::rng;
3use reqwest::Client;
4use std::time::Duration;
5use tokio::time::sleep;
6
7/// 1回だけHTTP GETを実行し、レスポンスのテキストを返す
8async fn fetch_once(
9    client: &Client,
10    url: &str,
11) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
12    let resp = client.get(url).send().await?;
13    let text = resp.text().await?;
14    Ok(text)
15}
16
17/// RIRファイルを取得してテキストとして返す。
18/// リトライ + 指数バックオフ付き
19/// Tokioのマルチスレッド環境でエラーにならないように、乱数生成器 `rng()` は
20/// `.await` をまたぐ前にスコープから取り除き、値だけを取得して使う。
21pub async fn fetch_with_retry(
22    client: &Client,
23    url: &str,
24) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
25    let retry_attempts = 10;
26
27    for i in 0..retry_attempts {
28        match fetch_once(client, url).await {
29            Ok(text) => {
30                return Ok(text);
31            }
32            Err(e) => {
33                eprintln!(
34                    "HTTP取得エラー (attempt {} / {}): {}",
35                    i + 1,
36                    retry_attempts,
37                    e
38                );
39                // 指数バックオフ + ランダムスリープ
40                let random_part = {
41                    let mut local_rng = rng();
42                    local_rng.random_range(0.0..1.0)
43                };
44                let sleep_time = (2u64.pow(i) as f64) + random_part;
45                sleep(Duration::from_secs_f64(sleep_time)).await;
46            }
47        }
48    }
49
50    Err(format!(
51        "{}回試みてもデータを取得できませんでした: {}",
52        retry_attempts, url
53    )
54    .into())
55}