poe_api_client/
fetch.rs

1use std::thread;
2
3use reqwest::{RequestBuilder, Response};
4
5use crate::{
6    ratelimit::limiter::{
7        LimiterOutcome, Policy, RateLimiter, RateLimiterError, Rule, RuleSet, RuleState, RuleType,
8    },
9    Client, ClientError,
10};
11
12impl<L: RateLimiter> Client<L> {
13    pub(crate) async fn fetch_api_response(
14        &mut self,
15        endpoint: &str,
16        request: RequestBuilder,
17    ) -> Result<Response, ClientError> {
18        tracing::debug!("recieved request for endpoint: {endpoint}");
19
20        let limiter_outcome = match self.limiter.check(endpoint).await {
21            Ok(o) => o,
22            Err(e) => match e {
23                RateLimiterError::UnknownEndpoint(_) => LimiterOutcome::Proceed,
24                _ => return Err(ClientError::UnknownError),
25            },
26        };
27
28        match limiter_outcome {
29            LimiterOutcome::Proceed => {
30                tracing::debug!("rate limiter decided to proceed");
31            }
32            LimiterOutcome::Retry { after } => {
33                tracing::debug!(
34                    "rate limiter decided to wait for {} seconds, sleeping!",
35                    after.as_secs()
36                );
37                thread::sleep(after);
38            }
39        };
40
41        let response = request.send().await.map_err(ClientError::SendFailed)?;
42
43        let headers = response.headers();
44        if headers.get("x-rate-limit-policy").is_some() {
45            tracing::debug!(
46                "rate limit headers detected, updating rate limit policy for endpoint: {endpoint}"
47            );
48            let rules_raw = headers
49                .get("x-rate-limit-rules")
50                .unwrap()
51                .to_str()
52                .unwrap()
53                .split(',')
54                .map(RuleType::from)
55                .collect::<Vec<RuleType>>();
56
57            let mut rules: Vec<Rule> = Vec::new();
58            for rr in rules_raw {
59                if let (Some(rset), Some(rstate)) = (
60                    headers.get(format!("x-rate-limit-{rr}")),
61                    headers.get(format!("x-rate-limit-{rr}-state")),
62                ) {
63                    let ruleset = RuleSet::try_from(rset.to_str().unwrap())
64                        .map_err(ClientError::RateLimiterRuleError)?;
65
66                    let rulestate = RuleState::try_from(rstate.to_str().unwrap())
67                        .map_err(ClientError::RateLimiterRuleError)?;
68
69                    let rule = Rule {
70                        rtype: rr,
71                        ruleset,
72                        state: rulestate,
73                    };
74
75                    rules.push(rule);
76                }
77            }
78
79            let policy = Policy { rules };
80
81            self.limiter
82                .update(endpoint, policy)
83                .await
84                .map_err(ClientError::RateLimiterRuleError)?;
85        };
86
87        Ok(response)
88    }
89}