Add support for system/environment proxies (#547)
This commit is contained in:
committed by
Sean McArthur
parent
564a08f230
commit
577d06c363
@@ -36,6 +36,7 @@ use into_url::{expect_uri, try_uri};
|
||||
use cookie;
|
||||
use redirect::{self, RedirectPolicy, remove_sensitive_headers};
|
||||
use {IntoUrl, Method, Proxy, StatusCode, Url};
|
||||
use ::proxy::get_proxies;
|
||||
#[cfg(feature = "tls")]
|
||||
use {Certificate, Identity};
|
||||
#[cfg(feature = "tls")]
|
||||
@@ -327,6 +328,26 @@ impl ClientBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Clear all `Proxies`, so `Client` will use no proxy anymore.
|
||||
pub fn no_proxy(mut self) -> ClientBuilder {
|
||||
self.config.proxies.clear();
|
||||
self
|
||||
}
|
||||
|
||||
/// Add system proxy setting to the list of proxies
|
||||
pub fn use_sys_proxy(mut self) -> ClientBuilder {
|
||||
let proxies = get_proxies();
|
||||
self.config.proxies.push(Proxy::custom(move |url| {
|
||||
return if proxies.contains_key(url.scheme()) {
|
||||
Some((*proxies.get(url.scheme()).unwrap()).clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}));
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
/// Set a `RedirectPolicy` for this client.
|
||||
///
|
||||
/// Default will follow redirects up to a maximum of 10.
|
||||
|
||||
@@ -84,6 +84,16 @@ impl ClientBuilder {
|
||||
})
|
||||
}
|
||||
|
||||
/// Disable proxy setting.
|
||||
pub fn no_proxy(self) -> ClientBuilder {
|
||||
self.with_inner(move |inner| inner.no_proxy())
|
||||
}
|
||||
|
||||
/// Enable system proxy setting.
|
||||
pub fn use_sys_proxy(self) -> ClientBuilder {
|
||||
self.with_inner(move |inner| inner.use_sys_proxy())
|
||||
}
|
||||
|
||||
/// Set that all sockets have `SO_NODELAY` set to `true`.
|
||||
pub fn tcp_nodelay(self) -> ClientBuilder {
|
||||
self.with_inner(move |inner| inner.tcp_nodelay())
|
||||
@@ -369,7 +379,7 @@ impl ClientBuilder {
|
||||
|
||||
/// Enable a persistent cookie store for the client.
|
||||
///
|
||||
/// Cookies received in responses will be preserved and included in
|
||||
/// Cookies received in responses will be preserved and included in
|
||||
/// additional requests.
|
||||
///
|
||||
/// By default, no cookie store is used.
|
||||
@@ -406,7 +416,8 @@ impl Client {
|
||||
|
||||
/// Creates a `ClientBuilder` to configure a `Client`.
|
||||
///
|
||||
/// This is the same as `ClientBuilder::new()`.
|
||||
/// This builder will use system proxy setting, you can use
|
||||
/// `reqwest::Client::builder().no_proxy()` to disable it.
|
||||
pub fn builder() -> ClientBuilder {
|
||||
ClientBuilder::new()
|
||||
}
|
||||
|
||||
@@ -133,6 +133,8 @@
|
||||
//! A `Client` can be configured to make use of HTTP proxies by adding
|
||||
//! [`Proxy`](Proxy)s to a `ClientBuilder`.
|
||||
//!
|
||||
//! ** NOTE** System proxies will be used in the next breaking change.
|
||||
//!
|
||||
//! ## TLS
|
||||
//!
|
||||
//! By default, a `Client` will make use of system-native transport layer
|
||||
@@ -206,6 +208,8 @@ extern crate url;
|
||||
extern crate uuid;
|
||||
#[cfg(feature = "socks")]
|
||||
extern crate socks;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate winreg;
|
||||
|
||||
#[cfg(feature = "rustls-tls")]
|
||||
extern crate hyper_rustls;
|
||||
|
||||
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