changeset 16610:949799faf431

8170868: DefaultProxySelector should use system defaults on Windows, MacOS and Gnome Reviewed-by: chegar, simonis, clanger, stuefe, erikj Contributed-by: arno.zeller@sap.com
author clanger
date Thu, 02 Feb 2017 10:28:47 +0100
parents a88d76c088c7
children 63f7055a2240
files make/lib/NetworkingLibraries.gmk make/mapfiles/libnet/mapfile-vers src/java.base/macosx/native/libnet/DefaultProxySelector.c src/java.base/share/classes/java/net/doc-files/net-properties.html src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java src/java.base/share/native/libnet/proxy_util.c src/java.base/share/native/libnet/proxy_util.h src/java.base/unix/native/libnet/DefaultProxySelector.c src/java.base/windows/native/libnet/DefaultProxySelector.c test/java/net/ProxySelector/SystemProxies.java
diffstat 10 files changed, 875 insertions(+), 380 deletions(-) [+]
line wrap: on
line diff
--- a/make/lib/NetworkingLibraries.gmk	Wed Feb 01 23:33:39 2017 +0300
+++ b/make/lib/NetworkingLibraries.gmk	Thu Feb 02 10:28:47 2017 +0100
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -44,8 +44,9 @@
     LIBS_linux := $(LIBDL) -lpthread, \
     LIBS_solaris := -lnsl -lsocket $(LIBDL) -lc, \
     LIBS_aix := $(LIBDL),\
-    LIBS_windows := ws2_32.lib jvm.lib secur32.lib iphlpapi.lib \
+    LIBS_windows := ws2_32.lib jvm.lib secur32.lib iphlpapi.lib winhttp.lib \
         delayimp.lib $(WIN_JAVA_LIB) advapi32.lib, \
+    LIBS_macosx := -framework CoreFoundation -framework CoreServices, \
     VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \
     RC_FLAGS := $(RC_FLAGS) \
         -D "JDK_FNAME=net.dll" \
--- a/make/mapfiles/libnet/mapfile-vers	Wed Feb 01 23:33:39 2017 +0300
+++ b/make/mapfiles/libnet/mapfile-vers	Thu Feb 02 10:28:47 2017 +0100
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -97,7 +97,7 @@
 		Java_sun_net_sdp_SdpSupport_convert0;
 		Java_sun_net_sdp_SdpSupport_create0;
 		Java_sun_net_spi_DefaultProxySelector_init;
-		Java_sun_net_spi_DefaultProxySelector_getSystemProxy;
+		Java_sun_net_spi_DefaultProxySelector_getSystemProxies;
 		NET_SockaddrToInetAddress;
                 NET_SockaddrEqualsInetAddress;
 		NET_InetAddressToSockaddr;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/macosx/native/libnet/DefaultProxySelector.c	Thu Feb 02 10:28:47 2017 +0100
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <string.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jvm_md.h"
+
+#include "proxy_util.h"
+
+#include "sun_net_spi_DefaultProxySelector.h"
+
+
+/**
+ * For more information on how to use the APIs in "CFProxySupport.h" see:
+ * https://developer.apple.com/legacy/library/samplecode/CFProxySupportTool/Introduction/Intro.html
+ */
+
+#define kResolveProxyRunLoopMode CFSTR("sun.net.spi.DefaultProxySelector")
+
+#define BUFFER_SIZE 1024
+
+/* Callback for CFNetworkExecuteProxyAutoConfigurationURL. */
+static void proxyUrlCallback(void * client, CFArrayRef proxies, CFErrorRef error) {
+    /* client is a pointer to a CFTypeRef and holds either proxies or an error. */
+    CFTypeRef* resultPtr = (CFTypeRef *)client;
+
+    if (error != NULL) {
+        *resultPtr = CFRetain(error);
+    } else {
+        *resultPtr = CFRetain(proxies);
+    }
+    CFRunLoopStop(CFRunLoopGetCurrent());
+}
+
+/*
+ * Returns a new array of proxies containing all the given non-PAC proxies as
+ * well as the results of executing all the given PAC-based proxies, for the
+ * specified URL. 'proxies' is a list that may contain both PAC and non-PAC
+ * proxies.
+ */
+static CFArrayRef createExpandedProxiesArray(CFArrayRef proxies, CFURLRef url) {
+
+    CFIndex count;
+    CFIndex index;
+    CFMutableArrayRef expandedProxiesArray;
+
+    expandedProxiesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+    if (expandedProxiesArray == NULL)
+        return NULL;
+
+    /* Iterate over the array of proxies */
+    count = CFArrayGetCount(proxies);
+    for (index = 0; index < count ; index++) {
+        CFDictionaryRef currentProxy;
+        CFStringRef     proxyType;
+
+        currentProxy = (CFDictionaryRef) CFArrayGetValueAtIndex(proxies, index);
+        if(currentProxy == NULL) {
+            CFRelease(expandedProxiesArray);
+            return NULL;
+        }
+        proxyType = (CFStringRef) CFDictionaryGetValue(currentProxy, kCFProxyTypeKey);
+        if (proxyType == NULL) {
+            CFRelease(expandedProxiesArray);
+            return NULL;
+        }
+
+        if (!CFEqual(proxyType, kCFProxyTypeAutoConfigurationURL)) {
+            /* Non-PAC entry, just copy it to the new array */
+            CFArrayAppendValue(expandedProxiesArray, currentProxy);
+        } else {
+            /* PAC-based URL, execute its script append its results */
+            CFRunLoopSourceRef      runLoop;
+            CFURLRef                scriptURL;
+            CFTypeRef               result = NULL;
+            CFStreamClientContext   context = { 0, &result, NULL, NULL, NULL };
+            CFTimeInterval timeout = 5;
+
+            scriptURL = CFDictionaryGetValue(currentProxy, kCFProxyAutoConfigurationURLKey);
+
+            runLoop = CFNetworkExecuteProxyAutoConfigurationURL(scriptURL, url, proxyUrlCallback,
+                                                                &context);
+            if (runLoop != NULL) {
+                /*
+                 * Despite the fact that CFNetworkExecuteProxyAutoConfigurationURL has
+                 * neither a "Create" nor a "Copy" in the name, we are required to
+                 * release the return CFRunLoopSourceRef <rdar://problem/5533931>.
+                 */
+                CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoop, kResolveProxyRunLoopMode);
+                CFRunLoopRunInMode(kResolveProxyRunLoopMode, timeout, false);
+                CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoop, kResolveProxyRunLoopMode);
+
+                /*
+                 * Once the runloop returns, there will be either an error result or
+                 * a proxies array result. Do the appropriate thing with that result.
+                 */
+                if (result != NULL) {
+                    if (CFGetTypeID(result) == CFArrayGetTypeID()) {
+                        /*
+                         * Append the new array from the PAC list - it contains
+                         * only non-PAC entries.
+                         */
+                        CFArrayAppendArray(expandedProxiesArray, result,
+                                           CFRangeMake(0, CFArrayGetCount(result)));
+                    }
+                    CFRelease(result);
+                }
+                CFRelease(runLoop);
+            }
+        }
+    }
+    return expandedProxiesArray;
+}
+
+
+/*
+ * Class:     sun_net_spi_DefaultProxySelector
+ * Method:    init
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_sun_net_spi_DefaultProxySelector_init(JNIEnv *env, jclass clazz) {
+    if (!initJavaClass(env)) {
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+
+/*
+ * Class:     sun_net_spi_DefaultProxySelector
+ * Method:    getSystemProxies
+ * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env,
+                                                       jobject this,
+                                                       jstring proto,
+                                                       jstring host)
+{
+    CFDictionaryRef proxyDicRef = NULL;
+    CFURLRef        urlRef = NULL;
+    bool proxyFound = false;
+    jobjectArray proxyArray = NULL;
+    const char *cproto;
+    const char *chost;
+
+    /* Get system proxy settings */
+    proxyDicRef = CFNetworkCopySystemProxySettings();
+    if (proxyDicRef == NULL) {
+        return NULL;
+    }
+
+    /* Create CFURLRef from proto and host */
+    cproto = (*env)->GetStringUTFChars(env, proto, NULL);
+    if (cproto != NULL) {
+        chost  = (*env)->GetStringUTFChars(env, host, NULL);
+        if (chost != NULL) {
+            char* uri = NULL;
+            size_t protoLen = 0;
+            size_t hostLen = 0;
+
+            protoLen = strlen(cproto);
+            hostLen = strlen(chost);
+
+            /* Construct the uri, cproto + "://" + chost */
+            uri = malloc(protoLen + hostLen + 4);
+            if (uri != NULL) {
+                memcpy(uri, cproto, protoLen);
+                memcpy(uri + protoLen, "://", 3);
+                memcpy(uri + protoLen + 3, chost, hostLen + 1);
+
+                urlRef = CFURLCreateWithBytes(NULL, (const UInt8 *) uri, strlen(uri),
+                                              kCFStringEncodingUTF8, NULL);
+                free(uri);
+            }
+            (*env)->ReleaseStringUTFChars(env, host, chost);
+        }
+        (*env)->ReleaseStringUTFChars(env, proto, cproto);
+    }
+    if (urlRef != NULL) {
+        CFArrayRef urlProxyArrayRef = CFNetworkCopyProxiesForURL(urlRef, proxyDicRef);
+        if (urlProxyArrayRef != NULL) {
+            CFIndex count;
+            CFIndex index;
+
+            CFArrayRef expandedProxyArray = createExpandedProxiesArray(urlProxyArrayRef, urlRef);
+            CFRelease(urlProxyArrayRef);
+
+            if (expandedProxyArray == NULL) {
+                CFRelease(urlRef);
+                CFRelease(proxyDicRef);
+                return NULL;
+            }
+
+            count = CFArrayGetCount(expandedProxyArray);
+
+            proxyArray = (*env)->NewObjectArray(env, count, proxy_class, NULL);
+            if (proxyArray != NULL || (*env)->ExceptionCheck(env)) {
+                /* Iterate over the expanded array of proxies */
+                for (index = 0; index < count ; index++) {
+                    CFDictionaryRef currentProxy;
+                    CFStringRef proxyType;
+                    jobject proxy = NULL;
+
+                    currentProxy = (CFDictionaryRef) CFArrayGetValueAtIndex(expandedProxyArray,
+                                                                            index);
+                    proxyType = (CFStringRef) CFDictionaryGetValue(currentProxy, kCFProxyTypeKey);
+                    if (CFEqual(proxyType, kCFProxyTypeNone)) {
+                        /* This entry states no proxy, therefore just add a NO_PROXY object. */
+                        proxy = (*env)->GetStaticObjectField(env, proxy_class, pr_no_proxyID);
+                    } else {
+                        /*
+                         * Create a proxy object for this entry.
+                         * Differentiate between SOCKS and HTTP type.
+                         */
+                        jfieldID typeID = ptype_httpID;
+                        if (CFEqual(proxyType, kCFProxyTypeSOCKS)) {
+                            typeID = ptype_socksID;
+                        }
+                        CFNumberRef portNumberRef = (CFNumberRef)CFDictionaryGetValue(currentProxy,
+                                                    (const void*)kCFProxyPortNumberKey);
+                        if (portNumberRef  != NULL) {
+                            int port = 0;
+                            if (CFNumberGetValue(portNumberRef, kCFNumberSInt32Type, &port)) {
+                                CFStringRef hostNameRef = (CFStringRef)CFDictionaryGetValue(
+                                              currentProxy, (const void*)kCFProxyHostNameKey);
+                                if (hostNameRef != NULL) {
+                                    char hostNameBuffer[BUFFER_SIZE];
+                                    if (CFStringGetCString(hostNameRef, hostNameBuffer,
+                                                           BUFFER_SIZE, kCFStringEncodingUTF8)) {
+                                        proxy = createProxy(env, typeID, &hostNameBuffer[0], port);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    if (proxy == NULL || (*env)->ExceptionCheck(env)) {
+                        proxyArray = NULL;
+                        break;
+                    }
+                    (*env)->SetObjectArrayElement(env, proxyArray, index, proxy);
+                    if ((*env)->ExceptionCheck(env)) {
+                        proxyArray = NULL;
+                        break;
+                    }
+                }
+            }
+            CFRelease(expandedProxyArray);
+        }
+        CFRelease(urlRef);
+    }
+    CFRelease(proxyDicRef);
+
+    return proxyArray;
+}
--- a/src/java.base/share/classes/java/net/doc-files/net-properties.html	Wed Feb 01 23:33:39 2017 +0300
+++ b/src/java.base/share/classes/java/net/doc-files/net-properties.html	Thu Feb 02 10:28:47 2017 +0100
@@ -149,7 +149,7 @@
 		the <B>user.name</B> property will be used with no password.</P>
 	</UL>
 	<LI><P><B>java.net.useSystemProxies</B> (default: false)<BR>
-	On recent Windows systems and on Gnome 2.x systems it is possible to
+	On Windows systems, macOS systems and on Gnome systems it is possible to
 	tell the java.net stack, setting this property to <B>true</B>, to use
 	the system proxy settings (both	these systems let you set proxies
 	globally through their user interface). Note that this property is
--- a/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java	Wed Feb 01 23:33:39 2017 +0300
+++ b/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java	Thu Feb 02 10:28:47 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,16 +30,19 @@
 import java.net.ProxySelector;
 import java.net.SocketAddress;
 import java.net.URI;
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.io.IOException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.StringJoiner;
 import java.util.regex.Pattern;
+import java.util.stream.Stream;
 import sun.net.NetProperties;
 import sun.net.SocksProxy;
 import static java.util.regex.Pattern.quote;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toList;
 
 /**
  * Supports proxy settings using system properties This proxy selector
@@ -87,6 +90,8 @@
 
     private static boolean hasSystemProxies = false;
 
+    private static final List<Proxy> NO_PROXY_LIST = List.of(Proxy.NO_PROXY);
+
     static {
         final String key = "java.net.useSystemProxies";
         Boolean b = AccessController.doPrivileged(
@@ -149,8 +154,9 @@
      * select() method. Where all the hard work is done.
      * Build a list of proxies depending on URI.
      * Since we're only providing compatibility with the system properties
-     * from previous releases (see list above), that list will always
-     * contain 1 single proxy, default being NO_PROXY.
+     * from previous releases (see list above), that list will typically
+     * contain one single proxy, default being NO_PROXY.
+     * If we can get a system proxy it might contain more entries.
      */
     public java.util.List<Proxy> select(URI uri) {
         if (uri == null) {
@@ -185,7 +191,6 @@
         if (protocol == null || host == null) {
             throw new IllegalArgumentException("protocol = "+protocol+" host = "+host);
         }
-        List<Proxy> proxyl = new ArrayList<Proxy>(1);
 
         NonProxyInfo pinfo = null;
 
@@ -214,9 +219,9 @@
          * System properties it does help having only 1 call to doPrivileged.
          * Be mindful what you do in here though!
          */
-        Proxy p = AccessController.doPrivileged(
-            new PrivilegedAction<Proxy>() {
-                public Proxy run() {
+        Proxy[] proxyArray = AccessController.doPrivileged(
+            new PrivilegedAction<Proxy[]>() {
+                public Proxy[] run() {
                     int i, j;
                     String phost =  null;
                     int pport = 0;
@@ -239,8 +244,8 @@
                                 /**
                                  * No system property defined for that
                                  * protocol. Let's check System Proxy
-                                 * settings (Gnome & Windows) if we were
-                                 * instructed to.
+                                 * settings (Gnome, MacOsX & Windows) if
+                                 * we were instructed to.
                                  */
                                 if (hasSystemProxies) {
                                     String sproto;
@@ -248,12 +253,9 @@
                                         sproto = "socks";
                                     else
                                         sproto = proto;
-                                    Proxy sproxy = getSystemProxy(sproto, urlhost);
-                                    if (sproxy != null) {
-                                        return sproxy;
-                                    }
+                                    return getSystemProxies(sproto, urlhost);
                                 }
-                                return Proxy.NO_PROXY;
+                                return null;
                             }
                             // If a Proxy Host is defined for that protocol
                             // Let's get the NonProxyHosts property
@@ -281,7 +283,7 @@
                                         }
                                     }
                                     if (shouldNotUseProxyFor(nprop.pattern, urlhost)) {
-                                        return Proxy.NO_PROXY;
+                                        return null;
                                     }
                                 }
                             }
@@ -311,22 +313,24 @@
                             saddr = InetSocketAddress.createUnresolved(phost, pport);
                             // Socks is *always* the last on the list.
                             if (j == (props[i].length - 1)) {
-                                return SocksProxy.create(saddr, socksProxyVersion());
-                            } else {
-                                return new Proxy(Proxy.Type.HTTP, saddr);
+                                return new Proxy[] {SocksProxy.create(saddr, socksProxyVersion())};
                             }
+                            return new Proxy[] {new Proxy(Proxy.Type.HTTP, saddr)};
                         }
                     }
-                    return Proxy.NO_PROXY;
+                    return null;
                 }});
 
-        proxyl.add(p);
 
-        /*
-         * If no specific property was set for that URI, we should be
-         * returning an iterator to an empty List.
-         */
-        return proxyl;
+        if (proxyArray != null) {
+            // Remove duplicate entries, while preserving order.
+            return Stream.of(proxyArray).distinct().collect(
+                    collectingAndThen(toList(), Collections::unmodifiableList));
+        }
+
+        // If no specific proxy was found, return a standard list containing
+        // only one NO_PROXY entry.
+        return NO_PROXY_LIST;
     }
 
     public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
@@ -354,7 +358,7 @@
     }
 
     private static native boolean init();
-    private synchronized native Proxy getSystemProxy(String protocol, String host);
+    private synchronized native Proxy[] getSystemProxies(String protocol, String host);
 
     /**
      * @return {@code true} if given this pattern for non-proxy hosts and this
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/native/libnet/proxy_util.c	Thu Feb 02 10:28:47 2017 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+
+#include "proxy_util.h"
+
+jclass proxy_class;
+jclass isaddr_class;
+jclass ptype_class;
+jmethodID isaddr_createUnresolvedID;
+jmethodID proxy_ctrID;
+jfieldID pr_no_proxyID;
+jfieldID ptype_httpID;
+jfieldID ptype_socksID;
+
+int initJavaClass(JNIEnv *env) {
+    jclass proxy_cls = NULL;
+    jclass ptype_cls = NULL;
+    jclass isaddr_cls = NULL;
+
+    // Proxy initialization
+    proxy_cls = (*env)->FindClass(env,"java/net/Proxy");
+    CHECK_NULL_RETURN(proxy_cls, 0);
+    proxy_class = (*env)->NewGlobalRef(env, proxy_cls);
+    CHECK_NULL_RETURN(proxy_class, 0);
+    proxy_ctrID = (*env)->GetMethodID(env, proxy_class, "<init>",
+            "(Ljava/net/Proxy$Type;Ljava/net/SocketAddress;)V");
+    CHECK_NULL_RETURN(proxy_ctrID, 0);
+
+    // Proxy$Type initialization
+    ptype_cls = (*env)->FindClass(env,"java/net/Proxy$Type");
+    CHECK_NULL_RETURN(ptype_cls, 0);
+    ptype_class = (*env)->NewGlobalRef(env, ptype_cls);
+    CHECK_NULL_RETURN(ptype_class, 0);
+    ptype_httpID = (*env)->GetStaticFieldID(env, ptype_class, "HTTP",
+                                            "Ljava/net/Proxy$Type;");
+    CHECK_NULL_RETURN(ptype_httpID, 0);
+    ptype_socksID = (*env)->GetStaticFieldID(env, ptype_class, "SOCKS",
+                                             "Ljava/net/Proxy$Type;");
+    CHECK_NULL_RETURN(ptype_socksID, 0);
+
+    // NO_PROXY
+    pr_no_proxyID = (*env)->GetStaticFieldID(env, proxy_class, "NO_PROXY",
+                                             "Ljava/net/Proxy;");
+    CHECK_NULL_RETURN(pr_no_proxyID, 0);
+
+    // InetSocketAddress initialization
+    isaddr_cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
+    CHECK_NULL_RETURN(isaddr_cls, 0);
+    isaddr_class = (*env)->NewGlobalRef(env, isaddr_cls);
+    CHECK_NULL_RETURN(isaddr_class, 0);
+    isaddr_createUnresolvedID = (*env)->GetStaticMethodID(env, isaddr_class,
+            "createUnresolved",
+            "(Ljava/lang/String;I)Ljava/net/InetSocketAddress;");
+
+    return isaddr_createUnresolvedID != NULL ? 1 : 0;
+}
+
+jobject createProxy(JNIEnv *env, jfieldID ptype_ID, const char* phost, unsigned short pport) {
+    jobject jProxy = NULL;
+    jobject type_proxy = NULL;
+    type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_ID);
+    if (type_proxy) {
+        jstring jhost = NULL;
+        jhost = (*env)->NewStringUTF(env, phost);
+        if (jhost) {
+            jobject isa = NULL;
+            isa = (*env)->CallStaticObjectMethod(env, isaddr_class,
+                    isaddr_createUnresolvedID, jhost, pport);
+            if (isa) {
+                jProxy = (*env)->NewObject(env, proxy_class, proxy_ctrID,
+                                          type_proxy, isa);
+            }
+        }
+    }
+    return jProxy;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/native/libnet/proxy_util.h	Thu Feb 02 10:28:47 2017 +0100
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+extern jclass proxy_class;
+extern jclass isaddr_class;
+extern jclass ptype_class;
+extern jmethodID isaddr_createUnresolvedID;
+extern jmethodID proxy_ctrID;
+extern jfieldID pr_no_proxyID;
+extern jfieldID ptype_httpID;
+extern jfieldID ptype_socksID;
+
+int initJavaClass(JNIEnv *env);
+
+jobject createProxy(JNIEnv *env, jfieldID ptype_ID, const char* phost, unsigned short pport);
--- a/src/java.base/unix/native/libnet/DefaultProxySelector.c	Wed Feb 01 23:33:39 2017 +0300
+++ b/src/java.base/unix/native/libnet/DefaultProxySelector.c	Thu Feb 02 10:28:47 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,24 +23,20 @@
  * questions.
  */
 
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
 #include "jni.h"
 #include "jni_util.h"
 #include "jvm.h"
 #include "jvm_md.h"
-#include "jlong.h"
+
+#include "proxy_util.h"
+
 #include "sun_net_spi_DefaultProxySelector.h"
-#include <dlfcn.h>
-#include <stdio.h>
-#include <stdlib.h>
-#if defined(__linux__) || defined(_ALLBSD_SOURCE)
-#include <string.h>
-#else
-#include <strings.h>
-#endif
 
-#ifndef CHECK_NULL_RETURN
-#define CHECK_NULL_RETURN(x, y) if ((x) == NULL) return y;
-#endif
 
 /**
  * These functions are used by the sun.net.spi.DefaultProxySelector class
@@ -112,43 +108,11 @@
 static g_network_address_get_port_func* g_network_address_get_port = NULL;
 static g_strfreev_func* g_strfreev = NULL;
 
-
-static jclass proxy_class;
-static jclass isaddr_class;
-static jclass ptype_class;
-static jmethodID isaddr_createUnresolvedID;
-static jmethodID proxy_ctrID;
-static jfieldID ptype_httpID;
-static jfieldID ptype_socksID;
-
-
 static void* gconf_client = NULL;
 static int use_gproxyResolver = 0;
 static int use_gconf = 0;
 
 
-static jobject createProxy(JNIEnv *env, jfieldID ptype_ID,
-                           const char* phost, unsigned short pport)
-{
-    jobject jProxy = NULL;
-    jobject type_proxy = NULL;
-    type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_ID);
-    if (type_proxy) {
-        jstring jhost = NULL;
-        jhost = (*env)->NewStringUTF(env, phost);
-        if (jhost) {
-            jobject isa = NULL;
-            isa = (*env)->CallStaticObjectMethod(env, isaddr_class,
-                    isaddr_createUnresolvedID, jhost, pport);
-            if (isa) {
-                jProxy = (*env)->NewObject(env, proxy_class, proxy_ctrID,
-                                          type_proxy, isa);
-            }
-        }
-    }
-    return jProxy;
-}
-
 static int initGConf() {
     /**
      * Let's try to load GConf-2 library
@@ -196,18 +160,18 @@
     return 0;
 }
 
-static jobject getProxyByGConf(JNIEnv *env, const char* cproto,
-                               const char* chost)
+static jobjectArray getProxyByGConf(JNIEnv *env, const char* cproto,
+                                    const char* chost)
 {
     char *phost = NULL;
     char *mode = NULL;
     int pport = 0;
     int use_proxy = 0;
     int use_same_proxy = 0;
-    jobject proxy = NULL;
+    jobjectArray proxy_array = NULL;
     jfieldID ptype_ID = ptype_httpID;
 
-    // We only check manual proxy configurations
+    /* We only check manual proxy configurations */
     mode =  (*my_get_string_func)(gconf_client, "/system/proxy/mode", NULL);
     if (mode && !strcasecmp(mode, "manual")) {
         /*
@@ -293,7 +257,7 @@
         char *s;
 
         /**
-         * check for the exclude list (aka "No Proxy For" list).
+         * Check for the exclude list (aka "No Proxy For" list).
          * It's a list of comma separated suffixes (e.g. domain name).
          */
         noproxyfor = (*my_get_string_func)(gconf_client, "/system/proxy/no_proxy_for", NULL);
@@ -313,11 +277,25 @@
                 s = strtok_r(NULL, ", ", tmpbuf);
             }
         }
-        if (use_proxy)
+        if (use_proxy) {
+            jobject proxy = NULL;
+            /* create a proxy array with one element. */
+            proxy_array = (*env)->NewObjectArray(env, 1, proxy_class, NULL);
+            if (proxy_array == NULL || (*env)->ExceptionCheck(env)) {
+                return NULL;
+            }
             proxy = createProxy(env, ptype_ID, phost, pport);
+            if (proxy == NULL || (*env)->ExceptionCheck(env)) {
+                return NULL;
+            }
+            (*env)->SetObjectArrayElement(env, proxy_array, 0, proxy);
+            if ((*env)->ExceptionCheck(env)) {
+                return NULL;
+            }
+        }
     }
 
-    return proxy;
+    return proxy_array;
 }
 
 static int initGProxyResolver() {
@@ -371,8 +349,8 @@
     return 1;
 }
 
-static jobject getProxyByGProxyResolver(JNIEnv *env, const char* cproto,
-                                        const char* chost)
+static jobjectArray getProxyByGProxyResolver(JNIEnv *env, const char *cproto,
+                                             const char *chost)
 {
     GProxyResolver* resolver = NULL;
     char** proxies = NULL;
@@ -382,19 +360,19 @@
     size_t hostLen = 0;
     char* uri = NULL;
 
-    jobject jProxy = NULL;
+    jobjectArray proxy_array = NULL;
 
     resolver = (*g_proxy_resolver_get_default)();
     if (resolver == NULL) {
         return NULL;
     }
 
-    // Construct the uri, cproto + "://" + chost
+    /* Construct the uri, cproto + "://" + chost */
     protoLen = strlen(cproto);
     hostLen = strlen(chost);
     uri = malloc(protoLen + hostLen + 4);
     if (!uri) {
-        // Out of memory
+        /* Out of memory */
         return NULL;
     }
     memcpy(uri, cproto, protoLen);
@@ -414,22 +392,56 @@
     if (proxies) {
         if (!error) {
             int i;
-            for(i = 0; proxies[i] && !jProxy; i++) {
-                if (strcmp(proxies[i], "direct://")) {
-                    GSocketConnectable* conn =
-                            (*g_network_address_parse_uri)(proxies[i], 0,
-                                                           &error);
-                    if (conn && !error) {
-                        const char* phost = NULL;
-                        unsigned short pport = 0;
-                        phost = (*g_network_address_get_hostname)(conn);
-                        pport = (*g_network_address_get_port)(conn);
-                        if (phost && pport > 0) {
-                            jfieldID ptype_ID = ptype_httpID;
-                            if (!strncmp(proxies[i], "socks", 5))
-                                ptype_ID = ptype_socksID;
+            int nr_proxies = 0;
+            char** p = proxies;
+            /* count the elements in the null terminated string vector. */
+            while (*p) {
+                nr_proxies++;
+                p++;
+            }
+            /* create a proxy array that has to be filled. */
+            proxy_array = (*env)->NewObjectArray(env, nr_proxies, proxy_class, NULL);
+            if (proxy_array != NULL && !(*env)->ExceptionCheck(env)) {
+                for (i = 0; proxies[i]; i++) {
+                    if (strncmp(proxies[i], "direct://", 9)) {
+                        GSocketConnectable* conn =
+                                (*g_network_address_parse_uri)(proxies[i], 0,
+                                                               &error);
+                        if (conn && !error) {
+                            const char *phost = NULL;
+                            unsigned short pport = 0;
+                            phost = (*g_network_address_get_hostname)(conn);
+                            pport = (*g_network_address_get_port)(conn);
+                            if (phost && pport > 0) {
+                                jobject proxy = NULL;
+                                jfieldID ptype_ID = ptype_httpID;
+                                if (!strncmp(proxies[i], "socks", 5))
+                                    ptype_ID = ptype_socksID;
 
-                            jProxy = createProxy(env, ptype_ID, phost, pport);
+                                proxy = createProxy(env, ptype_ID, phost, pport);
+                                if (proxy == NULL || (*env)->ExceptionCheck(env)) {
+                                    proxy_array = NULL;
+                                    break;
+                                }
+                                (*env)->SetObjectArrayElement(env, proxy_array, i, proxy);
+                                if ((*env)->ExceptionCheck(env)) {
+                                    proxy_array = NULL;
+                                    break;
+                                }
+                            }
+                        }
+                    } else {
+                        /* direct connection - no proxy */
+                        jobject proxy = (*env)->GetStaticObjectField(env, proxy_class,
+                                                                     pr_no_proxyID);
+                        if (proxy == NULL || (*env)->ExceptionCheck(env)) {
+                            proxy_array = NULL;
+                            break;
+                        }
+                        (*env)->SetObjectArrayElement(env, proxy_array, i, proxy);
+                        if ((*env)->ExceptionCheck(env)) {
+                            proxy_array = NULL;
+                            break;
                         }
                     }
                 }
@@ -438,48 +450,9 @@
         (*g_strfreev)(proxies);
     }
 
-    return jProxy;
+    return proxy_array;
 }
 
-static int initJavaClass(JNIEnv *env) {
-    jclass proxy_cls = NULL;
-    jclass ptype_cls = NULL;
-    jclass isaddr_cls = NULL;
-
-    // Proxy initialization
-    proxy_cls = (*env)->FindClass(env,"java/net/Proxy");
-    CHECK_NULL_RETURN(proxy_cls, 0);
-    proxy_class = (*env)->NewGlobalRef(env, proxy_cls);
-    CHECK_NULL_RETURN(proxy_class, 0);
-    proxy_ctrID = (*env)->GetMethodID(env, proxy_class, "<init>",
-            "(Ljava/net/Proxy$Type;Ljava/net/SocketAddress;)V");
-    CHECK_NULL_RETURN(proxy_ctrID, 0);
-
-    // Proxy$Type initialization
-    ptype_cls = (*env)->FindClass(env,"java/net/Proxy$Type");
-    CHECK_NULL_RETURN(ptype_cls, 0);
-    ptype_class = (*env)->NewGlobalRef(env, ptype_cls);
-    CHECK_NULL_RETURN(ptype_class, 0);
-    ptype_httpID = (*env)->GetStaticFieldID(env, ptype_class, "HTTP",
-                                            "Ljava/net/Proxy$Type;");
-    CHECK_NULL_RETURN(ptype_httpID, 0);
-    ptype_socksID = (*env)->GetStaticFieldID(env, ptype_class, "SOCKS",
-                                             "Ljava/net/Proxy$Type;");
-    CHECK_NULL_RETURN(ptype_socksID, 0);
-
-    // InetSocketAddress initialization
-    isaddr_cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
-    CHECK_NULL_RETURN(isaddr_cls, 0);
-    isaddr_class = (*env)->NewGlobalRef(env, isaddr_cls);
-    CHECK_NULL_RETURN(isaddr_class, 0);
-    isaddr_createUnresolvedID = (*env)->GetStaticMethodID(env, isaddr_class,
-            "createUnresolved",
-            "(Ljava/lang/String;I)Ljava/net/InetSocketAddress;");
-
-    return isaddr_createUnresolvedID != NULL ? 1 : 0;
-}
-
-
 /*
  * Class:     sun_net_spi_DefaultProxySelector
  * Method:    init
@@ -500,14 +473,14 @@
 
 /*
  * Class:     sun_net_spi_DefaultProxySelector
- * Method:    getSystemProxy
- * Signature: ([Ljava/lang/String;Ljava/lang/String;)Ljava/net/Proxy;
+ * Method:    getSystemProxies
+ * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy;
  */
-JNIEXPORT jobject JNICALL
-Java_sun_net_spi_DefaultProxySelector_getSystemProxy(JNIEnv *env,
-                                                     jobject this,
-                                                     jstring proto,
-                                                     jstring host)
+JNIEXPORT jobjectArray JNICALL
+Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env,
+                                                       jobject this,
+                                                       jstring proto,
+                                                       jstring host)
 {
     const char* cproto;
     const char* chost;
@@ -515,7 +488,7 @@
     jboolean isProtoCopy;
     jboolean isHostCopy;
 
-    jobject proxy = NULL;
+    jobjectArray proxyArray = NULL;
 
     cproto = (*env)->GetStringUTFChars(env, proto, &isProtoCopy);
 
@@ -523,16 +496,15 @@
         chost = (*env)->GetStringUTFChars(env, host, &isHostCopy);
         if (chost != NULL) {
             if (use_gproxyResolver)
-                proxy = getProxyByGProxyResolver(env, cproto, chost);
+                proxyArray = getProxyByGProxyResolver(env, cproto, chost);
             else if (use_gconf)
-                proxy = getProxyByGConf(env, cproto, chost);
-
+                proxyArray = getProxyByGConf(env, cproto, chost);
             if (isHostCopy == JNI_TRUE)
                 (*env)->ReleaseStringUTFChars(env, host, chost);
         }
         if (isProtoCopy == JNI_TRUE)
             (*env)->ReleaseStringUTFChars(env, proto, cproto);
     }
-    return proxy;
+    return proxyArray;
 }
 
--- a/src/java.base/windows/native/libnet/DefaultProxySelector.c	Wed Feb 01 23:33:39 2017 +0300
+++ b/src/java.base/windows/native/libnet/DefaultProxySelector.c	Thu Feb 02 10:28:47 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,26 +24,24 @@
  */
 
 #include <windows.h>
+#include <Winhttp.h>
+
 #include "jni.h"
 #include "jni_util.h"
 #include "jvm.h"
-#include "jlong.h"
+
+#include "proxy_util.h"
+
 #include "sun_net_spi_DefaultProxySelector.h"
 
-/**
+/*
  * These functions are used by the sun.net.spi.DefaultProxySelector class
  * to access some platform specific settings.
- * This is the Windows code using the registry settings.
+ * On Windows use WinHTTP functions to get the system settings.
  */
 
-static jclass proxy_class;
-static jclass isaddr_class;
-static jclass ptype_class;
-static jmethodID isaddr_createUnresolvedID;
-static jmethodID proxy_ctrID;
-static jfieldID pr_no_proxyID;
-static jfieldID ptype_httpID;
-static jfieldID ptype_socksID;
+/* Keep one static session for all requests. */
+static HINTERNET session = NULL;
 
 /*
  * Class:     sun_net_spi_DefaultProxySelector
@@ -52,233 +50,327 @@
  */
 JNIEXPORT jboolean JNICALL
 Java_sun_net_spi_DefaultProxySelector_init(JNIEnv *env, jclass clazz) {
-  HKEY hKey;
-  LONG ret;
-  jclass cls;
 
-  /**
-   * Get all the method & field IDs for later use.
-   */
-  cls = (*env)->FindClass(env,"java/net/Proxy");
-  CHECK_NULL_RETURN(cls, JNI_FALSE);
-  proxy_class = (*env)->NewGlobalRef(env, cls);
-  CHECK_NULL_RETURN(proxy_class, JNI_FALSE);
-  cls = (*env)->FindClass(env,"java/net/Proxy$Type");
-  CHECK_NULL_RETURN(cls, JNI_FALSE);
-  ptype_class = (*env)->NewGlobalRef(env, cls);
-  CHECK_NULL_RETURN(ptype_class, JNI_FALSE);
-  cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
-  CHECK_NULL_RETURN(cls, JNI_FALSE);
-  isaddr_class = (*env)->NewGlobalRef(env, cls);
-  CHECK_NULL_RETURN(isaddr_class, JNI_FALSE);
-  proxy_ctrID = (*env)->GetMethodID(env, proxy_class, "<init>",
-                                    "(Ljava/net/Proxy$Type;Ljava/net/SocketAddress;)V");
-  CHECK_NULL_RETURN(proxy_ctrID, JNI_FALSE);
-  pr_no_proxyID = (*env)->GetStaticFieldID(env, proxy_class, "NO_PROXY", "Ljava/net/Proxy;");
-  CHECK_NULL_RETURN(pr_no_proxyID, JNI_FALSE);
-  ptype_httpID = (*env)->GetStaticFieldID(env, ptype_class, "HTTP", "Ljava/net/Proxy$Type;");
-  CHECK_NULL_RETURN(ptype_httpID, JNI_FALSE);
-  ptype_socksID = (*env)->GetStaticFieldID(env, ptype_class, "SOCKS", "Ljava/net/Proxy$Type;");
-  CHECK_NULL_RETURN(ptype_socksID, JNI_FALSE);
-  isaddr_createUnresolvedID = (*env)->GetStaticMethodID(env, isaddr_class, "createUnresolved",
-                                                        "(Ljava/lang/String;I)Ljava/net/InetSocketAddress;");
-  CHECK_NULL_RETURN(isaddr_createUnresolvedID, JNI_FALSE);
+    /*
+     * Get one WinHTTP session handle to initialize the WinHTTP internal data
+     * structures. Keep and use only this one for the whole life time.
+     */
+    session = WinHttpOpen(L"Only used internal", /* we need no real agent string here */
+                          WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+                          WINHTTP_NO_PROXY_NAME,
+                          WINHTTP_NO_PROXY_BYPASS,
+                          0);
+    if (session == NULL) {
+        return JNI_FALSE;
+    }
 
-  /**
-   * Let's see if we can find the proper Registry entry.
-   */
-  ret = RegOpenKeyEx(HKEY_CURRENT_USER,
-                     "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
-                     0, KEY_READ, (PHKEY)&hKey);
-  if (ret == ERROR_SUCCESS) {
-    RegCloseKey(hKey);
-    /**
-     * It worked, we can probably rely on it then.
-     */
+    if (!initJavaClass(env)) {
+        return JNI_FALSE;
+    }
+
     return JNI_TRUE;
-  }
+}
 
-  return JNI_FALSE;
-}
 
 #define MAX_STR_LEN 1024
 
+/* A linked list element for a proxy */
+typedef struct list_item {
+    wchar_t *host;
+    int port;
+    struct list_item *next;
+} list_item;
+
+/* Free the linked list */
+static void freeList(list_item *head) {
+    list_item *next = NULL;
+    list_item *current = head;
+    while (current != NULL) {
+        next = current->next;
+        free(current->host);
+        free(current);
+        current = next;
+    }
+}
+
+
+/*
+ * Creates a linked list of list_item elements that has to be freed later on.
+ * Returns the size of the array as int.
+ */
+static int createProxyList(LPWSTR win_proxy, const WCHAR *pproto, list_item **head) {
+    static const wchar_t separators[] = L"\t\r\n ;";
+    list_item *current = NULL;
+    int nr_elems = 0;
+    wchar_t *context = NULL;
+    wchar_t *current_proxy = NULL;
+    BOOL error = FALSE;
+
+    /*
+     * The proxy server list contains one or more of the following strings
+     * separated by semicolons or whitespace:
+     *    ([<scheme>=][<scheme>"://"]<server>[":"<port>])
+     */
+    current_proxy = wcstok_s(win_proxy, separators, &context);
+    while (current_proxy != NULL) {
+        LPWSTR pport;
+        LPWSTR phost;
+        int portVal = 0;
+        wchar_t *next_proxy = NULL;
+        list_item *proxy = NULL;
+        wchar_t* pos = NULL;
+
+        /* Filter based on the scheme, if there is one */
+        pos = wcschr(current_proxy, L'=');
+        if (pos) {
+          *pos = L'\0';
+          if (wcscmp(current_proxy, pproto) != 0) {
+              current_proxy = wcstok_s(NULL, separators, &context);
+              continue;
+          }
+          current_proxy = pos + 1;
+        }
+
+        /* Let's check for a scheme and ignore it. */
+        if ((phost = wcsstr(current_proxy, L"://")) != NULL) {
+            phost += 3;
+        } else {
+            phost = current_proxy;
+        }
+
+        /* Get the port */
+        pport = wcschr(phost, L':');
+        if (pport != NULL) {
+            *pport = 0;
+            pport++;
+            swscanf(pport, L"%d", &portVal);
+        }
+
+        proxy = (list_item *)malloc(sizeof(list_item));
+        if (proxy != NULL) {
+            proxy->next = NULL;
+            proxy->port = portVal;
+            proxy->host = _wcsdup(phost);
+
+            if (proxy->host != NULL) {
+                if (*head == NULL) {
+                    *head = proxy; /* first elem */
+                }
+                if (current != NULL) {
+                    current->next = proxy;
+                }
+                current = proxy;
+                nr_elems++;
+            } else {
+                free(proxy); /* cleanup */
+            }
+        }
+        /* goto next proxy if available... */
+        current_proxy = wcstok_s(NULL, separators, &context);
+    }
+    return nr_elems;
+}
+
+
+
 /*
  * Class:     sun_net_spi_DefaultProxySelector
- * Method:    getSystemProxy
- * Signature: ([Ljava/lang/String;Ljava/lang/String;)Ljava/net/Proxy;
+ * Method:    getSystemProxies
+ * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy;
  */
-JNIEXPORT jobject JNICALL
-Java_sun_net_spi_DefaultProxySelector_getSystemProxy(JNIEnv *env,
-                                                     jobject this,
-                                                     jstring proto,
-                                                     jstring host)
+JNIEXPORT jobjectArray JNICALL
+Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env,
+                                                       jobject this,
+                                                       jstring proto,
+                                                       jstring host)
 {
-  jobject isa = NULL;
-  jobject proxy = NULL;
-  jobject type_proxy = NULL;
-  jobject no_proxy = NULL;
-  jboolean isCopy;
-  HKEY hKey;
-  LONG ret;
-  const char* cproto;
-  const char* urlhost;
-  char pproto[MAX_STR_LEN];
-  char regserver[MAX_STR_LEN];
-  char override[MAX_STR_LEN];
-  char *s, *s2;
-  char *ctx = NULL;
-  int pport = 0;
-  int defport = 0;
-  char *phost;
+    jobjectArray proxy_array = NULL;
+    jobject type_proxy = NULL;
+    LPCWSTR lpProto;
+    LPCWSTR lpHost;
+    list_item *head = NULL;
 
-  /**
-   * Let's open the Registry entry. We'll check a few values in it:
-   *
-   * - ProxyEnable: 0 means no proxy, 1 means use the proxy
-   * - ProxyServer: a string that can take 2 forms:
-   *    "server[:port]"
-   *    or
-   *    "protocol1=server[:port][;protocol2=server[:port]]..."
-   * - ProxyOverride: a string containing a list of prefixes for hostnames.
-   *   e.g.: hoth;localhost;<local>
-   */
-  ret = RegOpenKeyEx(HKEY_CURRENT_USER,
-                     "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
-                     0, KEY_READ, (PHKEY)&hKey);
-  if (ret == ERROR_SUCCESS) {
-    DWORD dwLen;
-    DWORD dwProxyEnabled;
-    ULONG ulType;
-    dwLen = sizeof(dwProxyEnabled);
+    BOOL                                   use_auto_proxy = FALSE;
+    WINHTTP_CURRENT_USER_IE_PROXY_CONFIG   ie_proxy_config;
+    WINHTTP_AUTOPROXY_OPTIONS              auto_proxy_options;
+    WINHTTP_PROXY_INFO                     proxy_info;
+    LPWSTR win_proxy = NULL;
+    LPWSTR win_bypass_proxy = NULL;
 
-    /**
-     * Let's see if the proxy settings are to be used.
-     */
-    ret = RegQueryValueEx(hKey, "ProxyEnable",  NULL, &ulType,
-                          (LPBYTE)&dwProxyEnabled, &dwLen);
-    if ((ret == ERROR_SUCCESS) && (dwProxyEnabled > 0)) {
-      /*
-       * Yes, ProxyEnable == 1
-       */
-      dwLen = sizeof(override);
-      override[0] = 0;
-      ret = RegQueryValueEx(hKey, "ProxyOverride", NULL, &ulType,
-                            (LPBYTE)&override, &dwLen);
-      dwLen = sizeof(regserver);
-      regserver[0] = 0;
-      ret = RegQueryValueEx(hKey, "ProxyServer",  NULL, &ulType,
-                            (LPBYTE)&regserver, &dwLen);
-      RegCloseKey(hKey);
-      if (ret == ERROR_SUCCESS) {
-        if (strlen(override) > 0) {
-          /**
-           * we did get ProxyServer and may have an override.
-           * So let's check the override list first, by walking down the list
-           * The semicolons (;) separated entries have to be matched with the
-           * the beginning of the hostname.
-           */
-          s = strtok_s(override, "; ", &ctx);
-          urlhost = (*env)->GetStringUTFChars(env, host, &isCopy);
-          if (urlhost == NULL) {
-            if (!(*env)->ExceptionCheck(env))
-              JNU_ThrowOutOfMemoryError(env, NULL);
-            return NULL;
-          }
-          while (s != NULL) {
-            if (strncmp(s, urlhost, strlen(s)) == 0) {
-              /**
-               * the URL host name matches with one of the prefixes,
-               * therefore we have to use a direct connection.
-               */
-              if (isCopy == JNI_TRUE)
-                (*env)->ReleaseStringUTFChars(env, host, urlhost);
-              goto noproxy;
+    memset(&ie_proxy_config, 0, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG));
+    memset(&auto_proxy_options, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS));
+    memset(&proxy_info, 0, sizeof(WINHTTP_PROXY_INFO));
+
+    lpHost = (*env)->GetStringChars(env, host, NULL);
+    if (lpHost == NULL) {
+        if (!(*env)->ExceptionCheck(env))
+            JNU_ThrowOutOfMemoryError(env, NULL);
+        return NULL;
+    }
+
+    lpProto = (*env)->GetStringChars(env, proto, NULL);
+    if (lpProto == NULL) {
+        (*env)->ReleaseStringChars(env, host, lpHost);
+        if (!(*env)->ExceptionCheck(env))
+            JNU_ThrowOutOfMemoryError(env, NULL);
+        return NULL;
+    }
+
+    if (WinHttpGetIEProxyConfigForCurrentUser(&ie_proxy_config) == FALSE) {
+        /* cleanup and exit */
+        (*env)->ReleaseStringChars(env, host, lpHost);
+        (*env)->ReleaseStringChars(env, proto, lpProto);
+        return NULL;
+    }
+
+    if (ie_proxy_config.fAutoDetect) {
+        /* Windows uses WPAD */
+        auto_proxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP |
+                                               WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+        auto_proxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
+        auto_proxy_options.fAutoLogonIfChallenged = TRUE;
+        use_auto_proxy = TRUE;
+    } else if (ie_proxy_config.lpszAutoConfigUrl != NULL) {
+        /* Windows uses PAC file */
+        auto_proxy_options.lpszAutoConfigUrl = ie_proxy_config.lpszAutoConfigUrl;
+        auto_proxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
+        use_auto_proxy = TRUE;
+    } else if (ie_proxy_config.lpszProxy != NULL) {
+        /* Windows uses manually entered proxy. */
+        use_auto_proxy = FALSE;
+        win_bypass_proxy = ie_proxy_config.lpszProxyBypass;
+        win_proxy = ie_proxy_config.lpszProxy;
+    }
+
+    if (use_auto_proxy) {
+        WCHAR url[MAX_STR_LEN];
+        /* Create url for WinHttpGetProxyForUrl */
+        _snwprintf(url, sizeof(url) - 1, L"%s://%s", lpProto, lpHost);
+        /* Get proxy for URL from Windows */
+        use_auto_proxy = WinHttpGetProxyForUrl(session, &url[0], &auto_proxy_options, &proxy_info);
+        if (use_auto_proxy) {
+            win_proxy = proxy_info.lpszProxy;
+            win_bypass_proxy = proxy_info.lpszProxyBypass;
+        }
+    }
+
+    /* Check the bypass entry. */
+    if (NULL != win_bypass_proxy) {
+        /*
+         * From MSDN:
+         * The proxy bypass list contains one or more server names separated by
+         * semicolons or whitespace. The proxy bypass list can also contain the
+         * string "<local>" to indicate that all local intranet sites are
+         * bypassed. Local intranet sites are considered to be all servers that
+         * do not contain a period in their name.
+         */
+        wchar_t *context = NULL;
+        LPWSTR s = wcstok_s(win_bypass_proxy, L"; ", &context);
+
+        while (s != NULL) {
+            size_t maxlen = wcslen(s);
+            if (wcsncmp(s, lpHost, maxlen) == 0) {
+                /*
+                 * The URL host name matches with one of the prefixes, use a
+                 * direct connection.
+                 */
+                goto noproxy;
             }
-            s = strtok_s(NULL, "; ", &ctx);
-          }
-          if (isCopy == JNI_TRUE)
-            (*env)->ReleaseStringUTFChars(env, host, urlhost);
+            if (wcsncmp(s, L"<local>", maxlen) == 0) {
+                /*
+                 * All local intranet sites are bypassed - Microsoft consider all
+                 * servers that do not contain a period in their name to be local.
+                 */
+                if (wcschr(lpHost, '.') == NULL) {
+                    goto noproxy;
+                }
+            }
+            s = wcstok_s(NULL, L"; ", &context);
+        }
+    }
+
+    if (win_proxy != NULL) {
+        wchar_t *context = NULL;
+        int defport = 0;
+        int nr_elems = 0;
+
+        /* Set the default port value & proxy type from protocol. */
+        if ((wcscmp(lpProto, L"http") == 0) ||
+            (wcscmp(lpProto, L"ftp") == 0) ||
+            (wcscmp(lpProto, L"gopher") == 0))
+            defport = 80;
+        if (wcscmp(lpProto, L"https") == 0)
+            defport = 443;
+        if (wcscmp(lpProto, L"socks") == 0) {
+            defport = 1080;
+            type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_socksID);
+        } else {
+            type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_httpID);
+        }
+        if (type_proxy == NULL || (*env)->ExceptionCheck(env)) {
+            goto noproxy;
         }
 
-        cproto = (*env)->GetStringUTFChars(env, proto, &isCopy);
-        if (cproto == NULL) {
-          if (!(*env)->ExceptionCheck(env))
-            JNU_ThrowOutOfMemoryError(env, NULL);
-          return NULL;
+        nr_elems = createProxyList(win_proxy, lpProto, &head);
+        if (nr_elems != 0 && head != NULL) {
+            int index = 0;
+            proxy_array = (*env)->NewObjectArray(env, nr_elems, proxy_class, NULL);
+            if (proxy_array == NULL || (*env)->ExceptionCheck(env)) {
+                goto noproxy;
+            }
+            while (head != NULL && index < nr_elems) {
+                jstring jhost;
+                jobject isa;
+                jobject proxy;
+
+                if (head->host != NULL && proxy_array != NULL) {
+                    /* Let's create the appropriate Proxy object then. */
+                    if (head->port == 0) {
+                        head->port = defport;
+                    }
+                    jhost = (*env)->NewString(env, head->host, (jsize)wcslen(head->host));
+                    if (jhost == NULL || (*env)->ExceptionCheck(env)) {
+                        proxy_array = NULL;
+                    }
+                    isa = (*env)->CallStaticObjectMethod(env, isaddr_class,
+                                                         isaddr_createUnresolvedID, jhost,
+                                                         head->port);
+                    if (isa == NULL || (*env)->ExceptionCheck(env)) {
+                        proxy_array = NULL;
+                    }
+                    proxy = (*env)->NewObject(env, proxy_class, proxy_ctrID, type_proxy, isa);
+                    if (proxy == NULL || (*env)->ExceptionCheck(env)) {
+                        proxy_array = NULL;
+                    }
+                    (*env)->SetObjectArrayElement(env, proxy_array, index, proxy);
+                    if ((*env)->ExceptionCheck(env)) {
+                        proxy_array = NULL;
+                    }
+                    index++;
+                }
+                head = head->next;
+            }
         }
-
-        /*
-         * Set default port value & proxy type from protocol.
-         */
-        if ((strcmp(cproto, "http") == 0) ||
-            (strcmp(cproto, "ftp") == 0) ||
-            (strcmp(cproto, "gopher") == 0))
-          defport = 80;
-        if (strcmp(cproto, "https") == 0)
-          defport = 443;
-        if (strcmp(cproto, "socks") == 0) {
-          defport = 1080;
-          type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_socksID);
-        } else {
-          type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_httpID);
-        }
-
-        sprintf(pproto,"%s=", cproto);
-        if (isCopy == JNI_TRUE)
-          (*env)->ReleaseStringUTFChars(env, proto, cproto);
-        /**
-         * Let's check the protocol specific form first.
-         */
-        if ((s = strstr(regserver, pproto)) != NULL) {
-          s += strlen(pproto);
-        } else {
-          /**
-           * If we couldn't find *this* protocol but the string is in the
-           * protocol specific format, then don't use proxy
-           */
-          if (strchr(regserver, '=') != NULL)
-            goto noproxy;
-          s = regserver;
-        }
-        s2 = strchr(s, ';');
-        if (s2 != NULL)
-          *s2 = 0;
-
-        /**
-         * Is there a port specified?
-         */
-        s2 = strchr(s, ':');
-        if (s2 != NULL) {
-          *s2 = 0;
-          s2++;
-          sscanf(s2, "%d", &pport);
-        }
-        phost = s;
-
-        if (phost != NULL) {
-          /**
-           * Let's create the appropriate Proxy object then.
-           */
-          jstring jhost;
-          if (pport == 0)
-            pport = defport;
-          jhost = (*env)->NewStringUTF(env, phost);
-          CHECK_NULL_RETURN(jhost, NULL);
-          isa = (*env)->CallStaticObjectMethod(env, isaddr_class, isaddr_createUnresolvedID, jhost, pport);
-          CHECK_NULL_RETURN(isa, NULL);
-          proxy = (*env)->NewObject(env, proxy_class, proxy_ctrID, type_proxy, isa);
-          return proxy;
-        }
-      }
-    } else {
-      /* ProxyEnable == 0 or Query failed      */
-      /* close the handle to the registry key  */
-      RegCloseKey(hKey);
     }
-  }
 
 noproxy:
-  no_proxy = (*env)->GetStaticObjectField(env, proxy_class, pr_no_proxyID);
-  return no_proxy;
+    if (head != NULL) {
+        freeList(head);
+    }
+    if (proxy_info.lpszProxy != NULL)
+      GlobalFree(proxy_info.lpszProxy);
+    if (proxy_info.lpszProxyBypass != NULL)
+      GlobalFree(proxy_info.lpszProxyBypass);
+    if (ie_proxy_config.lpszAutoConfigUrl != NULL)
+      GlobalFree(ie_proxy_config.lpszAutoConfigUrl);
+    if (ie_proxy_config.lpszProxy != NULL)
+      GlobalFree(ie_proxy_config.lpszProxy);
+    if (ie_proxy_config.lpszProxyBypass != NULL)
+      GlobalFree(ie_proxy_config.lpszProxyBypass);
+    if (lpHost != NULL)
+      (*env)->ReleaseStringChars(env, host, lpHost);
+    if (lpProto != NULL)
+      (*env)->ReleaseStringChars(env, proto, lpProto);
+
+    return proxy_array;
 }
--- a/test/java/net/ProxySelector/SystemProxies.java	Wed Feb 01 23:33:39 2017 +0300
+++ b/test/java/net/ProxySelector/SystemProxies.java	Thu Feb 02 10:28:47 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,9 +22,13 @@
  */
 
 /*
- * This is a manual test to determine the proxies set on the system for various
- * protocols. See bug 6912868.
+ * @test
+ * @bug 6912868 8170868
+ * @summary Basic test to provide some coverage of system proxy code. Will
+ * always pass. Should be run manually for specific systems to inspect output.
+ * @run main/othervm -Djava.net.useSystemProxies=true SystemProxies
  */
+
 import java.net.Proxy;
 import java.net.ProxySelector;
 import java.net.URI;