1use crate::constants::MAX_RIR_DOWNLOAD_BYTES;
2use crate::error::AppError;
3use futures::StreamExt;
4use rand::Rng;
5use reqwest::Client;
6use std::time::Duration;
7use tokio::time::sleep;
8use crate::common::debug_log;
9
10async fn read_body_with_limit_to_string(
12 resp: reqwest::Response,
13 max_bytes: u64,
14) -> Result<String, AppError> {
15 let mut total: u64 = 0;
16 let mut buf: Vec<u8> = Vec::new();
17
18 let mut stream = resp.bytes_stream();
19 while let Some(chunk) = stream.next().await {
20 let chunk = chunk?; total = total.saturating_add(chunk.len() as u64);
22 if total > max_bytes {
23 return Err(AppError::Other(format!(
24 "Response too large ({} bytes > {} bytes)",
25 total, max_bytes
26 )));
27 }
28 buf.extend_from_slice(&chunk);
29 }
30
31 let text = String::from_utf8(buf)?; Ok(text)
33}
34
35async fn fetch_once(client: &Client, url: &str) -> Result<String, AppError> {
36 let resp = client.get(url).send().await?.error_for_status()?; if let Some(len) = resp.content_length() {
39 if len > MAX_RIR_DOWNLOAD_BYTES {
40 return Err(AppError::Other(format!(
41 "Response too large ({} bytes > {} bytes): {}",
42 len, MAX_RIR_DOWNLOAD_BYTES, url
43 )));
44 }
45 }
46 read_body_with_limit_to_string(resp, MAX_RIR_DOWNLOAD_BYTES).await
48}
49
50pub async fn fetch_with_retry(
53 client: &Client,
54 url: &str,
55 retry_attempts: u32,
56 max_backoff_secs: u64,
57) -> Result<String, AppError> {
58 let attempts = retry_attempts.max(1);
59 for i in 0..attempts {
60 match fetch_once(client, url).await {
61 Ok(text) => {
62 return Ok(text);
63 }
64 Err(e) => {
65 debug_log(format!(
66 "fetch attempt {}/{} failed: {}",
67 i + 1,
68 attempts,
69 e
70 ));
71 if i + 1 < attempts {
73 let sleep_duration = calc_exponential_backoff_duration(i, max_backoff_secs);
74 sleep(sleep_duration).await;
75 }
76 }
77 }
78 }
79
80 Err(AppError::Other(format!(
82 "Failed to fetch data from {} after {} attempts",
83 url, attempts
84 )))
85}
86
87fn calc_exponential_backoff_duration(retry_count: u32, max_backoff_secs: u64) -> Duration {
89 let mut rng = rand::rng();
92 let exp = 2u64.saturating_pow(retry_count);
93 let cap = max_backoff_secs.max(1);
94 let range = exp.min(cap) as f64;
95 let wait_secs = rng.random::<f64>() * range;
96 Duration::from_secs_f64(wait_secs)
97}
98
99pub async fn fetch_json_with_limit<T: serde::de::DeserializeOwned>(
101 client: &Client,
102 url: &str,
103 max_bytes: u64,
104) -> Result<T, AppError> {
105 let resp = client.get(url).send().await?.error_for_status()?;
106
107 if let Some(len) = resp.content_length() {
108 if len > max_bytes {
109 return Err(AppError::Other(format!(
110 "JSON response too large ({} bytes > {} bytes): {}",
111 len, max_bytes, url
112 )));
113 }
114 }
115
116 let text = read_body_with_limit_to_string(resp, max_bytes).await?;
118 let value = serde_json::from_str::<T>(&text)
119 .map_err(|e| AppError::ParseError(format!("JSON parse error: {e}")))?;
120 Ok(value)
121}