futu_backend/auth/commconfig/
fetch.rs1use std::collections::HashMap;
5use std::sync::Arc;
6
7use super::parsers::{
8 fetch_page, parse_auth_guaranteed_domain_list, parse_forced_ip, parse_guaranteed_ip,
9 parse_web_tcp_config_identity, value_kind,
10};
11use super::types::{
12 AuthGuaranteedDomainMap, CommonConfigSnapshot, ForcedIpMap, GuaranteedBrokerIpMap,
13 GuaranteedIpMap, GuaranteedWebIpMap,
14};
15use arc_swap::ArcSwap;
16
17pub async fn fetch_all(
18 http: &reqwest::Client,
19 client_type: u8,
20 device_id: &str,
21 user_id: u64,
22 svr_time_offset: i64,
23) -> CommonConfigSnapshot {
24 let mut guaranteed_ip: GuaranteedIpMap = HashMap::new();
25 let mut guaranteed_ip_broker: GuaranteedBrokerIpMap = HashMap::new();
26 let mut guaranteed_ip_web: GuaranteedWebIpMap = HashMap::new();
27 let mut web_conn_identity: Option<u32> = None;
28 let mut auth_guaranteed_domains: AuthGuaranteedDomainMap = HashMap::new();
29 let mut auth_guaranteed_domains_configured = false;
30 let mut forced_ip: ForcedIpMap = HashMap::new();
31 let mut next_refresh_ts: i64 = 0;
32 let mut begin_id: i32 = -1; let max_pages = 20;
34
35 for page in 0..max_pages {
36 let json = match fetch_page(
37 http,
38 client_type,
39 device_id,
40 user_id,
41 begin_id,
42 svr_time_offset,
43 )
44 .await
45 {
46 Ok(j) => j,
47 Err(e) => {
48 tracing::warn!(error = %e, page, "commconfig: fetch page failed, giving up");
49 break;
50 }
51 };
52
53 let ret_code = json.get("ret_code").and_then(|v| v.as_i64());
55 if ret_code != Some(0) {
56 tracing::warn!(
57 ret_code = ?ret_code,
58 msg = ?json.get("ret_msg").and_then(|v| v.as_str()),
59 "commconfig: non-zero ret_code, abort"
60 );
61 break;
62 }
63
64 let data = match json.get("data").and_then(|v| v.as_object()) {
65 Some(d) => d,
66 None => {
67 tracing::warn!("commconfig: response missing `data` object");
68 break;
69 }
70 };
71
72 if let Some(conf_info) = data.get("conf_info").and_then(|v| v.as_object()) {
75 if let Some(val) = conf_info.get("guaranteed_ip_for_conn") {
76 tracing::debug!(
77 kind = value_kind(val),
78 "commconfig: guaranteed_ip_for_conn received"
79 );
80 let (platform_map, broker_map, web_map) = parse_guaranteed_ip(val);
81 for (attr, pool) in platform_map {
83 let slot = guaranteed_ip.entry(attr).or_default();
84 for ip in pool {
85 if !slot.contains(&ip) {
86 slot.push(ip);
87 }
88 }
89 }
90 for (broker_id, pool) in broker_map {
92 let slot = guaranteed_ip_broker.entry(broker_id).or_default();
93 for ip in pool {
94 if !slot.contains(&ip) {
95 slot.push(ip);
96 }
97 }
98 }
99 for (identity, pool) in web_map {
101 let slot = guaranteed_ip_web.entry(identity).or_default();
102 for ip in pool {
103 if !slot.contains(&ip) {
104 slot.push(ip);
105 }
106 }
107 }
108 }
109
110 if let Some(val) = conf_info.get("web_tcp_config") {
111 tracing::debug!(
112 kind = value_kind(val),
113 "commconfig: web_tcp_config received"
114 );
115 if let Some(identity) = parse_web_tcp_config_identity(val) {
116 web_conn_identity = Some(identity);
117 tracing::info!(
118 identity,
119 "commconfig: web_tcp_config.web_conn_identity loaded"
120 );
121 }
122 }
123
124 if let Some(val) = conf_info.get("auth_guaranteed_domain_list") {
125 tracing::debug!(
126 kind = value_kind(val),
127 "commconfig: auth_guaranteed_domain_list received"
128 );
129 let (domains, configured) = parse_auth_guaranteed_domain_list(val);
130 auth_guaranteed_domains_configured |= configured;
131 for (domain, retry_domain) in domains {
132 auth_guaranteed_domains.insert(domain, retry_domain);
133 }
134 }
135
136 if let Some(val) = conf_info.get("forced_ip_for_conn") {
139 tracing::debug!(
140 kind = value_kind(val),
141 "commconfig: forced_ip_for_conn received"
142 );
143 let page_map = parse_forced_ip(val);
144 for (attr, entry) in page_map {
145 let existing = forced_ip.get(&attr);
147 if existing.is_none_or(|e| e.expire_ts < entry.expire_ts) {
148 forced_ip.insert(attr, entry);
149 }
150 }
151 }
152 }
153
154 let control = data.get("config_control").and_then(|v| v.as_object());
155 if let Some(c) = control
156 && let Some(limit) = c.get("limit_time").and_then(|v| v.as_i64())
157 {
158 let now = chrono::Utc::now().timestamp();
159 next_refresh_ts = now + limit;
160 }
161
162 let has_more = control
164 .and_then(|c| c.get("has_more"))
165 .and_then(|v| v.as_bool())
166 .unwrap_or(false);
167 if !has_more {
168 break;
169 }
170 let next_id = control
171 .and_then(|c| c.get("next_id"))
172 .and_then(|v| v.as_i64())
173 .unwrap_or(0);
174 begin_id = next_id as i32;
175 }
176
177 tracing::info!(
178 platform_pools = guaranteed_ip.len(),
179 broker_pools = guaranteed_ip_broker.len(),
180 web_pools = guaranteed_ip_web.len(),
181 web_conn_identity,
182 auth_retry_domains = auth_guaranteed_domains.len(),
183 auth_retry_domain_configured = auth_guaranteed_domains_configured,
184 forced_count = forced_ip.len(),
185 "commconfig: fetched"
186 );
187 CommonConfigSnapshot {
188 guaranteed_ip,
189 guaranteed_ip_broker,
190 guaranteed_ip_web,
191 web_conn_identity,
192 auth_guaranteed_domains,
193 auth_guaranteed_domains_configured,
194 forced_ip,
195 next_refresh_ts,
196 }
197}
198
199pub type SharedCommConfig = Arc<ArcSwap<CommonConfigSnapshot>>;
202
203pub fn empty_snapshot() -> CommonConfigSnapshot {
205 CommonConfigSnapshot {
206 guaranteed_ip: HashMap::new(),
207 guaranteed_ip_broker: HashMap::new(),
208 guaranteed_ip_web: HashMap::new(),
209 web_conn_identity: None,
210 auth_guaranteed_domains: HashMap::new(),
211 auth_guaranteed_domains_configured: false,
212 forced_ip: HashMap::new(),
213 next_refresh_ts: 0,
214 }
215}