package de.tutorials.training.fx.proxy; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.*; import java.util.Hashtable; import java.util.function.Consumer; import static java.util.Arrays.asList; /** * @author Thomas Darimont */ public class URLStreamFactoryCustomizer { public static void useDedicatedProxyForWebkit(Proxy proxy, String protocols) { forceInitializationOfOriginalUrlStreamHandlers(); tryReplaceOriginalUrlStreamHandlersWithScopeProxyAwareVariants(proxy, protocols); } private static void tryReplaceOriginalUrlStreamHandlersWithScopeProxyAwareVariants(Proxy proxy, String protocols) { try { Hashtable handlers = tryExtractInternalHandlerTableFromUrl(); //System.out.println(handlers); Consumer wrapStreamHandlerWithScopedProxyHandler = protocol -> { URLStreamHandler originalHandler = (URLStreamHandler) handlers.get(protocol); handlers.put(protocol, new DelegatingScopedProxyAwareUrlStreamHandler(originalHandler, proxy)); }; asList(protocols.split(",")).stream().map(String::trim).filter(s -> !s.isEmpty()).forEach(wrapStreamHandlerWithScopedProxyHandler); //System.out.println(handlers); } catch (Exception ex) { throw new RuntimeException(ex); } } private static Hashtable tryExtractInternalHandlerTableFromUrl() { try { Field handlersField = URL.class.getDeclaredField("handlers"); handlersField.setAccessible(true); return (Hashtable) handlersField.get(null); } catch (Exception e) { throw new RuntimeException(e); } } private static void forceInitializationOfOriginalUrlStreamHandlers() { try { new URL("http://."); new URL("https://."); } catch (MalformedURLException e) { throw new RuntimeException(e); } } static class DelegatingScopedProxyAwareUrlStreamHandler extends URLStreamHandler { private static final Method openConnectionMethod; private static final Method openConnectionWithProxyMethod; static { try { openConnectionMethod = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class); openConnectionWithProxyMethod = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class, Proxy.class); openConnectionMethod.setAccessible(true); openConnectionWithProxyMethod.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } } private final URLStreamHandler delegatee; private final Proxy proxy; public DelegatingScopedProxyAwareUrlStreamHandler(URLStreamHandler delegatee, Proxy proxy) { this.delegatee = delegatee; this.proxy = proxy; } @Override protected URLConnection openConnection(URL url) throws IOException { try { if (isWebKitURLLoaderThread(Thread.currentThread())) { //WebKit requested loading the given url, use provided proxy. return (URLConnection) openConnectionWithProxyMethod.invoke(delegatee, url, proxy); } //Invoke the standard url handler. return (URLConnection) openConnectionMethod.invoke(delegatee, url); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } } private boolean isWebKitURLLoaderThread(Thread thread) { StackTraceElement[] st = thread.getStackTrace(); //TODO Add more robust stack-trace inspection. return st.length > 4 && st[4].getClassName().startsWith("com.sun.webkit.network"); } } }