Getting a reference to Java's default http(s) URLStreamHandler

Markus A. picture Markus A. · Aug 2, 2013 · Viewed 11.7k times · Source

I have a library that I need to use in one of my projects, which unfortunately registers its own URLStreamHandler to handle http-URLs. Is there a way to get a reference to Java's default http- and https-URLStreamHandlers, so I can specify one of them in the URL's constructor to open a standard http connection without using the protocol overridden by the library?

Answer

Markus A. picture Markus A. · Aug 2, 2013

Found it:

sun.net.www.protocol.http.Handler

With that, I can now do:

URL url = new URL(null, "http://...", new sun.net.www.protocol.http.Handler());
HttpURLConnection cxn = (HttpURLConnection) url.openConnection();

And I get a normal Java HttpURLConnection and not the one provided by the library.


Update:

I discovered another, more general, approach: Remove the library's URLStreamHandlerFactory!

This is a bit tricky, since the URL-class technically doesn't allow you to set the factory more than once or to clear it with an official function call, but with a bit of reflection-magic, we can do it anyways:

public static String unsetURLStreamHandlerFactory() {
    try {
        Field f = URL.class.getDeclaredField("factory");
        f.setAccessible(true);
        Object curFac = f.get(null);
        f.set(null, null);
        URL.setURLStreamHandlerFactory(null);
        return curFac.getClass().getName();
    } catch (Exception e) {
        return null;
    }
}

This method grabs a hold of the static field factory in the URL-class, makes it accessible, grabs its current value and changes it to null. Afterwards, it calls URL.setStreamHandlerFactory(null) (which now completes without error) to make this setting "official", i.e. give the function a chance to do any other clean-up that it might want to do. Then it returns the previously registered factory's class name, just for reference. If anything goes wrong, it swallows the exception (I know, bad idea...) and returns null.

For reference: Here is the relevant source-code for URL.java.

Note: This approach might be even more risky than using the internal sun-classes (as far as portability goes) since it relies on the specific internal structure of the URL-class (namely the existence and exact function of the factory-field), but it does have the advantage that I don't need to go through all of my code to find all URL-constructors and add the handler-parameter... Also, it might break some functionality of the library that relies on their registered handlers. Luckily, neither issue (portability and partially broken library functionality) are issues that are relevant in my case.


Update: #2

While we're using reflection: Here is the probably safest way to get a reference to the default handler:

public static URLStreamHandler getURLStreamHandler(String protocol) {
    try {
        Method method = URL.class.getDeclaredMethod("getURLStreamHandler", String.class);
        method.setAccessible(true);
        return (URLStreamHandler) method.invoke(null, protocol);        
    } catch (Exception e) {
        return null;
    }
}

which you then simply call as:

URLStreamHandler hander = getURLStreamHandler("http");

Note: This call needs to happen before the library registers its URLStreamHandlerFactory, otherwise you will end up with a reference to their handler.

Why would I consider this the safest approach? Because URL.getURLStreamHandler(...) is not a fully private method, but only package-private. So, modifying its call-signature could break other code in the same package. Also, its name doesn't really leave much room for returning anything other than what we are looking for. Thus, I would expect it to be unlikely (albeit still not impossible) that a different/future implementation of the URL-class would be incompatible with the assumptions made here.