• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

galaxy-sec / orion-variate / 16830056626

08 Aug 2025 12:09PM UTC coverage: 70.183% (-1.7%) from 71.915%
16830056626

push

github

sec-wukong
refactor(accessor): 优化代理和超时配置处理逻辑

将多处 map+flatten 替换为 and_then 提高可读性
移除不再使用的 creator 模块
统一代理配置处理方式

12 of 21 new or added lines in 5 files covered. (57.14%)

218 existing lines in 5 files now uncovered.

3027 of 4313 relevant lines covered (70.18%)

4.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.61
/src/addr/access_ctrl/serv.rs
1
use std::{path::PathBuf, rc::Rc};
2

3
use getset::Getters;
4
use orion_common::serde::Yamlable;
5
use orion_error::{ErrorOwe, ErrorWith};
6

7
use crate::vars::{EnvDict, EnvEvalable};
8
use crate::{
9
    addr::{
10
        AddrError, GitRepository, HttpResource,
11
        access_ctrl::{
12
            auth::AuthConfig,
13
            unit::{RedirectResult, Unit},
14
        },
15
        proxy::ProxyConfig,
16
    },
17
    timeout::TimeoutConfig,
18
};
19

20
use super::{rule::Rule, unit::UnitCtrl};
21
use serde_derive::{Deserialize, Serialize};
22

23
#[derive(Clone, Debug, Serialize, Deserialize, Getters)]
24
#[getset(get = "pub")]
25
pub struct NetAccessCtrl {
26
    units: Vec<Unit>,
27
    enable: bool,
28
}
29

30
pub type ServHandle = Rc<NetAccessCtrl>;
31

32
impl NetAccessCtrl {
33
    pub fn new(units: Vec<Unit>, enable: bool) -> Self {
14✔
34
        Self { units, enable }
14✔
35
    }
14✔
36
    pub fn redirect(&self, url: &str) -> RedirectResult {
3✔
37
        let mut path = RedirectResult::Origin(url.to_string());
3✔
38
        for unit in &self.units {
4✔
39
            path = unit.redirect(path.path());
3✔
40
            if path.is_proxy() {
3✔
41
                break;
2✔
42
            }
1✔
43
        }
44
        path
3✔
45
    }
3✔
46
    pub fn direct_http_addr(&self, origin: HttpResource) -> HttpResource {
1✔
47
        for unit in &self.units {
1✔
48
            if let Some(dirct) = unit.direct_http_addr(&origin) {
1✔
49
                return dirct;
1✔
50
            }
×
51
        }
52
        origin
×
53
    }
1✔
54
    pub fn direct_git_addr(&self, origin: GitRepository) -> GitRepository {
1✔
55
        for unit in &self.units {
1✔
56
            if let Some(dirct) = unit.direct_git_addr(&origin) {
1✔
57
                return dirct;
1✔
58
            }
×
59
        }
60
        origin
×
61
    }
1✔
62
    pub fn direct_git_ctrl(&self, origin: &GitRepository) -> Option<UnitCtrl> {
3✔
63
        for unit in &self.units {
3✔
64
            if unit.direct_git_addr(origin).is_some() {
3✔
65
                return Some(UnitCtrl::new(
3✔
66
                    unit.auth().clone(),
3✔
67
                    unit.timeout().clone(),
3✔
68
                    unit.proxy().clone(),
3✔
69
                ));
3✔
70
            }
×
71
        }
72
        None
×
73
    }
3✔
74
    pub fn proxy_git(&self, origin: &GitRepository) -> Option<ProxyConfig> {
3✔
75
        self.direct_git_ctrl(origin).and_then(|x| x.proxy().clone())
3✔
76
    }
3✔
77
    pub fn proxy_http(&self, origin: &HttpResource) -> Option<ProxyConfig> {
×
78
        self.direct_http_ctrl(origin)
×
NEW
79
            .and_then(|x| x.proxy().clone())
×
80
    }
×
81

82
    pub fn timeout_git(&self, origin: &GitRepository) -> Option<TimeoutConfig> {
×
83
        self.direct_git_ctrl(origin)
×
NEW
84
            .and_then(|x| x.timeout().clone())
×
85
    }
×
86
    pub fn timeout_http(&self, origin: &HttpResource) -> Option<TimeoutConfig> {
×
87
        self.direct_http_ctrl(origin)
×
NEW
88
            .and_then(|x| x.timeout().clone())
×
89
    }
×
90

91
    pub fn auth_git(&self, origin: &GitRepository) -> Option<AuthConfig> {
×
NEW
92
        self.direct_git_ctrl(origin).and_then(|x| x.auth().clone())
×
93
    }
×
94
    pub fn auth_http(&self, origin: &HttpResource) -> Option<AuthConfig> {
×
NEW
95
        self.direct_http_ctrl(origin).and_then(|x| x.auth().clone())
×
96
    }
×
97

98
    pub fn direct_http_ctrl(&self, origin: &HttpResource) -> Option<UnitCtrl> {
1✔
99
        for unit in &self.units {
2✔
100
            if unit.direct_http_addr(origin).is_some() {
1✔
101
                return Some(UnitCtrl::new(
×
102
                    unit.auth().clone(),
×
103
                    unit.timeout().clone(),
×
104
                    unit.proxy().clone(),
×
105
                ));
×
106
            }
1✔
107
        }
108
        None
1✔
109
    }
1✔
110

111
    pub fn from_rule(rule: Rule, auth: Option<AuthConfig>, proxy: Option<ProxyConfig>) -> Self {
5✔
112
        let unit = Unit::new(vec![rule], auth, proxy);
5✔
113
        Self::new(vec![unit], true)
5✔
114
    }
5✔
115
}
116
impl TryFrom<&PathBuf> for NetAccessCtrl {
117
    type Error = AddrError;
118

119
    fn try_from(value: &PathBuf) -> Result<Self, Self::Error> {
1✔
120
        NetAccessCtrl::from_yml(value).owe_res().with(value)
1✔
121
    }
1✔
122
}
123

124
impl EnvEvalable<NetAccessCtrl> for NetAccessCtrl {
125
    fn env_eval(self, dict: &EnvDict) -> NetAccessCtrl {
2✔
126
        NetAccessCtrl {
127
            units: self
2✔
128
                .units
2✔
129
                .into_iter()
2✔
130
                .map(|unit| unit.env_eval(dict))
3✔
131
                .collect(),
2✔
132
            enable: self.enable,
2✔
133
        }
134
    }
2✔
135
}
136

137
#[cfg(test)]
138
mod tests {
139
    use crate::addr::proxy::ProxyConfig;
140

141
    use super::*;
142
    use tempfile::tempdir;
143

144
    #[test]
145
    fn test_serv_serialization_basic() {
1✔
146
        let serv = NetAccessCtrl::new(vec![], false);
1✔
147
        let serialized = serde_yaml::to_string(&serv).unwrap();
1✔
148
        let deserialized: NetAccessCtrl = serde_yaml::from_str(&serialized).unwrap();
1✔
149

150
        assert_eq!(deserialized.units().len(), 0);
1✔
151
        assert!(!deserialized.enable());
1✔
152
    }
1✔
153

154
    #[test]
155
    fn test_serv_serialization_with_units() {
1✔
156
        let auth = Some(AuthConfig::new("test_user", "test_pass"));
1✔
157
        let rules = vec![
1✔
158
            Rule::new("https://github.com/*", "https://mirror.github.com/"),
1✔
159
            Rule::new("https://gitlab.com/*", "https://mirror.gitlab.com/"),
1✔
160
        ];
161
        let proxy = ProxyConfig::new("https://proxy.example.com:8080");
1✔
162
        let unit = Unit::new(rules, auth, Some(proxy));
1✔
163
        let serv = NetAccessCtrl::new(vec![unit], true);
1✔
164

165
        let serialized = serde_json::to_string(&serv).unwrap();
1✔
166
        let deserialized: NetAccessCtrl = serde_json::from_str(&serialized).unwrap();
1✔
167

168
        assert_eq!(deserialized.units().len(), 1);
1✔
169
        assert!(deserialized.enable());
1✔
170
        assert_eq!(deserialized.units()[0].rules().len(), 2);
1✔
171
        assert!(deserialized.units()[0].auth().is_some());
1✔
172
    }
1✔
173

174
    #[test]
175
    fn test_serv_serialization_yaml_format() {
1✔
176
        let yaml_content = r#"
1✔
177
units:
1✔
178
  - rules:
1✔
179
      - pattern: "https://example.com/*"
1✔
180
        target: "https://proxy.com/"
1✔
181
enable: true
1✔
182
"#;
1✔
183

184
        let deserialized: NetAccessCtrl = serde_yaml::from_str(yaml_content).unwrap();
1✔
185
        assert!(deserialized.enable());
1✔
186
        assert_eq!(deserialized.units().len(), 1);
1✔
187
        assert_eq!(
1✔
188
            deserialized.units()[0].rules()[0].pattern(),
1✔
189
            "https://example.com/*"
190
        );
191
    }
1✔
192

193
    #[test]
194
    fn test_serv_from_rule_serialization() {
1✔
195
        let rule = Rule::new("https://test.com/*", "https://redirect.com/");
1✔
196
        let auth = Some(AuthConfig::new("admin", "secret"));
1✔
197
        let serv = NetAccessCtrl::from_rule(rule, auth, None);
1✔
198

199
        let serialized = serde_json::to_string_pretty(&serv).unwrap();
1✔
200
        let deserialized: NetAccessCtrl = serde_json::from_str(&serialized).unwrap();
1✔
201

202
        assert!(deserialized.enable());
1✔
203
        assert_eq!(deserialized.units().len(), 1);
1✔
204
        assert_eq!(deserialized.units()[0].rules().len(), 1);
1✔
205
        assert!(deserialized.units()[0].auth().is_some());
1✔
206
    }
1✔
207

208
    #[test]
209
    fn test_serv_multiple_units_serialization() {
1✔
210
        let unit1 = Unit::new(
1✔
211
            vec![Rule::new("https://api1.com/*", "https://proxy1.com/")],
1✔
212
            Some(AuthConfig::new("user1", "pass1")),
1✔
213
            None,
1✔
214
        );
215

216
        let unit2 = Unit::new(
1✔
217
            vec![Rule::new("https://api2.com/*", "https://proxy2.com/")],
1✔
218
            None,
1✔
219
            None,
1✔
220
        );
221

222
        let unit3 = Unit::new(
1✔
223
            vec![
1✔
224
                Rule::new("https://api3.com/v1/*", "https://proxy3.com/v1/"),
1✔
225
                Rule::new("https://api3.com/v2/*", "https://proxy3.com/v2/"),
1✔
226
            ],
227
            Some(AuthConfig::new("user3", "pass3")),
1✔
228
            None,
1✔
229
        );
230

231
        let serv = NetAccessCtrl::new(vec![unit1, unit2, unit3], true);
1✔
232

233
        let serialized = serde_yaml::to_string(&serv).unwrap();
1✔
234
        let deserialized: NetAccessCtrl = serde_yaml::from_str(&serialized).unwrap();
1✔
235

236
        assert_eq!(deserialized.units().len(), 3);
1✔
237
        assert!(deserialized.enable());
1✔
238
    }
1✔
239

240
    #[test]
241
    fn test_serv_complex_yaml_structure() {
1✔
242
        let yaml_content = r#"
1✔
243
units:
1✔
244
  - rules:
1✔
245
      - pattern: "https://github.com/*"
1✔
246
        target: "https://ghproxy.com/"
1✔
247
      - pattern: "https://raw.githubusercontent.com/*"
1✔
248
        target: "https://raw.ghproxy.com/"
1✔
249
    auth:
1✔
250
      username: "proxy_user"
1✔
251
      password: "proxy_pass"
1✔
252
  - rules:
1✔
253
      - pattern: "https://npmjs.com/*"
1✔
254
        target: "https://npmmirror.com/"
1✔
255
      - pattern: "https://registry.npmjs.org/*"
1✔
256
        target: "https://registry.npmmirror.com/"
1✔
257
enable: true
1✔
258
"#;
1✔
259

260
        let deserialized: NetAccessCtrl = serde_yaml::from_str(yaml_content).unwrap();
1✔
261

262
        assert_eq!(deserialized.units().len(), 2);
1✔
263
        assert!(deserialized.enable());
1✔
264

265
        let first_unit = &deserialized.units()[0];
1✔
266
        assert_eq!(first_unit.rules().len(), 2);
1✔
267
        assert_eq!(first_unit.rules()[0].pattern(), "https://github.com/*");
1✔
268
        assert_eq!(first_unit.rules()[0].target(), "https://ghproxy.com/");
1✔
269
        assert!(first_unit.auth().is_some());
1✔
270
        assert_eq!(first_unit.auth().as_ref().unwrap().username(), "proxy_user");
1✔
271

272
        let second_unit = &deserialized.units()[1];
1✔
273
        assert_eq!(second_unit.rules().len(), 2);
1✔
274
        assert!(second_unit.auth().is_none());
1✔
275
    }
1✔
276

277
    #[test]
278
    fn test_serv_empty_yaml() {
1✔
279
        let yaml_content = r#"
1✔
280
units: []
1✔
281
enable: false
1✔
282
"#;
1✔
283

284
        let deserialized: NetAccessCtrl = serde_yaml::from_str(yaml_content).unwrap();
1✔
285

286
        assert_eq!(deserialized.units().len(), 0);
1✔
287
        assert!(!deserialized.enable());
1✔
288
    }
1✔
289

290
    #[test]
291
    fn test_serv_json_format() {
1✔
292
        let json_content = r#"
1✔
293
{
1✔
294
  "units": [
1✔
295
    {
1✔
296
      "rules": [
1✔
297
        {
1✔
298
          "pattern": "https://test.example.com/*",
1✔
299
          "target": "https://proxy.example.com/"
1✔
300
        }
1✔
301
      ],
1✔
302
      "auth": {
1✔
303
        "username": "testuser",
1✔
304
        "password": "testpass"
1✔
305
      }
1✔
306
    }
1✔
307
  ],
1✔
308
  "enable": true
1✔
309
}
1✔
310
"#;
1✔
311

312
        let deserialized: NetAccessCtrl = serde_json::from_str(json_content).unwrap();
1✔
313

314
        assert_eq!(deserialized.units().len(), 1);
1✔
315
        assert!(deserialized.enable());
1✔
316
        assert_eq!(
1✔
317
            deserialized.units()[0].rules()[0].pattern(),
1✔
318
            "https://test.example.com/*"
319
        );
320
    }
1✔
321

322
    #[test]
323
    fn test_serv_redirect_functionality() {
1✔
324
        let rules = vec![Rule::new("https://github.com/*", "https://mirror.com/")];
1✔
325
        let unit = Unit::new(rules, None, None);
1✔
326
        let serv = NetAccessCtrl::new(vec![unit], true);
1✔
327

328
        let result = serv.redirect("https://github.com/user/repo");
1✔
329
        match result {
1✔
330
            RedirectResult::Direct(path, _) => {
1✔
331
                assert_eq!(path, "https://mirror.com/user/repo");
1✔
332
            }
333
            RedirectResult::Origin(_) => panic!("Expected proxy path"),
×
334
        }
335
    }
1✔
336

337
    #[test]
338
    fn test_serv_no_redirect_match() {
1✔
339
        let rules = vec![Rule::new("https://github.com/*", "https://mirror.com/")];
1✔
340
        let unit = Unit::new(rules, None, None);
1✔
341
        let serv = NetAccessCtrl::new(vec![unit], true);
1✔
342

343
        let result = serv.redirect("https://gitlab.com/user/repo");
1✔
344
        match result {
1✔
345
            RedirectResult::Origin(path) => {
1✔
346
                assert_eq!(path, "https://gitlab.com/user/repo");
1✔
347
            }
348
            RedirectResult::Direct(_, _) => panic!("Expected origin path"),
×
349
        }
350
    }
1✔
351

352
    #[test]
353
    fn test_serv_file_roundtrip() {
1✔
354
        let temp_dir = tempdir().unwrap();
1✔
355
        let file_path = temp_dir.path().join("test_serv.yml");
1✔
356

357
        let rules = vec![Rule::new(
1✔
358
            "https://file-test.com/*",
359
            "https://file-proxy.com/",
360
        )];
361
        let unit = Unit::new(rules, Some(AuthConfig::new("file_user", "file_pass")), None);
1✔
362
        let original_serv = NetAccessCtrl::new(vec![unit], true);
1✔
363

364
        // 写入文件
365
        original_serv.save_yml(&file_path).unwrap();
1✔
366

367
        // 从文件读取
368
        let loaded_serv = NetAccessCtrl::try_from(&file_path).unwrap();
1✔
369

370
        assert_eq!(loaded_serv.units().len(), original_serv.units().len());
1✔
371
        assert_eq!(loaded_serv.enable(), original_serv.enable());
1✔
372
    }
1✔
373

374
    #[test]
375
    fn test_redirect_service() {
1✔
376
        let service = NetAccessCtrl::new(
1✔
377
            vec![Unit::new(
1✔
378
                vec![Rule::new(
1✔
379
                    "https://github.com/galaxy-sec/galaxy-flow*",
380
                    "https://gflow.com",
381
                )],
382
                None,
1✔
383
                None,
1✔
384
            )],
385
            true,
386
        );
387
        let result = service.redirect("https://github.com/galaxy-sec/galaxy-flow");
1✔
388
        match result {
1✔
389
            RedirectResult::Direct(path, _) => {
1✔
390
                assert_eq!(path, "https://gflow.com");
1✔
391
            }
392
            RedirectResult::Origin(_) => panic!("Expected proxy path"),
×
393
        }
394
    }
1✔
395

396
    #[test]
397
    fn test_redirect_service_env_eval() {
1✔
398
        use crate::vars::{EnvDict, ValueType};
399

400
        let mut env_dict = EnvDict::new();
1✔
401
        env_dict.insert(
1✔
402
            "DOMAIN".to_string(),
1✔
403
            ValueType::String("example.com".to_string()),
1✔
404
        );
405
        env_dict.insert(
1✔
406
            "TARGET".to_string(),
1✔
407
            ValueType::String("redirect.com".to_string()),
1✔
408
        );
409
        env_dict.insert(
1✔
410
            "USERNAME".to_string(),
1✔
411
            ValueType::String("test_user".to_string()),
1✔
412
        );
413

414
        let service = NetAccessCtrl::new(
1✔
415
            vec![
1✔
416
                Unit::new(
1✔
417
                    vec![Rule::new("https://${DOMAIN}/*", "https://${TARGET}")],
1✔
418
                    None,
1✔
419
                    None,
1✔
420
                ),
421
                Unit::new(
1✔
422
                    vec![Rule::new(
1✔
423
                        "https://github.com/*",
424
                        "https://mirror.${DOMAIN}",
425
                    )],
426
                    Some(AuthConfig::new("${USERNAME}", "password")),
1✔
427
                    None,
1✔
428
                ),
429
            ],
430
            true,
431
        );
432

433
        let evaluated = service.env_eval(&env_dict);
1✔
434

435
        assert_eq!(evaluated.units().len(), 2);
1✔
436
        assert_eq!(
1✔
437
            evaluated.units()[0].rules()[0].pattern(),
1✔
438
            "https://example.com/*"
439
        );
440
        assert_eq!(
1✔
441
            evaluated.units()[0].rules()[0].target(),
1✔
442
            "https://redirect.com"
443
        );
444
        assert!(evaluated.units()[0].auth().is_none());
1✔
445

446
        assert_eq!(
1✔
447
            evaluated.units()[1].rules()[0].pattern(),
1✔
448
            "https://github.com/*"
449
        );
450
        assert_eq!(
1✔
451
            evaluated.units()[1].rules()[0].target(),
1✔
452
            "https://mirror.example.com"
453
        );
454
        assert!(evaluated.units()[1].auth().is_some());
1✔
455
        assert_eq!(
1✔
456
            evaluated.units()[1].auth().as_ref().unwrap().username(),
1✔
457
            "test_user"
458
        );
459
    }
1✔
460

461
    #[test]
462
    fn test_redirect_service_env_eval_disabled() {
1✔
463
        use crate::vars::EnvDict;
464

465
        let env_dict = EnvDict::new();
1✔
466

467
        let service = NetAccessCtrl::new(
1✔
468
            vec![Unit::new(
1✔
469
                vec![Rule::new("https://github.com/*", "https://mirror.com")],
1✔
470
                None,
1✔
471
                None,
1✔
472
            )],
473
            false,
474
        );
475

476
        let evaluated = service.env_eval(&env_dict);
1✔
477

478
        assert_eq!(evaluated.units().len(), 1);
1✔
479
        assert!(!evaluated.enable());
1✔
480
    }
1✔
481
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc