@@ -10,7 +10,6 @@ use ipnet::IpNet;
1010use percent_encoding:: percent_decode;
1111use std:: collections:: HashMap ;
1212use std:: env;
13- #[ cfg( target_os = "windows" ) ]
1413use std:: error:: Error ;
1514use std:: net:: IpAddr ;
1615#[ cfg( target_os = "windows" ) ]
@@ -124,13 +123,33 @@ impl<S: IntoUrl> IntoProxyScheme for S {
124123 let url = match self . as_str ( ) . into_url ( ) {
125124 Ok ( ok) => ok,
126125 Err ( e) => {
127- // the issue could have been caused by a missing scheme, so we try adding http://
128- format ! ( "http://{}" , self . as_str( ) )
129- . into_url ( )
130- . map_err ( |_| {
126+ let mut presumed_to_have_scheme = true ;
127+ let mut source = e. source ( ) ;
128+ while let Some ( err) = source {
129+ if let Some ( parse_error) = err. downcast_ref :: < url:: ParseError > ( ) {
130+ match parse_error {
131+ url:: ParseError :: RelativeUrlWithoutBase => {
132+ presumed_to_have_scheme = false ;
133+ break ;
134+ }
135+ _ => { }
136+ }
137+ } else if let Some ( _) = err. downcast_ref :: < crate :: error:: BadScheme > ( ) {
138+ presumed_to_have_scheme = false ;
139+ break ;
140+ }
141+ source = err. source ( ) ;
142+ }
143+ if !presumed_to_have_scheme {
144+ // the issue could have been caused by a missing scheme, so we try adding http://
145+ let try_this = format ! ( "http://{}" , self . as_str( ) ) ;
146+ try_this. into_url ( ) . map_err ( |_| {
131147 // return the original error
132148 crate :: error:: builder ( e)
133149 } ) ?
150+ } else {
151+ return Err ( crate :: error:: builder ( e) ) ;
152+ }
134153 }
135154 } ;
136155 ProxyScheme :: parse ( url)
@@ -1107,14 +1126,14 @@ mod tests {
11071126 let disabled_proxies = get_sys_proxies ( Some ( ( 0 , String :: from ( "http://127.0.0.1/" ) ) ) ) ;
11081127 // set valid proxy
11091128 let valid_proxies = get_sys_proxies ( Some ( ( 1 , String :: from ( "http://127.0.0.1/" ) ) ) ) ;
1110- let valid_proxies_no_schema = get_sys_proxies ( Some ( ( 1 , String :: from ( "127.0.0.1" ) ) ) ) ;
1129+ let valid_proxies_no_scheme = get_sys_proxies ( Some ( ( 1 , String :: from ( "127.0.0.1" ) ) ) ) ;
11111130 let valid_proxies_explicit_https =
11121131 get_sys_proxies ( Some ( ( 1 , String :: from ( "https://127.0.0.1/" ) ) ) ) ;
11131132 let multiple_proxies = get_sys_proxies ( Some ( (
11141133 1 ,
11151134 String :: from ( "http=127.0.0.1:8888;https=127.0.0.2:8888" ) ,
11161135 ) ) ) ;
1117- let multiple_proxies_explicit_schema = get_sys_proxies ( Some ( (
1136+ let multiple_proxies_explicit_scheme = get_sys_proxies ( Some ( (
11181137 1 ,
11191138 String :: from ( "http=http://127.0.0.1:8888;https=https://127.0.0.2:8888" ) ,
11201139 ) ) ) ;
@@ -1132,11 +1151,11 @@ mod tests {
11321151 assert_eq ! ( p. scheme( ) , "http" ) ;
11331152 assert_eq ! ( p. host( ) , "127.0.0.1" ) ;
11341153
1135- let p = & valid_proxies_no_schema [ "http" ] ;
1154+ let p = & valid_proxies_no_scheme [ "http" ] ;
11361155 assert_eq ! ( p. scheme( ) , "http" ) ;
11371156 assert_eq ! ( p. host( ) , "127.0.0.1" ) ;
11381157
1139- let p = & valid_proxies_no_schema [ "https" ] ;
1158+ let p = & valid_proxies_no_scheme [ "https" ] ;
11401159 assert_eq ! ( p. scheme( ) , "http" ) ;
11411160 assert_eq ! ( p. host( ) , "127.0.0.1" ) ;
11421161
@@ -1152,11 +1171,11 @@ mod tests {
11521171 assert_eq ! ( p. scheme( ) , "http" ) ;
11531172 assert_eq ! ( p. host( ) , "127.0.0.2:8888" ) ;
11541173
1155- let p = & multiple_proxies_explicit_schema [ "http" ] ;
1174+ let p = & multiple_proxies_explicit_scheme [ "http" ] ;
11561175 assert_eq ! ( p. scheme( ) , "http" ) ;
11571176 assert_eq ! ( p. host( ) , "127.0.0.1:8888" ) ;
11581177
1159- let p = & multiple_proxies_explicit_schema [ "https" ] ;
1178+ let p = & multiple_proxies_explicit_scheme [ "https" ] ;
11601179 assert_eq ! ( p. scheme( ) , "https" ) ;
11611180 assert_eq ! ( p. host( ) , "127.0.0.2:8888" ) ;
11621181 }
@@ -1511,3 +1530,223 @@ mod tests {
15111530 ) ;
15121531 }
15131532}
1533+
1534+ #[ cfg( test) ]
1535+ mod test {
1536+ mod into_proxy_scheme {
1537+ use crate :: Proxy ;
1538+ use std:: error:: Error ;
1539+ use std:: mem:: discriminant;
1540+
1541+ fn includes ( haystack : & crate :: error:: Error , needle : url:: ParseError ) -> bool {
1542+ let mut source = haystack. source ( ) ;
1543+ while let Some ( error) = source {
1544+ if let Some ( parse_error) = error. downcast_ref :: < url:: ParseError > ( ) {
1545+ if discriminant ( parse_error) == discriminant ( & needle) {
1546+ return true ;
1547+ }
1548+ }
1549+ source = error. source ( ) ;
1550+ }
1551+ false
1552+ }
1553+
1554+ fn check_parse_error ( url : & str , needle : url:: ParseError ) {
1555+ let error = Proxy :: http ( url) . unwrap_err ( ) ;
1556+ if !includes ( & error, needle) {
1557+ panic ! ( "{:?} expected; {:?}, {} found" , needle, error, error) ;
1558+ }
1559+ }
1560+
1561+ mod when_scheme_missing {
1562+ mod and_url_is_valid {
1563+ use crate :: Proxy ;
1564+
1565+ #[ test]
1566+ fn lookback_works ( ) {
1567+ let _ = Proxy :: http ( "127.0.0.1" ) . unwrap ( ) ;
1568+ }
1569+
1570+ #[ test]
1571+ fn loopback_port_works ( ) {
1572+ let _ = Proxy :: http ( "127.0.0.1:8080" ) . unwrap ( ) ;
1573+ }
1574+
1575+ #[ test]
1576+ fn loopback_username_works ( ) {
1577+ let _ =
Proxy :: http ( "[email protected] " ) . unwrap ( ) ; 1578+ }
1579+
1580+ #[ test]
1581+ fn loopback_username_password_works ( ) {
1582+ let _ =
Proxy :: http ( "username:[email protected] " ) . unwrap ( ) ; 1583+ }
1584+
1585+ #[ test]
1586+ fn loopback_username_password_port_works ( ) {
1587+ let _ =
Proxy :: http ( "ldap%5Cgremlin:pass%[email protected] :8080" ) . unwrap ( ) ; 1588+ }
1589+
1590+ #[ test]
1591+ fn domain_works ( ) {
1592+ let _ = Proxy :: http ( "proxy.example.com" ) . unwrap ( ) ;
1593+ }
1594+
1595+ #[ test]
1596+ fn domain_port_works ( ) {
1597+ let _ = Proxy :: http ( "proxy.example.com:8080" ) . unwrap ( ) ;
1598+ }
1599+
1600+ #[ test]
1601+ fn domain_username_works ( ) {
1602+ let _ =
Proxy :: http ( "[email protected] " ) . unwrap ( ) ; 1603+ }
1604+
1605+ #[ test]
1606+ fn domain_username_password_works ( ) {
1607+ let _ =
Proxy :: http ( "username:[email protected] " ) . unwrap ( ) ; 1608+ }
1609+
1610+ #[ test]
1611+ fn domain_username_password_port_works ( ) {
1612+ let _ =
1613+ Proxy :: http ( "ldap%5Cgremlin:pass%[email protected] :8080" ) . unwrap ( ) ; 1614+ }
1615+ }
1616+ mod and_url_has_bad {
1617+ use super :: super :: check_parse_error;
1618+
1619+ #[ test]
1620+ fn host ( ) {
1621+ check_parse_error ( "username@" , url:: ParseError :: RelativeUrlWithoutBase ) ;
1622+ }
1623+
1624+ #[ test]
1625+ fn idna_encoding ( ) {
1626+ check_parse_error ( "xn---" , url:: ParseError :: RelativeUrlWithoutBase ) ;
1627+ }
1628+
1629+ #[ test]
1630+ fn port ( ) {
1631+ check_parse_error ( "127.0.0.1:808080" , url:: ParseError :: RelativeUrlWithoutBase ) ;
1632+ }
1633+
1634+ #[ test]
1635+ fn ip_v4_address ( ) {
1636+ check_parse_error ( "421.627.718.469" , url:: ParseError :: RelativeUrlWithoutBase ) ;
1637+ }
1638+
1639+ #[ test]
1640+ fn ip_v6_address ( ) {
1641+ check_parse_error (
1642+ "[56FE::2159:5BBC::6594]" ,
1643+ url:: ParseError :: RelativeUrlWithoutBase ,
1644+ ) ;
1645+ }
1646+
1647+ #[ test]
1648+ fn invalid_domain_character ( ) {
1649+ check_parse_error ( "abc 123" , url:: ParseError :: RelativeUrlWithoutBase ) ;
1650+ }
1651+ }
1652+ }
1653+
1654+ mod when_scheme_present {
1655+ mod and_url_is_valid {
1656+ use crate :: Proxy ;
1657+
1658+ #[ test]
1659+ fn loopback_works ( ) {
1660+ let _ = Proxy :: http ( "http://127.0.0.1" ) . unwrap ( ) ;
1661+ }
1662+
1663+ #[ test]
1664+ fn loopback_port_works ( ) {
1665+ let _ = Proxy :: http ( "https://127.0.0.1:8080" ) . unwrap ( ) ;
1666+ }
1667+
1668+ #[ test]
1669+ fn loopback_username_works ( ) {
1670+ let _ =
Proxy :: http ( "http://[email protected] " ) . unwrap ( ) ; 1671+ }
1672+
1673+ #[ test]
1674+ fn loopback_username_password_works ( ) {
1675+ let _ =
Proxy :: http ( "https://username:[email protected] " ) . unwrap ( ) ; 1676+ }
1677+
1678+ #[ test]
1679+ fn loopback_username_password_port_works ( ) {
1680+ let _ =
1681+ Proxy :: http ( "http://ldap%5Cgremlin:pass%[email protected] :8080" ) . unwrap ( ) ; 1682+ }
1683+
1684+ #[ test]
1685+ fn domain_works ( ) {
1686+ let _ = Proxy :: http ( "https://proxy.example.com" ) . unwrap ( ) ;
1687+ }
1688+
1689+ #[ test]
1690+ fn domain_port_works ( ) {
1691+ let _ = Proxy :: http ( "http://proxy.example.com:8080" ) . unwrap ( ) ;
1692+ }
1693+
1694+ #[ test]
1695+ fn domain_username_works ( ) {
1696+ let _ =
Proxy :: http ( "https://[email protected] " ) . unwrap ( ) ; 1697+ }
1698+
1699+ #[ test]
1700+ fn domain_username_password_works ( ) {
1701+ let _ =
Proxy :: http ( "http://username:[email protected] " ) . unwrap ( ) ; 1702+ }
1703+
1704+ #[ test]
1705+ fn domain_username_password_port_works ( ) {
1706+ let _ =
1707+ Proxy :: http ( "https://ldap%5Cgremlin:pass%[email protected] :8080" ) 1708+ . unwrap ( ) ;
1709+ }
1710+ }
1711+ mod and_url_has_bad {
1712+ use super :: super :: check_parse_error;
1713+
1714+ #[ test]
1715+ fn host ( ) {
1716+ check_parse_error ( "http://username@" , url:: ParseError :: EmptyHost ) ;
1717+ }
1718+
1719+ #[ test]
1720+ fn idna_encoding ( ) {
1721+ check_parse_error ( "http://xn---" , url:: ParseError :: IdnaError ) ;
1722+ }
1723+
1724+ #[ test]
1725+ fn port ( ) {
1726+ check_parse_error ( "http://127.0.0.1:808080" , url:: ParseError :: InvalidPort ) ;
1727+ }
1728+
1729+ #[ test]
1730+ fn ip_v4_address ( ) {
1731+ check_parse_error (
1732+ "http://421.627.718.469" ,
1733+ url:: ParseError :: InvalidIpv4Address ,
1734+ ) ;
1735+ }
1736+
1737+ #[ test]
1738+ fn ip_v6_address ( ) {
1739+ check_parse_error (
1740+ "http://[56FE::2159:5BBC::6594]" ,
1741+ url:: ParseError :: InvalidIpv6Address ,
1742+ ) ;
1743+ }
1744+
1745+ #[ test]
1746+ fn invalid_domain_character ( ) {
1747+ check_parse_error ( "http://abc 123/" , url:: ParseError :: InvalidDomainCharacter ) ;
1748+ }
1749+ }
1750+ }
1751+ }
1752+ }
0 commit comments