From 9649d878d17f8792d3a8b319e8c73f34ad863595 Mon Sep 17 00:00:00 2001 From: Andre Dietisheim Date: Wed, 25 Sep 2024 16:31:45 +0200 Subject: [PATCH] fix: allow pre/post-2024.3 proxy settings (#242) Signed-off-by: Andre Dietisheim --- .../common/utils/IdeProxyAdapter.java | 355 ++++++++++++++++++ .../intellij/common/utils/NetworkUtils.java | 82 ++-- 2 files changed, 388 insertions(+), 49 deletions(-) create mode 100644 src/main/java/com/redhat/devtools/intellij/common/utils/IdeProxyAdapter.java diff --git a/src/main/java/com/redhat/devtools/intellij/common/utils/IdeProxyAdapter.java b/src/main/java/com/redhat/devtools/intellij/common/utils/IdeProxyAdapter.java new file mode 100644 index 0000000..13c3f0c --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/common/utils/IdeProxyAdapter.java @@ -0,0 +1,355 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.intellij.common.utils; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.net.Authenticator; +import java.net.InetAddress; +import java.net.PasswordAuthentication; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +public class IdeProxyAdapter { + + public static ProxySelector getProxySelector() { + ProxySelector selector = Pre243.getProxySelector(); + if (selector != null) { + return selector; + } else { + return Post243.getProxySelector(); + } + } + + public static Authenticator getAuthenticator() { + Authenticator authenticator = Pre243.getAuthenticator(); + if (authenticator != null) { + return authenticator; + } else { + return Post243.getAuthenticator(); + } + } + + /** + * + * Returns the proxies that exist for the given url. + * + * @param url the url to get proxies for + * @return the proxies for the given url + * + */ + public static List getProxies(String url) { + List proxies = Pre243.getProxies(url); + if (proxies != null + && !proxies.isEmpty()) { + return proxies; + } else { + return Post243.getProxies(url); + } + } + + public static PasswordAuthentication getPasswordAuthentication() { + PasswordAuthentication authentication = Pre243.getPasswordAuthentication(); + if (authentication != null) { + return authentication; + } else { + return Post243.getPasswordAuthentication(); + } + } + + private static class Pre243 { + + static ProxySelector getProxySelector() { + Object httpConfigurable = httpConfigurable_getInstance(); + if (httpConfigurable == null) { + return null; + } + return (ProxySelector) ideaWideProxySelector_newInstance(httpConfigurable); + } + + /** + * + * new IdeaWideAuthenticator(HttpConfigurable) + * + */ + static Authenticator getAuthenticator() { + Object httpConfigurable = httpConfigurable_getInstance(); + if (httpConfigurable == null) { + return null; + } + return (Authenticator) ideaWideAuthenticator_newInstance(httpConfigurable); + } + + static List getProxies(String url) { + List proxies = new ArrayList<>(); + try { + Object httpConfigurable = httpConfigurable_getInstance(); + if (httpConfigurable != null) { + Object ideaWideProxySelector = ideaWideProxySelector_newInstance(httpConfigurable); + if (ideaWideProxySelector != null) { + proxies = ideaWideProxySelector_select(ideaWideProxySelector, new URI(url)); + } + } + } catch (URISyntaxException e) { + // swallow only + } + return proxies; + } + + /** + * + * new IdeaWideAuthenticator(HttpConfigurable.getInstance()).getPasswordAuthentication() + * + */ + static private PasswordAuthentication getPasswordAuthentication() { + PasswordAuthentication authentication = null; + Object httpConfigurable = httpConfigurable_getInstance(); + if (httpConfigurable != null) { + authentication = httpConfigurable_getPromptedAuthentication("*", httpConfigurable); + } + return authentication; + } + + /** + * + * IdeaWideProxySelector.select(URI) + * + */ + @SuppressWarnings("unchecked") + private static List ideaWideProxySelector_select(Object ideaWideProxySelector, URI uri) { + try { + Method method = ideaWideProxySelector.getClass().getDeclaredMethod("select", URI.class); + return (List) method.invoke(ideaWideProxySelector, uri); + } catch (ReflectiveOperationException | IllegalArgumentException e) { + return null; + } + } + + /** + * + * new IdeaWideProxySelector() + * + */ + @SuppressWarnings("JavaReflectionMemberAccess") + private static Object ideaWideProxySelector_newInstance(Object httpConfigurable) { + try { + Class clazz = Class.forName("com.intellij.util.net.IdeaWideProxySelector"); + Constructor constructor = clazz.getConstructor(httpConfigurable.getClass()); + return constructor.newInstance(httpConfigurable); + } catch (ReflectiveOperationException | IllegalArgumentException e) { + return null; + } + } + + /** + * + * new IdeaWideAuthenticator(HttpConfigurable) + * + */ + @SuppressWarnings("JavaReflectionMemberAccess") + private static Object ideaWideAuthenticator_newInstance(Object httpConfigurable) { + try { + Class clazz = Class.forName("com.intellij.util.net.IdeaWideAuthenticator"); + Constructor constructor = clazz.getConstructor(httpConfigurable.getClass()); + return constructor.newInstance(httpConfigurable); + } catch (Exception e) { + return null; + } + } + + /** + * + * HttpConfigurable.getPromptedAuthentication(String host, String prompt) + * + */ + private static PasswordAuthentication httpConfigurable_getPromptedAuthentication(String url, Object httpConfigurable) { + try { + Method method = httpConfigurable.getClass().getMethod("getPromptedAuthentication", String.class, String.class); + return (PasswordAuthentication) method.invoke(httpConfigurable, url, null); + } catch (Exception e) { + return null; + } + } + + /** + * + * HttpConfigurable.getInstance() + * + */ + private static Object httpConfigurable_getInstance() { + try { + Class clazz = Class.forName("com.intellij.util.net.HttpConfigurable"); + Method getInstanceMethod = clazz.getDeclaredMethod("getInstance"); + return getInstanceMethod.invoke(null); + } catch (ReflectiveOperationException e) { + return null; + } + } + } + + private static class Post243 { + + /** + * + * JdkProxyProvider.getInstance().getProxySelector() + * + */ + static ProxySelector getProxySelector() { + Object provider = jdkProxyProvider_getInstance(); + if (provider == null) { + return null; + } + return (ProxySelector) jdkProxyProvider_getProxySelector(provider); + } + + /** + * + * JdkProxyProvider.getInstance().getAuthenticator() + * + */ + public static Authenticator getAuthenticator() { + Object proxyAuthentication = jdkProxyProvider_getInstance(); + if (proxyAuthentication == null) { + return null; + } + return (Authenticator) jdkProxyProvider_getAuthenticator(proxyAuthentication); + } + + /** + * + * JdkProxyProvider.getInstance().getProxySelector().select(URI.create(url)) + * + */ + static List getProxies(String url) { + List proxies = new ArrayList<>(); + try { + ProxySelector selector = getProxySelector(); + if (selector != null) { + proxies = proxySelector_select(selector, new URI(url)); + } + } catch (Exception e) { + // swallow only + } + return proxies; + } + + /** + * + * JdkProxyProvider.getInstance().getAuthenticator().requestPasswordAuthentication( + * null, + * 0, + * null, + * null, + * null + * ); + * + */ + private static PasswordAuthentication getPasswordAuthentication() { + PasswordAuthentication authentication = null; + Object provider = jdkProxyProvider_getInstance(); + if (provider != null) { + Object authenticator = jdkProxyProvider_getAuthenticator(provider); + if (authenticator != null) { + authentication = authenticator_requestPasswordAuthentication(authenticator); + } + } + return authentication; + } + + /** + * + * ProxySelector.select(URI) + * + */ + @SuppressWarnings("unchecked") + private static List proxySelector_select(Object proxySelector, URI uri) { + try { + Method method = proxySelector.getClass().getDeclaredMethod("select", URI.class); + method.setAccessible(true); + return (List) method.invoke(proxySelector, uri); + } catch (Exception e) { + return null; + } + } + + /** + * + * Authenticator.requestPasswordAuthentication(InetAddress addr, int port, String protocol, String prompt, String scheme) + * + */ + private static PasswordAuthentication authenticator_requestPasswordAuthentication(Object authenticator) { + try { + Method method = authenticator.getClass().getMethod("requestPasswordAuthentication", + InetAddress.class, + Integer.TYPE, + String.class, + String.class, + String.class); + return (PasswordAuthentication) method.invoke(authenticator, + null, + 0, + null, + null, + null + ); + } catch (Exception e) { + return null; + } + } + + /** + * + * JdkProxyProvider.getProxySelector() + * + */ + private static Object jdkProxyProvider_getProxySelector(Object jdkProxyProvider) { + try { + Method getInstanceMethod = jdkProxyProvider.getClass().getDeclaredMethod("getProxySelector"); + return getInstanceMethod.invoke(jdkProxyProvider); + } catch (Exception e) { + return null; + } + } + + /** + * + * JdkProxyProvider.getAuthenticator() + * + */ + private static Object jdkProxyProvider_getAuthenticator(Object jdkProxyProvider) { + try { + Method getAuthenticatorMethod = jdkProxyProvider.getClass().getDeclaredMethod("getAuthenticator"); + return getAuthenticatorMethod.invoke(jdkProxyProvider); + } catch (Exception e) { + return null; + } + } + + /** + * + * JdkProxyProvider.getInstance() + * + */ + private static Object jdkProxyProvider_getInstance() { + try { + Class clazz = Class.forName("com.intellij.util.net.JdkProxyProvider"); + Method getInstanceMethod = clazz.getDeclaredMethod("getInstance"); + return getInstanceMethod.invoke(null); + } catch (Exception e) { + return null; + } + } + } +} diff --git a/src/main/java/com/redhat/devtools/intellij/common/utils/NetworkUtils.java b/src/main/java/com/redhat/devtools/intellij/common/utils/NetworkUtils.java index 137b0ba..f5047ab 100644 --- a/src/main/java/com/redhat/devtools/intellij/common/utils/NetworkUtils.java +++ b/src/main/java/com/redhat/devtools/intellij/common/utils/NetworkUtils.java @@ -11,9 +11,6 @@ package com.redhat.devtools.intellij.common.utils; import com.google.common.collect.Sets; -import com.intellij.util.net.HttpConfigurable; -import com.intellij.util.net.IdeaWideAuthenticator; -import com.intellij.util.net.IdeaWideProxySelector; import okhttp3.Authenticator; import okhttp3.OkHttpClient; import org.jetbrains.annotations.NotNull; @@ -22,15 +19,13 @@ import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.Proxy; +import java.net.ProxySelector; import java.net.SocketAddress; -import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Optional; import java.util.Set; import static com.intellij.openapi.util.text.StringUtil.isNotEmpty; @@ -39,24 +34,21 @@ public class NetworkUtils { public static OkHttpClient getClient() { - final HttpConfigurable httpConfigurable = HttpConfigurable.getInstance(); - final IdeaWideProxySelector ideaWideProxySelector = new IdeaWideProxySelector(httpConfigurable); - final IdeaWideAuthenticator ideaWideAuthenticator = new IdeaWideAuthenticator(httpConfigurable); - final Authenticator proxyAuthenticator = getProxyAuthenticator(ideaWideAuthenticator); + final ProxySelector proxySelector = IdeProxyAdapter.getProxySelector(); + final Authenticator proxyAuthenticator = getProxyAuthenticator(IdeProxyAdapter.getPasswordAuthentication()); final OkHttpClient.Builder builder = new OkHttpClient.Builder(); - builder.proxySelector(ideaWideProxySelector) + builder.proxySelector(proxySelector) .proxyAuthenticator(proxyAuthenticator); return builder.build(); } - private static Authenticator getProxyAuthenticator(IdeaWideAuthenticator ideaWideAuthenticator) { + private static Authenticator getProxyAuthenticator(PasswordAuthentication authentication) { Authenticator proxyAuthenticator = null; - if (Objects.nonNull(ideaWideAuthenticator)) { + if (authentication != null) { proxyAuthenticator = (route, response) -> { - final PasswordAuthentication authentication = ideaWideAuthenticator.getPasswordAuthentication(); final String credential = basic(authentication.getUserName(), Arrays.toString(authentication.getPassword())); return response.request().newBuilder() .header("Proxy-Authorization", credential) @@ -69,49 +61,23 @@ private static Authenticator getProxyAuthenticator(IdeaWideAuthenticator ideaWid @NotNull public static Map buildEnvironmentVariables(String url) throws URISyntaxException { - final int FIRST = 0; - final String HTTP_PROXY = "HTTP_PROXY"; - final String HTTPS_PROXY = "HTTPS_PROXY"; - final String ALL_PROXY = "ALL_PROXY"; - final Set proxyEnvironmentVariables = Sets.newHashSet(HTTP_PROXY, HTTPS_PROXY, ALL_PROXY); - final Map environmentVariables = new HashMap<>(6); - final HttpConfigurable httpConfigurable = HttpConfigurable.getInstance(); - final IdeaWideProxySelector ideaWideProxySelector = new IdeaWideProxySelector(httpConfigurable); - final URI uri = new URI(url); - final List proxies = ideaWideProxySelector.select(uri); - - if (!proxies.isEmpty()) { - final Proxy proxy = proxies.get(FIRST); + final List proxies = IdeProxyAdapter.getProxies(url); + // FIXME only use first proxy for cmd-lines + final Proxy proxy = proxies.stream().findFirst().orElse(null); + if (proxy != null) { final Proxy.Type type = proxy.type(); switch (type) { case HTTP: case SOCKS: final SocketAddress address = proxy.address(); - if (address instanceof InetSocketAddress) { - final InetSocketAddress socketAddress = (InetSocketAddress) address; - final InetAddress inetAddress = socketAddress.getAddress(); - final int port = socketAddress.getPort(); - - final IdeaWideAuthenticator ideaWideAuthenticator = new IdeaWideAuthenticator(httpConfigurable); - final Optional optionalPasswordAuthentication = Optional.ofNullable(ideaWideAuthenticator.getPasswordAuthentication()); - - String userName = null; - String password = null; - if(optionalPasswordAuthentication.isPresent()) { - final PasswordAuthentication passwordAuthentication = optionalPasswordAuthentication.get(); - userName = passwordAuthentication.getUserName(); - password = Arrays.toString(passwordAuthentication.getPassword()); - } - - String finalUserName = userName; - String finalPassword = password; + final PasswordAuthentication authentication = IdeProxyAdapter.getPasswordAuthentication(); + final String envVarValue = buildHttpProxy(type, authentication, (InetSocketAddress) address); + final Set proxyEnvironmentVariables = Sets.newHashSet("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"); proxyEnvironmentVariables.forEach(envVarName -> { - final String envVarValue = buildHttpProxy(type, finalUserName, finalPassword, inetAddress, port); - environmentVariables.put(envVarName, envVarValue); environmentVariables.put(envVarName.toLowerCase(), envVarValue); }); @@ -124,7 +90,25 @@ public static Map buildEnvironmentVariables(String url) throws U } @NotNull - private static String buildHttpProxy(Proxy.Type type, String userName, String password, InetAddress address, int port) { + private static String buildHttpProxy(Proxy.Type type, PasswordAuthentication authentication, InetSocketAddress address) { + final String host = address.getHostName(); + final int port = address.getPort(); + return buildHttpProxy(type, authentication, host, port); + } + + @NotNull + private static String buildHttpProxy(Proxy.Type type, PasswordAuthentication authentication, String host, int port) { + String userName = null; + String password = null; + if (authentication != null) { + userName = authentication.getUserName(); + password = Arrays.toString(authentication.getPassword()); + } + return buildHttpProxy(type, userName, password, host, port); + } + + @NotNull + private static String buildHttpProxy(Proxy.Type type, String userName, String password, String host, int port) { final StringBuilder builder = new StringBuilder(); switch (type) { @@ -140,7 +124,7 @@ private static String buildHttpProxy(Proxy.Type type, String userName, String pa builder.append(userName).append(":").append(password).append("@"); } - builder.append(address.getHostAddress()).append(":").append(port); + builder.append(host).append(":").append(port); return builder.toString(); }