JDK-8169295 : http.nonProxyHosts entries are ignored when it contains duplicates
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 8u112
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2016-11-04
  • Updated: 2016-11-07
  • Resolved: 2016-11-07
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_102"
Java(TM) SE Runtime Environment (build 1.8.0_102-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Darwin bruichladdich.local 16.1.0 Darwin Kernel Version 16.1.0: Thu Oct 13 21:26:57 PDT 2016; root:xnu-3789.21.3~60/RELEASE_X86_64 x86_64

Probably affects other OSes.

EXTRA RELEVANT SYSTEM CONFIGURATION :
Configured with native OSX VPN client with http and https proxies.

A DESCRIPTION OF THE PROBLEM :
When http.nonProxyHosts contains duplicate entries, only entries that occur before the duplicate are accessed without a proxy.

eg. if:
http.nonProxyHosts=api.example.com,api.google.com,api.example.com,api.java.com

Then opening an http connection to api.java.com will use a proxy. This is only problematic once you can not reach a host through a proxy.

This problem occurs because DefaultProxySelector will skip additional entries once it encounters a duplicate. The problematic code can be found in sun.net.spi.DefaultProxySelector at line 268-276. My current hg tip of openjdk is 0eb62e4a75e6.

RegexpPool pool = new RegexpPool();
StringTokenizer st = new StringTokenizer(nphosts, "|", false);
try {
    while (st.hasMoreTokens()) {
        pool.add(st.nextToken().toLowerCase(), Boolean.TRUE);
    }
} catch (sun.misc.REException ex) {
}

RegexpPool.add will throw sun.misc.REException if the token was already added. This causes DefaultProxySelector to jump out of the looping where it is adding patterns and continue.

My proposed fix would be to move the try/catch block into the loop:

diff -r 0eb62e4a75e6 src/share/classes/sun/net/spi/DefaultProxySelector.java
--- a/src/share/classes/sun/net/spi/DefaultProxySelector.java	Thu Nov 05 11:45:08 2015 +0000
+++ b/src/share/classes/sun/net/spi/DefaultProxySelector.java	Fri Nov 04 10:47:50 2016 +0100
@@ -268,11 +268,11 @@
                                         if (!nphosts.equals(nprop.hostsSource)) {
                                             RegexpPool pool = new RegexpPool();
                                             StringTokenizer st = new StringTokenizer(nphosts, "|", false);
-                                            try {
-                                                while (st.hasMoreTokens()) {
+                                            while (st.hasMoreTokens()) {
+                                                try {
                                                     pool.add(st.nextToken().toLowerCase(), Boolean.TRUE);
+                                                } catch (sun.misc.REException ex) {
                                                 }
-                                            } catch (sun.misc.REException ex) {
                                             }
                                             nprop.hostsPool = pool;
                                             nprop.hostsSource = nphosts;

Or to make public the variant of sun.misc.RegexpPool.add that accepts a boolean replace parameter and use that one:

diff -r 0eb62e4a75e6 src/share/classes/sun/misc/RegexpPool.java
--- a/src/share/classes/sun/misc/RegexpPool.java	Thu Nov 05 11:45:08 2015 +0000
+++ b/src/share/classes/sun/misc/RegexpPool.java	Fri Nov 04 10:50:15 2016 +0100
@@ -162,7 +162,7 @@
         return matchAfter(s, lastDepth);
     }

-    private void add(String re, Object ret, boolean replace) throws REException {
+    public void add(String re, Object ret, boolean replace) throws REException {
         int len = re.length();
         RegexpNode p;
         if (re.charAt(0) == '*') {
diff -r 0eb62e4a75e6 src/share/classes/sun/net/spi/DefaultProxySelector.java
--- a/src/share/classes/sun/net/spi/DefaultProxySelector.java	Thu Nov 05 11:45:08 2015 +0000
+++ b/src/share/classes/sun/net/spi/DefaultProxySelector.java	Fri Nov 04 10:50:15 2016 +0100
@@ -270,7 +270,7 @@
                                             StringTokenizer st = new StringTokenizer(nphosts, "|", false);
                                             try {
                                                 while (st.hasMoreTokens()) {
-                                                    pool.add(st.nextToken().toLowerCase(), Boolean.TRUE);
+                                                    pool.add(st.nextToken().toLowerCase(), Boolean.TRUE, true);
                                                 }
                                             } catch (sun.misc.REException ex) {
                                             }

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
On OSX:

Go to System Preferences, choose Network. In the left hand pane, select your currently active network connection (eg. Wi-Fi). Click Advanced. Click the Proxies pane. Configure Web Proxy. Set up a proxy address that does not work, eg. . In the textfield under "Bypass proxy settings for these Hosts & Domains", type:

localhost, localhost, *.com

Run a java program that uses URL.connect to create connections to a .com address.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I expect connections to .com addresses to go through.
ACTUAL -
Connections attempt to use the proxy for .com addresses, which fails.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.net.ProxySelector;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;

// This will fail with an exception on OSX when network is configured with
// proxy that does not work, and  the proxy ignore list is, 
// for example, "localhost, localhost, java.com"
public  class Main {
    public static void main(String [] args) throws Exception {
        System.out.println(ProxySelector.getDefault().select(URI.create("http://java.com")));
        URLConnection urlConnection = new URL("http://java.com").openConnection(ProxySelector.getDefault().select(URI.create("http://java.com")).get(0));
        urlConnection.connect();
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Ensure that no duplicates exist in system property nonProxyHosts.


Comments
This is a duplicate of JDK-8145732 which is fixed for JDK 9. It has been backported with JDK-8164209 to JDK 8u122. Please download the JDK EA versions 8u122 and JDK 9 from the following urls and test the issue. Please let us know if the issue is still seen. https://jdk9.java.net/download/ https://jdk8.java.net/download.html
07-11-2016