IJPL-148832 IJPL-148512 New Proxy API, extension point for proxy settings override

GitOrigin-RevId: e81473636c238412508593bff48b544817c6470b
This commit is contained in:
Vadim Salavatov
2024-05-06 15:27:14 +02:00
committed by intellij-monorepo-bot
parent 797f6253ae
commit 2acdc81bed
42 changed files with 2361 additions and 1080 deletions

View File

@@ -3144,18 +3144,13 @@ f:com.intellij.util.proxy.CommonProxy
- getAuthenticator():java.net.Authenticator
- s:getHostNameReliably(java.lang.String,java.net.InetAddress,java.net.URL):java.lang.String
- s:getInstance():com.intellij.util.proxy.CommonProxy
- s:getMessageFromProps(java.util.Map):java.lang.String
- s:getOldStyleProperties():java.util.Map
- s:isInstalledAssertion():V
- noAuthentication(java.lang.String,java.lang.String,I):V
- noProxy(java.lang.String,java.lang.String,I):V
- removeCustom(java.lang.String):V
- removeCustomAuth(java.lang.String):V
- removeNoAuthentication(java.lang.String,java.lang.String,I):V
- removeNoProxy(java.lang.String,java.lang.String,I):V
- select(java.net.URI):java.util.List
- select(java.net.URL):java.util.List
- setCustom(java.lang.String,java.net.ProxySelector):V
- setCustomAuth(java.lang.String,com.intellij.util.proxy.NonStaticAuthenticator):V
f:com.intellij.util.proxy.CommonProxy$HostInfo
- f:myHost:java.lang.String
@@ -3171,6 +3166,7 @@ com.intellij.util.proxy.JavaProxyProperty
- sf:HTTPS_HOST:java.lang.String
- sf:HTTPS_PORT:java.lang.String
- sf:HTTP_HOST:java.lang.String
- sf:HTTP_NON_PROXY_HOSTS:java.lang.String
- sf:HTTP_PASSWORD:java.lang.String
- sf:HTTP_PORT:java.lang.String
- sf:HTTP_USERNAME:java.lang.String

View File

@@ -2,14 +2,14 @@
package com.intellij.util.proxy;
import com.intellij.ide.IdeCoreBundle;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.Strings;
import com.intellij.openapi.vfs.VfsUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -20,14 +20,18 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* @deprecated use {@link com.intellij.util.net.JdkProxyProvider} for the main proxy provider of the application
*/
@SuppressWarnings("JavadocReference")
@Deprecated
public final class CommonProxy extends ProxySelector {
private static final Logger LOG = Logger.getInstance(CommonProxy.class);
static final Logger LOG = Logger.getInstance(CommonProxy.class);
private static final CommonProxy ourInstance = new CommonProxy();
private final CommonAuthenticator myAuthenticator = new CommonAuthenticator();
private static final ThreadLocal<Boolean> ourReenterDefence = new ThreadLocal<>();
/** @deprecated use {@link com.intellij.util.net.ProxyUtils#NO_PROXY_LIST} */
@Deprecated
public static final List<Proxy> NO_PROXY_LIST = Collections.singletonList(Proxy.NO_PROXY);
private static final long ourErrorInterval = TimeUnit.MINUTES.toMillis(3);
private static final AtomicInteger ourNotificationCount = new AtomicInteger();
@@ -35,15 +39,10 @@ public final class CommonProxy extends ProxySelector {
private static volatile ProxySelector ourWrong;
private static final AtomicReference<Map<String, String>> ourProps = new AtomicReference<>();
static {
ProxySelector.setDefault(ourInstance);
}
private final Object myLock = new Object();
private final Set<Pair<HostInfo, Thread>> myNoProxy = new HashSet<>();
private final Map<String, ProxySelector> myCustom = new HashMap<>();
private final Map<String, NonStaticAuthenticator> myCustomAuth = new HashMap<>();
private final Map<String, AccessToken> myCustomRegistrations = new HashMap<>();
private final Map<String, AccessToken> myCustomAuthRegistrations = new HashMap<>();
public static CommonProxy getInstance() {
return ourInstance;
@@ -53,17 +52,21 @@ public final class CommonProxy extends ProxySelector {
ensureAuthenticator();
}
/**
* @deprecated use {@link com.intellij.util.net.JdkProxyProvider#ensureDefault()}
*/
@Deprecated
public static void isInstalledAssertion() {
final ProxySelector aDefault = ProxySelector.getDefault();
if (ourInstance != aDefault) {
if (CommonProxyCompatibility.mainProxySelector != null && CommonProxyCompatibility.mainProxySelector != aDefault) {
// to report only once
if (ourWrong != aDefault || itsTime()) {
LOG.error("ProxySelector.setDefault() was changed to [" + aDefault.toString() + "] - other than com.intellij.util.proxy.CommonProxy.ourInstance.\n" +
LOG.error("ProxySelector.setDefault() was changed to [" + aDefault.toString() + "] - other than com.intellij.util.proxy.CommonProxy.myMainProxySelector.\n" +
"This will make some " + ApplicationNamesInfo.getInstance().getProductName() + " network calls fail.\n" +
"Instead, methods of com.intellij.util.proxy.CommonProxy should be used for proxying.");
"Instead, methods of com.intellij.util.net.ProxyService should be used for proxying.");
ourWrong = aDefault;
}
ProxySelector.setDefault(ourInstance);
ProxySelector.setDefault(CommonProxyCompatibility.mainProxySelector);
ourInstance.ensureAuthenticator();
}
assertSystemPropertiesSet();
@@ -96,6 +99,7 @@ public final class CommonProxy extends ProxySelector {
}
}
@ApiStatus.Internal
public static @Nullable @NlsContexts.DialogMessage String getMessageFromProps(Map<String, String> props) {
String message = null;
for (Map.Entry<String, String> entry : props.entrySet()) {
@@ -107,6 +111,7 @@ public final class CommonProxy extends ProxySelector {
return message;
}
@ApiStatus.Internal
public static Map<String, String> getOldStyleProperties() {
final Map<String, String> props = new HashMap<>();
props.put(JavaProxyProperty.HTTP_HOST, System.getProperty(JavaProxyProperty.HTTP_HOST));
@@ -115,64 +120,62 @@ public final class CommonProxy extends ProxySelector {
return props;
}
/**
* @deprecated use {@link com.intellij.util.net.JdkProxyProvider#ensureDefault())}
*/
@Deprecated
public void ensureAuthenticator() {
Authenticator.setDefault(myAuthenticator);
}
public void noProxy(final @NotNull String protocol, final @NotNull String host, final int port) {
synchronized (myLock) {
LOG.debug("no proxy added: " + protocol + "://" + host + ":" + port);
myNoProxy.add(Pair.create(new HostInfo(protocol, host, port), Thread.currentThread()));
if (CommonProxyCompatibility.mainAuthenticator != null) {
Authenticator.setDefault(CommonProxyCompatibility.mainAuthenticator);
}
else {
LOG.warn("main authenticator is not yet registered");
}
}
public void removeNoProxy(final @NotNull String protocol, final @NotNull String host, final int port) {
/** @deprecated no replacement, existing usages are internal and are no-op since noProxy has no usages */
@Deprecated
public void removeNoProxy(final @NotNull String protocol, final @NotNull String host, final int port) { }
/**
* @deprecated no replacement, only two internal usages, and the rule is never removed, logic should be implemented by other means,
* see {@link com.intellij.util.net.ProxyAuthentication}
*/
@Deprecated
public void noAuthentication(final @NotNull String protocol, final @NotNull String host, final int port) { }
/**
* @deprecated see {@link com.intellij.util.net.JdkProxyCustomizer}
*/
@Deprecated
public void removeCustom(final @NotNull String key) {}
/**
* @deprecated see {@link com.intellij.util.net.JdkProxyCustomizer}
*/
@Deprecated
public void setCustomAuth(@NotNull String key, @NotNull NonStaticAuthenticator nonStaticAuthenticator) {
synchronized (myLock) {
LOG.debug("no proxy removed: " + protocol + "://" + host + ":" + port);
myNoProxy.remove(Pair.create(new HostInfo(protocol, host, port), Thread.currentThread()));
}
}
public void noAuthentication(final @NotNull String protocol, final @NotNull String host, final int port) {
synchronized (myLock) {
LOG.debug("no proxy added: " + protocol + "://" + host + ":" + port);
myNoProxy.add(Pair.create(new HostInfo(protocol, host, port), Thread.currentThread()));
}
}
@SuppressWarnings("unused")
public void removeNoAuthentication(final @NotNull String protocol, final @NotNull String host, final int port) {
synchronized (myLock) {
LOG.debug("no proxy removed: " + protocol + "://" + host + ":" + port);
myNoProxy.remove(Pair.create(new HostInfo(protocol, host, port), Thread.currentThread()));
}
}
public void setCustom(final @NotNull String key, final @NotNull ProxySelector proxySelector) {
synchronized (myLock) {
LOG.debug("custom set: " + key + ", " + proxySelector);
myCustom.put(key, proxySelector);
}
}
public void setCustomAuth(@NotNull String key, @NotNull NonStaticAuthenticator authenticator) {
synchronized (myLock) {
LOG.debug("custom auth set: " + key + ", " + authenticator);
myCustomAuth.put(key, authenticator);
var register = CommonProxyCompatibility.registerCustomAuthenticator;
if (register != null) {
LOG.debug("custom auth set: " + key + ", " + nonStaticAuthenticator);
//noinspection resource
myCustomRegistrations.put(key, register.invoke(nonStaticAuthenticator.asAuthenticator()));
}
}
}
/**
* @deprecated see {@link com.intellij.util.net.JdkProxyCustomizer}
*/
@Deprecated
public void removeCustomAuth(final @NotNull String key) {
synchronized (myLock) {
LOG.debug("custom auth removed: " + key);
myCustomAuth.remove(key);
}
}
public void removeCustom(final @NotNull String key) {
synchronized (myLock) {
LOG.debug("custom set: " + key);
myCustom.remove(key);
@SuppressWarnings("resource")
var registration = myCustomAuthRegistrations.remove(key);
if (registration != null) registration.finish();
}
}
@@ -180,138 +183,38 @@ public final class CommonProxy extends ProxySelector {
return select(createUri(url));
}
private static boolean isLocalhost(@NotNull String hostName) {
return hostName.equalsIgnoreCase("localhost") || hostName.equals("127.0.0.1") || hostName.equals("::1");
}
@Override
public @NotNull List<Proxy> select(@Nullable URI uri) {
ProxySelector mainProxySelector = CommonProxyCompatibility.mainProxySelector;
if (mainProxySelector == null) {
LOG.warn("main proxy selector is not yet installed");
return NO_PROXY_LIST;
}
isInstalledAssertion();
if (uri == null) {
return NO_PROXY_LIST;
}
LOG.debug("CommonProxy.select called for " + uri);
if (Boolean.TRUE.equals(ourReenterDefence.get())) {
return NO_PROXY_LIST;
}
try {
ourReenterDefence.set(Boolean.TRUE);
String host = Strings.notNullize(uri.getHost());
if (isLocalhost(host)) {
return NO_PROXY_LIST;
}
final HostInfo info = new HostInfo(uri.getScheme(), host, correctPortByProtocol(uri));
final Map<String, ProxySelector> copy;
synchronized (myLock) {
if (myNoProxy.contains(Pair.create(info, Thread.currentThread()))) {
LOG.debug("CommonProxy.select returns no proxy (in no proxy list) for " + uri);
return NO_PROXY_LIST;
}
copy = Map.copyOf(myCustom);
}
for (Map.Entry<String, ProxySelector> entry : copy.entrySet()) {
List<Proxy> proxies = entry.getValue().select(uri);
if (proxies != null && !proxies.isEmpty()) {
LOG.debug("CommonProxy.select returns custom proxy for " + uri + ", " + proxies);
return proxies;
}
}
return NO_PROXY_LIST;
}
finally {
ourReenterDefence.remove();
}
}
private static int correctPortByProtocol(@NotNull URI uri) {
if (uri.getPort() == -1) {
if ("http".equals(uri.getScheme())) {
return ProtocolDefaultPorts.HTTP;
}
else if ("https".equals(uri.getScheme())) {
return ProtocolDefaultPorts.SSL;
}
}
return uri.getPort();
return mainProxySelector.select(uri);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
LOG.info("connect failed to " + uri.toString() + ", sa: " + sa.toString(), ioe);
final Map<String, ProxySelector> copy;
synchronized (myLock) {
copy = new HashMap<>(myCustom);
}
for (Map.Entry<String, ProxySelector> entry : copy.entrySet()) {
entry.getValue().connectFailed(uri, sa, ioe);
}
var mainProxySelector = CommonProxyCompatibility.mainProxySelector;
if (mainProxySelector == null) return;
mainProxySelector.connectFailed(uri, sa, ioe);
}
/** @deprecated use {@link com.intellij.util.net.JdkProxyProvider#getAuthenticator()} */
@Deprecated
public Authenticator getAuthenticator() {
return myAuthenticator;
}
private final class CommonAuthenticator extends Authenticator {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
String siteStr = getRequestingSite() == null ? null : getRequestingSite().toString();
LOG.debug("CommonAuthenticator.getPasswordAuthentication called for " + siteStr);
String host = getHostNameReliably(getRequestingHost(), getRequestingSite(), getRequestingURL());
int port = getRequestingPort();
final Map<String, NonStaticAuthenticator> copy;
synchronized (myLock) {
// for hosts defined as no proxy we will NOT pass authentication to not provoke credentials
HostInfo hostInfo = new HostInfo(getRequestingProtocol(), host, port);
Pair<HostInfo, Thread> pair = new Pair<>(hostInfo, Thread.currentThread());
if (myNoProxy.contains(pair)) {
LOG.debug("CommonAuthenticator.getPasswordAuthentication found host in no proxies set (" + siteStr + ")");
return null;
}
copy = Map.copyOf(myCustomAuth);
}
if (!copy.isEmpty()) {
for (Map.Entry<String, NonStaticAuthenticator> entry : copy.entrySet()) {
final NonStaticAuthenticator authenticator = entry.getValue();
prepareAuthenticator(authenticator);
final PasswordAuthentication authentication = authenticator.getPasswordAuthentication();
if (authentication != null) {
LOG.debug("CommonAuthenticator.getPasswordAuthentication found custom authenticator for " + siteStr + ", " + entry.getKey() +
", " + authenticator);
logAuthentication(authentication);
return authentication;
}
}
}
return null;
}
private void prepareAuthenticator(NonStaticAuthenticator authenticator) {
authenticator.setRequestingHost(getRequestingHost());
authenticator.setRequestingSite(getRequestingSite());
authenticator.setRequestingPort(getRequestingPort());
authenticator.setRequestingProtocol(getRequestingProtocol());//http
@NlsSafe String requestingPrompt = getRequestingPrompt();
authenticator.setRequestingPrompt(requestingPrompt);
authenticator.setRequestingScheme(getRequestingScheme());//ntlm
authenticator.setRequestingURL(getRequestingURL());
authenticator.setRequestorType(getRequestorType());
}
private static void logAuthentication(PasswordAuthentication authentication) {
if (authentication == null) {
LOG.debug("CommonAuthenticator.getPasswordAuthentication returned null");
}
else {
LOG.debug("CommonAuthenticator.getPasswordAuthentication returned authentication pair with login: " + authentication.getUserName());
}
}
return CommonProxyCompatibility.mainAuthenticator != null ? CommonProxyCompatibility.mainAuthenticator : Authenticator.getDefault();
}
/**
* @apiNote no external usages
* @deprecated use {@link com.intellij.util.net.ProxyUtils#getHostNameReliably(String, InetAddress, URL)} (it is nullable now)
*/
@Deprecated
public static String getHostNameReliably(final String requestingHost, final InetAddress site, final URL requestingUrl) {
String host = requestingHost;
if (host == null) {
@@ -330,6 +233,8 @@ public final class CommonProxy extends ProxySelector {
return VfsUtil.toUri(url.toString());
}
/** @deprecated one external usage in the method that is deprecated, remove after migration */
@Deprecated
public static final class HostInfo {
public final String myProtocol;
public final String myHost;

View File

@@ -0,0 +1,38 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.proxy
import com.intellij.openapi.application.AccessToken
import org.jetbrains.annotations.ApiStatus
import java.net.Authenticator
import java.net.ProxySelector
import kotlin.concurrent.Volatile
@ApiStatus.Internal
@Suppress("DEPRECATION")
object CommonProxyCompatibility {
@JvmField
@Volatile internal var mainProxySelector: ProxySelector? = null
@JvmField
@Volatile internal var mainAuthenticator: Authenticator? = null
@JvmField
@Volatile internal var registerCustomProxySelector: ((ProxySelector) -> AccessToken)? = null
@JvmField
@Volatile internal var registerCustomAuthenticator: ((Authenticator) -> AccessToken)? = null
@Synchronized
fun register(proxySelector: ProxySelector,
authenticator: Authenticator,
registerCustomProxySelector: (ProxySelector) -> AccessToken,
registerCustomAuthenticator: (Authenticator) -> AccessToken) {
if (mainProxySelector != null) {
CommonProxy.LOG.error("multiple registration of main proxy selector",
RuntimeException("current main proxy selector=$mainProxySelector, registration=$proxySelector"))
}
else {
mainProxySelector = proxySelector
mainAuthenticator = authenticator
this.registerCustomProxySelector = registerCustomProxySelector
this.registerCustomAuthenticator = registerCustomAuthenticator
}
}
}

View File

@@ -9,6 +9,7 @@ public interface JavaProxyProperty {
String PROXY_SET = "proxySet";
String HTTP_HOST = "http.proxyHost";
String HTTP_PORT = "http.proxyPort";
String HTTP_NON_PROXY_HOSTS = "http.nonProxyHosts";
String HTTPS_HOST = "https.proxyHost";
String HTTPS_PORT = "https.proxyPort";

View File

@@ -1,6 +1,7 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.proxy;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
import java.net.Authenticator;
@@ -8,6 +9,12 @@ import java.net.InetAddress;
import java.net.PasswordAuthentication;
import java.net.URL;
/**
* @deprecated given that {@link Authenticator#requestPasswordAuthenticationInstance(String, InetAddress, int, String, String, String, URL, Authenticator.RequestorType)}
* was introduced in java 9, this class is not needed anymore, please migrate to {@link Authenticator}
*/
@Deprecated
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
public abstract class NonStaticAuthenticator {
private String requestingHost;
private InetAddress requestingSite;
@@ -18,6 +25,8 @@ public abstract class NonStaticAuthenticator {
private URL requestingURL;
private Authenticator.RequestorType requestingAuthType;
private final Authenticator wrapper = new AuthenticatorWrapper();
public abstract PasswordAuthentication getPasswordAuthentication();
protected String getRequestingHost() {
@@ -84,4 +93,43 @@ public abstract class NonStaticAuthenticator {
protected void setRequestorType(Authenticator.RequestorType requestingAuthType) {
this.requestingAuthType = requestingAuthType;
}
@ApiStatus.Internal
public PasswordAuthentication requestPasswordAuthenticationInstance(String host,
InetAddress addr,
int port,
String protocol,
@Nls String prompt,
String scheme,
URL url,
Authenticator.RequestorType reqType) {
synchronized (this) {
this.requestingHost = host;
this.requestingSite = addr;
this.requestingPort = port;
this.requestingProtocol = protocol;
this.requestingPrompt = prompt;
this.requestingScheme = scheme;
this.requestingURL = url;
this.requestingAuthType = reqType;
return this.getPasswordAuthentication();
}
}
@ApiStatus.Internal
public Authenticator asAuthenticator() {
return wrapper;
}
private class AuthenticatorWrapper extends Authenticator {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
synchronized (NonStaticAuthenticator.this) {
//noinspection HardCodedStringLiteral
return NonStaticAuthenticator.this
.requestPasswordAuthenticationInstance(getRequestingHost(), getRequestingSite(), getRequestingPort(), getRequestingProtocol(),
getRequestingPrompt(), getRequestingScheme(), getRequestingURL(), getRequestorType());
}
}
}
}

View File

@@ -12466,7 +12466,6 @@ c:com.intellij.util.net.HttpConfigurable
- USE_PAC_URL:Z
- USE_PROXY_PAC:Z
- <init>():V
- clearGenericPasswords():V
- dispose():V
- s:editConfigurable(javax.swing.JComponent):Z
- getGenericPassword(java.lang.String,I):java.net.PasswordAuthentication
@@ -12493,29 +12492,34 @@ c:com.intellij.util.net.HttpConfigurable
- setPlainProxyPassword(java.lang.String):V
- setProxyLogin(java.lang.String):V
- writeExternal(org.jdom.Element):V
c:com.intellij.util.net.HttpConfigurable$ProxyInfo
- myPasswordCrypt:java.lang.String
- myStore:Z
- myUsername:java.lang.String
- <init>():V
- <init>(Z,java.lang.String,java.lang.String):V
- getPasswordCrypt():java.lang.String
- getUsername():java.lang.String
- isStore():Z
- setPasswordCrypt(java.lang.String):V
- setStore(Z):V
- setUsername(java.lang.String):V
f:com.intellij.util.net.HttpConnectionUtils
- sf:INSTANCE:com.intellij.util.net.HttpConnectionUtils
- sf:openConnection(java.lang.String):java.net.URLConnection
- sf:openHttpConnection(java.lang.String):java.net.HttpURLConnection
- sf:prepareUrl(java.lang.String):V
c:com.intellij.util.net.HttpProxyConfigurable
- com.intellij.openapi.options.ConfigurableBase
- com.intellij.openapi.options.Configurable$NoMargin
- <init>():V
- <init>(com.intellij.util.net.HttpConfigurable):V
- p:createUi():com.intellij.util.net.HttpProxySettingsUi
- p:getSettings():com.intellij.util.net.HttpConfigurable
- <init>(com.intellij.util.net.ProxySettings):V
- p:createUi():com.intellij.openapi.options.ConfigurableUi
- p:getSettings():com.intellij.util.net.ProxySettings
f:com.intellij.util.net.IOExceptionDialog
- com.intellij.openapi.ui.DialogWrapper
- <init>(java.lang.String,java.lang.String):V
- s:showErrorDialog(java.lang.String,java.lang.String):Z
f:com.intellij.util.net.IdeProxyAuthenticator
- java.net.Authenticator
- <init>(com.intellij.util.net.ProxyAuthentication):V
- toString():java.lang.String
f:com.intellij.util.net.IdeProxySelector
- java.net.ProxySelector
- sf:Companion:com.intellij.util.net.IdeProxySelector$Companion
- <init>(com.intellij.util.net.ProxyConfigurationProvider):V
- connectFailed(java.net.URI,java.net.SocketAddress,java.io.IOException):V
- select(java.net.URI):java.util.List
f:com.intellij.util.net.IdeProxySelector$Companion
f:com.intellij.util.net.IdeaWideAuthenticator
- com.intellij.util.proxy.NonStaticAuthenticator
- <init>(com.intellij.util.net.HttpConfigurable):V
@@ -12525,6 +12529,114 @@ f:com.intellij.util.net.IdeaWideProxySelector
- <init>(com.intellij.util.net.HttpConfigurable):V
- connectFailed(java.net.URI,java.net.SocketAddress,java.io.IOException):V
- select(java.net.URI):java.util.List
com.intellij.util.net.JdkProxyCustomizer
- sf:Companion:com.intellij.util.net.JdkProxyCustomizer$Companion
- a:customizeAuthenticator(java.net.Authenticator):com.intellij.openapi.application.AccessToken
- a:customizeProxySelector(java.net.ProxySelector):com.intellij.openapi.application.AccessToken
- s:getInstance():com.intellij.util.net.JdkProxyCustomizer
- *a:getOriginalAuthenticator():java.net.Authenticator
- *a:getOriginalProxySelector():java.net.ProxySelector
f:com.intellij.util.net.JdkProxyCustomizer$Companion
- f:getInstance():com.intellij.util.net.JdkProxyCustomizer
com.intellij.util.net.JdkProxyProvider
- sf:Companion:com.intellij.util.net.JdkProxyProvider$Companion
- s:ensureDefault():V
- a:getAuthenticator():java.net.Authenticator
- s:getInstance():com.intellij.util.net.JdkProxyProvider
- a:getProxySelector():java.net.ProxySelector
f:com.intellij.util.net.JdkProxyProvider$Companion
- f:ensureDefault():V
- f:getInstance():com.intellij.util.net.JdkProxyProvider
com.intellij.util.net.ProxyAuthentication
- sf:Companion:com.intellij.util.net.ProxyAuthentication$Companion
- a:enablePromptedAuthentication(java.lang.String,I):V
- s:getInstance():com.intellij.util.net.ProxyAuthentication
- a:getOrPromptAuthentication(java.lang.String,java.lang.String,I):com.intellij.credentialStore.Credentials
- a:getPromptedAuthentication(java.lang.String,java.lang.String,I):com.intellij.credentialStore.Credentials
- a:isPromptedAuthenticationCancelled(java.lang.String,I):Z
f:com.intellij.util.net.ProxyAuthentication$Companion
- f:getInstance():com.intellij.util.net.ProxyAuthentication
com.intellij.util.net.ProxyConfiguration
- sf:Companion:com.intellij.util.net.ProxyConfiguration$Companion
- s:buildProxyExceptionsMatcher(java.lang.String):java.util.function.Predicate
- s:getAutodetect():com.intellij.util.net.ProxyConfiguration$AutoDetectProxy
- s:getDirect():com.intellij.util.net.ProxyConfiguration$DirectProxy
- s:proxy(com.intellij.util.net.ProxyConfiguration$ProxyProtocol,java.lang.String,I,java.lang.String):com.intellij.util.net.ProxyConfiguration$StaticProxyConfiguration
- s:proxyAutoConfiguration(java.net.URL):com.intellij.util.net.ProxyConfiguration$ProxyAutoConfiguration
com.intellij.util.net.ProxyConfiguration$AutoDetectProxy
- com.intellij.util.net.ProxyConfiguration
f:com.intellij.util.net.ProxyConfiguration$Companion
- f:buildProxyExceptionsMatcher(java.lang.String):java.util.function.Predicate
- f:getAutodetect():com.intellij.util.net.ProxyConfiguration$AutoDetectProxy
- f:getDirect():com.intellij.util.net.ProxyConfiguration$DirectProxy
- f:proxy(com.intellij.util.net.ProxyConfiguration$ProxyProtocol,java.lang.String,I,java.lang.String):com.intellij.util.net.ProxyConfiguration$StaticProxyConfiguration
- bs:proxy$default(com.intellij.util.net.ProxyConfiguration$Companion,com.intellij.util.net.ProxyConfiguration$ProxyProtocol,java.lang.String,I,java.lang.String,I,java.lang.Object):com.intellij.util.net.ProxyConfiguration$StaticProxyConfiguration
- f:proxyAutoConfiguration(java.net.URL):com.intellij.util.net.ProxyConfiguration$ProxyAutoConfiguration
com.intellij.util.net.ProxyConfiguration$DirectProxy
- com.intellij.util.net.ProxyConfiguration
com.intellij.util.net.ProxyConfiguration$ProxyAutoConfiguration
- com.intellij.util.net.ProxyConfiguration
- a:getPacUrl():java.net.URL
e:com.intellij.util.net.ProxyConfiguration$ProxyProtocol
- java.lang.Enum
- sf:HTTP:com.intellij.util.net.ProxyConfiguration$ProxyProtocol
- sf:SOCKS:com.intellij.util.net.ProxyConfiguration$ProxyProtocol
- f:getDefaultPort():I
- s:getEntries():kotlin.enums.EnumEntries
- s:valueOf(java.lang.String):com.intellij.util.net.ProxyConfiguration$ProxyProtocol
- s:values():com.intellij.util.net.ProxyConfiguration$ProxyProtocol[]
com.intellij.util.net.ProxyConfiguration$StaticProxyConfiguration
- com.intellij.util.net.ProxyConfiguration
- a:getExceptions():java.lang.String
- a:getHost():java.lang.String
- a:getPort():I
- a:getProtocol():com.intellij.util.net.ProxyConfiguration$ProxyProtocol
com.intellij.util.net.ProxyConfigurationProvider
- a:getProxyConfiguration():com.intellij.util.net.ProxyConfiguration
com.intellij.util.net.ProxyCredentialProvider
- a:getCredentials(java.lang.String,I):com.intellij.credentialStore.Credentials
com.intellij.util.net.ProxyCredentialStore
- sf:Companion:com.intellij.util.net.ProxyCredentialStore$Companion
- a:areCredentialsRemembered(java.lang.String,I):Z
- a:clearAllCredentials():V
- a:clearTransientCredentials():V
- a:getCredentials(java.lang.String,I):com.intellij.credentialStore.Credentials
- s:getInstance():com.intellij.util.net.ProxyCredentialStore
- a:setCredentials(java.lang.String,I,com.intellij.credentialStore.Credentials,Z):V
f:com.intellij.util.net.ProxyCredentialStore$Companion
- f:getInstance():com.intellij.util.net.ProxyCredentialStore
f:com.intellij.util.net.ProxyCredentialStoreKt
- sf:asProxyCredentialProvider(com.intellij.util.net.ProxyCredentialStore):com.intellij.util.net.ProxyCredentialProvider
com.intellij.util.net.ProxySettings
- sf:Companion:com.intellij.util.net.ProxySettings$Companion
- s:getDefaultProxyConfiguration():com.intellij.util.net.ProxyConfiguration
- s:getInstance():com.intellij.util.net.ProxySettings
- a:getProxyConfiguration():com.intellij.util.net.ProxyConfiguration
- *a:setProxyConfiguration(com.intellij.util.net.ProxyConfiguration):V
f:com.intellij.util.net.ProxySettings$Companion
- f:getDefaultProxyConfiguration():com.intellij.util.net.ProxyConfiguration
- f:getInstance():com.intellij.util.net.ProxySettings
f:com.intellij.util.net.ProxySettingsKt
- sf:asConfigurationProvider(com.intellij.util.net.ProxySettings):com.intellij.util.net.ProxyConfigurationProvider
com.intellij.util.net.ProxySettingsOverrideProvider
- sf:Companion:com.intellij.util.net.ProxySettingsOverrideProvider$Companion
- s:areProxySettingsOverridden():Z
- a:getProxyConfigurationProvider():com.intellij.util.net.ProxyConfigurationProvider
- a:getShouldUserSettingsBeOverriden():Z
f:com.intellij.util.net.ProxySettingsOverrideProvider$Companion
- f:areProxySettingsOverridden():Z
f:com.intellij.util.net.ProxyUtils
- sf:NO_PROXY_LIST:java.util.List
- sf:asJavaProxy(com.intellij.util.net.ProxyConfiguration$StaticProxyConfiguration):java.net.Proxy
- sf:asJvmProperties(com.intellij.util.net.ProxyConfiguration$StaticProxyConfiguration,com.intellij.util.net.ProxyCredentialProvider):java.util.Map
- sf:asJvmProperties(java.net.Proxy):java.util.Map
- sf:editConfigurable(com.intellij.util.net.ProxySettings,javax.swing.JComponent):Z
- sf:getApplicableProxiesAsJvmProperties(java.net.URI,com.intellij.util.net.ProxyCredentialProvider,java.net.ProxySelector):java.util.List
- bs:getApplicableProxiesAsJvmProperties$default(java.net.URI,com.intellij.util.net.ProxyCredentialProvider,java.net.ProxySelector,I,java.lang.Object):java.util.List
- sf:getHostNameReliably(java.lang.String,java.net.InetAddress,java.net.URL):java.lang.String
- sf:getStaticProxyCredentials(com.intellij.util.net.ProxySettings,com.intellij.util.net.ProxyCredentialProvider):com.intellij.credentialStore.Credentials
- sf:isRealProxy(java.net.Proxy):Z
- sf:setStaticProxyCredentials(com.intellij.util.net.ProxySettings,com.intellij.util.net.ProxyCredentialStore,com.intellij.credentialStore.Credentials,Z):V
c:com.intellij.util.net.ssl.CertificateInfoPanel
- javax.swing.JPanel
- <init>(java.security.cert.X509Certificate):V

View File

@@ -6,4 +6,3 @@ com/intellij/ui/ReorderableListController$ActionBehaviour
com/intellij/ui/components/JBScrollPane$ViewportBorder
com/intellij/ui/messager/CalloutComponent$Pointer
com/intellij/util/animation/JBAnimator$Statistic
com/intellij/util/net/HttpProxySettingsUi

View File

@@ -2464,6 +2464,8 @@ dialog.message.host.name.empty=The host name is empty
dialog.message.invalid.host.value=Invalid host value
dialog.message.login.empty=The login is empty
dialog.message.password.empty=The password is empty
dialog.message.url.is.empty=The URL is empty
dialog.message.url.is.invalid=The URL is invalid
editor.banner.close.tooltip=Close

View File

@@ -243,6 +243,7 @@ proxy.manual.exclude.example=Example\: *.domain.com, 192.168.*
proxy.manual.auth=Proxy &authentication
proxy.test.button=Check connection
proxy.system.proxy.settings=&System proxy settings...
proxy.settings.override.by.plugin.checkbox=Proxy settings provided by ''{0}'' plugin
date.dialog.title=Choose Date

View File

@@ -0,0 +1,93 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.openapi.application.AccessToken
import java.io.IOException
import java.net.*
internal object CustomizedPlatformJdkProxyProvider : JdkProxyProvider, JdkProxyCustomizer {
override val originalProxySelector: ProxySelector get() = PlatformJdkProxyProvider.proxySelector
override val originalAuthenticator: Authenticator get() = PlatformJdkProxyProvider.authenticator
override val proxySelector: ProxySelector = CustomizedProxySelector()
override val authenticator: Authenticator = CustomizedAuthenticator()
@Volatile
private var proxySelectors: List<ProxySelector> = emptyList()
@Volatile
private var authenticators: List<Authenticator> = emptyList()
override fun customizeProxySelector(proxySelector: ProxySelector): AccessToken {
synchronized(this) {
proxySelectors = proxySelectors + proxySelector
}
return object : AccessToken() {
override fun finish() {
synchronized(this@CustomizedPlatformJdkProxyProvider) {
proxySelectors = proxySelectors - proxySelector
}
}
}
}
override fun customizeAuthenticator(authenticator: Authenticator): AccessToken {
synchronized(this) {
authenticators = authenticators + authenticator
}
return object : AccessToken() {
override fun finish() {
synchronized(this@CustomizedPlatformJdkProxyProvider) {
authenticators = authenticators - authenticator
}
}
}
}
private class CustomizedProxySelector : ProxySelector() {
private val selectorReenterDefence: ThreadLocal<Boolean> = ThreadLocal()
override fun select(uri: URI?): List<Proxy> {
if (selectorReenterDefence.get() == true) {
return NO_PROXY_LIST
}
selectorReenterDefence.set(true)
try {
for (proxySelector in proxySelectors) {
val result = proxySelector.select(uri)
if (!result.isNullOrEmpty()) return result
}
return originalProxySelector.select(uri)
}
finally {
selectorReenterDefence.set(false)
}
}
override fun connectFailed(uri: URI?, sa: SocketAddress?, ioe: IOException?) {
for (proxySelector in proxySelectors) {
proxySelector.connectFailed(uri, sa, ioe)
}
originalProxySelector.connectFailed(uri, sa, ioe)
}
}
private class CustomizedAuthenticator : Authenticator() {
override fun getPasswordAuthentication(): PasswordAuthentication? {
for (authenticator in authenticators) {
val creds = getPasswordAuthenticationUsing(authenticator)
if (creds != null) {
return creds
}
}
return getPasswordAuthenticationUsing(originalAuthenticator)
}
private fun getPasswordAuthenticationUsing(authenticator: Authenticator): PasswordAuthentication? {
return authenticator.requestPasswordAuthenticationInstance(
requestingHost, requestingSite, requestingPort, requestingProtocol,
requestingPrompt, requestingScheme, requestingURL, requestorType,
)
}
}
}

View File

@@ -2,38 +2,30 @@
package com.intellij.util.net;
import com.intellij.configurationStore.XmlSerializer;
import com.intellij.credentialStore.CredentialAttributesKt;
import com.intellij.credentialStore.Credentials;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.util.PopupUtil;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.util.WaitForProgressToShow;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.net.internal.ProxyNewUserService;
import com.intellij.util.net.internal.ProxyMigrationService;
import com.intellij.util.proxy.CommonProxy;
import com.intellij.util.proxy.JavaProxyProperty;
import com.intellij.util.proxy.PropertiesEncryptionSupport;
import com.intellij.util.xmlb.XmlSerializerUtil;
import com.intellij.util.xmlb.annotations.Transient;
import org.jdom.Element;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.*;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.*;
@@ -44,10 +36,16 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Pattern;
import static com.intellij.openapi.util.Pair.pair;
/**
* @deprecated Use {@link ProxySettings}, {@link ProxyAuthentication}, {@link HttpConnectionUtils}, and {@link ProxyUtils} instead.
* See method deprecation notices for more details.
* <p/>
* For removal in version 24.3
*/
@Deprecated(forRemoval = true)
@State(name = "HttpConfigurable",
category = SettingsCategory.SYSTEM,
exportable = true,
@@ -57,35 +55,58 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
private static final Path PROXY_CREDENTIALS_FILE = Paths.get(PathManager.getOptionsPath(), "proxy.settings.pwd");
// only one out of these three should be true
public boolean USE_HTTP_PROXY;
public boolean USE_PROXY_PAC;
/** @deprecated use {@link ProxySettings#getProxyConfiguration()} or {@link ProxySettings#setProxyConfiguration(ProxyConfiguration)} */
@Deprecated(forRemoval = true) public boolean USE_HTTP_PROXY;
/** @deprecated use {@link ProxySettings#getProxyConfiguration()} or {@link ProxySettings#setProxyConfiguration(ProxyConfiguration)} */
@Deprecated(forRemoval = true) public boolean USE_PROXY_PAC;
// USE_NO_PROXY = !USE_HTTP_PROXY && !USE_PROXY_PAC
public boolean PROXY_TYPE_IS_SOCKS;
public transient volatile boolean AUTHENTICATION_CANCELLED;
public String PROXY_HOST;
public int PROXY_PORT = 80;
/** @deprecated use {@link ProxySettings#getProxyConfiguration()} or {@link ProxySettings#setProxyConfiguration(ProxyConfiguration)} */
@Deprecated(forRemoval = true) public boolean PROXY_TYPE_IS_SOCKS;
/** @deprecated use {@link ProxySettings#getProxyConfiguration()} or {@link ProxySettings#setProxyConfiguration(ProxyConfiguration)} */
@Deprecated(forRemoval = true) public String PROXY_HOST;
/** @deprecated use {@link ProxySettings#getProxyConfiguration()} or {@link ProxySettings#setProxyConfiguration(ProxyConfiguration)} */
@Deprecated(forRemoval = true) public int PROXY_PORT = 80;
/** @deprecated use {@link ProxySettings#getProxyConfiguration()} or {@link ProxySettings#setProxyConfiguration(ProxyConfiguration)} */
@Deprecated(forRemoval = true) public String PROXY_EXCEPTIONS;
/** @deprecated use {@link ProxySettings#getProxyConfiguration()} or {@link ProxySettings#setProxyConfiguration(ProxyConfiguration)} */
@Deprecated(forRemoval = true) public boolean USE_PAC_URL;
/** @deprecated use {@link ProxySettings#getProxyConfiguration()} or {@link ProxySettings#setProxyConfiguration(ProxyConfiguration)} */
@Deprecated(forRemoval = true) public String PAC_URL;
public volatile boolean PROXY_AUTHENTICATION;
public boolean KEEP_PROXY_PASSWORD;
public transient String LAST_ERROR;
public transient String CHECK_CONNECTION_URL = "http://";
/** @deprecated use {@link ProxyUtils#getStaticProxyCredentials(ProxySettings, ProxyCredentialProvider)} or {@link ProxyUtils#setStaticProxyCredentials(ProxySettings, ProxyCredentialStore, Credentials, boolean)} */
@Deprecated(forRemoval = true) public volatile boolean PROXY_AUTHENTICATION;
/** @deprecated this flag shouldn't be persisted. In HttpConfigurable it controls whether the password is dropped from the persistence.
* But if the user wants the password to not be remembered, then such a password should never reach persistence in the first place.
*
* @see ProxyUtils#getStaticProxyCredentials(ProxySettings, ProxyCredentialProvider)
* @see ProxyAuthentication
*/
@Deprecated(forRemoval = true) public boolean KEEP_PROXY_PASSWORD;
/** @deprecated without replacement */
@Deprecated(forRemoval = true) public transient String LAST_ERROR;
/** @deprecated belongs to UI, for removal without a replacement */
@Deprecated(forRemoval = true) public transient String CHECK_CONNECTION_URL = "http://";
/** @deprecated use {@link ProxyAuthentication#isPromptedAuthenticationCancelled(String, int)} with StaticProxy configuration
* from {@link ProxySettings#getProxyConfiguration()} */
@Deprecated(forRemoval = true) public transient volatile boolean AUTHENTICATION_CANCELLED;
private final Map<CommonProxy.HostInfo, ProxyInfo> myGenericPasswords = new HashMap<>();
private final Set<CommonProxy.HostInfo> myGenericCancelled = new HashSet<>();
public String PROXY_EXCEPTIONS;
public boolean USE_PAC_URL;
public String PAC_URL;
private transient IdeaWideProxySelector mySelector;
private final transient Object myLock = new Object();
//private transient IdeaWideProxySelector mySelector;
// -> drop, unify auth methods, use base64 encoding like it is done for generic auth
private final transient PropertiesEncryptionSupport myEncryptionSupport = new PropertiesEncryptionSupport(new SecretKeySpec(new byte[] {
(byte)0x50, (byte)0x72, (byte)0x6f, (byte)0x78, (byte)0x79, (byte)0x20, (byte)0x43, (byte)0x6f,
(byte)0x6e, (byte)0x66, (byte)0x69, (byte)0x67, (byte)0x20, (byte)0x53, (byte)0x65, (byte)0x63
}, "AES"));
// -> drop, see explanation above
private final transient NotNullLazyValue<Properties> myProxyCredentials = NotNullLazyValue.createValue(() -> {
try {
if (!Files.exists(PROXY_CREDENTIALS_FILE)) {
@@ -104,6 +125,8 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
return ApplicationManager.getApplication().getService(HttpConfigurable.class);
}
/** @deprecated use {@link ProxyUtils#editConfigurable(ProxySettings, JComponent)} */
@Deprecated
public static boolean editConfigurable(@Nullable JComponent parent) {
return ShowSettingsUtil.getInstance().editConfigurable(parent, new HttpProxyConfigurable());
}
@@ -123,28 +146,21 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
@Override
public void initializeComponent() {
if (ApplicationManager.getApplication().getService(ProxyNewUserService.class).isNewUser()) { // temporary! will be removed in new proxy settings API
if (ProxyMigrationService.getInstance().isNewUser()) { // temporary! will be removed in new proxy settings implementation
switchDefaultForNewUser();
}
mySelector = new IdeaWideProxySelector(this);
String name = getClass().getName();
CommonProxy commonProxy = CommonProxy.getInstance();
commonProxy.setCustom(name, mySelector);
commonProxy.setCustomAuth(name, new IdeaWideAuthenticator(this));
}
/** @deprecated use {@link JdkProxyCustomizer#getOriginalProxySelector()} */
@Deprecated(forRemoval = true)
public @NotNull ProxySelector getOnlyBySettingsSelector() {
return mySelector;
return JdkProxyCustomizer.getInstance().getOriginalProxySelector();
}
@Override
public void dispose() {
String name = getClass().getName();
CommonProxy commonProxy = CommonProxy.getInstance();
commonProxy.removeCustom(name);
commonProxy.removeCustomAuth(name);
}
public void dispose() { }
// -> drop, transient auth will be stored separately from persisted auth
private void correctPasswords(@NotNull HttpConfigurable to) {
synchronized (myLock) {
to.myGenericPasswords.values().removeIf(it -> !it.isStore());
@@ -160,18 +176,37 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
correctPasswords(this);
}
/** @deprecated use {@link ProxyAuthentication#isPromptedAuthenticationCancelled(String, int)} */
@Deprecated
public boolean isGenericPasswordCanceled(@NotNull String host, int port) {
synchronized (myLock) {
return myGenericCancelled.contains(new CommonProxy.HostInfo(null, host, port));
}
}
private void setGenericPasswordCanceled(final String host, final int port) {
@ApiStatus.Internal
public void removeGenericPasswordCancellation(@NotNull String host, int port) {
synchronized (myLock) {
myGenericCancelled.remove(new CommonProxy.HostInfo(null, host, port));
}
}
@ApiStatus.Internal
public void clearGenericCancellations() {
synchronized (myLock) {
myGenericCancelled.clear();
}
}
@ApiStatus.Internal
public void setGenericPasswordCanceled(final String host, final int port) { // IdeProxyService auth
synchronized (myLock) {
myGenericCancelled.add(new CommonProxy.HostInfo(null, host, port));
}
}
/** @deprecated use {@link ProxyCredentialStore#getCredentials(String, int)} */
@Deprecated(forRemoval = true)
public PasswordAuthentication getGenericPassword(@NotNull String host, int port) {
ProxyInfo proxyInfo;
synchronized (myLock) {
@@ -186,6 +221,8 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
return new PasswordAuthentication(proxyInfo.getUsername(), decode(String.valueOf(proxyInfo.getPasswordCrypt())).toCharArray());
}
/** @deprecated use {@link ProxyCredentialStore#setCredentials(String, int, Credentials, boolean)} */
@Deprecated(forRemoval = true)
@SuppressWarnings("WeakerAccess")
public void putGenericPassword(final String host, final int port, @NotNull PasswordAuthentication authentication, boolean remember) {
PasswordAuthentication coded = new PasswordAuthentication(authentication.getUserName(), encode(String.valueOf(authentication.getPassword())).toCharArray());
@@ -194,21 +231,29 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
}
}
/** @deprecated use {@link ProxyUtils#getStaticProxyCredentials(ProxySettings, ProxyCredentialProvider)} or {@link ProxyUtils#setStaticProxyCredentials(ProxySettings, ProxyCredentialStore, Credentials, boolean)} */
@Deprecated(forRemoval = true)
@Transient
public @Nullable String getProxyLogin() {
return getSecure("proxy.login");
}
/** @deprecated use {@link ProxyUtils#getStaticProxyCredentials(ProxySettings, ProxyCredentialProvider)} or {@link ProxyUtils#setStaticProxyCredentials(ProxySettings, ProxyCredentialStore, Credentials, boolean)} */
@Deprecated(forRemoval = true)
@Transient
public void setProxyLogin(String login) {
storeSecure("proxy.login", login);
}
/** @deprecated use {@link ProxyUtils#getStaticProxyCredentials(ProxySettings, ProxyCredentialProvider)} or {@link ProxyUtils#setStaticProxyCredentials(ProxySettings, ProxyCredentialStore, Credentials, boolean)} */
@Deprecated(forRemoval = true)
@Transient
public @Nullable String getPlainProxyPassword() {
return getSecure("proxy.password");
}
/** @deprecated use {@link ProxyUtils#getStaticProxyCredentials(ProxySettings, ProxyCredentialProvider)} or {@link ProxyUtils#setStaticProxyCredentials(ProxySettings, ProxyCredentialStore, Credentials, boolean)} */
@Deprecated(forRemoval = true)
@Transient
public void setPlainProxyPassword (String password) {
storeSecure("proxy.password", password);
@@ -223,112 +268,32 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
return Base64.getEncoder().encodeToString(password.getBytes(StandardCharsets.UTF_8));
}
public PasswordAuthentication getGenericPromptedAuthentication(final @Nls String prefix, final @NlsSafe String host, final String prompt, final int port, final boolean remember) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return null;
}
final Ref<PasswordAuthentication> value = Ref.create();
runAboveAll(() -> {
if (isGenericPasswordCanceled(host, port)) {
return;
}
PasswordAuthentication password = getGenericPassword(host, port);
if (password != null) {
value.set(password);
return;
}
AuthenticationDialog dialog = new AuthenticationDialog(PopupUtil.getActiveComponent(), prefix + ": "+ host,
IdeBundle.message("dialog.message.please.enter.credentials.for", prompt), "", "", remember);
dialog.show();
if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
AuthenticationPanel panel = dialog.getPanel();
PasswordAuthentication passwordAuthentication = new PasswordAuthentication(panel.getLogin(), panel.getPassword());
putGenericPassword(host, port, passwordAuthentication, remember && panel.isRememberPassword());
value.set(passwordAuthentication);
}
else {
setGenericPasswordCanceled(host, port);
}
});
return value.get();
/**
* @deprecated use {@link ProxyAuthentication#getPromptedAuthentication(String, String, int)}.
* <b>ARGUMENT ORDER HAS BEEN CHANGED!</b>
* <p/>
* @param prefix is never used with anything other than "Proxy authentication: "
* @param remember should be a hint, dropped in new API
*/
@Deprecated(forRemoval = true)
public PasswordAuthentication getGenericPromptedAuthentication(final @Nls String prefix, final @NlsSafe String host,
@Nls final String prompt, final int port, final boolean remember) {
Credentials credentials = ProxyAuthentication.getInstance().getPromptedAuthentication(prompt, host, port);
return credentialsToPasswordAuth(credentials);
}
public PasswordAuthentication getPromptedAuthentication(final String host, final String prompt) {
if (AUTHENTICATION_CANCELLED) {
private static PasswordAuthentication credentialsToPasswordAuth(Credentials credentials) {
if (!CredentialAttributesKt.isFulfilled(credentials)) {
return null;
}
final String password = getPlainProxyPassword();
if (PROXY_AUTHENTICATION) {
final String login = getSecure("proxy.login");
if (!StringUtil.isEmptyOrSpaces(login) && !StringUtil.isEmptyOrSpaces(password)) {
return new PasswordAuthentication(login, password.toCharArray());
}
}
// do not try to show any dialogs if application is exiting
if (ApplicationManager.getApplication() == null || ApplicationManager.getApplication().isDisposed()) {
return null;
}
if (ApplicationManager.getApplication().isUnitTestMode()) {
return null;
}
final PasswordAuthentication[] value = new PasswordAuthentication[1];
runAboveAll(() -> {
if (AUTHENTICATION_CANCELLED) {
return;
}
// password might have changed, and the check below is for that
final String password1 = getPlainProxyPassword();
if (PROXY_AUTHENTICATION) {
final String login = getSecure("proxy.login");
if (!StringUtil.isEmptyOrSpaces(login) && !StringUtil.isEmptyOrSpaces(password1)) {
value[0] = new PasswordAuthentication(login, password1.toCharArray());
return;
}
}
AuthenticationDialog dialog = new AuthenticationDialog(
PopupUtil.getActiveComponent(),
IdeBundle.message("dialog.title.proxy.authentication", host),
IdeBundle.message("dialog.message.please.enter.credentials.for", prompt),
getSecure("proxy.login"),
"",
KEEP_PROXY_PASSWORD
);
dialog.show();
if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
PROXY_AUTHENTICATION = true;
AuthenticationPanel panel = dialog.getPanel();
final boolean keepPass = panel.isRememberPassword();
KEEP_PROXY_PASSWORD = keepPass;
storeSecure("proxy.login", StringUtil.nullize(panel.getLogin()));
if (keepPass) {
setPlainProxyPassword(String.valueOf(panel.getPassword()));
}
else {
removeSecure("proxy.password");
}
value[0] = new PasswordAuthentication(panel.getLogin(), panel.getPassword());
} else {
AUTHENTICATION_CANCELLED = true;
}
});
return value[0];
return new PasswordAuthentication(credentials.getUserName(), Objects.requireNonNull(credentials.getPassword()).toCharArray());
}
private static void runAboveAll(final @NotNull Runnable runnable) {
ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
if (progressIndicator != null && progressIndicator.isModal()) {
WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(runnable);
}
else {
Application app = ApplicationManager.getApplication();
app.invokeAndWait(runnable, ModalityState.any());
}
/** @deprecated use {@link ProxyAuthentication#getPromptedAuthentication(String, String, int)} */
@Deprecated(forRemoval = true)
public PasswordAuthentication getPromptedAuthentication(final String host, @Nls final String prompt) {
Credentials credentials = ProxyAuthentication.getInstance().getPromptedAuthentication(prompt, host, PROXY_PORT);
return credentialsToPasswordAuth(credentials);
}
/** @deprecated left for compatibility with com.intellij.openapi.project.impl.IdeaServerSettings */
@@ -361,52 +326,18 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
* Also, this method is useful in a way that it tests connection to the host [through proxy].
*
* @param url URL for HTTP connection
*
* @deprecated use {@link HttpConnectionUtils#prepareUrl(String)}
*/
@Deprecated(forRemoval = true)
public void prepareURL(@NotNull String url) throws IOException {
URLConnection connection = openConnection(url);
try {
connection.connect();
connection.getInputStream();
}
catch (IOException e) {
throw e;
}
catch (Throwable ignored) { }
finally {
if (connection instanceof HttpURLConnection) {
((HttpURLConnection)connection).disconnect();
}
}
HttpConnectionUtils.prepareUrl(url);
}
/** @deprecated use {@link HttpConnectionUtils#openConnection(String)} */
@Deprecated(forRemoval = true)
public @NotNull URLConnection openConnection(@NotNull String location) throws IOException {
URL url = new URL(location);
URLConnection urlConnection = null;
List<Proxy> proxies = CommonProxy.getInstance().select(url);
if (proxies.isEmpty()) {
urlConnection = url.openConnection();
}
else {
IOException exception = null;
for (Proxy proxy : proxies) {
try {
urlConnection = url.openConnection(proxy);
break;
}
catch (IOException e) {
// continue iteration
exception = e;
}
}
if (urlConnection == null && exception != null) {
throw exception;
}
}
assert urlConnection != null;
urlConnection.setReadTimeout(HttpRequests.READ_TIMEOUT);
urlConnection.setConnectTimeout(HttpRequests.CONNECTION_TIMEOUT);
return urlConnection;
return HttpConnectionUtils.openConnection(location);
}
/**
@@ -414,23 +345,31 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
* @param location url to connect to
* @return instance of {@link HttpURLConnection}
* @throws IOException in case of any I/O troubles or if created connection isn't instance of HttpURLConnection.
*
* @deprecated use {@link HttpConnectionUtils#openHttpConnection(String)}
*/
@Deprecated(forRemoval = true)
public @NotNull HttpURLConnection openHttpConnection(@NotNull String location) throws IOException {
URLConnection urlConnection = openConnection(location);
if (urlConnection instanceof HttpURLConnection) {
return (HttpURLConnection) urlConnection;
}
else {
throw new IOException("Expected " + HttpURLConnection.class + ", but got " + urlConnection.getClass());
}
return HttpConnectionUtils.openHttpConnection(location);
}
/** @deprecated this method is 1. a utility that shouldn't be a method;
* 2. error-prone as it only considers the case when proxy is specified statically, i.e., PAC configuration is not considered.
* Reimplement at use site if necessary.
* @see ProxyAuthentication
*/
@Deprecated(forRemoval = true)
public boolean isHttpProxyEnabledForUrl(@Nullable String url) {
if (!USE_HTTP_PROXY) return false;
URI uri = url != null ? VfsUtil.toUri(url) : null;
return uri == null || !isProxyException(uri.getHost());
}
/** @deprecated use {@link ProxyUtils#getApplicableProxiesAsJvmProperties(URI, ProxyCredentialProvider, ProxySelector)}.
* If autodetection really needs to be disallowed, check {@link ProxySettings} first. Keep in mind that
* proxy configuration depends on the URI, so it cannot be null. If you only care about statically configured proxies, see
* {@link ProxyUtils#asJvmProperties(ProxyConfiguration.StaticProxyConfiguration, ProxyCredentialProvider)} */
@Deprecated(forRemoval = true)
public @NotNull List<Pair<String, String>> getJvmProperties(boolean withAutodetection, @Nullable URI uri) {
if (!USE_HTTP_PROXY && !USE_PROXY_PAC) {
return Collections.emptyList();
@@ -489,6 +428,8 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
return result;
}
/** @deprecated use {@link ProxyConfiguration#buildProxyExceptionsMatcher(String)} */
@Deprecated(forRemoval = true)
public boolean isProxyException(URI uri) {
String uriHost = uri.getHost();
return isProxyException(uriHost);
@@ -499,22 +440,16 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
if (StringUtil.isEmptyOrSpaces(uriHost) || StringUtil.isEmptyOrSpaces(PROXY_EXCEPTIONS)) {
return false;
}
List<String> hosts = StringUtil.split(PROXY_EXCEPTIONS, ",");
for (String hostPattern : hosts) {
String regexpPattern = StringUtil.escapeToRegexp(hostPattern.trim()).replace("\\*", ".*");
if (Pattern.compile(regexpPattern).matcher(uriHost).matches()) {
return true;
}
}
return false;
return ProxyConfiguration.buildProxyExceptionsMatcher(PROXY_EXCEPTIONS).test(uriHost);
}
/** @deprecated use {@link ProxyUtils#isRealProxy(Proxy)} */
@Deprecated(forRemoval = true)
public static boolean isRealProxy(@NotNull Proxy proxy) {
return !Proxy.NO_PROXY.equals(proxy) && !Proxy.Type.DIRECT.equals(proxy.type());
return ProxyUtils.isRealProxy(proxy);
}
@ApiStatus.Internal
public void clearGenericPasswords() {
synchronized (myLock) {
myGenericPasswords.clear();
@@ -522,21 +457,33 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
}
}
public void removeGeneric(@NotNull CommonProxy.HostInfo info) {
/** @deprecated use {@link ProxyCredentialStore#setCredentials(String, int, Credentials, boolean)} */
@Deprecated(forRemoval = true)
public void removeGeneric(@NotNull CommonProxy.HostInfo info) { // IdeAuthenticatorService
synchronized (myLock) {
myGenericPasswords.remove(info);
}
}
public static class ProxyInfo {
@ApiStatus.Internal
public boolean isGenericPasswordRemembered(@NotNull String host, int port) {
synchronized (myLock) {
if (myGenericPasswords.isEmpty()) return false;
var proxyInfo = myGenericPasswords.get(new CommonProxy.HostInfo(null, host, port));
if (proxyInfo == null) return false;
return proxyInfo.myStore;
}
}
private static class ProxyInfo {
public boolean myStore;
public String myUsername;
public String myPasswordCrypt;
@SuppressWarnings("UnusedDeclaration")
public ProxyInfo() { }
ProxyInfo() { }
public ProxyInfo(boolean store, String username, String passwordCrypt) {
ProxyInfo(boolean store, String username, String passwordCrypt) {
myStore = store;
myUsername = username;
myPasswordCrypt = passwordCrypt;

View File

@@ -0,0 +1,88 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.util.io.HttpRequests
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLConnection
/**
* Uses platform network settings (e.g., [ProxySettings]).
*/
object HttpConnectionUtils {
/**
* Tests http connection to the host (through proxy if configured).
* @see [JdkProxyProvider]
*/
@Throws(IOException::class)
@JvmStatic
fun prepareUrl(url: String) {
val connection = openConnection(url)
try {
connection.connect()
connection.getInputStream()
}
catch (e: IOException) {
throw e
}
catch (_: Throwable) { }
finally {
if (connection is HttpURLConnection) {
connection.disconnect()
}
}
}
/**
* Opens a connection to a given location using configured http proxy settings.
* @see [JdkProxyProvider]
*/
@Throws(IOException::class)
@JvmStatic
fun openConnection(location: String): URLConnection {
val url = URL(location)
var urlConnection: URLConnection? = null
val proxies = JdkProxyProvider.getInstance().proxySelector.select(VfsUtil.toUri(url.toString()))
if (proxies.isEmpty()) {
urlConnection = url.openConnection()
}
else {
var exception: IOException? = null
for (proxy in proxies) {
try {
urlConnection = url.openConnection(proxy)
break
}
catch (e: IOException) {
// continue iteration
exception = e
}
}
if (urlConnection == null && exception != null) {
throw exception
}
}
check(urlConnection != null)
urlConnection.setReadTimeout(HttpRequests.READ_TIMEOUT)
urlConnection.setConnectTimeout(HttpRequests.CONNECTION_TIMEOUT)
return urlConnection
}
/**
* Opens HTTP connection to a given location using configured http proxy settings.
* @param location url to connect to
* @throws IOException in case of any I/O troubles or if created connection isn't an instance of HttpURLConnection.
* @see [JdkProxyProvider]
*/
@Throws(IOException::class)
@JvmStatic
fun openHttpConnection(location: String): HttpURLConnection {
val urlConnection = openConnection(location)
if (urlConnection is HttpURLConnection) {
return urlConnection
}
throw IOException("Expected ${HttpURLConnection::class}, but got ${urlConnection::class}")
}
}

View File

@@ -4,28 +4,56 @@ package com.intellij.util.net;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurableBase;
import com.intellij.openapi.options.ConfigurableUi;
import com.intellij.util.net.internal.HttpConfigurableMigrationUtilsKt;
import com.intellij.util.net.internal.ProxyMigrationService;
import org.jetbrains.annotations.NotNull;
public class HttpProxyConfigurable extends ConfigurableBase<HttpProxySettingsUi, HttpConfigurable> implements Configurable.NoMargin {
private final HttpConfigurable settings;
public class HttpProxyConfigurable extends ConfigurableBase<ConfigurableUi<ProxySettings>, ProxySettings> implements Configurable.NoMargin {
private final ProxySettings proxySettings;
private final ProxyCredentialStore credentialStore;
private final DisabledProxyAuthPromptsManager disabledProxyAuthPromptsManager;
public HttpProxyConfigurable() {
this(HttpConfigurable.getInstance());
this(ProxySettings.getInstance());
}
public HttpProxyConfigurable(@NotNull HttpConfigurable settings) {
public HttpProxyConfigurable(@NotNull ProxySettings proxySettings) {
this(proxySettings, ProxyCredentialStore.getInstance(), DisabledProxyAuthPromptsManager.getInstance());
}
HttpProxyConfigurable(
@NotNull ProxySettings proxySettings,
@NotNull ProxyCredentialStore credentialStore,
@NotNull DisabledProxyAuthPromptsManager disabledProxyAuthPromptsManager
) {
super("http.proxy", IdeBundle.message("http.proxy.configurable"), "http.proxy");
this.settings = settings;
this.proxySettings = proxySettings;
this.credentialStore = credentialStore;
this.disabledProxyAuthPromptsManager = disabledProxyAuthPromptsManager;
}
/**
* @deprecated use {@link HttpProxyConfigurable#HttpProxyConfigurable(ProxySettings)}
*/
@Deprecated
@SuppressWarnings("removal")
public HttpProxyConfigurable(@NotNull HttpConfigurable httpConfigurable) {
this(
HttpConfigurableMigrationUtilsKt.asProxySettings(() -> httpConfigurable),
HttpConfigurableMigrationUtilsKt.asProxyCredentialStore(() -> httpConfigurable),
HttpConfigurableMigrationUtilsKt.asDisabledProxyAuthPromptsManager(() -> httpConfigurable)
);
}
@Override
protected @NotNull HttpConfigurable getSettings() {
return settings;
protected @NotNull ProxySettings getSettings() {
return proxySettings;
}
@Override
protected HttpProxySettingsUi createUi() {
return new HttpProxySettingsUi(settings);
protected ConfigurableUi<ProxySettings> createUi() {
return ProxyMigrationService.getInstance().createProxySettingsUi(proxySettings, credentialStore, disabledProxyAuthPromptsManager);
}
}

View File

@@ -1,283 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.util.net.HttpProxySettingsUi">
<grid id="111c4" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="13" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="1" y="44" width="733" height="764"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="fe3b6" layout-manager="GridLayoutManager" row-count="8" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="8" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="7e62" class="com.intellij.ui.PortField" binding="myProxyPortTextField">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="fc937" class="javax.swing.JTextField" binding="myProxyLoginTextField">
<constraints>
<grid row="5" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="1acb8" class="javax.swing.JPasswordField" binding="myProxyPasswordTextField">
<constraints>
<grid row="6" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="cc77" class="javax.swing.JLabel" binding="myProxyExceptionsLabel">
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.manual.exclude.example"/>
</properties>
</component>
<component id="2008d" class="javax.swing.JTextField" binding="myProxyHostTextField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="51cae" class="javax.swing.JCheckBox" binding="myRememberProxyPasswordCheckBox">
<constraints>
<grid row="7" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="auth.remember.cb"/>
</properties>
</component>
<component id="2a6e8" class="javax.swing.JLabel" binding="myHostNameLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="2008d"/>
<text resource-bundle="messages/UIBundle" key="proxy.manual.host"/>
</properties>
</component>
<component id="a2263" class="javax.swing.JLabel" binding="myPortNumberLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="7e62"/>
<text resource-bundle="messages/UIBundle" key="proxy.manual.port"/>
</properties>
</component>
<component id="29784" class="javax.swing.JCheckBox" binding="myProxyAuthCheckBox">
<constraints>
<grid row="4" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
</constraints>
<properties>
<margin top="2" left="1" bottom="2" right="2"/>
<selected value="false"/>
<text resource-bundle="messages/UIBundle" key="proxy.manual.auth"/>
</properties>
</component>
<component id="e8502" class="javax.swing.JLabel" binding="myProxyLoginLabel">
<constraints>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="4" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="fc937"/>
<text resource-bundle="messages/UIBundle" key="auth.login.label"/>
</properties>
</component>
<component id="7ced" class="javax.swing.JLabel" binding="myProxyPasswordLabel">
<constraints>
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="4" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="1acb8"/>
<text resource-bundle="messages/UIBundle" key="auth.password.label"/>
</properties>
</component>
<component id="8655a" class="com.intellij.ui.RawCommandLineEditor" binding="myProxyExceptions" custom-create="true">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<dialogCaption resource-bundle="messages/IdeBundle" key="dialog.title.http.proxy.exceptions"/>
</properties>
</component>
<component id="ad79c" class="javax.swing.JLabel" binding="myNoProxyForLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.manual.exclude"/>
</properties>
</component>
</children>
</grid>
<component id="862df" class="com.intellij.ui.components.JBRadioButton" binding="myAutoDetectProxyRb" default-binding="true">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.pac.rb"/>
<toolTipText resource-bundle="messages/UIBundle" key="proxy.pac.rb.tt"/>
</properties>
</component>
<component id="9e485" class="com.intellij.ui.components.JBRadioButton" binding="myUseHTTPProxyRb" default-binding="true">
<constraints>
<grid row="6" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.manual.rb"/>
</properties>
</component>
<component id="b027d" class="com.intellij.ui.components.JBRadioButton" binding="myNoProxyRb">
<constraints>
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.direct.rb"/>
</properties>
</component>
<grid id="1bb9b" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="7" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="5a9f8" class="com.intellij.ui.components.JBRadioButton" binding="myHTTP">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="0" indent="2" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.manual.type.http"/>
</properties>
</component>
<component id="2538c" class="com.intellij.ui.components.JBRadioButton" binding="mySocks">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="0" indent="2" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.manual.type.socks"/>
</properties>
</component>
</children>
</grid>
<component id="de36" class="javax.swing.JButton" binding="myCheckButton">
<constraints>
<grid row="11" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.test.button"/>
</properties>
</component>
<component id="98030" class="javax.swing.JLabel" binding="myOtherWarning">
<constraints>
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="2" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
<maximum-size width="800" height="-1"/>
</grid>
</constraints>
<properties>
<verticalTextPosition value="1"/>
</properties>
</component>
<grid id="fee34" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="4" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="8f836" class="javax.swing.JCheckBox" binding="myPacUrlCheckBox">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.pac.url.label"/>
</properties>
</component>
<component id="77d11" class="javax.swing.JTextField" binding="myPacUrlTextField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="9" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
</children>
</grid>
<component id="c299" class="javax.swing.JButton" binding="myClearPasswordsButton" default-binding="true">
<constraints>
<grid row="5" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.pac.pw.clear.button"/>
</properties>
</component>
<component id="c8100" class="com.intellij.ui.components.ActionLink" binding="mySystemProxySettingsLink">
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.system.proxy.settings"/>
</properties>
</component>
<component id="336ff" class="javax.swing.JLabel" binding="myTunnelingAuthSchemeDisabledWarning">
<constraints>
<grid row="9" column="0" row-span="1" col-span="2" vsize-policy="2" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false">
<maximum-size width="800" height="-1"/>
</grid>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.tunneling.disabled.warning"/>
<verticalAlignment value="0"/>
<verticalTextPosition value="1"/>
</properties>
</component>
<vspacer id="5270d">
<constraints>
<grid row="12" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="ffae6" class="javax.swing.JLabel" binding="myErrorLabel">
<constraints>
<grid row="10" column="0" row-span="1" col-span="2" vsize-policy="2" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
<maximum-size width="800" height="-1"/>
</grid>
</constraints>
<properties>
<text value="Label" noi18n="true"/>
</properties>
</component>
<component id="4b9f9" class="javax.swing.JLabel" binding="mySystemProxyDefined">
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="2" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
<maximum-size width="800" height="-1"/>
</grid>
</constraints>
<properties>
<text resource-bundle="messages/UIBundle" key="proxy.system.label"/>
<verticalAlignment value="0"/>
<verticalTextPosition value="1"/>
</properties>
</component>
</children>
</grid>
</form>

View File

@@ -1,348 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net;
import com.intellij.ide.IdeBundle;
import com.intellij.notification.NotificationAction;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.ConfigurableUi;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.PortField;
import com.intellij.ui.RawCommandLineEditor;
import com.intellij.ui.components.ActionLink;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.jcef.JBCefApp;
import com.intellij.util.io.HttpRequests;
import com.intellij.util.proxy.CommonProxy;
import com.intellij.util.proxy.JavaProxyProperty;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER;
import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS;
class HttpProxySettingsUi implements ConfigurableUi<HttpConfigurable> {
private JPanel myMainPanel;
private JTextField myProxyLoginTextField;
private JPasswordField myProxyPasswordTextField;
private JCheckBox myProxyAuthCheckBox;
private PortField myProxyPortTextField;
private JTextField myProxyHostTextField;
private JCheckBox myRememberProxyPasswordCheckBox;
private JLabel myProxyLoginLabel;
private JLabel myProxyPasswordLabel;
private JLabel myHostNameLabel;
private JLabel myPortNumberLabel;
private JBRadioButton myAutoDetectProxyRb;
private JBRadioButton myUseHTTPProxyRb;
private JLabel mySystemProxyDefined;
private JBRadioButton myNoProxyRb;
private JBRadioButton myHTTP;
private JBRadioButton mySocks;
private JButton myClearPasswordsButton;
private JLabel myErrorLabel;
private JButton myCheckButton;
private JLabel myOtherWarning;
private JLabel myProxyExceptionsLabel;
private RawCommandLineEditor myProxyExceptions;
private JLabel myNoProxyForLabel;
private JCheckBox myPacUrlCheckBox;
private JTextField myPacUrlTextField;
private ActionLink mySystemProxySettingsLink;
private JLabel myTunnelingAuthSchemeDisabledWarning;
@Override
public boolean isModified(@NotNull HttpConfigurable settings) {
return !Comparing.strEqual(myProxyExceptions.getText().trim(), settings.PROXY_EXCEPTIONS) ||
settings.USE_PROXY_PAC != myAutoDetectProxyRb.isSelected() ||
settings.USE_PAC_URL != myPacUrlCheckBox.isSelected() ||
!Comparing.strEqual(settings.PAC_URL, myPacUrlTextField.getText()) ||
settings.USE_HTTP_PROXY != myUseHTTPProxyRb.isSelected() ||
settings.PROXY_AUTHENTICATION != myProxyAuthCheckBox.isSelected() ||
settings.KEEP_PROXY_PASSWORD != myRememberProxyPasswordCheckBox.isSelected() ||
settings.PROXY_TYPE_IS_SOCKS != mySocks.isSelected() ||
!Comparing.strEqual(settings.getProxyLogin(), myProxyLoginTextField.getText()) ||
!Comparing.strEqual(settings.getPlainProxyPassword(), new String(myProxyPasswordTextField.getPassword())) ||
settings.PROXY_PORT != myProxyPortTextField.getNumber() ||
!Comparing.strEqual(settings.PROXY_HOST, myProxyHostTextField.getText());
}
HttpProxySettingsUi(final @NotNull HttpConfigurable settings) {
ButtonGroup group = new ButtonGroup();
group.add(myUseHTTPProxyRb);
group.add(myAutoDetectProxyRb);
group.add(myNoProxyRb);
myNoProxyRb.setSelected(true);
ButtonGroup proxyTypeGroup = new ButtonGroup();
proxyTypeGroup.add(myHTTP);
proxyTypeGroup.add(mySocks);
myHTTP.setSelected(true);
Boolean property = Boolean.getBoolean(JavaProxyProperty.USE_SYSTEM_PROXY);
mySystemProxyDefined.setVisible(Boolean.TRUE.equals(property));
mySystemProxyDefined.setIcon(Messages.getWarningIcon());
myProxyAuthCheckBox.addActionListener(e -> enableProxyAuthentication(myProxyAuthCheckBox.isSelected()));
myPacUrlCheckBox.addActionListener(e -> myPacUrlTextField.setEnabled(myPacUrlCheckBox.isSelected()));
ActionListener listener = e -> enableProxy(myUseHTTPProxyRb.isSelected());
myUseHTTPProxyRb.addActionListener(listener);
myAutoDetectProxyRb.addActionListener(listener);
myNoProxyRb.addActionListener(listener);
myClearPasswordsButton.addActionListener(e -> {
settings.clearGenericPasswords();
//noinspection DialogTitleCapitalization
Messages.showMessageDialog(myMainPanel, IdeBundle.message("message.text.proxy.passwords.were.cleared"),
IdeBundle.message("dialog.title.auto.detected.proxy"), Messages.getInformationIcon());
});
mySystemProxySettingsLink.setExternalLinkIcon();
mySystemProxySettingsLink.setAutoHideOnDisable(true);
mySystemProxySettingsLink.setEnabled(SystemProxySettings.getInstance().isProxySettingsOpenSupported());
mySystemProxySettingsLink.addActionListener((event) -> {
try {
SystemProxySettings.getInstance().openProxySettings();
} catch (Exception e) {
Logger.getInstance(HttpProxySettingsUi.class).error("failed to open system proxy settings", e);
}
});
myTunnelingAuthSchemeDisabledWarning.setIcon(Messages.getWarningIcon());
myTunnelingAuthSchemeDisabledWarning.setVisible(JavaNetworkUtils.isTunnelingAuthSchemeDisabled(JavaNetworkUtils.BASIC_AUTH_SCHEME));
myErrorLabel.setIcon(Messages.getErrorIcon());
configureCheckButton();
}
private void configureCheckButton() {
if (HttpConfigurable.getInstance() == null) {
myCheckButton.setVisible(false);
return;
}
myCheckButton.addActionListener(event -> {
String error = isValid();
if (error != null) {
Messages.showErrorDialog(myMainPanel, error);
return;
}
final HttpConfigurable settings = HttpConfigurable.getInstance();
final String title = IdeBundle.message("dialog.title.check.proxy.settings");
final String url =
Messages.showInputDialog(myMainPanel,
IdeBundle.message("message.text.enter.url.to.check.connection"),
title, Messages.getQuestionIcon(), settings.CHECK_CONNECTION_URL, null);
if (StringUtil.isEmptyOrSpaces(url)) {
return;
}
settings.CHECK_CONNECTION_URL = url;
try {
apply(settings);
}
catch (ConfigurationException e) {
return;
}
final AtomicReference<IOException> exceptionReference = new AtomicReference<>();
ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
try {
HttpRequests.request(url).readTimeout(3 * 1000).tryConnect();
}
catch (IOException e) {
exceptionReference.set(e);
}
}, IdeBundle.message("progress.title.check.connection"), true, null);
reset(settings); // since password might have been set
final IOException exception = exceptionReference.get();
if (exception == null) {
Messages.showMessageDialog(myMainPanel, IdeBundle.message("message.connection.successful"), title, Messages.getInformationIcon());
}
else {
final String message = exception.getMessage();
if (settings.USE_HTTP_PROXY) {
settings.LAST_ERROR = message;
}
Messages.showErrorDialog(myMainPanel, errorText(message));
}
});
}
@Override
public void reset(@NotNull HttpConfigurable settings) {
myNoProxyRb.setSelected(true); // default
myAutoDetectProxyRb.setSelected(settings.USE_PROXY_PAC);
myPacUrlCheckBox.setSelected(settings.USE_PAC_URL);
myPacUrlTextField.setText(settings.PAC_URL);
myUseHTTPProxyRb.setSelected(settings.USE_HTTP_PROXY);
myProxyAuthCheckBox.setSelected(settings.PROXY_AUTHENTICATION);
enableProxy(settings.USE_HTTP_PROXY);
myProxyLoginTextField.setText(settings.getProxyLogin());
myProxyPasswordTextField.setText(settings.getPlainProxyPassword());
myProxyPortTextField.setNumber(settings.PROXY_PORT);
myProxyHostTextField.setText(settings.PROXY_HOST);
myProxyExceptions.setText(StringUtil.notNullize(settings.PROXY_EXCEPTIONS));
myRememberProxyPasswordCheckBox.setSelected(settings.KEEP_PROXY_PASSWORD);
mySocks.setSelected(settings.PROXY_TYPE_IS_SOCKS);
myHTTP.setSelected(!settings.PROXY_TYPE_IS_SOCKS);
boolean showError = !StringUtil.isEmptyOrSpaces(settings.LAST_ERROR);
myErrorLabel.setVisible(showError);
myErrorLabel.setText(showError ? errorText(settings.LAST_ERROR) : null);
final String oldStyleText = CommonProxy.getMessageFromProps(CommonProxy.getOldStyleProperties());
myOtherWarning.setVisible(oldStyleText != null);
if (oldStyleText != null) {
myOtherWarning.setText(oldStyleText);
myOtherWarning.setIcon(Messages.getWarningIcon());
}
}
private void createUIComponents() {
myProxyExceptions = new RawCommandLineEditor(text -> {
List<String> result = new ArrayList<>();
for (String token : text.split(",")) {
String trimmedToken = token.trim();
if (!trimmedToken.isEmpty()) {
result.add(trimmedToken);
}
}
return result;
}, strings -> StringUtil.join(strings, ", "));
}
private static @NotNull @NlsContexts.DialogMessage String errorText(@NotNull String s) {
return IdeBundle.message("dialog.message.problem.with.connection", StringUtil.escapeXmlEntities(s));
}
private @Nullable @NlsContexts.DialogMessage String isValid() {
if (myUseHTTPProxyRb.isSelected()) {
String host = getText(myProxyHostTextField);
if (host == null) {
return IdeBundle.message("dialog.message.host.name.empty");
}
switch (NetUtils.isValidHost(host)) {
case INVALID -> {
return IdeBundle.message("dialog.message.invalid.host.value");
}
case VALID -> {
return null;
}
case VALID_PROXY -> {
}
}
if (myProxyAuthCheckBox.isSelected()) {
if (StringUtil.isEmptyOrSpaces(myProxyLoginTextField.getText())) {
return IdeBundle.message("dialog.message.login.empty");
}
if (myProxyPasswordTextField.getPassword().length == 0) {
return IdeBundle.message("dialog.message.password.empty");
}
}
}
return null;
}
@Override
public void apply(@NotNull HttpConfigurable settings) throws ConfigurationException {
String error = isValid();
if (error != null) {
throw new ConfigurationException(error);
}
boolean modified = isModified(settings);
if (modified) {
settings.AUTHENTICATION_CANCELLED = false;
}
settings.USE_PROXY_PAC = myAutoDetectProxyRb.isSelected();
settings.USE_PAC_URL = myPacUrlCheckBox.isSelected();
settings.PAC_URL = getText(myPacUrlTextField);
settings.USE_HTTP_PROXY = myUseHTTPProxyRb.isSelected();
settings.PROXY_TYPE_IS_SOCKS = mySocks.isSelected();
settings.PROXY_AUTHENTICATION = myProxyAuthCheckBox.isSelected();
settings.KEEP_PROXY_PASSWORD = myRememberProxyPasswordCheckBox.isSelected();
settings.setProxyLogin(getText(myProxyLoginTextField));
settings.setPlainProxyPassword(new String(myProxyPasswordTextField.getPassword()));
settings.PROXY_EXCEPTIONS = StringUtil.nullize(myProxyExceptions.getText(), true);
settings.PROXY_PORT = myProxyPortTextField.getNumber();
settings.PROXY_HOST = getText(myProxyHostTextField);
if (modified && JBCefApp.isStarted()) {
JBCefApp.getNotificationGroup()
.createNotification(IdeBundle.message("notification.title.jcef.proxyChanged"), IdeBundle.message("notification.content.jcef.applySettings"), NotificationType.WARNING)
.addAction(NotificationAction.createSimple(IdeBundle.message("action.jcef.restart"), () -> ApplicationManager.getApplication().restart()))
.notify(null);
}
}
private static @Nullable String getText(@NotNull JTextField field) {
return StringUtil.nullize(field.getText(), true);
}
private void enableProxy(boolean enabled) {
myHostNameLabel.setEnabled(enabled);
myPortNumberLabel.setEnabled(enabled);
myProxyHostTextField.setEnabled(enabled);
myProxyPortTextField.setEnabled(enabled);
mySocks.setEnabled(enabled);
myHTTP.setEnabled(enabled);
myProxyExceptions.setEnabled(enabled);
myProxyExceptionsLabel.setEnabled(enabled);
myNoProxyForLabel.setEnabled(enabled);
myProxyAuthCheckBox.setEnabled(enabled);
enableProxyAuthentication(enabled && myProxyAuthCheckBox.isSelected());
final boolean autoDetectProxy = myAutoDetectProxyRb.isSelected();
myPacUrlCheckBox.setEnabled(autoDetectProxy);
myClearPasswordsButton.setEnabled(autoDetectProxy);
myPacUrlTextField.setEnabled(autoDetectProxy && myPacUrlCheckBox.isSelected());
}
private void enableProxyAuthentication(boolean enabled) {
myProxyLoginLabel.setEnabled(enabled);
myProxyLoginTextField.setEnabled(enabled);
myProxyPasswordLabel.setEnabled(enabled);
myProxyPasswordTextField.setEnabled(enabled);
myRememberProxyPasswordCheckBox.setEnabled(enabled);
}
@Override
public @NotNull JComponent getComponent() {
myMainPanel.setBorder(JBUI.Borders.empty(11, 16, 16, 16));
JBScrollPane scrollPane = new JBScrollPane(myMainPanel, VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setBorder(null);
return scrollPane;
}
}

View File

@@ -0,0 +1,39 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.credentialStore.Credentials
import com.intellij.credentialStore.isFulfilled
import java.net.Authenticator
import java.net.PasswordAuthentication
class IdeProxyAuthenticator(
private val proxyAuth: ProxyAuthentication,
): Authenticator() {
private val seenLocations = HashSet<String>()
@Synchronized
override fun getPasswordAuthentication(): PasswordAuthentication? {
// java.base/java/net/SocksSocketImpl.java:176 : there is SOCKS proxy auth, but without RequestorType passing
val isProxy = RequestorType.PROXY == requestorType || "SOCKS authentication" == requestingPrompt
if (!isProxy) {
return null
}
val host = getHostNameReliably(requestingHost, requestingSite, requestingURL) ?: ""
val port = requestingPort
val notSeenYet = seenLocations.add(locationKey(host, port))
if (notSeenYet) {
return proxyAuth.getOrPromptAuthentication(requestingPrompt, host, port)?.toPasswordAuthentication()
}
return proxyAuth.getPromptedAuthentication(requestingPrompt, host, port)?.toPasswordAuthentication()
}
override fun toString(): String = "Auth{type=$requestorType, prompt=$requestingPrompt, host=$requestingHost, " +
"port=$requestingPort, site=$requestingSite, url=$requestingURL}"
}
private fun locationKey(host: String, port: Int) = "$host:$port"
private fun Credentials.toPasswordAuthentication(): PasswordAuthentication? {
if (!isFulfilled()) return null
return PasswordAuthentication(userName!!, password!!.toCharArray())
}

View File

@@ -0,0 +1,142 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.openapi.diagnostic.debug
import com.intellij.openapi.diagnostic.logger
import com.intellij.util.SystemProperties
import com.intellij.util.net.NetUtils.isLocalhost
import org.jetbrains.annotations.ApiStatus
import java.io.IOException
import java.net.Proxy
import java.net.ProxySelector
import java.net.SocketAddress
import java.net.URI
import java.net.URL
import java.util.Collections
import java.util.concurrent.atomic.AtomicReference
import java.util.function.Predicate
class IdeProxySelector(
private val configurationProvider: ProxyConfigurationProvider,
) : ProxySelector() {
private val autoProxyResult = AtomicReference<AutoProxyHolder?>()
private val exceptionsMatcher = AtomicReference<ExceptionsMatcherHolder?>()
override fun select(uri: URI): List<Proxy> {
logger.debug { "$uri: select" }
if (!("http" == uri.scheme || "https" == uri.scheme)) {
logger.debug { "$uri: no proxy, not http/https scheme: ${uri.scheme}" }
return NO_PROXY_LIST
}
if (isLocalhost(uri.host ?: "")) {
logger.debug { "$uri: no proxy, localhost" }
return NO_PROXY_LIST
}
when (val conf = configurationProvider.getProxyConfiguration()) {
is ProxyConfiguration.DirectProxy -> {
logger.debug { "$uri: no proxy, DIRECT configuration" }
return NO_PROXY_LIST
}
is ProxyConfiguration.AutoDetectProxy, is ProxyConfiguration.ProxyAutoConfiguration -> {
return selectUsingPac((conf as? ProxyConfiguration.ProxyAutoConfiguration)?.pacUrl, uri)
}
is ProxyConfiguration.StaticProxyConfiguration -> {
if (getExceptionsMatcher(conf.exceptions).test(uri.host)) {
logger.debug { "$uri: no proxy, uri is in exception list" }
return NO_PROXY_LIST
}
val proxy = conf.asJavaProxy()
logger.debug { "$uri: proxy $proxy" }
return Collections.singletonList(proxy)
}
else -> {
logger.warn("$uri: no proxy, unknown proxy configuration: $conf")
return NO_PROXY_LIST
}
}
}
private fun getExceptionsMatcher(exceptions: String): Predicate<String> {
val cached = exceptionsMatcher.get()
if (cached?.exceptions == exceptions) {
return cached.matcher
}
val matcher = ProxyConfiguration.buildProxyExceptionsMatcher(exceptions)
exceptionsMatcher.set(ExceptionsMatcherHolder(exceptions, matcher))
return matcher
}
private fun selectUsingPac(pacUrl: URL?, uri: URI): List<Proxy> {
// https://youtrack.jetbrains.com/issue/IDEA-262173
val oldDocumentBuilderFactory = System.setProperty(DOCUMENT_BUILDER_FACTORY_KEY, "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl")
try {
val selector = getAutoProxySelector(pacUrl)
try {
val result = selector.select(uri)
logger.debug { "$uri: pac/autodetect proxy select result: $result" }
return result
} catch (_: StackOverflowError) {
logger.warn("$uri: no proxy, too large PAC script (JRE-247)")
return NO_PROXY_LIST
}
}
catch (e: Throwable) {
logger.error("$uri: no proxy, failed to select using PAC/autodetect", e)
return NO_PROXY_LIST
}
finally {
SystemProperties.setProperty(DOCUMENT_BUILDER_FACTORY_KEY, oldDocumentBuilderFactory)
}
}
private fun getAutoProxySelector(pacUrl: URL?): ProxySelector {
val autoProxy = autoProxyResult.get()
if (autoProxy != null && autoProxy.pacUrl?.toString() == pacUrl?.toString()) return autoProxy.selector
synchronized(this) {
val autoProxy = autoProxyResult.get()
if (autoProxy != null && autoProxy.pacUrl?.toString() == pacUrl?.toString()) return autoProxy.selector
val searchStartMs = System.currentTimeMillis()
val detectedSelector = NetUtils.getProxySelector(pacUrl?.toString())
?: DirectSelector.also { // just in case
logger.warn("failed to configure proxy by pacUrl=$pacUrl (null if autodetect)")
}
if (pacUrl == null) {
proxyAutodetectDurationMs = System.currentTimeMillis() - searchStartMs
}
autoProxyResult.set(AutoProxyHolder(pacUrl, detectedSelector))
return detectedSelector
}
}
override fun connectFailed(uri: URI?, sa: SocketAddress?, ioe: IOException?) {
// MAYBE adjust the selection result; in previous implementation (IdeaWideProxySelector) it was effectively no-op
}
companion object {
private val logger = logger<IdeProxySelector>()
private const val DOCUMENT_BUILDER_FACTORY_KEY = "javax.xml.parsers.DocumentBuilderFactory"
// holds either autodetected proxy or a pac proxy, pac url is null if autodetect is used
private data class AutoProxyHolder(val pacUrl: URL?, val selector: ProxySelector)
private data class ExceptionsMatcherHolder(val exceptions: String, val matcher: Predicate<String>)
@Volatile
private var proxyAutodetectDurationMs: Long = -1L
/**
* @return duration that proxy auto-detection took (ms), or -1 in case automatic proxy detection wasn't triggered
*/
@ApiStatus.Internal
fun getProxyAutoDetectDurationMs(): Long = proxyAutodetectDurationMs
private object DirectSelector : ProxySelector() {
override fun select(uri: URI?): List<Proxy?>? = NO_PROXY_LIST
override fun connectFailed(uri: URI?, sa: SocketAddress?, ioe: IOException?) = Unit
}
}
}

View File

@@ -12,6 +12,11 @@ import org.jetbrains.annotations.NotNull;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
/**
* @deprecated use {@link JdkProxyProvider#getAuthenticator()} or {@link IdeProxyAuthenticator}
*/
@SuppressWarnings("removal")
@Deprecated
public final class IdeaWideAuthenticator extends NonStaticAuthenticator {
private static final Logger LOG = Logger.getInstance(IdeaWideAuthenticator.class);
private final HttpConfigurable myHttpConfigurable;
@@ -21,10 +26,12 @@ public final class IdeaWideAuthenticator extends NonStaticAuthenticator {
}
@Override
public PasswordAuthentication getPasswordAuthentication() {
public synchronized PasswordAuthentication getPasswordAuthentication() {
final String host = CommonProxy.getHostNameReliably(getRequestingHost(), getRequestingSite(), getRequestingURL());
// java.base/java/net/SocksSocketImpl.java:176 : there is SOCKS proxy auth, but without RequestorType passing
final boolean isProxy = Authenticator.RequestorType.PROXY.equals(getRequestorType()) || "SOCKS authentication".equals(getRequestingPrompt());
// FIXME prefix for server auth is never used since 7ea74ea400b03cf92d1621ea0e8aa1d386cb886a.
// This means that this class manages strictly proxy authentication since 2013
final String prefix = isProxy ? IdeBundle.message("prompt.proxy.authentication") : IdeBundle.message("prompt.server.authentication");
Application application = ApplicationManager.getApplication();
if (isProxy) {
@@ -50,6 +57,7 @@ public final class IdeaWideAuthenticator extends NonStaticAuthenticator {
}
}
// FIXME dead logic, all ends up with return null
// do not try to show any dialogs if application is exiting
if (application == null || application.isDisposed()) {
return null;

View File

@@ -16,6 +16,11 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
/**
* @deprecated use {@link JdkProxyProvider#getProxySelector()} or {@link IdeProxySelector}
*/
@SuppressWarnings("removal")
@Deprecated
public final class IdeaWideProxySelector extends ProxySelector {
private static final Logger LOG = Logger.getInstance(IdeaWideProxySelector.class);
private static final String DOCUMENT_BUILDER_FACTORY_KEY = "javax.xml.parsers.DocumentBuilderFactory";
@@ -116,6 +121,7 @@ public final class IdeaWideProxySelector extends ProxySelector {
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
if (myHttpConfigurable.USE_PROXY_PAC) {
// FIXME bug here! scheme is never used in proxy code, uri.getScheme() -> null
myHttpConfigurable.removeGeneric(new CommonProxy.HostInfo(uri.getScheme(), uri.getHost(), uri.getPort()));
LOG.debug("generic proxy credentials (if were saved) removed");
return;

View File

@@ -0,0 +1,47 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.openapi.application.AccessToken
import org.jetbrains.annotations.ApiStatus
import java.net.Authenticator
import java.net.ProxySelector
/**
* Provides functionality to customize the behavior of the default [JdkProxyProvider]. Use of this class should be avoided if possible.
*
* One should not rely on any ordering guarantees of [customizeProxySelector] or [customizeAuthenticator].
*
* Applied customizations take effect before the original [JdkProxyProvider.proxySelector] and [JdkProxyProvider.authenticator].
*
* @see [ProxyAuthentication]
*/
interface JdkProxyCustomizer {
companion object {
@JvmStatic
fun getInstance(): JdkProxyCustomizer = CustomizedPlatformJdkProxyProvider
}
/**
* Returns the main [ProxySelector] from [JdkProxyProvider] without customizations applied, i.e., the one that is configured solely by user settings.
* Shouldn't be used unless absolutely necessary.
*/
@get:ApiStatus.Experimental
val originalProxySelector: ProxySelector
/**
* Returns the main [Authenticator] from [JdkProxyProvider] without customizations applied, i.e., the one that is configured solely by user settings.
* Shouldn't be used unless absolutely necessary.
*/
@get:ApiStatus.Experimental
val originalAuthenticator: Authenticator
/**
* @param proxySelector [ProxySelector.select] should return an _empty_ list in case this [proxySelector] is not applicable.
*/
fun customizeProxySelector(proxySelector: ProxySelector): AccessToken
/**
* @param authenticator [Authenticator.getPasswordAuthentication] should return non-null credentials if it is applicable.
*/
fun customizeAuthenticator(authenticator: Authenticator): AccessToken
}

View File

@@ -0,0 +1,86 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.openapi.application.ApplicationNamesInfo
import com.intellij.openapi.diagnostic.logger
import com.intellij.util.proxy.CommonProxyCompatibility
import java.net.Authenticator
import java.net.ProxySelector
sealed interface JdkProxyProvider {
val proxySelector: ProxySelector
val authenticator: Authenticator
companion object {
/**
* [JdkProxyProvider.getInstance] acts as the main proxy provider in the application and is configured by user [ProxySettings].
* It registers its [proxySelector] and [authenticator] as the default ([ProxySelector.setDefault] and [Authenticator.setDefault]).
* This setting must not be changed. To ensure this contract, use [JdkProxyProvider.ensureDefault].
*
* If customization of the default [JdkProxyProvider] is required, prefer to implement your own [ProxySelector] and [Authenticator]
* for your subsystem, delegating the base functionality to [ProxyAuthentication.getInstance] or [JdkProxyProvider.getInstance].
* If this is not possible or too hard to implement, use [JdkProxyCustomizer].
*
* @see [IdeProxySelector]
* @see [ProxyAuthentication]
*/
@JvmStatic
fun getInstance(): JdkProxyProvider = CustomizedPlatformJdkProxyProvider
/**
* This utility ensures that [ProxySelector] and [Authenticator] from [JdkProxyProvider.getInstance] are used by default by the
* java network stack.
*/
@JvmStatic
fun ensureDefault(): Unit = ensureDefaultProxyProviderImpl()
}
}
private class OverrideDefaultJdkProxy {
// TODO: maybe change this from service preloading to ApplicationLoadListener,
// but after the application is registered and we can access services
init {
CommonProxyCompatibility.register(
JdkProxyProvider.getInstance().proxySelector,
JdkProxyProvider.getInstance().authenticator,
JdkProxyCustomizer.getInstance()::customizeProxySelector,
JdkProxyCustomizer.getInstance()::customizeAuthenticator
)
JdkProxyProvider.ensureDefault()
}
}
@Synchronized
private fun ensureDefaultProxyProviderImpl() {
val provider = JdkProxyProvider.getInstance()
val proxySelector = provider.proxySelector
val authenticator = provider.authenticator
if (!javaProxyInstallationFlag) {
ProxySelector.setDefault(proxySelector)
Authenticator.setDefault(authenticator)
javaProxyInstallationFlag = true
return
}
val defaultProxySelector = ProxySelector.getDefault()
if (defaultProxySelector !== proxySelector) {
logger<ProxySelector>().error("""
ProxySelector.setDefault() was changed to [$defaultProxySelector] - other than [$proxySelector].
This will make some ${ApplicationNamesInfo.getInstance().productName} network calls fail.
Instead, ProxyService.instance.proxySelector should be the default proxy selector.
""".trimIndent()
)
ProxySelector.setDefault(proxySelector)
}
val defaultAuthenticator = Authenticator.getDefault()
if (defaultAuthenticator !== authenticator) {
logger<ProxySelector>().error("""
Authenticator.setDefault() was changed to [$defaultAuthenticator] - other than [$authenticator].
This may make some ${ApplicationNamesInfo.getInstance().productName} network calls fail.
Instead, ProxyService.instance.authenticator should be used as a default proxy authenticator.
""".trimIndent()
)
Authenticator.setDefault(authenticator)
}
}
private var javaProxyInstallationFlag: Boolean = false

View File

@@ -0,0 +1,31 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
abstract class OverrideCapableProxySettings : ProxySettings, ProxyConfigurationProvider {
abstract val originalProxySettings: ProxySettings
abstract var isOverrideEnabled: Boolean
/**
* contract: if not null, then [ProxySettingsOverrideProvider.shouldUserSettingsBeOverriden] == true
*/
abstract val overrideProvider: ProxySettingsOverrideProvider?
final override fun getProxyConfiguration(): ProxyConfiguration {
if (isOverrideEnabled) {
val overrideConf = overrideProvider?.proxyConfigurationProvider?.getProxyConfiguration()
if (overrideConf != null) {
return overrideConf
}
}
return originalProxySettings.getProxyConfiguration()
}
final override fun setProxyConfiguration(proxyConfiguration: ProxyConfiguration) {
if (isOverrideEnabled && overrideProvider != null) return
originalProxySettings.setProxyConfiguration(proxyConfiguration)
}
}

View File

@@ -0,0 +1,10 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import java.net.Authenticator
import java.net.ProxySelector
internal object PlatformJdkProxyProvider {
val proxySelector: ProxySelector by lazy { IdeProxySelector(ProxySettings.getInstance().asConfigurationProvider()) }
val authenticator: Authenticator by lazy { IdeProxyAuthenticator(ProxyAuthentication.getInstance()) }
}

View File

@@ -0,0 +1,177 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.credentialStore.Credentials
import com.intellij.ide.IdeBundle
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.diagnostic.debug
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.popup.util.PopupUtil
import com.intellij.util.WaitForProgressToShow
import com.intellij.util.net.internal.asDisabledProxyAuthPromptsManager
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
/**
* [ProxyAuthentication] provides functionality for requesting authentication for proxy from the user.
*/
interface ProxyAuthentication {
companion object {
@JvmStatic
fun getInstance(): ProxyAuthentication = defaultPlatformProxyAuth
private val defaultPlatformProxyAuth by lazy {
PlatformProxyAuthentication(ProxyCredentialStore.getInstance(), DisabledProxyAuthPromptsManager.getInstance())
}
}
/**
* @return already known credentials if there are any, fallbacks to [getPromptedAuthentication] otherwise.
*/
fun getOrPromptAuthentication(prompt: @Nls String, host: String, port: Int): Credentials?
/**
* Always requests authentication from the user for a proxy located at the provided host and port.
* If the user has refused to do so before, returns null without asking them again.
*
* One may want to first use [getOrPromptAuthentication] to not ask the user for credentials if they are already known.
*
* TODO behaviour in headless mode
*
* @param prompt prompt from the authentication request to be shown to the user
* @return null if the user has refused to provide credentials
*/
fun getPromptedAuthentication(prompt: @Nls String, host: String, port: Int): Credentials?
/**
* Whether the user refused to provide credentials for the specified proxy
*/
fun isPromptedAuthenticationCancelled(host: String, port: Int): Boolean
/**
* Allows prompting the user for proxy authentication even if they refused to provide credentials before.
* @see isPromptedAuthenticationCancelled
*/
fun enablePromptedAuthentication(host: String, port: Int)
}
@ApiStatus.Internal
interface DisabledProxyAuthPromptsManager {
companion object {
@JvmStatic
fun getInstance(): DisabledProxyAuthPromptsManager = defaultPlatformDisabledPromptsManager
@Suppress("DEPRECATION", "removal")
private val defaultPlatformDisabledPromptsManager = (HttpConfigurable::getInstance).asDisabledProxyAuthPromptsManager()
}
/**
* Remembers that the user canceled the prompted authentication.
*/
fun disablePromptedAuthentication(host: String, port: Int)
/**
* Whether the user refused to provide credentials for the specified proxy
*/
fun isPromptedAuthenticationDisabled(host: String, port: Int): Boolean
/**
* Allows prompting the user for proxy authentication even if they refused to provide credentials before.
* @see isPromptedAuthenticationDisabled
*/
fun enablePromptedAuthentication(host: String, port: Int)
/**
* Allow prompting the user for authentication for all proxies.
* @see isPromptedAuthenticationDisabled
*/
fun enableAllPromptedAuthentications()
}
@ApiStatus.Internal
class PlatformProxyAuthentication(
private val credentialStore: ProxyCredentialStore,
private val disabledPromptsManager: DisabledProxyAuthPromptsManager
) : ProxyAuthentication {
override fun getOrPromptAuthentication(prompt: @Nls String, host: String, port: Int): Credentials? {
val knownCredentials = credentialStore.getCredentials(host, port)
if (knownCredentials != null) {
return knownCredentials
}
return getPromptedAuthentication(prompt, host, port)
}
override fun getPromptedAuthentication(prompt: String, host: String, port: Int): Credentials? {
val app = ApplicationManager.getApplication()
if (app == null || app.isDisposed) {
logger.debug { "prompted auth for $host:$port: null, application is not initialized yet/already disposed " }
return null
}
if (app.isUnitTestMode) {
logger.warn("prompted auth for $host:$port: can't prompt proxy authentication in tests")
return null
}
if (app.isHeadlessEnvironment) {
// TODO request from terminal if allowed by system property ? and maybe check EnvironmentService ?
logger.debug { "prompted auth for $host:$port: null, can't prompt in headless mode " }
return null
}
if (isPromptedAuthenticationCancelled(host, port)) {
logger.debug { "prompted auth for $host:$port: prompted auth was cancelled " }
return null
}
var result: Credentials? = null
val login: String = credentialStore.getCredentials(host, port)?.userName ?: ""
runAboveAll {
val dialog = AuthenticationDialog(
PopupUtil.getActiveComponent(),
IdeBundle.message("dialog.title.proxy.authentication", host),
IdeBundle.message("dialog.message.please.enter.credentials.for", prompt),
login,
"",
@Suppress("DEPRECATION", "removal") // fix after migration to PasswordSafe
HttpConfigurable.getInstance().KEEP_PROXY_PASSWORD
)
dialog.show()
if (dialog.exitCode == DialogWrapper.OK_EXIT_CODE) {
val panel = dialog.panel
val credentials = Credentials(panel.login, panel.password)
credentialStore.setCredentials(host, port, credentials, panel.isRememberPassword)
@Suppress("DEPRECATION", "removal") // fix after migration to PasswordSafe
HttpConfigurable.getInstance().KEEP_PROXY_PASSWORD = panel.isRememberPassword
result = credentials
}
else {
disabledPromptsManager.disablePromptedAuthentication(host, port)
}
}
logger.debug { "prompted auth for $host:$port: input=$result" }
return result
}
override fun isPromptedAuthenticationCancelled(host: String, port: Int): Boolean {
return disabledPromptsManager.isPromptedAuthenticationDisabled(host, port)
}
override fun enablePromptedAuthentication(host: String, port: Int) {
return disabledPromptsManager.enablePromptedAuthentication(host, port)
}
private companion object {
val logger = logger<PlatformProxyAuthentication>()
private fun runAboveAll(runnable: Runnable) {
val progressIndicator = ProgressManager.getInstance().getProgressIndicator()
if (progressIndicator != null && progressIndicator.isModal()) {
WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(runnable)
}
else {
ApplicationManager.getApplication().invokeAndWait(runnable, ModalityState.any())
}
}
}
}

View File

@@ -0,0 +1,110 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.net.ProxyConfiguration.ProxyAutoConfiguration
import com.intellij.util.net.ProxyConfiguration.StaticProxyConfiguration
import java.net.URL
import java.util.function.Predicate
import java.util.regex.Pattern
/**
* Represents types of proxies that can be configured by the user and which are supported by the IDE.
*
* If you need to know the exact type of [ProxyConfiguration], check the instance against [StaticProxyConfiguration], [ProxyAutoConfiguration], etc.
*
* To instantiate a [ProxyConfiguration], use one of [direct], [autodetect], [proxy], or [proxyAutoConfiguration].
*
* This class does not handle authentication or credentials.
*
* @see ProxyAuthentication
* @see ProxySettings
*/
sealed interface ProxyConfiguration {
companion object {
/**
* Use no proxy.
*/
@JvmStatic
val direct: DirectProxy get() = DirectProxyData
/**
* Automatically determine proxy settings using java system properties, OS settings or environment variables.
*/
@JvmStatic
val autodetect: AutoDetectProxy get() = AutoDetectProxyData
/**
* @param exceptions comma-delimited list of host globs which must not be proxied, e.g. `*.domain.com,192.168.*`
*/
@JvmStatic
fun proxy(protocol: ProxyProtocol, host: String, port: Int = protocol.defaultPort, exceptions: String = ""): StaticProxyConfiguration {
require(port in 0..65535) {
"port is invalid: $port"
}
require(NetUtils.isValidHost(host) != NetUtils.ValidHostInfo.INVALID) {
"host is invalid: $host"
}
return StaticProxyConfigurationData(protocol, host, port, exceptions)
}
/**
* also known as PAC
*/
@JvmStatic
fun proxyAutoConfiguration(pacUrl: URL): ProxyAutoConfiguration = ProxyAutoConfigurationData(pacUrl)
/**
* @param exceptions as in [com.intellij.util.net.ProxyConfiguration.proxy]
* @return a predicate that tests if the provided URI host is an exception for proxying
*/
@JvmStatic
fun buildProxyExceptionsMatcher(exceptions: String): Predicate<String> {
if (exceptions.isBlank()) {
return Predicate { false }
}
val regexp = exceptions
.split(",")
.map { StringUtil.escapeToRegexp(it.trim()).replace("\\*", ".*") }
.joinToString("|")
return Pattern.compile(regexp).asMatchPredicate()
}
}
enum class ProxyProtocol(val defaultPort: Int) {
HTTP(80),
SOCKS(1080)
}
interface StaticProxyConfiguration : ProxyConfiguration {
val protocol: ProxyProtocol
val host: String
val port: Int
/**
* comma-delimited list of host globs which must not be proxied
*/
val exceptions: String
}
interface ProxyAutoConfiguration : ProxyConfiguration {
val pacUrl: URL
}
interface AutoDetectProxy : ProxyConfiguration
interface DirectProxy : ProxyConfiguration
private data class StaticProxyConfigurationData(
override val protocol: ProxyProtocol,
override val host: String,
override val port: Int = protocol.defaultPort,
override val exceptions: String = ""
) : StaticProxyConfiguration
private data class ProxyAutoConfigurationData(override val pacUrl: URL) : ProxyAutoConfiguration
private data object AutoDetectProxyData : AutoDetectProxy
private data object DirectProxyData : DirectProxy
}

View File

@@ -0,0 +1,56 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.credentialStore.Credentials
import com.intellij.util.net.internal.asProxyCredentialStore
/**
* Implementations may invoke IO and must provide thread-safety guarantees.
*/
interface ProxyCredentialStore {
companion object {
@JvmStatic
fun getInstance(): ProxyCredentialStore = platformCredentialStore
@Suppress("DEPRECATION", "removal")
private val platformCredentialStore by lazy { (HttpConfigurable::getInstance).asProxyCredentialStore() }
}
/**
* Retrieves known credentials for the proxy located at the specified host and port
*/
fun getCredentials(host: String, port: Int): Credentials?
/**
* Stores credentials for the proxy located at the specified host and port.
* @param credentials null to clear the associated credentials from the persistence
* @param remember whether to persist the credentials to be reused in future sessions. Has no effect if [credentials] is null
*/
fun setCredentials(host: String, port: Int, credentials: Credentials?, remember: Boolean)
/**
* @return true if credentials for specified proxy location exist and are remembered (not session-only).
*/
fun areCredentialsRemembered(host: String, port: Int): Boolean
/**
* Clears known credentials which are said to be not remembered
*/
fun clearTransientCredentials()
/**
* Clears all known credentials, including the ones that were said to be remembered
*/
fun clearAllCredentials()
}
fun interface ProxyCredentialProvider {
/**
* Retrieves known credentials for the proxy located at the specified host and port
*/
fun getCredentials(host: String, port: Int): Credentials?
}
fun ProxyCredentialStore.asProxyCredentialProvider(): ProxyCredentialProvider {
return this as? ProxyCredentialProvider ?: ProxyCredentialProvider(this::getCredentials)
}

View File

@@ -0,0 +1,35 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.openapi.application.ApplicationManager
import com.intellij.util.net.ProxyConfiguration.Companion.autodetect
import org.jetbrains.annotations.ApiStatus
/**
* [ProxySettings] holds user-specified proxy settings (Settings | Appearance & Behavior | System Settings | HTTP Proxy).
*
* @see JdkProxyProvider
* @see ProxyAuthentication
*/
interface ProxySettings {
companion object {
@JvmStatic
fun getInstance(): ProxySettings = ApplicationManager.getApplication().getService(ProxySettings::class.java)
@JvmStatic
val defaultProxyConfiguration: ProxyConfiguration get() = autodetect
}
fun getProxyConfiguration(): ProxyConfiguration
@ApiStatus.Experimental // maybe make it internal if we decide that plugins must not edit user settings
fun setProxyConfiguration(proxyConfiguration: ProxyConfiguration)
}
fun interface ProxyConfigurationProvider {
fun getProxyConfiguration(): ProxyConfiguration
}
fun ProxySettings.asConfigurationProvider(): ProxyConfigurationProvider {
return this as? ProxyConfigurationProvider ?: ProxyConfigurationProvider(this::getProxyConfiguration)
}

View File

@@ -0,0 +1,47 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.PluginDescriptor
import org.jetbrains.annotations.ApiStatus
/**
* Only the first extension with [shouldUserSettingsBeOverriden]=`true` has effect.
*
* If the proxy configuration requires authentication and the credentials are known, the credential store can be populated via
* [ProxyAuthentication.instance].
*/
interface ProxySettingsOverrideProvider {
companion object {
@JvmField
@ApiStatus.Internal
val EP_NAME: ExtensionPointName<ProxySettingsOverrideProvider> = ExtensionPointName("com.intellij.proxySettingsOverrideProvider")
@ApiStatus.Internal
fun getPluginDescriptorForProvider(overrideProvider: ProxySettingsOverrideProvider): PluginDescriptor? {
var result: PluginDescriptor? = null
EP_NAME.processWithPluginDescriptor { provider, plugin ->
if (overrideProvider === provider) {
result = plugin
}
}
return result
}
@JvmStatic
fun areProxySettingsOverridden(): Boolean {
val settings = ProxySettings.getInstance() as? OverrideCapableProxySettings ?: return false
return settings.isOverrideEnabled && settings.overrideProvider != null
}
}
/**
* Should return true if the provider wants to override user's proxy settings. Expected to be immutable.
*/
val shouldUserSettingsBeOverriden: Boolean
/**
* [ProxyConfigurationProvider] which provides the proxy configuration to be used instead of user settings.
*/
val proxyConfigurationProvider: ProxyConfigurationProvider
}

View File

@@ -0,0 +1,29 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net.internal
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.options.ConfigurableUi
import com.intellij.util.net.DisabledProxyAuthPromptsManager
import com.intellij.util.net.ProxyCredentialStore
import com.intellij.util.net.ProxySettings
import org.jetbrains.annotations.ApiStatus
/**
* This is a **temporary** utility for switching the default in HttpConfigurable and for migrating stuff out of platform-api module.
* Do not use. It will be removed once HttpConfigurable is deprecated and migration to a new API for proxy settings is made.
*/
@ApiStatus.Internal
interface ProxyMigrationService {
companion object {
@JvmStatic
fun getInstance(): ProxyMigrationService = ApplicationManager.getApplication().getService(ProxyMigrationService::class.java)
}
fun isNewUser(): Boolean
fun createProxySettingsUi(
proxySettings: ProxySettings,
credentialStore: ProxyCredentialStore,
disabledProxyAuthPromptsManager: DisabledProxyAuthPromptsManager
): ConfigurableUi<ProxySettings>
}

View File

@@ -1,13 +0,0 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net.internal
import org.jetbrains.annotations.ApiStatus
/**
* This is a **temporary** hack for switching the default in HttpConfigurable. Do not use.
* It will be removed once HttpConfigurable is deprecated and migration to a new API for proxy settings is made.
*/
@ApiStatus.Internal
interface ProxyNewUserService {
fun isNewUser(): Boolean
}

View File

@@ -0,0 +1,218 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:ApiStatus.Internal
@file:Suppress("removal", "DEPRECATION")
package com.intellij.util.net.internal
import com.intellij.credentialStore.Credentials
import com.intellij.util.net.DisabledProxyAuthPromptsManager
import com.intellij.util.net.HttpConfigurable
import com.intellij.util.net.ProxyConfiguration
import com.intellij.util.net.ProxyConfiguration.ProxyProtocol
import com.intellij.util.net.ProxyConfigurationProvider
import com.intellij.util.net.ProxyCredentialProvider
import com.intellij.util.net.ProxyCredentialStore
import com.intellij.util.net.ProxySettings
import com.intellij.util.proxy.CommonProxy
import com.intellij.util.text.nullize
import org.jetbrains.annotations.ApiStatus
import java.net.PasswordAuthentication
import java.net.URL
fun HttpConfigurable.getProxyConfiguration(): ProxyConfiguration {
try {
return when {
USE_PROXY_PAC -> {
val pacUrl = PAC_URL
if (USE_PAC_URL && !pacUrl.isNullOrEmpty()) {
ProxyConfiguration.proxyAutoConfiguration(URL(pacUrl))
}
else {
ProxyConfiguration.autodetect
}
}
USE_HTTP_PROXY -> {
ProxyConfiguration.proxy(
if (PROXY_TYPE_IS_SOCKS) ProxyProtocol.SOCKS else ProxyProtocol.HTTP,
PROXY_HOST,
PROXY_PORT,
PROXY_EXCEPTIONS ?: ""
)
}
else -> { // USE_NO_PROXY
ProxyConfiguration.direct
}
}
}
catch (_: IllegalArgumentException) { // just in case
return ProxySettings.defaultProxyConfiguration
}
}
fun HttpConfigurable.setFromProxyConfiguration(proxyConf: ProxyConfiguration) {
when (proxyConf) {
is ProxyConfiguration.DirectProxy -> {
USE_HTTP_PROXY = false
USE_PROXY_PAC = false
}
is ProxyConfiguration.AutoDetectProxy -> {
USE_HTTP_PROXY = false
USE_PROXY_PAC = true
USE_PAC_URL = false
PAC_URL = null
}
is ProxyConfiguration.ProxyAutoConfiguration -> {
USE_HTTP_PROXY = false
USE_PROXY_PAC = true
USE_PAC_URL = true
PAC_URL = proxyConf.pacUrl.toString()
}
is ProxyConfiguration.StaticProxyConfiguration -> {
USE_PROXY_PAC = false
USE_HTTP_PROXY = true
PROXY_TYPE_IS_SOCKS = proxyConf.protocol == ProxyProtocol.SOCKS
PROXY_HOST = proxyConf.host
PROXY_PORT = proxyConf.port
PROXY_EXCEPTIONS = proxyConf.exceptions
}
}
}
fun HttpConfigurable.getCredentials(): Credentials? {
// as in com.intellij.util.net.HttpConfigurable.getPromptedAuthentication
if (!PROXY_AUTHENTICATION) {
return null
}
val login = proxyLogin
if (login.isNullOrEmpty()) {
return null
}
val password = plainProxyPassword?.nullize()
return Credentials(login, password?.toCharArray())
}
fun HttpConfigurable.setCredentials(credentials: Credentials?) {
if (credentials == null) {
PROXY_AUTHENTICATION = false
proxyLogin = null
plainProxyPassword = null
}
else {
PROXY_AUTHENTICATION = true
proxyLogin = credentials.userName
plainProxyPassword = credentials.password?.toString()
}
}
fun PasswordAuthentication.toCredentials(): Credentials = Credentials(userName, password)
fun (() -> HttpConfigurable).asProxySettings(): ProxySettings = HttpConfigurableToProxySettingsAdapter(this)
fun (() -> HttpConfigurable).asProxyCredentialStore(): ProxyCredentialStore = HttpConfigurableToCredentialStoreAdapter(this)
fun (() -> HttpConfigurable).asDisabledProxyAuthPromptsManager(): DisabledProxyAuthPromptsManager = HttpConfigurableToDisabledPromptsManager(this)
private class HttpConfigurableToProxySettingsAdapter(private val getHttpConfigurable: () -> HttpConfigurable) : ProxySettings, ProxyConfigurationProvider {
override fun getProxyConfiguration(): ProxyConfiguration = getHttpConfigurable().getProxyConfiguration()
override fun setProxyConfiguration(proxyConfiguration: ProxyConfiguration) = getHttpConfigurable().setFromProxyConfiguration(proxyConfiguration)
}
private class HttpConfigurableToCredentialStoreAdapter(private val getHttpConfigurable: () -> HttpConfigurable) : ProxyCredentialStore, ProxyCredentialProvider {
private val httpConfigurable: HttpConfigurable get() = getHttpConfigurable()
// host is not checked in com.intellij.util.net.HttpConfigurable.getPromptedAuthentication, but here we check it
// theoretically might change the behavior, but shouldn't be critical
@Synchronized
override fun getCredentials(host: String, port: Int): Credentials? {
val conf = httpConfigurable.getProxyConfiguration()
if (conf is ProxyConfiguration.StaticProxyConfiguration && conf.host == host && conf.port == port) {
return httpConfigurable.getCredentials()
}
else {
return httpConfigurable.getGenericPassword(host, port)?.toCredentials()
}
}
@Synchronized
override fun setCredentials(host: String, port: Int, credentials: Credentials?, remember: Boolean) {
val conf = httpConfigurable.getProxyConfiguration()
if (conf is ProxyConfiguration.StaticProxyConfiguration && conf.host == host && conf.port == port) {
httpConfigurable.setCredentials(credentials)
httpConfigurable.KEEP_PROXY_PASSWORD = credentials != null && remember
}
else {
if (credentials == null || credentials.password == null) {
httpConfigurable.removeGeneric(CommonProxy.HostInfo(null, host, port))
}
else {
httpConfigurable.putGenericPassword(host, port, PasswordAuthentication(credentials.userName, credentials.password!!.toCharArray()), remember)
}
}
}
@Synchronized
override fun areCredentialsRemembered(host: String, port: Int): Boolean {
val conf = httpConfigurable.getProxyConfiguration()
return if (conf is ProxyConfiguration.StaticProxyConfiguration && conf.host == host && conf.port == port) {
httpConfigurable.KEEP_PROXY_PASSWORD
}
else {
httpConfigurable.isGenericPasswordRemembered(host, port)
}
}
@Synchronized
override fun clearTransientCredentials() {
httpConfigurable.clearGenericPasswords()
}
@Synchronized
override fun clearAllCredentials() {
httpConfigurable.setCredentials(null)
httpConfigurable.plainProxyPassword = null
httpConfigurable.KEEP_PROXY_PASSWORD = false
httpConfigurable.clearGenericPasswords()
}
}
private class HttpConfigurableToDisabledPromptsManager(val getHttpConfigurable: () -> HttpConfigurable) : DisabledProxyAuthPromptsManager {
private val httpConfigurable get() = getHttpConfigurable()
@Synchronized
override fun disablePromptedAuthentication(host: String, port: Int) {
val conf = httpConfigurable.getProxyConfiguration()
if (conf is ProxyConfiguration.StaticProxyConfiguration && conf.host == host && conf.port == port) {
httpConfigurable.AUTHENTICATION_CANCELLED = true
}
else {
httpConfigurable.setGenericPasswordCanceled(host, port)
}
}
@Synchronized
override fun isPromptedAuthenticationDisabled(host: String, port: Int): Boolean {
val conf = httpConfigurable.getProxyConfiguration()
if (conf is ProxyConfiguration.StaticProxyConfiguration && conf.host == host && conf.port == port) {
return httpConfigurable.AUTHENTICATION_CANCELLED
}
else {
return httpConfigurable.isGenericPasswordCanceled(host, port)
}
}
@Synchronized
override fun enablePromptedAuthentication(host: String, port: Int) {
val conf = httpConfigurable.getProxyConfiguration()
if (conf is ProxyConfiguration.StaticProxyConfiguration && conf.host == host && conf.port == port) {
httpConfigurable.AUTHENTICATION_CANCELLED = false
}
else {
httpConfigurable.removeGenericPasswordCancellation(host, port)
}
}
@Synchronized
override fun enableAllPromptedAuthentications() {
httpConfigurable.AUTHENTICATION_CANCELLED = false
httpConfigurable.clearGenericCancellations()
}
}

View File

@@ -0,0 +1,129 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:JvmName("ProxyUtils")
@file:Suppress("removal", "DEPRECATION")
package com.intellij.util.net
import com.intellij.credentialStore.Credentials
import com.intellij.credentialStore.isFulfilled
import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.util.net.ProxyConfiguration.ProxyProtocol
import com.intellij.util.proxy.JavaProxyProperty
import java.net.*
import javax.swing.JComponent
fun Proxy.isRealProxy(): Boolean {
return Proxy.NO_PROXY != this && Proxy.Type.DIRECT != this.type()
}
fun Proxy.asJvmProperties(): Map<String, String> {
if (!isRealProxy()) {
return emptyMap()
}
val address = address()
if (address !is InetSocketAddress) {
return emptyMap()
}
return buildMap {
when (type()) {
Proxy.Type.SOCKS -> {
put(JavaProxyProperty.SOCKS_HOST, address.hostString)
put(JavaProxyProperty.SOCKS_PORT, address.port.toString())
}
Proxy.Type.HTTP -> {
put(JavaProxyProperty.HTTP_HOST, address.hostString)
put(JavaProxyProperty.HTTP_PORT, address.port.toString())
put(JavaProxyProperty.HTTPS_HOST, address.hostString)
put(JavaProxyProperty.HTTPS_PORT, address.port.toString())
}
else -> {}
}
}
}
/**
* N.B.: does not honor [exceptions][com.intellij.util.net.ProxyConfiguration.StaticProxyConfiguration.exceptions].
*/
fun ProxyConfiguration.StaticProxyConfiguration.asJavaProxy(): Proxy = Proxy(
when (protocol) {
ProxyProtocol.HTTP -> Proxy.Type.HTTP
ProxyProtocol.SOCKS -> Proxy.Type.SOCKS
},
InetSocketAddress.createUnresolved(host, port)
)
fun ProxyConfiguration.StaticProxyConfiguration.asJvmProperties(credentialProvider: ProxyCredentialProvider?): Map<String, String> {
val javaProxy = asJavaProxy()
val jvmPropertiesWithCredentials = javaProxy.asJvmPropertiesWithCredentials(credentialProvider)
return if (jvmPropertiesWithCredentials.isEmpty() || exceptions.isEmpty()) {
jvmPropertiesWithCredentials
}
else {
jvmPropertiesWithCredentials + (JavaProxyProperty.HTTP_NON_PROXY_HOSTS to exceptions.replace(",", "|"))
}
}
fun ProxySettings.editConfigurable(parent: JComponent?): Boolean {
return ShowSettingsUtil.getInstance().editConfigurable(parent, HttpProxyConfigurable(this))
}
fun ProxySettings.getStaticProxyCredentials(credentialStore: ProxyCredentialProvider): Credentials? {
val conf = getProxyConfiguration()
if (conf !is ProxyConfiguration.StaticProxyConfiguration) return null
return credentialStore.getCredentials(conf.host, conf.port)
}
fun ProxySettings.setStaticProxyCredentials(credentialStore: ProxyCredentialStore, value: Credentials?, remember: Boolean) {
val conf = getProxyConfiguration()
if (conf !is ProxyConfiguration.StaticProxyConfiguration) return
credentialStore.setCredentials(conf.host, conf.port, value, remember)
}
fun getHostNameReliably(requestingHost: String?, requestingSite: InetAddress?, requestingUrl: URL?): String? {
/** from [com.intellij.util.proxy.CommonProxy.getHostNameReliably] */
return requestingHost
?: requestingSite?.hostName
?: requestingUrl?.host
}
@JvmField
val NO_PROXY_LIST: List<Proxy> = listOf(Proxy.NO_PROXY)
/**
* @param credentialProvider specify a non-null value in case credentials should be included as properties (if they are known).
* Use [ProxyAuthentication.getInstance] for the default proxy credential provider.
* @return a list of non-direct proxy configurations for the specified [URI]. Each element is a map consisting of corresponding jvm properties.
*/
fun URI.getApplicableProxiesAsJvmProperties(
credentialProvider: ProxyCredentialProvider?,
proxySelector: ProxySelector = JdkProxyProvider.getInstance().proxySelector,
): List<Map<String, String>> {
return proxySelector.select(this)
.map { proxy ->
proxy.asJvmPropertiesWithCredentials(credentialProvider)
}
.filter {
it.isNotEmpty()
}
}
private fun Proxy.asJvmPropertiesWithCredentials(credentialProvider: ProxyCredentialProvider?): Map<String, String> {
val props = asJvmProperties().toMutableMap()
if (credentialProvider == null || props.isEmpty()) {
return props
}
val address = address()
if (address !is InetSocketAddress) {
return emptyMap()
}
val credentials = credentialProvider.getCredentials(address.hostString, address.port)
if (credentials == null || !credentials.isFulfilled()) {
return emptyMap()
}
val proxyType = type()
val usernameProp = if (proxyType == Proxy.Type.SOCKS) JavaProxyProperty.SOCKS_USERNAME else JavaProxyProperty.HTTP_USERNAME
val passwordProp = if (proxyType == Proxy.Type.SOCKS) JavaProxyProperty.SOCKS_PASSWORD else JavaProxyProperty.HTTP_PASSWORD
props[usernameProp] = credentials.userName!!
props[passwordProp] = credentials.password!!.toString()
return props
}

View File

@@ -7,7 +7,7 @@ import com.intellij.internal.statistic.eventLog.events.EventFields
import com.intellij.internal.statistic.eventLog.events.EventId1
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector
import com.intellij.util.net.HttpConfigurable
import com.intellij.util.net.IdeaWideProxySelector
import com.intellij.util.net.IdeProxySelector
private class ProxySettingsCollector : ApplicationUsagesCollector() {
private val GROUP: EventLogGroup = EventLogGroup("proxy.settings", 2)
@@ -33,7 +33,7 @@ private class ProxySettingsCollector : ApplicationUsagesCollector() {
}
result.add(TYPE.metric(type.name))
if (type == ProxyType.Auto) {
val autoDetectMs = IdeaWideProxySelector.getProxyAutoDetectDurationMs().takeIf { it != -1L }
val autoDetectMs = IdeProxySelector.getProxyAutoDetectDurationMs().takeIf { it != -1L }
if (autoDetectMs != null) result.add(AUTO_DETECT_DURATION.metric(autoDetectMs))
}
}

View File

@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
internal object JavaNetworkUtils {
const val HTTP_AUTH_TUNNELING_DISABLED_SCHEMES_PROPERTY: String = "jdk.http.auth.tunneling.disabledSchemes"

View File

@@ -0,0 +1,32 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.openapi.Disposable
internal class OverrideCapableProxySettingsImpl : OverrideCapableProxySettings(), Disposable {
private val userProxySettings: ProxySettings = ProxySettingsCompatibilityImpl()
override val originalProxySettings: ProxySettings get() = userProxySettings
private fun findOverrideProvider(): ProxySettingsOverrideProvider? {
return ProxySettingsOverrideProvider.EP_NAME.extensionList.firstOrNull { it.shouldUserSettingsBeOverriden }
}
@Volatile
private var _overrideProvider: ProxySettingsOverrideProvider? = findOverrideProvider()
init {
ProxySettingsOverrideProvider.EP_NAME.addChangeListener(
{ _overrideProvider = findOverrideProvider() },
this
)
}
override var isOverrideEnabled: Boolean
get() = ProxyOverrideSettings.isOverrideEnabled
set(value) { ProxyOverrideSettings.isOverrideEnabled = value }
override val overrideProvider: ProxySettingsOverrideProvider? get() = _overrideProvider
override fun dispose() {}
}

View File

@@ -0,0 +1,16 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.ide.util.PropertiesComponent
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
object ProxyOverrideSettings {
var isOverrideEnabled: Boolean
get() = PropertiesComponent.getInstance().getBoolean(IS_OVERRIDE_ENABLED, DEFAULT)
set(value) = PropertiesComponent.getInstance().setValue(IS_OVERRIDE_ENABLED, value, DEFAULT)
private const val IS_OVERRIDE_ENABLED = "intellij.platform.proxy.override.enabled"
private const val DEFAULT = true
}

View File

@@ -0,0 +1,12 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.util.net.internal.asProxySettings
/**
* Currently delegates to [HttpConfigurable].
* After [HttpConfigurable] is made internal, we can either drop it and implement a new PSC with migration from previous storage,
* or continue using [HttpConfigurable] but now being able to modify it.
*/
@Suppress("removal", "DEPRECATION")
internal class ProxySettingsCompatibilityImpl : ProxySettings by (HttpConfigurable::getInstance).asProxySettings(), ProxyConfigurationProvider

View File

@@ -0,0 +1,421 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net
import com.intellij.credentialStore.Credentials
import com.intellij.ide.IdeBundle
import com.intellij.notification.NotificationAction
import com.intellij.notification.NotificationType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.options.ConfigurableUi
import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.util.text.StringUtil
import com.intellij.platform.ide.progress.ModalTaskOwner
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
import com.intellij.ui.JBIntSpinner
import com.intellij.ui.RawCommandLineEditor
import com.intellij.ui.UIBundle
import com.intellij.ui.components.JBRadioButton
import com.intellij.ui.components.JBScrollPane
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.actionListener
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.jcef.JBCefApp
import com.intellij.ui.layout.and
import com.intellij.ui.layout.not
import com.intellij.ui.layout.selected
import com.intellij.util.io.HttpRequests
import com.intellij.util.net.ProxyConfiguration.*
import com.intellij.util.proxy.CommonProxy
import com.intellij.util.proxy.JavaProxyProperty
import com.intellij.util.ui.JBUI
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus
import java.io.IOException
import java.net.MalformedURLException
import java.net.URL
import java.util.concurrent.atomic.AtomicReference
import javax.swing.JButton
import javax.swing.JCheckBox
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JPanel
import javax.swing.JPasswordField
import javax.swing.JTextField
import javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
import javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
// TODO: shouldn't accept services, should instead accept UI model or some temporary mutable representation of the settings
@Suppress("DEPRECATION")
@ApiStatus.Internal
class ProxySettingsUi(
proxySettings: ProxySettings,
private val credentialStore: ProxyCredentialStore,
private val disabledPromptsManager: DisabledProxyAuthPromptsManager
) : ConfigurableUi<ProxySettings> {
private var mainPanel: JPanel
private lateinit var proxyLoginTextField: JTextField
private lateinit var proxyPasswordTextField: JPasswordField
private lateinit var proxyAuthCheckBox: JCheckBox
private lateinit var proxyPortTextField: JBIntSpinner
private lateinit var proxyHostTextField: JTextField
private lateinit var rememberProxyPasswordCheckBox: JCheckBox
private lateinit var autoDetectProxyRb: JBRadioButton
private lateinit var useHttpProxyRb: JBRadioButton
private lateinit var systemProxyDefinedWarning: JLabel
private lateinit var noProxyRb: JBRadioButton
private lateinit var typeHttpRb: JBRadioButton
private lateinit var typeSocksRb: JBRadioButton
private lateinit var clearPasswordsButton: JButton
private lateinit var errorLabel: JLabel
private lateinit var checkButton: JButton
private lateinit var javaPropertiesWarning: JLabel
private lateinit var proxyExceptionsField: RawCommandLineEditor
private lateinit var pacUrlCheckBox: JCheckBox
private lateinit var pacUrlTextField: JTextField
private lateinit var tunnelingAuthSchemeDisabledWarning: JLabel
private lateinit var pluginOverrideCheckbox: JCheckBox
private var lastOverriderProvidedConfiguration: ProxyConfiguration? =
if (proxySettings is OverrideCapableProxySettings) proxySettings.overrideProvider?.proxyConfigurationProvider?.getProxyConfiguration() else null
private var lastUserConfiguration: ProxyConfiguration =
if (proxySettings is OverrideCapableProxySettings) proxySettings.originalProxySettings.getProxyConfiguration() else proxySettings.getProxyConfiguration()
private var checkConnectionUrl = "https://"
private var lastProxyError: @NlsSafe String = ""
private fun getMainPanel() = mainPanel
init {
// TODO would be nice to reimplement it using bindings
mainPanel = panel {
row {
systemProxyDefinedWarning = label(UIBundle.message("proxy.system.label")).applyToComponent {
icon = Messages.getWarningIcon()
isVisible = java.lang.Boolean.getBoolean(JavaProxyProperty.USE_SYSTEM_PROXY)
}.component
}
row {
javaPropertiesWarning = label("").applyToComponent { icon = Messages.getWarningIcon() }.component
}
row {
pluginOverrideCheckbox = checkBox(UIBundle.message("proxy.settings.override.by.plugin.checkbox", "")).actionListener { _, checkbox ->
resetByOverrideState(checkbox.isSelected)
}.component
}
buttonsGroup {
row {
noProxyRb = radioButton(UIBundle.message("proxy.direct.rb")).component
}
row {
autoDetectProxyRb = radioButton(UIBundle.message("proxy.pac.rb")).component
link(UIBundle.message("proxy.system.proxy.settings")) {
try {
SystemProxySettings.getInstance().openProxySettings()
}
catch (e: Exception) {
logger<ProxySettingsUi>().error("failed to open system proxy settings", e)
}
}.applyToComponent {
setExternalLinkIcon()
autoHideOnDisable = true
isEnabled = SystemProxySettings.getInstance().isProxySettingsOpenSupported()
}.align(AlignX.RIGHT)
}
indent {
row {
pacUrlCheckBox = checkBox(UIBundle.message("proxy.pac.url.label")).component
pacUrlTextField = textField().align(AlignX.FILL).component
}
row {
@Suppress("DialogTitleCapitalization")
clearPasswordsButton = button(UIBundle.message("proxy.pac.pw.clear.button")) {
credentialStore.clearAllCredentials()
@Suppress("DialogTitleCapitalization")
Messages.showMessageDialog(getMainPanel(), IdeBundle.message("message.text.proxy.passwords.were.cleared"),
IdeBundle.message("dialog.title.auto.detected.proxy"), Messages.getInformationIcon())
}.component
}
}.enabledIf(autoDetectProxyRb.selected and pluginOverrideCheckbox.selected.not())
row {
useHttpProxyRb = radioButton(UIBundle.message("proxy.manual.rb")).component
}
indent {
buttonsGroup {
row {
typeHttpRb = radioButton(UIBundle.message("proxy.manual.type.http")).component
typeSocksRb = radioButton(UIBundle.message("proxy.manual.type.socks")).component
}
}
panel {
row(UIBundle.message("proxy.manual.host")) {
proxyHostTextField = textField().align(AlignX.FILL).component
}
row(UIBundle.message("proxy.manual.port")) {
proxyPortTextField = spinner(0..65535).component
}
row(UIBundle.message("proxy.manual.exclude")) {
proxyExceptionsField = RawCommandLineEditor(
{ text ->
val result = ArrayList<String>()
for (token in text.split(",")) {
val trimmedToken = token.trim();
if (!trimmedToken.isEmpty()) {
result.add(trimmedToken);
}
}
result
}, { StringUtil.join(it, ", ") }
)
cell(proxyExceptionsField).align(AlignX.FILL).comment(UIBundle.message("proxy.manual.exclude.example"))
}
}
row {
proxyAuthCheckBox = checkBox(UIBundle.message("proxy.manual.auth")).component
}
indent {
panel {
row(UIBundle.message("auth.login.label")) {
proxyLoginTextField = textField().align(AlignX.FILL).component
}
row(UIBundle.message("auth.password.label")) {
proxyPasswordTextField = passwordField().align(AlignX.FILL).component
}
row("") {
rememberProxyPasswordCheckBox = checkBox(UIBundle.message("auth.remember.cb")).component
}
}
}.enabledIf(proxyAuthCheckBox.selected and pluginOverrideCheckbox.selected.not())
}.enabledIf(useHttpProxyRb.selected and pluginOverrideCheckbox.selected.not())
}
indent {
row {
tunnelingAuthSchemeDisabledWarning = label(UIBundle.message("proxy.tunneling.disabled.warning")).applyToComponent {
icon = Messages.getWarningIcon()
isVisible = JavaNetworkUtils.isTunnelingAuthSchemeDisabled(JavaNetworkUtils.BASIC_AUTH_SCHEME)
}.component
}
}
row {
errorLabel = label("").applyToComponent {
icon = Messages.getErrorIcon()
}.component
}
row {
@Suppress("DialogTitleCapitalization")
checkButton = button(UIBundle.message("proxy.test.button")) {
doCheckConnection()
}.applyToComponent {
isVisible = proxySettings === ProxySettings.getInstance()
}.component
}
}
noProxyRb.setSelected(true)
typeHttpRb.setSelected(true)
}
private fun doCheckConnection() {
parseProxyConfiguration().getOrElse {
Messages.showErrorDialog(mainPanel, it.message)
return
}
parseCredentials().getOrElse {
Messages.showErrorDialog(mainPanel, it.message)
return
}
val title = IdeBundle.message("dialog.title.check.proxy.settings")
val url = Messages.showInputDialog(mainPanel, IdeBundle.message("message.text.enter.url.to.check.connection"),
title, Messages.getQuestionIcon(), checkConnectionUrl, null)
if (url.isNullOrBlank()) return
checkConnectionUrl = url
try {
apply(ProxySettings.getInstance())
}
catch (_: ConfigurationException) {
return
}
val exceptionReference = AtomicReference<IOException>()
runWithModalProgressBlocking(ModalTaskOwner.component(mainPanel), IdeBundle.message("progress.title.check.connection")) {
withContext(Dispatchers.IO) {
try {
HttpRequests.request(url).readTimeout(3 * 1000).tryConnect()
}
catch (e: IOException) {
exceptionReference.set(e)
}
}
}
val exception = exceptionReference.get()
if (exception == null) {
Messages.showMessageDialog(mainPanel, IdeBundle.message("message.connection.successful"), title, Messages.getInformationIcon())
}
else {
val msg = StringUtil.escapeXmlEntities(exception.message.orEmpty())
lastProxyError = msg
Messages.showErrorDialog(mainPanel, IdeBundle.message("dialog.message.problem.with.connection", msg, title))
}
reset(ProxySettings.getInstance())
}
private fun parseProxyConfiguration(): Result<ProxyConfiguration> = runCatching {
if (noProxyRb.isSelected) return@runCatching ProxyConfiguration.direct
if (autoDetectProxyRb.isSelected) {
if (!pacUrlCheckBox.isSelected) return@runCatching ProxyConfiguration.autodetect
val pacUrl = pacUrlTextField.text
if (pacUrl.isNullOrBlank()) throw ConfigurationException(IdeBundle.message("dialog.message.url.is.empty"))
try {
return@runCatching ProxyConfiguration.proxyAutoConfiguration(URL(pacUrl))
} catch (_: MalformedURLException) {
throw ConfigurationException(IdeBundle.message("dialog.message.url.is.invalid"))
}
}
if (useHttpProxyRb.isSelected) {
val protocol = if (typeHttpRb.isSelected) ProxyProtocol.HTTP else ProxyProtocol.SOCKS
val host = proxyHostTextField.text
if (host.isNullOrBlank()) throw ConfigurationException(IdeBundle.message("dialog.message.host.name.empty"))
if (NetUtils.isValidHost(host) == NetUtils.ValidHostInfo.INVALID) {
throw ConfigurationException(IdeBundle.message("dialog.message.invalid.host.value"))
}
val port = proxyPortTextField.number
val exceptions = proxyExceptionsField.text.orEmpty()
return@runCatching ProxyConfiguration.proxy(protocol, host, port, exceptions)
}
error("unreachable")
}
private fun parseCredentials(): Result<Credentials?> = runCatching {
if (!useHttpProxyRb.isSelected || !proxyAuthCheckBox.isSelected) return@runCatching null
val login = proxyLoginTextField.text
if (login.isNullOrBlank()) throw ConfigurationException(IdeBundle.message("dialog.message.login.empty"))
val password = proxyPasswordTextField.password
if (password.isEmpty()) throw ConfigurationException(IdeBundle.message("dialog.message.password.empty"))
Credentials(login, password)
}
override fun isModified(settings: ProxySettings): Boolean {
if (settings is OverrideCapableProxySettings && settings.overrideProvider != null) {
if (settings.isOverrideEnabled != pluginOverrideCheckbox.isSelected) return true
if (settings.isOverrideEnabled) return false
// else user configuration is enabled
}
val uiConf = parseProxyConfiguration().getOrElse { return true }
if (uiConf != settings.getProxyConfiguration()) return true
if (uiConf is StaticProxyConfiguration) {
val credentials = parseCredentials().getOrElse { return true }
val storeCreds = credentialStore.getCredentials(uiConf.host, uiConf.port)
if (credentials != storeCreds) return true
if (rememberProxyPasswordCheckBox.isSelected != credentialStore.areCredentialsRemembered(uiConf.host, uiConf.port)) return true
}
return false
}
private fun resetProxyConfiguration(conf: ProxyConfiguration) {
when (val conf = conf) {
is DirectProxy -> noProxyRb.isSelected = true
is AutoDetectProxy -> {
autoDetectProxyRb.isSelected = true
pacUrlCheckBox.isSelected = false
pacUrlTextField.text = ""
}
is ProxyAutoConfiguration -> {
autoDetectProxyRb.isSelected = true
pacUrlCheckBox.isSelected = true
pacUrlTextField.text = conf.pacUrl.toString()
}
is StaticProxyConfiguration -> {
useHttpProxyRb.isSelected = true
proxyHostTextField.text = conf.host
proxyPortTextField.number = conf.port
proxyExceptionsField.text = conf.exceptions
val creds = credentialStore.getCredentials(conf.host, conf.port)
proxyAuthCheckBox.isSelected = creds != null
proxyLoginTextField.text = creds?.userName
proxyPasswordTextField.text = creds?.password?.toString() ?: ""
rememberProxyPasswordCheckBox.isSelected = creds != null && credentialStore.areCredentialsRemembered(conf.host, conf.port)
}
}
}
private fun resetByOverrideState(overridden: Boolean) {
if (!overridden || lastOverriderProvidedConfiguration == null) {
noProxyRb.isEnabled = true
autoDetectProxyRb.isEnabled = true
useHttpProxyRb.isEnabled = true
resetProxyConfiguration(lastUserConfiguration)
} else {
noProxyRb.isEnabled = false
autoDetectProxyRb.isEnabled = false
useHttpProxyRb.isEnabled = false
resetProxyConfiguration(lastOverriderProvidedConfiguration!!)
}
}
override fun reset(settings: ProxySettings) {
if (settings is OverrideCapableProxySettings) {
lastOverriderProvidedConfiguration = settings.overrideProvider?.proxyConfigurationProvider?.getProxyConfiguration()
lastUserConfiguration = settings.originalProxySettings.getProxyConfiguration()
val overrideProvider = settings.overrideProvider
pluginOverrideCheckbox.isVisible = overrideProvider != null
pluginOverrideCheckbox.isSelected = overrideProvider != null && settings.isOverrideEnabled
if (overrideProvider != null) {
val pluginName = ProxySettingsOverrideProvider.getPluginDescriptorForProvider(overrideProvider)?.name ?: "<unknown>".also {
logger<ProxySettingsUi>().error("couldn't find plugin descriptor for $overrideProvider")
}
pluginOverrideCheckbox.text = UIBundle.message("proxy.settings.override.by.plugin.checkbox", pluginName)
}
} else {
lastOverriderProvidedConfiguration = null
lastUserConfiguration = settings.getProxyConfiguration()
}
resetByOverrideState(lastOverriderProvidedConfiguration != null && pluginOverrideCheckbox.isSelected)
errorLabel.isVisible = lastProxyError.isNotEmpty()
errorLabel.text = lastProxyError
val javaPropsMessage = CommonProxy.getMessageFromProps(CommonProxy.getOldStyleProperties())
javaPropertiesWarning.isVisible = !javaPropsMessage.isNullOrBlank()
javaPropertiesWarning.text = javaPropsMessage
}
override fun apply(settings: ProxySettings) {
val modified = isModified(settings)
if (settings is OverrideCapableProxySettings && settings.overrideProvider != null) {
settings.isOverrideEnabled = pluginOverrideCheckbox.isSelected
}
val conf = parseProxyConfiguration().getOrThrow()
val credentials = parseCredentials().getOrThrow()
settings.setProxyConfiguration(conf)
if (conf is StaticProxyConfiguration) {
credentialStore.setCredentials(conf.host, conf.port, credentials, rememberProxyPasswordCheckBox.isSelected)
}
disabledPromptsManager.enableAllPromptedAuthentications()
if (modified && JBCefApp.isStarted()) {
JBCefApp.getNotificationGroup()
.createNotification(IdeBundle.message("notification.title.jcef.proxyChanged"),
IdeBundle.message("notification.content.jcef.applySettings"), NotificationType.WARNING)
.addAction(
NotificationAction.createSimple(IdeBundle.message("action.jcef.restart")) { ApplicationManager.getApplication().restart() })
.notify(null)
}
lastProxyError = ""
}
override fun getComponent(): JComponent {
mainPanel.setBorder(JBUI.Borders.empty(11, 16, 16, 16))
val scrollPane = JBScrollPane(mainPanel, VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_NEVER)
scrollPane.setBorder(null)
return scrollPane
}
}

View File

@@ -0,0 +1,23 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net.internal
import com.intellij.openapi.application.ConfigImportHelper
import com.intellij.openapi.options.ConfigurableUi
import com.intellij.util.net.DisabledProxyAuthPromptsManager
import com.intellij.util.net.ProxyCredentialStore
import com.intellij.util.net.ProxySettings
import com.intellij.util.net.ProxySettingsUi
/**
* This is a **temporary** hack for switching the default in HttpConfigurable. Do not use.
* It will be removed once HttpConfigurable is deprecated and migration to a new API for proxy settings is made.
*/
private class ProxyMigrationServiceImpl : ProxyMigrationService {
override fun isNewUser(): Boolean = ConfigImportHelper.isNewUser()
override fun createProxySettingsUi(
proxySettings: ProxySettings,
credentialStore: ProxyCredentialStore,
disabledProxyAuthPromptsManager: DisabledProxyAuthPromptsManager
): ConfigurableUi<ProxySettings> = ProxySettingsUi(proxySettings, credentialStore, disabledProxyAuthPromptsManager)
}

View File

@@ -1,12 +0,0 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.net.internal
import com.intellij.openapi.application.ConfigImportHelper
/**
* This is a **temporary** hack for switching the default in HttpConfigurable. Do not use.
* It will be removed once HttpConfigurable is deprecated and migration to a new API for proxy settings is made.
*/
private class ProxyNewUserServiceImpl : ProxyNewUserService {
override fun isNewUser(): Boolean = ConfigImportHelper.isNewUser()
}

View File

@@ -555,5 +555,7 @@
<extensionPoint name="endUserAgreementUpdater" beanClass="com.intellij.ide.gdpr.EndUserAgreement$PluginAgreementUpdateDescriptor" dynamic="true"/>
<extensionPoint name="cefDelegate" interface="com.intellij.ui.jcef.CefDelegate"/>
<extensionPoint name="proxySettingsOverrideProvider" interface="com.intellij.util.net.ProxySettingsOverrideProvider" dynamic="true"/>
</extensionPoints>
</idea-plugin>

View File

@@ -119,9 +119,13 @@
<persistentFsConnectionListener implementation="com.intellij.openapi.fileTypes.impl.IgnoredFileCacheCleaner"/>
<applicationService serviceImplementation="com.intellij.util.net.HttpConfigurable" preload="true"/>
<applicationService serviceInterface="com.intellij.util.net.internal.ProxyNewUserService"
serviceImplementation="com.intellij.util.net.internal.ProxyNewUserServiceImpl" preload="true"/>
<applicationService serviceImplementation="com.intellij.util.net.OverrideDefaultJdkProxy" preload="true"/>
<applicationService serviceImplementation="com.intellij.util.net.HttpConfigurable"/>
<applicationService serviceInterface="com.intellij.util.net.ProxySettings"
serviceImplementation="com.intellij.util.net.OverrideCapableProxySettingsImpl"/>
<applicationService serviceInterface="com.intellij.util.net.internal.ProxyMigrationService"
serviceImplementation="com.intellij.util.net.internal.ProxyMigrationServiceImpl"/>
<applicationService serviceImplementation="com.intellij.ide.ClipboardSynchronizer" preload="true"/>
<applicationService serviceInterface="com.intellij.openapi.keymap.KeymapManager"
serviceImplementation="com.intellij.openapi.keymap.impl.KeymapManagerImpl"/>