Skip to main content

longbridge_httpcli/
geo.rs

1use std::{cell::RefCell, time::Duration};
2
3// because we may call `is_cn` multi times in a short time, we cache the result
4thread_local! {
5    static REGION: RefCell<Option<String>> = const { RefCell::new(None) };
6}
7
8async fn region() -> Option<String> {
9    // check user defined REGION (LONGBRIDGE_REGION takes precedence,
10    // LONGPORT_REGION is the fallback)
11    let user_region = std::env::var("LONGBRIDGE_REGION")
12        .ok()
13        .or_else(|| std::env::var("LONGPORT_REGION").ok());
14    if let Some(region) = user_region {
15        return Some(region);
16    }
17
18    // check network connectivity
19    // make sure block_on doesn't block the outer tokio runtime
20    ping().await
21}
22
23async fn ping() -> Option<String> {
24    if let Some(region) = REGION.with_borrow(Clone::clone) {
25        return Some(region.clone());
26    }
27
28    let Ok(resp) = reqwest::Client::new()
29        .get("https://api.lbkrs.com/_ping")
30        .timeout(Duration::from_secs(1))
31        .send()
32        .await
33    else {
34        return None;
35    };
36    let region = resp
37        .headers()
38        .get("X-Ip-Region")
39        .and_then(|v| v.to_str().ok())?;
40    REGION.set(Some(region.to_string()));
41    Some(region.to_string())
42}
43
44/// do the best to guess whether the access point is in China Mainland or not
45pub async fn is_cn() -> bool {
46    region()
47        .await
48        .is_some_and(|region| region.eq_ignore_ascii_case("CN"))
49}