Issues an idempotent HTTP request, retrying transient connection-level errors (the server racing its own readiness under parallel load) with a short bounded backoff. `send` performs one attempt; a `ureq::Error` that passes [`is_transient_connection_error`] is retried, any other error (or exhausted retries) panics with `label`.
(
label: &str,
send: impl Fn() -> Result<ureq::http::Response<ureq::Body>, ureq::Error>,
)
| 531 | /// passes [`is_transient_connection_error`] is retried, any other error (or |
| 532 | /// exhausted retries) panics with `label`. |
| 533 | pub fn http_call_with_retry( |
| 534 | label: &str, |
| 535 | send: impl Fn() -> Result<ureq::http::Response<ureq::Body>, ureq::Error>, |
| 536 | ) -> ureq::http::Response<ureq::Body> { |
| 537 | let mut last_err: Option<ureq::Error> = None; |
| 538 | for attempt in 0..12 { |
| 539 | match send() { |
| 540 | Ok(response) => return response, |
| 541 | Err(err) if is_transient_connection_error(&err) => { |
| 542 | last_err = Some(err); |
| 543 | std::thread::sleep(Duration::from_millis(25 * (attempt + 1))); |
| 544 | } |
| 545 | Err(err) => panic!("{label} failed: {err}"), |
| 546 | } |
| 547 | } |
| 548 | panic!("{label} failed after retries: {last_err:?}"); |
| 549 | } |
| 550 | |
| 551 | pub fn get_json(agent: &ureq::Agent, url: &str) -> (u16, Value) { |
| 552 | let response = http_call_with_retry(&format!("GET {url}"), || agent.get(url).call()); |
no test coverage detected