capmonstercloud_client/
proxy.rs1use serde::Serialize;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
8#[serde(rename_all = "lowercase")]
9pub enum ProxyType {
10 Http,
11 Https,
12 Socks4,
13 Socks5,
14}
15
16impl ProxyType {
17 pub fn as_str(&self) -> &'static str {
18 match self {
19 ProxyType::Http => "http",
20 ProxyType::Https => "https",
21 ProxyType::Socks4 => "socks4",
22 ProxyType::Socks5 => "socks5",
23 }
24 }
25}
26
27#[derive(Clone, PartialEq, Eq, Serialize)]
32#[serde(rename_all = "camelCase")]
33pub struct ProxyInfo {
34 pub proxy_type: ProxyType,
35 pub proxy_address: String,
37 pub proxy_port: u16,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub proxy_login: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub proxy_password: Option<String>,
42}
43
44impl ProxyInfo {
45 pub fn new(proxy_type: ProxyType, address: impl Into<String>, port: u16) -> Self {
46 Self {
47 proxy_type,
48 proxy_address: address.into(),
49 proxy_port: port,
50 proxy_login: None,
51 proxy_password: None,
52 }
53 }
54
55 pub fn with_auth(mut self, login: impl Into<String>, password: impl Into<String>) -> Self {
57 self.proxy_login = Some(login.into());
58 self.proxy_password = Some(password.into());
59 self
60 }
61}
62
63#[derive(Clone, PartialEq, Eq)]
66pub struct ClientProxy {
67 pub proxy_type: ProxyType,
68 pub address: String,
69 pub port: u16,
70 pub login: Option<String>,
71 pub password: Option<String>,
72}
73
74impl ClientProxy {
75 pub fn new(proxy_type: ProxyType, address: impl Into<String>, port: u16) -> Self {
76 Self {
77 proxy_type,
78 address: address.into(),
79 port,
80 login: None,
81 password: None,
82 }
83 }
84
85 pub fn with_auth(mut self, login: impl Into<String>, password: impl Into<String>) -> Self {
86 self.login = Some(login.into());
87 self.password = Some(password.into());
88 self
89 }
90}
91
92fn redact(secret: &Option<String>) -> &'static str {
93 if secret.is_some() {
94 "Some(\"***\")"
95 } else {
96 "None"
97 }
98}
99
100impl std::fmt::Debug for ProxyInfo {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct("ProxyInfo")
104 .field("proxy_type", &self.proxy_type)
105 .field("proxy_address", &self.proxy_address)
106 .field("proxy_port", &self.proxy_port)
107 .field("proxy_login", &self.proxy_login)
108 .field(
109 "proxy_password",
110 &format_args!("{}", redact(&self.proxy_password)),
111 )
112 .finish()
113 }
114}
115
116impl std::fmt::Debug for ClientProxy {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 f.debug_struct("ClientProxy")
119 .field("proxy_type", &self.proxy_type)
120 .field("address", &self.address)
121 .field("port", &self.port)
122 .field("login", &self.login)
123 .field("password", &format_args!("{}", redact(&self.password)))
124 .finish()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn debug_does_not_leak_passwords() {
134 let p = ProxyInfo::new(ProxyType::Http, "h", 1).with_auth("u", "SECRET");
135 let c = ClientProxy::new(ProxyType::Http, "h", 1).with_auth("u", "SECRET");
136 assert!(!format!("{p:?}").contains("SECRET"));
137 assert!(!format!("{c:?}").contains("SECRET"));
138 }
139}