support http proxy addresses with no scheme
Other implementations (curl and Go) accept HTTPS_PROXY values with no protocol scheme. When the scheme is not present, http:// is assumed. For example 192.168.1.1 is interpreted as http://192.168.1.1 This commit adds support for http proxy addresses without a scheme by retrying the URL parsing mechanisms with http:// prepended.
This commit is contained in:
committed by
Sean McArthur
parent
c27cd06a11
commit
f5450f534a
@@ -13,6 +13,8 @@ pub trait PolyfillTryInto {
|
|||||||
// Besides parsing as a valid `Url`, the `Url` must be a valid
|
// Besides parsing as a valid `Url`, the `Url` must be a valid
|
||||||
// `http::Uri`, in that it makes sense to use in a network request.
|
// `http::Uri`, in that it makes sense to use in a network request.
|
||||||
fn into_url(self) -> crate::Result<Url>;
|
fn into_url(self) -> crate::Result<Url>;
|
||||||
|
|
||||||
|
fn _as_str(&self) -> &str;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PolyfillTryInto for Url {
|
impl PolyfillTryInto for Url {
|
||||||
@@ -23,18 +25,30 @@ impl PolyfillTryInto for Url {
|
|||||||
Err(crate::error::url_bad_scheme(self))
|
Err(crate::error::url_bad_scheme(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn _as_str(&self) -> &str {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PolyfillTryInto for &'a str {
|
impl<'a> PolyfillTryInto for &'a str {
|
||||||
fn into_url(self) -> crate::Result<Url> {
|
fn into_url(self) -> crate::Result<Url> {
|
||||||
Url::parse(self).map_err(crate::error::builder)?.into_url()
|
Url::parse(self).map_err(crate::error::builder)?.into_url()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn _as_str(&self) -> &str {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PolyfillTryInto for &'a String {
|
impl<'a> PolyfillTryInto for &'a String {
|
||||||
fn into_url(self) -> crate::Result<Url> {
|
fn into_url(self) -> crate::Result<Url> {
|
||||||
(&**self).into_url()
|
(&**self).into_url()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn _as_str(&self) -> &str {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if_hyper! {
|
if_hyper! {
|
||||||
|
|||||||
81
src/proxy.rs
81
src/proxy.rs
@@ -3,7 +3,8 @@ use std::fmt;
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{IntoUrl, Url};
|
use crate::into_url::{IntoUrl, PolyfillTryInto};
|
||||||
|
use crate::Url;
|
||||||
use http::{header::HeaderValue, Uri};
|
use http::{header::HeaderValue, Uri};
|
||||||
use ipnet::IpNet;
|
use ipnet::IpNet;
|
||||||
use percent_encoding::percent_decode;
|
use percent_encoding::percent_decode;
|
||||||
@@ -107,9 +108,33 @@ pub trait IntoProxyScheme {
|
|||||||
fn into_proxy_scheme(self) -> crate::Result<ProxyScheme>;
|
fn into_proxy_scheme(self) -> crate::Result<ProxyScheme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: IntoUrl> IntoProxyScheme for T {
|
impl<S: IntoUrl> IntoProxyScheme for S {
|
||||||
fn into_proxy_scheme(self) -> crate::Result<ProxyScheme> {
|
fn into_proxy_scheme(self) -> crate::Result<ProxyScheme> {
|
||||||
ProxyScheme::parse(self.into_url()?)
|
// validate the URL
|
||||||
|
let url = match self._as_str().into_url() {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(e) => {
|
||||||
|
// the issue could have been caused by a missing scheme, so we try adding http://
|
||||||
|
format!("http://{}", self._as_str())
|
||||||
|
.into_url()
|
||||||
|
.map_err(|_| {
|
||||||
|
// return the original error
|
||||||
|
crate::error::builder(e)
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ProxyScheme::parse(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These bounds are accidentally leaked by the blanket impl of IntoProxyScheme
|
||||||
|
// for all types that implement IntoUrl. So, this function exists to detect
|
||||||
|
// if we were to break those bounds for a user.
|
||||||
|
fn _implied_bounds() {
|
||||||
|
fn prox<T: IntoProxyScheme>(_t: T) {}
|
||||||
|
|
||||||
|
fn url<T: IntoUrl>(t: T) {
|
||||||
|
prox(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -842,8 +867,7 @@ fn extract_type_prefix(address: &str) -> Option<&str> {
|
|||||||
if let Some(indice) = address.find("://") {
|
if let Some(indice) = address.find("://") {
|
||||||
if indice == 0 {
|
if indice == 0 {
|
||||||
None
|
None
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
let prefix = &address[..indice];
|
let prefix = &address[..indice];
|
||||||
let contains_banned = prefix.contains(|c| c == ':' || c == '/');
|
let contains_banned = prefix.contains(|c| c == ':' || c == '/');
|
||||||
|
|
||||||
@@ -853,8 +877,7 @@ fn extract_type_prefix(address: &str) -> Option<&str> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -977,6 +1000,33 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_proxy_scheme_ip_address_default_http() {
|
||||||
|
let ps = "192.168.1.1:8888".into_proxy_scheme().unwrap();
|
||||||
|
|
||||||
|
match ps {
|
||||||
|
ProxyScheme::Http { auth, host } => {
|
||||||
|
assert!(auth.is_none());
|
||||||
|
assert_eq!(host, "192.168.1.1:8888");
|
||||||
|
}
|
||||||
|
other => panic!("unexpected: {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_proxy_scheme_parse_default_http_with_auth() {
|
||||||
|
// this should fail because `foo` is interpreted as the scheme and no host can be found
|
||||||
|
let ps = "foo:bar@localhost:1239".into_proxy_scheme().unwrap();
|
||||||
|
|
||||||
|
match ps {
|
||||||
|
ProxyScheme::Http { auth, host } => {
|
||||||
|
assert_eq!(auth.unwrap(), encode_basic_auth("foo", "bar"));
|
||||||
|
assert_eq!(host, "localhost:1239");
|
||||||
|
}
|
||||||
|
other => panic!("unexpected: {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Smallest possible content for a mutex
|
// Smallest possible content for a mutex
|
||||||
struct MutexInner;
|
struct MutexInner;
|
||||||
|
|
||||||
@@ -996,10 +1046,10 @@ mod tests {
|
|||||||
// to avoid assert! -> panic! -> Mutex Poisoned.
|
// to avoid assert! -> panic! -> Mutex Poisoned.
|
||||||
let baseline_proxies = get_sys_proxies(None);
|
let baseline_proxies = get_sys_proxies(None);
|
||||||
// the system proxy setting url is invalid.
|
// the system proxy setting url is invalid.
|
||||||
env::set_var("http_proxy", "123465");
|
env::set_var("http_proxy", "file://123465");
|
||||||
let invalid_proxies = get_sys_proxies(None);
|
let invalid_proxies = get_sys_proxies(None);
|
||||||
// set valid proxy
|
// set valid proxy
|
||||||
env::set_var("http_proxy", "http://127.0.0.1/");
|
env::set_var("http_proxy", "127.0.0.1/");
|
||||||
let valid_proxies = get_sys_proxies(None);
|
let valid_proxies = get_sys_proxies(None);
|
||||||
|
|
||||||
// reset user setting when guards drop
|
// reset user setting when guards drop
|
||||||
@@ -1033,9 +1083,16 @@ mod tests {
|
|||||||
// set valid proxy
|
// set valid proxy
|
||||||
let valid_proxies = get_sys_proxies(Some((1, String::from("http://127.0.0.1/"))));
|
let valid_proxies = get_sys_proxies(Some((1, String::from("http://127.0.0.1/"))));
|
||||||
let valid_proxies_no_schema = get_sys_proxies(Some((1, String::from("127.0.0.1"))));
|
let valid_proxies_no_schema = get_sys_proxies(Some((1, String::from("127.0.0.1"))));
|
||||||
let valid_proxies_explicit_https = get_sys_proxies(Some((1, String::from("https://127.0.0.1/"))));
|
let valid_proxies_explicit_https =
|
||||||
let multiple_proxies = get_sys_proxies(Some((1, String::from("http=127.0.0.1:8888;https=127.0.0.2:8888"))));
|
get_sys_proxies(Some((1, String::from("https://127.0.0.1/"))));
|
||||||
let multiple_proxies_explicit_schema = get_sys_proxies(Some((1, String::from("http=http://127.0.0.1:8888;https=https://127.0.0.2:8888"))));
|
let multiple_proxies = get_sys_proxies(Some((
|
||||||
|
1,
|
||||||
|
String::from("http=127.0.0.1:8888;https=127.0.0.2:8888"),
|
||||||
|
)));
|
||||||
|
let multiple_proxies_explicit_schema = get_sys_proxies(Some((
|
||||||
|
1,
|
||||||
|
String::from("http=http://127.0.0.1:8888;https=https://127.0.0.2:8888"),
|
||||||
|
)));
|
||||||
|
|
||||||
// reset user setting when guards drop
|
// reset user setting when guards drop
|
||||||
drop(_g1);
|
drop(_g1);
|
||||||
|
|||||||
Reference in New Issue
Block a user