mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
IJPL-148832 IJPL-148512 New Proxy API, extension point for proxy settings override
GitOrigin-RevId: e81473636c238412508593bff48b544817c6470b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
797f6253ae
commit
2acdc81bed
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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}")
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()) }
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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>
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
129
platform/platform-api/src/com/intellij/util/net/proxyUtils.kt
Normal file
129
platform/platform-api/src/com/intellij/util/net/proxyUtils.kt
Normal 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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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() {}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user