ngrok/config/
labeled.rs

1use std::collections::HashMap;
2
3use url::Url;
4
5// These are used for doc comment links.
6#[allow(unused_imports)]
7use crate::config::{
8    ForwarderBuilder,
9    TunnelBuilder,
10};
11use crate::{
12    config::common::{
13        default_forwards_to,
14        CommonOpts,
15        TunnelConfig,
16    },
17    internals::proto::{
18        BindExtra,
19        BindOpts,
20    },
21    tunnel::LabeledTunnel,
22    Session,
23};
24
25/// Options for labeled tunnels.
26#[derive(Default, Clone)]
27struct LabeledOptions {
28    pub(crate) common_opts: CommonOpts,
29    pub(crate) labels: HashMap<String, String>,
30}
31
32impl TunnelConfig for LabeledOptions {
33    fn forwards_to(&self) -> String {
34        self.common_opts
35            .forwards_to
36            .clone()
37            .unwrap_or(default_forwards_to().into())
38    }
39
40    fn forwards_proto(&self) -> String {
41        self.common_opts.forwards_proto.clone().unwrap_or_default()
42    }
43
44    fn verify_upstream_tls(&self) -> bool {
45        self.common_opts.verify_upstream_tls()
46    }
47
48    fn extra(&self) -> BindExtra {
49        BindExtra {
50            token: Default::default(),
51            ip_policy_ref: Default::default(),
52            metadata: self.common_opts.metadata.clone().unwrap_or_default(),
53            bindings: Vec::new(),
54            pooling_enabled: self.common_opts.pooling_enabled.unwrap_or(false),
55        }
56    }
57    fn proto(&self) -> String {
58        "".into()
59    }
60    fn opts(&self) -> Option<BindOpts> {
61        None
62    }
63    fn labels(&self) -> HashMap<String, String> {
64        self.labels.clone()
65    }
66}
67
68impl_builder! {
69    /// A builder for a labeled tunnel.
70    LabeledTunnelBuilder, LabeledOptions, LabeledTunnel, edge
71}
72
73impl LabeledTunnelBuilder {
74    /// Sets the opaque metadata string for this tunnel.
75    /// Viewable via the API.
76    ///
77    /// https://ngrok.com/docs/api/resources/tunnels/#tunnel-fields
78    pub fn metadata(&mut self, metadata: impl Into<String>) -> &mut Self {
79        self.options.common_opts.metadata = Some(metadata.into());
80        self
81    }
82
83    /// Add a label, value pair for this tunnel.
84    ///
85    /// https://ngrok.com/docs/network-edge/edges/#tunnel-group
86    pub fn label(&mut self, label: impl Into<String>, value: impl Into<String>) -> &mut Self {
87        self.options.labels.insert(label.into(), value.into());
88        self
89    }
90
91    /// Sets the ForwardsTo string for this tunnel. This can be viewed via the
92    /// API or dashboard.
93    ///
94    /// This overrides the default process info if using
95    /// [TunnelBuilder::listen], and is in turn overridden by the url provided
96    /// to [ForwarderBuilder::listen_and_forward].
97    ///
98    /// https://ngrok.com/docs/api/resources/tunnels/#tunnel-fields
99    pub fn forwards_to(&mut self, forwards_to: impl Into<String>) -> &mut Self {
100        self.options.common_opts.forwards_to = forwards_to.into().into();
101        self
102    }
103
104    /// Sets the L7 protocol string for this tunnel.
105    pub fn app_protocol(&mut self, app_protocol: impl Into<String>) -> &mut Self {
106        self.options.common_opts.forwards_proto = Some(app_protocol.into());
107        self
108    }
109
110    /// Disables backend TLS certificate verification for forwards from this tunnel.
111    pub fn verify_upstream_tls(&mut self, verify_upstream_tls: bool) -> &mut Self {
112        self.options
113            .common_opts
114            .set_verify_upstream_tls(verify_upstream_tls);
115        self
116    }
117
118    pub(crate) async fn for_forwarding_to(&mut self, to_url: &Url) -> &mut Self {
119        self.options.common_opts.for_forwarding_to(to_url);
120        self
121    }
122}
123
124#[cfg(test)]
125mod test {
126    use super::*;
127
128    const METADATA: &str = "testmeta";
129    const LABEL_KEY: &str = "edge";
130    const LABEL_VAL: &str = "edghts_2IC6RJ6CQnuh7waciWyaGKc50Nt";
131
132    #[test]
133    fn test_interface_to_proto() {
134        // pass to a function accepting the trait to avoid
135        // "creates a temporary which is freed while still in use"
136        tunnel_test(
137            &LabeledTunnelBuilder {
138                session: None,
139                options: Default::default(),
140            }
141            .metadata(METADATA)
142            .label(LABEL_KEY, LABEL_VAL)
143            .options,
144        );
145    }
146
147    fn tunnel_test<C>(tunnel_cfg: &C)
148    where
149        C: TunnelConfig,
150    {
151        assert_eq!(default_forwards_to(), tunnel_cfg.forwards_to());
152
153        let extra = tunnel_cfg.extra();
154        assert_eq!(String::default(), *extra.token);
155        assert_eq!(METADATA, extra.metadata);
156        assert_eq!(String::default(), extra.ip_policy_ref);
157
158        assert_eq!("", tunnel_cfg.proto());
159
160        assert!(tunnel_cfg.opts().is_none());
161
162        let mut labels: HashMap<String, String> = HashMap::new();
163        labels.insert(LABEL_KEY.into(), LABEL_VAL.into());
164        assert_eq!(labels, tunnel_cfg.labels());
165    }
166}