A lexicon-driven AppView for ATProto.
0

Configure Feed

Select the types of activity you want to include in your feed.

happyview / src / http_retry.rs
1.6 kB 38 lines
1use std::time::Duration; 2 3/// Per-request timeout for outbound atproto network fetches (relay, PLC/DID 4/// resolution, PDS reads). Bounds a single HTTP attempt so a host that connects 5/// but never responds fails instead of stalling a backfill job forever. 6pub const REQUEST_TIMEOUT: Duration = Duration::from_secs(30); 7 8/// Overall deadline for resolving a single DID's PDS endpoint. Unlike 9/// [`REQUEST_TIMEOUT`], this bounds the *entire* resolution of one DID — 10/// including DNS, connection setup, and any rate-limit retry/backoff loop — so 11/// a single stuck DID can never hang the resolver stream. On expiry the DID is 12/// treated as a resolution failure and skipped, and the backfill moves on. 13pub const RESOLVE_DEADLINE: Duration = Duration::from_secs(30); 14 15/// Parse rate-limit sleep duration from response headers. 16/// Checks `RateLimit-Reset` (Unix timestamp, used by XRPC servers) first, 17/// then `retry-after` (seconds), defaulting to 5s. 18pub fn parse_retry_after(headers: &reqwest::header::HeaderMap) -> u64 { 19 if let Some(reset) = headers 20 .get("ratelimit-reset") 21 .and_then(|v| v.to_str().ok()) 22 .and_then(|v| v.parse::<i64>().ok()) 23 { 24 let now = std::time::SystemTime::now() 25 .duration_since(std::time::UNIX_EPOCH) 26 .unwrap_or_default() 27 .as_secs() as i64; 28 let wait = (reset - now).max(1) as u64; 29 return wait.min(120); 30 } 31 32 headers 33 .get("retry-after") 34 .and_then(|v| v.to_str().ok()) 35 .and_then(|v| v.parse::<u64>().ok()) 36 .unwrap_or(5) 37 .min(120) 38}