1
//! Code to construct paths to a directory for non-anonymous downloads
2
use super::TorPath;
3
use crate::Result;
4
use tor_guardmgr::{GuardMgr, GuardMonitor, GuardUsable};
5
use tor_rtcompat::Runtime;
6
use tracing::instrument;
7

            
8
/// A PathBuilder that can connect to a directory.
9
#[non_exhaustive]
10
pub(crate) struct DirPathBuilder {}
11

            
12
impl Default for DirPathBuilder {
13
    fn default() -> Self {
14
        Self::new()
15
    }
16
}
17

            
18
impl DirPathBuilder {
19
    /// Create a new DirPathBuilder.
20
492
    pub(crate) fn new() -> Self {
21
492
        DirPathBuilder {}
22
492
    }
23

            
24
    /// Try to create and return a path corresponding to the requirements of
25
    /// this builder.
26
    #[instrument(skip_all, level = "trace")]
27
492
    pub(crate) fn pick_path<'a, RT: Runtime>(
28
492
        &self,
29
492
        guards: &GuardMgr<RT>,
30
492
    ) -> Result<(TorPath<'a>, GuardMonitor, GuardUsable)> {
31
492
        let guard_usage = tor_guardmgr::GuardUsageBuilder::default()
32
492
            .kind(tor_guardmgr::GuardUsageKind::OneHopDirectory)
33
492
            .build()
34
492
            .expect("Unable to build directory guard usage");
35
492
        let (guard, mon, usable) = guards.select_guard(guard_usage)?;
36
492
        Ok((TorPath::new_one_hop_owned(&guard), mon, usable))
37
492
    }
38
}
39

            
40
#[cfg(test)]
41
mod test {
42
    // @@ begin test lint list maintained by maint/add_warning @@
43
    #![allow(clippy::bool_assert_comparison)]
44
    #![allow(clippy::clone_on_copy)]
45
    #![allow(clippy::dbg_macro)]
46
    #![allow(clippy::mixed_attributes_style)]
47
    #![allow(clippy::print_stderr)]
48
    #![allow(clippy::print_stdout)]
49
    #![allow(clippy::single_char_pattern)]
50
    #![allow(clippy::unwrap_used)]
51
    #![allow(clippy::unchecked_time_subtraction)]
52
    #![allow(clippy::useless_vec)]
53
    #![allow(clippy::needless_pass_by_value)]
54
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
55

            
56
    use super::*;
57
    use std::collections::HashSet;
58
    use tor_guardmgr::TestConfig;
59
    use tor_linkspec::RelayIds;
60
    use tor_netdir::testnet;
61
    use tor_persist::TestingStateMgr;
62

            
63
    #[test]
64
    fn dirpath() {
65
        tor_rtcompat::test_with_all_runtimes!(|rt| async move {
66
            let netdir = testnet::construct_netdir().unwrap_if_sufficient().unwrap();
67
            let statemgr = TestingStateMgr::new();
68
            let guards =
69
                tor_guardmgr::GuardMgr::new(rt.clone(), statemgr, &TestConfig::default()).unwrap();
70
            guards.install_test_netdir(&netdir);
71

            
72
            let mut distinct_guards = HashSet::new();
73

            
74
            // This is a nice easy case, since we tested the harder cases
75
            // in guard-spec.  We'll just have every path succeed.
76
            for _ in 0..40 {
77
                let (path, mon, usable) = DirPathBuilder::new().pick_path(&guards).unwrap();
78
                if let crate::path::TorPathInner::OwnedOneHop(relay) = path.inner {
79
                    distinct_guards.insert(RelayIds::from_relay_ids(&relay));
80
                    mon.succeeded();
81
                    assert!(usable.await.unwrap());
82
                } else {
83
                    panic!("Generated the wrong kind of path.");
84
                }
85
            }
86
            assert_eq!(
87
                distinct_guards.len(),
88
                netdir.params().guard_dir_use_parallelism.get() as usize
89
            );
90
        });
91
    }
92
}