mirror of
https://gitlab.com/famedly/conduit.git
synced 2025-07-22 17:18:35 +00:00
95 lines
3.2 KiB
Rust
95 lines
3.2 KiB
Rust
use reqwest::{Proxy, Url};
|
|
use serde::Deserialize;
|
|
|
|
use super::wild_carded_domain::WildCardedDomain;
|
|
use crate::Result;
|
|
|
|
/// ## Examples:
|
|
/// - No proxy (default):
|
|
/// ```toml
|
|
/// proxy ="none"
|
|
/// ```
|
|
/// - Global proxy
|
|
/// ```toml
|
|
/// [global.proxy]
|
|
/// global = { url = "socks5h://localhost:9050" }
|
|
/// ```
|
|
/// - Proxy some domains
|
|
/// ```toml
|
|
/// [global.proxy]
|
|
/// [[global.proxy.by_domain]]
|
|
/// url = "socks5h://localhost:9050"
|
|
/// include = ["*.onion", "matrix.myspecial.onion"]
|
|
/// exclude = ["*.myspecial.onion"]
|
|
/// ```
|
|
/// ## Include vs. Exclude
|
|
/// If include is an empty list, it is assumed to be `["*"]`.
|
|
///
|
|
/// If a domain matches both the exclude and include list, the proxy will only be used if it was
|
|
/// included because of a more specific rule than it was excluded. In the above example, the proxy
|
|
/// would be used for `ordinary.onion`, `matrix.myspecial.onion`, but not `hello.myspecial.onion`.
|
|
#[derive(Clone, Debug, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[derive(Default)]
|
|
pub enum ProxyConfig {
|
|
#[default]
|
|
None,
|
|
Global {
|
|
#[serde(deserialize_with = "crate::utils::deserialize_from_str")]
|
|
url: Url,
|
|
},
|
|
ByDomain(Vec<PartialProxyConfig>),
|
|
}
|
|
impl ProxyConfig {
|
|
pub fn to_proxy(&self) -> Result<Option<Proxy>> {
|
|
Ok(match self.clone() {
|
|
ProxyConfig::None => None,
|
|
ProxyConfig::Global { url } => Some(Proxy::all(url)?),
|
|
ProxyConfig::ByDomain(proxies) => Some(Proxy::custom(move |url| {
|
|
proxies.iter().find_map(|proxy| proxy.for_url(url)).cloned() // first matching proxy
|
|
})),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize)]
|
|
pub struct PartialProxyConfig {
|
|
#[serde(deserialize_with = "crate::utils::deserialize_from_str")]
|
|
url: Url,
|
|
#[serde(default)]
|
|
include: Vec<WildCardedDomain>,
|
|
#[serde(default)]
|
|
exclude: Vec<WildCardedDomain>,
|
|
}
|
|
impl PartialProxyConfig {
|
|
pub fn for_url(&self, url: &Url) -> Option<&Url> {
|
|
let domain = url.domain()?;
|
|
let mut included_because = None; // most specific reason it was included
|
|
let mut excluded_because = None; // most specific reason it was excluded
|
|
if self.include.is_empty() {
|
|
// treat empty include list as `*`
|
|
included_because = Some(&WildCardedDomain::WildCard)
|
|
}
|
|
for wc_domain in &self.include {
|
|
if wc_domain.matches(domain) {
|
|
match included_because {
|
|
Some(prev) if !wc_domain.more_specific_than(prev) => (),
|
|
_ => included_because = Some(wc_domain),
|
|
}
|
|
}
|
|
}
|
|
for wc_domain in &self.exclude {
|
|
if wc_domain.matches(domain) {
|
|
match excluded_because {
|
|
Some(prev) if !wc_domain.more_specific_than(prev) => (),
|
|
_ => excluded_because = Some(wc_domain),
|
|
}
|
|
}
|
|
}
|
|
match (included_because, excluded_because) {
|
|
(Some(a), Some(b)) if a.more_specific_than(b) => Some(&self.url), // included for a more specific reason than excluded
|
|
(Some(_), None) => Some(&self.url),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|