src/http/send.rs 2.0 K raw
1
//! App-level send orchestration: OAuth token caching + one 401 retry.
2
3
use std::collections::HashMap;
4
5
use super::client::{HttpResponse, send_request};
6
use super::oauth::{OAuthToken, fetch_token, token_valid};
7
use crate::model::{OAuthConfig, SavedRequest};
8
9
pub struct SendOutcome {
10
    pub result: Result<HttpResponse, String>,
11
    /// Latest token cache (unchanged on failure, refreshed on (re)fetch).
12
    pub token: Option<OAuthToken>,
13
}
14
15
/// Send a request, transparently handling OAuth client-credentials auth:
16
/// reuse a cached token while valid, fetch one otherwise, and retry the
17
/// request once with a fresh token on a 401 response.
18
pub async fn send_with_auth(
19
    client: &reqwest::Client,
20
    base_url: &str,
21
    req: &SavedRequest,
22
    vars: &HashMap<String, String>,
23
    auth: Option<&OAuthConfig>,
24
    cached: Option<OAuthToken>,
25
) -> SendOutcome {
26
    let mut token = cached;
27
    let auth = auth.filter(|c| c.is_configured());
28
29
    let mut bearer: Option<String> = None;
30
    if let Some(cfg) = auth {
31
        let stale = token.as_ref().map(|t| !token_valid(t)).unwrap_or(true);
32
        if stale {
33
            match fetch_token(client, cfg).await {
34
                Ok(t) => token = Some(t),
35
                Err(e) => {
36
                    return SendOutcome {
37
                        result: Err(format!("token fetch failed: {e:#}")),
38
                        token,
39
                    };
40
                }
41
            }
42
        }
43
        bearer = token.as_ref().map(|t| t.access_token.clone());
44
    }
45
46
    let mut resp = send_request(client, base_url, req, vars, bearer.as_deref())
47
        .await
48
        .map_err(|e| format!("{e:#}"));
49
50
    if let (Ok(r), Some(cfg)) = (&resp, auth)
51
        && r.status == 401
52
        && let Ok(t) = fetch_token(client, cfg).await
53
    {
54
        bearer = Some(t.access_token.clone());
55
        token = Some(t);
56
        resp = send_request(client, base_url, req, vars, bearer.as_deref())
57
            .await
58
            .map_err(|e| format!("{e:#}"));
59
    }
60
61
    SendOutcome {
62
        result: resp,
63
        token,
64
    }
65
}