Add support for system/environment proxies (#547)
This commit is contained in:
committed by
Sean McArthur
parent
564a08f230
commit
577d06c363
129
src/proxy.rs
129
src/proxy.rs
@@ -7,6 +7,14 @@ use http::{header::HeaderValue, Uri};
|
||||
use hyper::client::connect::Destination;
|
||||
use url::percent_encoding::percent_decode;
|
||||
use {IntoUrl, Url};
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::error::Error;
|
||||
#[cfg(target_os = "windows")]
|
||||
use winreg::enums::HKEY_CURRENT_USER;
|
||||
#[cfg(target_os = "windows")]
|
||||
use winreg::RegKey;
|
||||
|
||||
/// Configuration of a proxy that a `Client` should pass requests to.
|
||||
///
|
||||
@@ -469,6 +477,101 @@ impl Dst for Uri {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get system proxies information.
|
||||
///
|
||||
/// It can only support Linux, Unix like, and windows system. Note that it will always
|
||||
/// return a HashMap, even if something runs into error when find registry information in
|
||||
/// Windows system. Note that invalid proxy url in the system setting will be ignored.
|
||||
///
|
||||
/// Returns:
|
||||
/// System proxies information as a hashmap like
|
||||
/// {"http": Url::parse("http://127.0.0.1:80"), "https": Url::parse("https://127.0.0.1:80")}
|
||||
pub fn get_proxies() -> HashMap<String, Url> {
|
||||
let proxies: HashMap<String, Url> = get_from_environment();
|
||||
|
||||
// TODO: move the following #[cfg] to `if expression` when attributes on `if` expressions allowed
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if proxies.is_empty() {
|
||||
// don't care errors if can't get proxies from registry, just return an empty HashMap.
|
||||
return get_from_registry();
|
||||
}
|
||||
}
|
||||
proxies
|
||||
}
|
||||
|
||||
fn insert_proxy(proxies: &mut HashMap<String, Url>, schema: String, addr: String)
|
||||
{
|
||||
if let Ok(valid_addr) = Url::parse(&addr) {
|
||||
proxies.insert(schema, valid_addr);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_from_environment() -> HashMap<String, Url> {
|
||||
let mut proxies: HashMap<String, Url> = HashMap::new();
|
||||
|
||||
const PROXY_KEY_ENDS: &str = "_proxy";
|
||||
|
||||
for (key, value) in env::vars() {
|
||||
let key: String = key.to_lowercase();
|
||||
if key.ends_with(PROXY_KEY_ENDS) {
|
||||
let end_indx = key.len() - PROXY_KEY_ENDS.len();
|
||||
let schema = &key[..end_indx];
|
||||
insert_proxy(&mut proxies, String::from(schema), String::from(value));
|
||||
}
|
||||
}
|
||||
proxies
|
||||
}
|
||||
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn get_from_registry_impl() -> Result<HashMap<String, Url>, Box<dyn Error>> {
|
||||
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
||||
let internet_setting: RegKey =
|
||||
hkcu.open_subkey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings")?;
|
||||
// ensure the proxy is enable, if the value doesn't exist, an error will returned.
|
||||
let proxy_enable: u32 = internet_setting.get_value("ProxyEnable")?;
|
||||
let proxy_server: String = internet_setting.get_value("ProxyServer")?;
|
||||
|
||||
if proxy_enable == 0 {
|
||||
return Ok(HashMap::new());
|
||||
}
|
||||
|
||||
let mut proxies: HashMap<String, Url> = HashMap::new();
|
||||
if proxy_server.contains("=") {
|
||||
// per-protocol settings.
|
||||
for p in proxy_server.split(";") {
|
||||
let protocol_parts: Vec<&str> = p.split("=").collect();
|
||||
match protocol_parts.as_slice() {
|
||||
[protocol, address] => {
|
||||
insert_proxy(&mut proxies, String::from(*protocol), String::from(*address));
|
||||
}
|
||||
_ => {
|
||||
// Contains invalid protocol setting, just break the loop
|
||||
// And make proxies to be empty.
|
||||
proxies.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Use one setting for all protocols.
|
||||
if proxy_server.starts_with("http:") {
|
||||
insert_proxy(&mut proxies, String::from("http"), proxy_server);
|
||||
} else {
|
||||
insert_proxy(&mut proxies, String::from("http"), format!("http://{}", proxy_server));
|
||||
insert_proxy(&mut proxies, String::from("https"), format!("https://{}", proxy_server));
|
||||
insert_proxy(&mut proxies, String::from("ftp"), format!("https://{}", proxy_server));
|
||||
}
|
||||
}
|
||||
Ok(proxies)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn get_from_registry() -> HashMap<String, Url> {
|
||||
get_from_registry_impl().unwrap_or(HashMap::new())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -562,4 +665,30 @@ mod tests {
|
||||
assert_eq!(intercepted_uri(&p, https), target1);
|
||||
assert!(p.intercept(&url(other)).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_proxies() {
|
||||
// save system setting first.
|
||||
let system_proxy = env::var("http_proxy");
|
||||
|
||||
// remove proxy.
|
||||
env::remove_var("http_proxy");
|
||||
assert_eq!(get_proxies().contains_key("http"), false);
|
||||
|
||||
// the system proxy setting url is invalid.
|
||||
env::set_var("http_proxy", "123465");
|
||||
assert_eq!(get_proxies().contains_key("http"), false);
|
||||
|
||||
// set valid proxy
|
||||
env::set_var("http_proxy", "http://127.0.0.1/");
|
||||
let proxies = get_proxies();
|
||||
let http_target = proxies.get("http").unwrap().as_str();
|
||||
|
||||
assert_eq!(http_target, "http://127.0.0.1/");
|
||||
// reset user setting.
|
||||
match system_proxy {
|
||||
Err(_) => env::remove_var("http_proxy"),
|
||||
Ok(proxy) => env::set_var("http_proxy", proxy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user