2727
2828package org .apache .hc .client5 .http .impl .routing ;
2929
30+ import java .net .InetAddress ;
3031import java .net .InetSocketAddress ;
3132import java .net .Proxy ;
3233import java .net .ProxySelector ;
3334import java .net .URI ;
3435import java .net .URISyntaxException ;
36+ import java .security .AccessController ;
37+ import java .security .PrivilegedAction ;
3538import java .util .List ;
39+ import java .util .Locale ;
3640
3741import org .apache .hc .client5 .http .SchemePortResolver ;
3842import org .apache .hc .core5 .annotation .Contract ;
@@ -85,7 +89,7 @@ protected HttpHost determineProxy(final HttpHost target, final HttpContext conte
8589 }
8690 if (proxySelectorInstance == null ) {
8791 //The proxy selector can be "unset", so we must be able to deal with a null selector
88- return null ;
92+ return determineEnvProxy ( target ); // === env-fallback ===
8993 }
9094 final List <Proxy > proxies = proxySelectorInstance .select (targetURI );
9195 final Proxy p = chooseProxy (proxies );
@@ -100,8 +104,79 @@ protected HttpHost determineProxy(final HttpHost target, final HttpContext conte
100104 result = new HttpHost (null , isa .getAddress (), isa .getHostString (), isa .getPort ());
101105 }
102106
107+ if (result == null ) {
108+ result = determineEnvProxy (target );
109+ }
110+
103111 return result ;
104112 }
113+ private static HttpHost determineEnvProxy (final HttpHost target ) {
114+ final boolean secure = "https" .equalsIgnoreCase (target .getSchemeName ());
115+ HttpHost proxy = proxyFromEnv (secure ? "HTTPS_PROXY" : "HTTP_PROXY" );
116+ if (proxy == null && !secure ) {
117+ proxy = proxyFromEnv ("HTTPS_PROXY" ); // reuse HTTPS proxy for HTTP if only that exists
118+ }
119+ if (proxy != null && !isNoProxy (target )) {
120+ return proxy ;
121+ }
122+ return null ;
123+ }
124+
125+ private static HttpHost proxyFromEnv (final String var ) {
126+ String val = getenv (var );
127+ if (val == null || val .isEmpty ()) {
128+ val = getenv (var .toLowerCase (Locale .ROOT ));
129+ }
130+ if (val == null || val .isEmpty ()) {
131+ return null ;
132+ }
133+ if (!val .contains ("://" )) {
134+ val = "http://" + val ;
135+ }
136+ try {
137+ final URI uri = new URI (val );
138+ final String host = uri .getHost ();
139+ final int port = uri .getPort () != -1
140+ ? uri .getPort ()
141+ : ("https" .equalsIgnoreCase (uri .getScheme ()) ? 443 : 80 );
142+ return new HttpHost (uri .getScheme (), InetAddress .getByName (host ), port );
143+ } catch (final Exception ignore ) {
144+ return null ;
145+ }
146+ }
147+
148+ private static boolean isNoProxy (final HttpHost target ) {
149+ String list = getenv ("NO_PROXY" );
150+ if (list == null || list .isEmpty ()) {
151+ list = getenv ("no_proxy" );
152+ }
153+ if (list == null || list .isEmpty ()) {
154+ return false ;
155+ }
156+ final String host = target .getHostName ().toLowerCase (Locale .ROOT );
157+ final String hostPort = host + (target .getPort () != -1 ? ":" + target .getPort () : "" );
158+ for (String rule : list .split ("," )) {
159+ rule = rule .trim ().toLowerCase (Locale .ROOT );
160+ if (rule .isEmpty ()) {
161+ continue ;
162+ }
163+ if (rule .equals (host ) || rule .equals (hostPort )) {
164+ return true ; // exact
165+ }
166+ if (rule .startsWith ("*." ) && host .endsWith (rule .substring (1 ))) {
167+ return true ; // *.example.com
168+ }
169+ if (rule .endsWith ("/16" ) && host .startsWith (rule .substring (0 , rule .length () - 3 ))) {
170+ return true ; // cidr /16
171+ }
172+ }
173+ return false ;
174+ }
175+
176+ private static String getenv (final String key ) {
177+ return AccessController .doPrivileged (
178+ (PrivilegedAction <String >) () -> System .getenv (key ));
179+ }
105180
106181 private Proxy chooseProxy (final List <Proxy > proxies ) {
107182 Proxy result = null ;
0 commit comments