JDK-8217364 : Custom URLStreamHandler for jrt or file protocol can override default handler.
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 11.0.2,12
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2019-01-18
  • Updated: 2019-07-02
  • Resolved: 2019-05-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 13
11.0.5-oracleFixed 13 b20Fixed
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
JDK 11.0.2

A DESCRIPTION OF THE PROBLEM :
java.net.URL.isOverrideable(String) for "jrt" or "file" returns false,
but custom URLStreamHandlerFactory can override URLStreamHandler for jrt or file protocol
providing the default URLStreamHandlerFactory.

In java.net.URL#getURLStreamHandler,
custom URLStreamHandler's createURLStreamHandler is invoked twice.
But the 2nd invocation does not check by isOverrideable(protocol).

So, custom URLStreamHandlerFactory can override URLStreamHandler for jrt or file protocol
providing the default URLStreamHandlerFactory.

        ==== the 1st invocation ====
        if (isOverrideable(protocol) && jdk.internal.misc.VM.isBooted()) {
            // Use the factory (if any). Volatile read makes
            // URLStreamHandlerFactory appear fully initialized to current thread.
            fac = factory;
            if (fac != null) {
                handler = fac.createURLStreamHandler(protocol);
                checkedWithFactory = true;
            }

            if (handler == null && !protocol.equalsIgnoreCase("jar")) {
                handler = lookupViaProviders(protocol);
            }

            if (handler == null) {
                handler = lookupViaProperty(protocol);
            }
        }

        ==== the 2nd invocation ====
            // Check with factory if another thread set a
            // factory since our last check
            if (!checkedWithFactory && (fac = factory) != null) {
                handler2 = fac.createURLStreamHandler(protocol);
            }

REGRESSION : Last worked in version 11.0.1

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Execute the following test case to reproduce

java CustomURLStreamHandlerFactory jrt:/foo

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
[output]
Default URLStreamHandler used for jrt:/foo
ACTUAL -
[output]
java.lang.RuntimeException: CustomURLStreamHandler used for jrt:/foo
        at CustomURLStreamHandlerFactory$CustomURLStreamHandler.openConnection(CustomURLStreamHandlerFactory.java:13)
        at java.base/java.net.URL.openConnection(URL.java:1051)
        at CustomURLStreamHandlerFactory.main(CustomURLStreamHandlerFactory.java:22)

---------- BEGIN SOURCE ----------
import java.io.*;
import java.net.*;

public class CustomURLStreamHandlerFactory implements URLStreamHandlerFactory {
    @Override
    public URLStreamHandler createURLStreamHandler(String protocol) {
        return new CustomURLStreamHandler();
    }

    private static class CustomURLStreamHandler extends URLStreamHandler {
        @Override
        protected URLConnection openConnection(URL u) throws IOException {
            throw new RuntimeException("CustomURLStreamHandler used for " + u);
        }
    }

    public static void main(String[] args) {
        URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory());

        for (String url : args) {
            try {
                new URL(url).openConnection();
                System.out.println("Default URLStreamHandler used for " + url);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
---------- END SOURCE ----------

FREQUENCY : always



Comments
Fix request (11u): Requesting backport to match with Oracle 11.0.5. Patch applies cleanly net copyright header. Will be tested @SAP before push.
23-06-2019

The JDK-8213942 change is responsible for this regression. Perhaps we should fast path the scenario where a non-overridable protocol is encountered. Something like this perhaps: --- a/src/java.base/share/classes/java/net/URL.java +++ b/src/java.base/share/classes/java/net/URL.java @@ -1401,10 +1401,16 @@ return handler; } + if (!isOverrideable(protocol)) { + handler = defaultFactory.createURLStreamHandler(protocol); + handlers.put(protocol, handler); + return handler; + } + URLStreamHandlerFactory fac; boolean checkedWithFactory = false; - if (isOverrideable(protocol) && jdk.internal.misc.VM.isBooted()) { + if (jdk.internal.misc.VM.isBooted()) { // Use the factory (if any). Volatile read makes // URLStreamHandlerFactory appear fully initialized to current thread. fac = factory;
18-01-2019

To reproduce the issue, run the attached test case. JDK 11.0.1 - Pass JDK 11.0.2 - Fail JDK 12-ea + 26 - Fail Output in failed versions: java.lang.RuntimeException: CustomURLStreamHandler used for jrt:/foo at CustomURLStreamHandlerFactory$CustomURLStreamHandler.openConnection(CustomURLStreamHandlerFactory.java:13) at java.base/java.net.URL.openConnection(URL.java:1074) at CustomURLStreamHandlerFactory.main(CustomURLStreamHandlerFactory.java:22) Output in passed versions: Default URLStreamHandler used for jrt:/foo
18-01-2019