use std::fmt;
use std::str::FromStr;
use qubit_config::{
ConfigReader,
ConfigResult,
};
use super::HttpConfigError;
use super::proxy_type::ProxyType;
#[derive(Clone, PartialEq, Eq)]
pub struct ProxyOptions {
pub enabled: bool,
pub proxy_type: ProxyType,
pub host: Option<String>,
pub port: Option<u16>,
pub username: Option<String>,
pub password: Option<String>,
}
impl fmt::Debug for ProxyOptions {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let password = self.password.as_ref().map(|_| "****");
formatter
.debug_struct("ProxyOptions")
.field("enabled", &self.enabled)
.field("proxy_type", &self.proxy_type)
.field("host", &self.host)
.field("port", &self.port)
.field("username", &self.username)
.field("password", &password)
.finish()
}
}
impl Default for ProxyOptions {
fn default() -> Self {
Self {
enabled: false,
proxy_type: ProxyType::Http,
host: None,
port: None,
username: None,
password: None,
}
}
}
struct ProxyConfigInput {
enabled: Option<bool>,
proxy_type: Option<String>,
host: Option<String>,
port: Option<u16>,
username: Option<String>,
password: Option<String>,
}
fn read_proxy_config<R>(config: &R) -> ConfigResult<ProxyConfigInput>
where
R: ConfigReader + ?Sized,
{
Ok(ProxyConfigInput {
enabled: config.get_optional("enabled")?,
proxy_type: config.get_optional_string("proxy_type")?,
host: config.get_optional_string("host")?,
port: config.get_optional("port")?,
username: config.get_optional_string("username")?,
password: config.get_optional_string("password")?,
})
}
impl ProxyOptions {
pub fn from_config<R>(config: &R) -> Result<Self, HttpConfigError>
where
R: ConfigReader + ?Sized,
{
let raw = read_proxy_config(config).map_err(HttpConfigError::from)?;
let mut opts = ProxyOptions::default();
if let Some(v) = raw.enabled {
opts.enabled = v;
}
if let Some(s) = raw.proxy_type {
opts.proxy_type = parse_proxy_type("proxy_type", &s)?;
}
opts.host = raw.host;
if let Some(p) = raw.port {
opts.port = Some(p);
}
opts.username = raw.username;
opts.password = raw.password;
Ok(opts)
}
pub fn validate(&self) -> Result<(), HttpConfigError> {
if self.enabled {
match self.host.as_deref() {
None => {
return Err(HttpConfigError::missing(
"proxy.host",
"Proxy is enabled but host is missing",
));
}
Some(host) if host.trim().is_empty() => {
return Err(HttpConfigError::invalid_value(
"proxy.host",
"Proxy host cannot be empty when proxy is enabled",
));
}
_ => {}
}
match self.port {
None => {
return Err(HttpConfigError::missing(
"proxy.port",
"Proxy is enabled but port is missing",
));
}
Some(0) => {
return Err(HttpConfigError::invalid_value(
"proxy.port",
"Proxy port must be greater than 0",
));
}
_ => {}
}
}
if let Some(username) = self.username.as_deref() {
if username.trim().is_empty() {
return Err(HttpConfigError::invalid_value(
"proxy.username",
"Proxy username cannot be empty when provided",
));
}
}
if self.username.is_none() && self.password.is_some() {
return Err(HttpConfigError::missing(
"proxy.username",
"Proxy password is configured but username is missing",
));
}
Ok(())
}
}
fn parse_proxy_type(path: &str, s: &str) -> Result<ProxyType, HttpConfigError> {
ProxyType::from_str(s.trim()).map_err(|_| {
HttpConfigError::invalid_value(
path,
format!("Unknown proxy type '{}'; expected http, https, or socks5", s,),
)
})
}