Skip to main content

cloudpub_common/
config.rs

1use crate::constants::DEFAULT_CONNECT_TIMEOUT_SECS;
2use anyhow::{anyhow, Result};
3use serde::{Deserialize, Serialize};
4use std::fmt::{Debug, Formatter};
5use std::ops::Deref;
6use url::Url;
7
8pub use crate::protocol::Protocol;
9
10/// String with Debug implementation that emits "MASKED"
11/// Used to mask sensitive strings when logging
12#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone)]
13pub struct MaskedString(pub String);
14
15impl Debug for MaskedString {
16    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
17        if self.0.is_empty() {
18            f.write_str("EMPTY")
19        } else {
20            #[cfg(debug_assertions)]
21            f.write_str(&self.0)?;
22            #[cfg(not(debug_assertions))]
23            f.write_str("MASKED")?;
24            Ok(())
25        }
26    }
27}
28
29impl Deref for MaskedString {
30    type Target = str;
31    fn deref(&self) -> &Self::Target {
32        &self.0
33    }
34}
35
36impl From<&str> for MaskedString {
37    fn from(s: &str) -> MaskedString {
38        MaskedString(String::from(s))
39    }
40}
41
42#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Default)]
43pub enum TransportType {
44    #[serde(rename = "websocket")]
45    #[default]
46    Websocket,
47    #[serde(rename = "tcp")]
48    Tcp,
49    #[cfg(feature = "rustls")]
50    #[serde(rename = "tls")]
51    Tls,
52}
53
54#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
55#[serde(deny_unknown_fields)]
56#[derive(Default)]
57pub struct TlsConfig {
58    pub hostname: Option<String>,
59    pub trusted_root: Option<String>,
60    pub pkcs12: Option<String>,
61    pub pkcs12_password: Option<MaskedString>,
62    pub danger_ignore_certificate_verification: Option<bool>,
63}
64
65#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
66#[serde(deny_unknown_fields)]
67pub struct WebsocketConfig {
68    pub tls: bool,
69}
70
71impl Default for WebsocketConfig {
72    fn default() -> Self {
73        Self { tls: true }
74    }
75}
76
77#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
78pub struct TcpConfig {
79    pub proxy: Option<Url>,
80    /// Deadline for the whole dial: TCP connect, TLS handshake and the
81    /// WebSocket upgrade. 0 disables it.
82    #[serde(default = "default_connect_timeout_secs")]
83    pub connect_timeout_secs: u64,
84}
85
86fn default_connect_timeout_secs() -> u64 {
87    DEFAULT_CONNECT_TIMEOUT_SECS
88}
89
90impl Default for TcpConfig {
91    fn default() -> Self {
92        Self {
93            proxy: None,
94            connect_timeout_secs: default_connect_timeout_secs(),
95        }
96    }
97}
98
99#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
100#[serde(deny_unknown_fields)]
101pub struct TransportConfig {
102    #[serde(rename = "type")]
103    pub transport_type: TransportType,
104    pub tcp: TcpConfig,
105    pub tls: Option<TlsConfig>,
106    pub websocket: Option<WebsocketConfig>,
107}
108
109impl Default for TransportConfig {
110    fn default() -> Self {
111        Self {
112            transport_type: TransportType::Websocket,
113            tcp: TcpConfig::default(),
114            tls: TlsConfig::default().into(),
115            websocket: WebsocketConfig::default().into(),
116        }
117    }
118}
119
120impl TransportConfig {
121    pub fn validate(config: &TransportConfig, _is_server: bool) -> Result<()> {
122        config
123            .tcp
124            .proxy
125            .as_ref()
126            .map_or(Ok(()), |u| match u.scheme() {
127                "socks5" => Ok(()),
128                "http" => Ok(()),
129                _ => Err(anyhow!(format!("Unknown proxy scheme: {}", u.scheme()))),
130            })?;
131        match config.transport_type {
132            TransportType::Tcp => Ok(()),
133            #[cfg(feature = "rustls")]
134            TransportType::Tls => {
135                let tls_config = config
136                    .tls
137                    .as_ref()
138                    .ok_or_else(|| anyhow!("Missing TLS configuration"))?;
139                if _is_server {
140                    tls_config
141                        .pkcs12
142                        .as_ref()
143                        .and(tls_config.pkcs12_password.as_ref())
144                        .ok_or_else(|| anyhow!("Missing `pkcs12` or `pkcs12_password`"))?;
145                }
146                Ok(())
147            }
148            TransportType::Websocket => Ok(()),
149        }
150    }
151
152    pub fn notls() -> Self {
153        Self {
154            transport_type: TransportType::Websocket,
155            tcp: TcpConfig::default(),
156            tls: None,
157            websocket: WebsocketConfig { tls: false }.into(),
158        }
159    }
160}