add Proxy settings

This commit is contained in:
2022-06-20 10:17:20 +00:00
committed by GitHub
parent f9c2cfa260
commit eae366f130
4 changed files with 147 additions and 4 deletions

View File

@@ -2,20 +2,39 @@ use crate::ext::try_err::TryErr;
use crate::gettext;
use json::JsonValue;
use std::convert::TryFrom;
use std::fmt::Display;
use std::ops::Deref;
use std::ops::DerefMut;
use url::Url;
/// The error when parsing Proxy settings
#[derive(Debug, derive_more::From)]
pub enum ProxyError {
/// String error
String(String),
/// Url parse error
UrlParseError(url::ParseError),
}
impl Display for ProxyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::String(s) => f.write_str(s.as_str()),
Self::UrlParseError(s) => {
f.write_str(gettext("Failed to parse URL:"))?;
s.fmt(f)
}
}
}
}
impl From<&str> for ProxyError {
fn from(v: &str) -> Self {
Self::String(String::from(v))
}
}
#[derive(Clone, Debug)]
/// Proxy settings
pub enum Proxy {
/// Apply for all HTTP requests, [None] means do not proxy
@@ -87,3 +106,104 @@ impl TryFrom<&JsonValue> for Proxy {
)))
}
}
impl Proxy {
/// Match the url.
/// * `url` - Url
pub fn r#match(&self, url: &Url) -> Option<Option<Url>> {
match self {
Self::All(d) => Some(d.clone()),
Self::HTTP(d) => {
let scheme = url.scheme();
if scheme.is_empty() || scheme == "http" {
Some(d.clone())
} else {
None
}
}
Self::HTTPS(d) => {
if url.scheme() == "https" {
Some(d.clone())
} else {
None
}
}
}
}
}
#[derive(Clone, Debug)]
/// A list of [Proxy]
pub struct ProxyChain {
/// Proxies
proxies: Vec<Proxy>,
}
impl ProxyChain {
/// Match the url
/// * `url` - The url
pub fn r#match(&self, url: &Url) -> Option<Url> {
for i in self.proxies.iter() {
match i.r#match(url) {
Some(u) => {
return u;
}
None => {}
}
}
None
}
}
impl Default for ProxyChain {
fn default() -> Self {
Self {
proxies: Vec::new(),
}
}
}
impl Deref for ProxyChain {
type Target = Vec<Proxy>;
fn deref(&self) -> &Self::Target {
&self.proxies
}
}
impl DerefMut for ProxyChain {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.proxies
}
}
impl TryFrom<&JsonValue> for ProxyChain {
type Error = ProxyError;
fn try_from(value: &JsonValue) -> Result<Self, Self::Error> {
let mut list = Vec::new();
if value.is_array() {
for i in value.members() {
list.push(Proxy::try_from(i)?);
}
Ok(Self { proxies: list })
} else {
Err(ProxyError::from(gettext("Failed to parse proxy list.")))
}
}
}
impl TryFrom<JsonValue> for ProxyChain {
type Error = ProxyError;
fn try_from(value: JsonValue) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
pub fn check_proxy(v: &JsonValue) -> bool {
match ProxyChain::try_from(v) {
Ok(_) => true,
Err(e) => {
println!("{}", e);
false
}
}
}

View File

@@ -5,11 +5,14 @@ use crate::ext::rw_lock::GetRwLock;
use crate::ext::use_or_not::ToBool;
use crate::ext::use_or_not::UseOrNot;
use crate::list::NonTailList;
use crate::opt::proxy::ProxyChain;
use crate::opt::size::parse_u32_size;
use crate::opt::use_progress_bar::UseProgressBar;
use crate::opts::CommandOpts;
use crate::retry_interval::parse_retry_interval_from_json;
use crate::settings::SettingStore;
use std::convert::TryFrom;
use std::ops::Deref;
use std::sync::Arc;
use std::sync::RwLock;
use std::sync::RwLockReadGuard;
@@ -25,6 +28,8 @@ pub struct OptHelper {
default_retry_interval: NonTailList<Duration>,
_author_name_filters: RwLock<Vec<AuthorNameFilter>>,
_use_progress_bar: RwLock<Option<UseProgressBar>>,
/// Proxy settings
_proxy_chain: RwLock<ProxyChain>,
}
impl OptHelper {
@@ -171,6 +176,10 @@ impl OptHelper {
} else {
None
});
if settings.have("proxy") {
self._proxy_chain
.replace_with2(ProxyChain::try_from(settings.get("proxy").unwrap()).unwrap());
}
self.opt.replace_with2(opt);
self.settings.replace_with2(settings);
}
@@ -179,6 +188,11 @@ impl OptHelper {
self.opt.get_ref().overwrite
}
/// The proxy chain
pub fn proxy_chain(&self) -> ProxyChain {
self._proxy_chain.get_ref().deref().clone()
}
pub fn verbose(&self) -> bool {
self.opt.get_ref().verbose
}
@@ -284,6 +298,7 @@ impl Default for OptHelper {
default_retry_interval: l,
_author_name_filters: RwLock::new(Vec::new()),
_use_progress_bar: RwLock::new(None),
_proxy_chain: RwLock::new(ProxyChain::default()),
}
}
}

View File

@@ -5,6 +5,7 @@ use crate::gettext;
use crate::retry_interval::check_retry_interval;
use crate::settings::SettingDes;
use crate::settings::JsonValueType;
use crate::opt::proxy::check_proxy;
use crate::opt::size::parse_u32_size;
use json::JsonValue;
@@ -28,6 +29,7 @@ pub fn get_settings_list() -> Vec<SettingDes> {
SettingDes::new("download-part-retry", gettext("Max retry count of each part when downloading in multiple thread mode."), JsonValueType::Number, Some(check_i64)).unwrap(),
SettingDes::new("max-threads", gettext("The maximun threads when downloading file."), JsonValueType::Number, Some(check_u64)).unwrap(),
SettingDes::new("part-size", gettext("The size of the each part when downloading file."), JsonValueType::Number, Some(check_parse_size_u32)).unwrap(),
SettingDes::new("proxy", gettext("Proxy settings."), JsonValueType::Array, Some(check_proxy)).unwrap(),
]
}

View File

@@ -7,7 +7,7 @@ use crate::gettext;
use crate::list::NonTailList;
use crate::opthelper::get_helper;
use json::JsonValue;
use reqwest::{Client, IntoUrl, RequestBuilder, Response};
use reqwest::{Client, ClientBuilder, IntoUrl, RequestBuilder, Response};
use std::collections::HashMap;
use std::convert::TryInto;
use std::default::Default;
@@ -94,9 +94,9 @@ impl WebClient {
/// Create a new instance of client
///
/// This function will not handle any basic options, please use [Self::default()] instead.
pub fn new() -> Self {
pub fn new(client: Client) -> Self {
Self {
client: Client::new(),
client,
headers: RwLock::new(HashMap::new()),
cookies: RwLock::new(CookieJar::new()),
verbose: Arc::new(AtomicBool::new(false)),
@@ -374,8 +374,14 @@ impl WebClient {
impl Default for WebClient {
fn default() -> Self {
let c = Self::new();
let opt = get_helper();
let mut c = ClientBuilder::new();
let chain = opt.proxy_chain();
if !chain.is_empty() {
c = c.proxy(reqwest::Proxy::custom(move |url| chain.r#match(url)));
}
let c = c.build().unwrap();
let c = Self::new(c);
c.set_verbose(opt.verbose());
match opt.retry() {
Some(retry) => c.set_retry(retry),