From 46ce0110168f3e7de8986193c3f519ff6bc2dc42 Mon Sep 17 00:00:00 2001 From: Steve Hawkins Date: Fri, 26 May 2023 11:23:04 -0400 Subject: [PATCH] fix #5086: adding support for socks proxies --- CHANGELOG.md | 3 + .../jdkhttp/JdkHttpClientBuilderImpl.java | 11 ++- .../client/jetty/JettyHttpClientBuilder.java | 21 +++- .../okhttp/OkHttpClientBuilderImpl.java | 22 ++++- .../client/vertx/VertxHttpClientBuilder.java | 18 +++- .../kubernetes/client/http/HttpClient.java | 9 ++ .../http/StandardHttpClientBuilder.java | 17 +++- .../client/utils/HttpClientUtils.java | 96 ++++++++++++------- .../client/utils/HttpClientUtilsTest.java | 87 ++++++++++------- 9 files changed, 198 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c7be0d9cb3..b3d35c68b56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,14 @@ * Fix #5164: [java-generator] handle more special characters in field names #### Improvements +* Fix #1335: HttpClient Factory additionalConfig consistently applied for all client types #### Dependency Upgrade * Fix #4989: Upgrade Tekton Model to v0.47.0 * Fix #5107: The Camel-k extension has been deprecated in favor of the official release of the generated one #### New Features +* Fix #5086: Allow for supporting socks proxies via proxy urls with the socks4 or socks5 protocol. Note that the JDK client does not support socks, and the Jetty client does not support socks5 #### _**Note**_: Breaking changes * Fix #4911: Config/RequestConfig.scaleTimeout has been deprecated along with Scalable.scale(count, wait) and DeployableScalableResource.deployLatest(wait). withTimeout may be called before the operation to control the timeout. @@ -25,6 +27,7 @@ * Fix #4662: deprecated Helper.getAnnotationValue, use HasMetadata methods instead. * Fix #5125: support for TLSv1.3 is now enabled by default * Fix #5125: usage of TlsVersion.TLS_1_1, TLS_1_0, and SSL_3_0 have been deprecated +* Fix #1335: The JDK and OkHttp clients will default to using the VM's standard configuration for proxies if an applicalbe proxy configuration is not found in the Kubernetes client Config ### 6.6.2 (2023-05-15) diff --git a/httpclient-jdk/src/main/java/io/fabric8/kubernetes/client/jdkhttp/JdkHttpClientBuilderImpl.java b/httpclient-jdk/src/main/java/io/fabric8/kubernetes/client/jdkhttp/JdkHttpClientBuilderImpl.java index 7374ad49780..8e4f2bb9928 100644 --- a/httpclient-jdk/src/main/java/io/fabric8/kubernetes/client/jdkhttp/JdkHttpClientBuilderImpl.java +++ b/httpclient-jdk/src/main/java/io/fabric8/kubernetes/client/jdkhttp/JdkHttpClientBuilderImpl.java @@ -16,6 +16,7 @@ package io.fabric8.kubernetes.client.jdkhttp; +import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.http.HttpClient; import io.fabric8.kubernetes.client.http.StandardHttpClientBuilder; import io.fabric8.kubernetes.client.http.TlsVersion; @@ -59,11 +60,15 @@ public HttpClient build() { if (followRedirects) { builder.followRedirects(Redirect.ALWAYS); } - if (proxyAddress != null) { + if (proxyType == HttpClient.ProxyType.DIRECT) { + builder.proxy(java.net.http.HttpClient.Builder.NO_PROXY); + } else if (proxyAddress != null) { + if (proxyType != HttpClient.ProxyType.HTTP) { + // https://bugs.openjdk.org/browse/JDK-8214516 + throw new KubernetesClientException("JDK HttpClient only support HTTP proxies"); + } builder.proxy(ProxySelector.of(proxyAddress)); addProxyAuthInterceptor(); - } else { - builder.proxy(java.net.http.HttpClient.Builder.NO_PROXY); } if (preferHttp11) { builder.version(Version.HTTP_1_1); diff --git a/httpclient-jetty/src/main/java/io/fabric8/kubernetes/client/jetty/JettyHttpClientBuilder.java b/httpclient-jetty/src/main/java/io/fabric8/kubernetes/client/jetty/JettyHttpClientBuilder.java index 7394a549bf1..219981848c4 100644 --- a/httpclient-jetty/src/main/java/io/fabric8/kubernetes/client/jetty/JettyHttpClientBuilder.java +++ b/httpclient-jetty/src/main/java/io/fabric8/kubernetes/client/jetty/JettyHttpClientBuilder.java @@ -15,12 +15,15 @@ */ package io.fabric8.kubernetes.client.jetty; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.http.HttpClient.ProxyType; import io.fabric8.kubernetes.client.http.StandardHttpClientBuilder; import io.fabric8.kubernetes.client.http.TlsVersion; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClientTransport; import org.eclipse.jetty.client.HttpProxy; import org.eclipse.jetty.client.Origin; +import org.eclipse.jetty.client.Socks4Proxy; import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic; import org.eclipse.jetty.client.http.HttpClientConnectionFactory; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; @@ -77,9 +80,21 @@ public JettyHttpClient build() { // the work that can be done sharedHttpClient.setMaxConnectionsPerDestination(MAX_CONNECTIONS); sharedWebSocketClient.getHttpClient().setMaxConnectionsPerDestination(MAX_CONNECTIONS); - if (proxyAddress != null) { - sharedHttpClient.getProxyConfiguration() - .addProxy(new HttpProxy(new Origin.Address(proxyAddress.getHostString(), proxyAddress.getPort()), false)); + if (proxyType != ProxyType.DIRECT && proxyAddress != null) { + Origin.Address address = new Origin.Address(proxyAddress.getHostString(), proxyAddress.getPort()); + // Jetty allows for the differentiation of proxy being secure separately from the destination, + // but we'll always set that flag to false + switch (proxyType) { + case HTTP: + sharedHttpClient.getProxyConfiguration().addProxy(new HttpProxy(address, false)); + break; + case SOCKS4: + sharedHttpClient.getProxyConfiguration().addProxy(new Socks4Proxy(address, false)); + break; + default: + throw new KubernetesClientException("Unsupported proxy type"); + } + sharedHttpClient.getProxyConfiguration().addProxy(new HttpProxy(address, false)); addProxyAuthInterceptor(); } clientFactory.additionalConfig(sharedHttpClient, sharedWebSocketClient); diff --git a/httpclient-okhttp/src/main/java/io/fabric8/kubernetes/client/okhttp/OkHttpClientBuilderImpl.java b/httpclient-okhttp/src/main/java/io/fabric8/kubernetes/client/okhttp/OkHttpClientBuilderImpl.java index f0ca09279be..ae89df21e30 100644 --- a/httpclient-okhttp/src/main/java/io/fabric8/kubernetes/client/okhttp/OkHttpClientBuilderImpl.java +++ b/httpclient-okhttp/src/main/java/io/fabric8/kubernetes/client/okhttp/OkHttpClientBuilderImpl.java @@ -16,6 +16,8 @@ package io.fabric8.kubernetes.client.okhttp; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.http.HttpClient.ProxyType; import io.fabric8.kubernetes.client.http.StandardHttpClientBuilder; import io.fabric8.kubernetes.client.http.StandardHttpHeaders; import okhttp3.Authenticator; @@ -68,10 +70,10 @@ public OkHttpClientImpl initialBuild(okhttp3.OkHttpClient.Builder builder) { if (followRedirects) { builder.followRedirects(true).followSslRedirects(true); } - if (proxyAddress == null) { + if (proxyType == ProxyType.DIRECT) { builder.proxy(Proxy.NO_PROXY); - } else { - builder.proxy(new Proxy(Proxy.Type.HTTP, proxyAddress)); + } else if (proxyAddress != null) { + builder.proxy(new Proxy(convertProxyType(), proxyAddress)); if (proxyAuthorization != null) { builder.proxyAuthenticator( (route, response) -> response.request().newBuilder() @@ -92,6 +94,20 @@ public OkHttpClientImpl initialBuild(okhttp3.OkHttpClient.Builder builder) { return completeBuild(builder, false); } + private Proxy.Type convertProxyType() { + switch (proxyType) { + case DIRECT: + return Proxy.Type.DIRECT; + case HTTP: + return Proxy.Type.HTTP; + case SOCKS4: + case SOCKS5: + return Proxy.Type.SOCKS; + default: + throw new KubernetesClientException("Unsupported proxy type"); + } + } + private OkHttpClientImpl completeBuild(okhttp3.OkHttpClient.Builder builder, boolean derived) { if (authenticatorNone) { builder.authenticator(Authenticator.NONE); diff --git a/httpclient-vertx/src/main/java/io/fabric8/kubernetes/client/vertx/VertxHttpClientBuilder.java b/httpclient-vertx/src/main/java/io/fabric8/kubernetes/client/vertx/VertxHttpClientBuilder.java index 02042c82203..8e8c2ed9cb5 100644 --- a/httpclient-vertx/src/main/java/io/fabric8/kubernetes/client/vertx/VertxHttpClientBuilder.java +++ b/httpclient-vertx/src/main/java/io/fabric8/kubernetes/client/vertx/VertxHttpClientBuilder.java @@ -15,6 +15,7 @@ */ package io.fabric8.kubernetes.client.vertx; +import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.http.HttpClient; import io.fabric8.kubernetes.client.http.StandardHttpClientBuilder; import io.fabric8.kubernetes.client.http.TlsVersion; @@ -67,11 +68,11 @@ public VertxHttpClient build() { options.setFollowRedirects(followRedirects); } - if (this.proxyAddress != null) { + if (this.proxyType != HttpClient.ProxyType.DIRECT && this.proxyAddress != null) { ProxyOptions proxyOptions = new ProxyOptions() .setHost(this.proxyAddress.getHostName()) .setPort(this.proxyAddress.getPort()) - .setType(ProxyType.HTTP); + .setType(convertProxyType()); options.setProxyOptions(proxyOptions); addProxyAuthInterceptor(); } @@ -124,4 +125,17 @@ protected VertxHttpClientBuilder newInstance(F clientFactory) { return new VertxHttpClientBuilder<>(clientFactory, vertx); } + private ProxyType convertProxyType() { + switch (proxyType) { + case HTTP: + return ProxyType.HTTP; + case SOCKS4: + return ProxyType.SOCKS4; + case SOCKS5: + return ProxyType.SOCKS5; + default: + throw new KubernetesClientException("Unsupported proxy type"); + } + } + } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java index b87b890bd73..d8f71d7b168 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java @@ -30,6 +30,13 @@ public interface HttpClient extends AutoCloseable { + enum ProxyType { + HTTP, + SOCKS4, + SOCKS5, + DIRECT + } + interface Factory { /** @@ -114,6 +121,8 @@ interface Builder extends DerivedClientBuilder { Builder tlsVersions(TlsVersion... tlsVersions); Builder preferHttp11(); + + Builder proxyType(ProxyType type); } @Override diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpClientBuilder.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpClientBuilder.java index d503bc94617..081a1b385e7 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpClientBuilder.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpClientBuilder.java @@ -17,19 +17,20 @@ package io.fabric8.kubernetes.client.http; import io.fabric8.kubernetes.client.http.HttpClient.DerivedClientBuilder; +import io.fabric8.kubernetes.client.http.HttpClient.ProxyType; import io.fabric8.kubernetes.client.internal.SSLUtils; import lombok.Getter; import org.slf4j.LoggerFactory; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + import java.net.InetSocketAddress; import java.time.Duration; import java.util.LinkedHashMap; import java.util.concurrent.TimeUnit; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; - @SuppressWarnings("unchecked") @Getter public abstract class StandardHttpClientBuilder> @@ -49,6 +50,7 @@ public abstract class StandardHttpClientBuilder, Object> tags = new LinkedHashMap<>(); + protected ProxyType proxyType = ProxyType.HTTP; // for backwards compatibility if the builder is manually configured protected StandardHttpClientBuilder(F clientFactory) { this.clientFactory = clientFactory; @@ -106,6 +108,12 @@ public T proxyAuthorization(String credentials) { return (T) this; } + @Override + public T proxyType(ProxyType type) { + this.proxyType = type; + return (T) this; + } + @Override public T tlsVersions(TlsVersion... tlsVersions) { this.tlsVersions = tlsVersions; @@ -148,6 +156,7 @@ public T copy(C client) { copy.authenticatorNone = this.authenticatorNone; copy.client = client; copy.tags = new LinkedHashMap<>(this.tags); + copy.proxyType = this.proxyType; return copy; } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/HttpClientUtils.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/HttpClientUtils.java index e2a84386b0f..b66ef48f05c 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/HttpClientUtils.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/HttpClientUtils.java @@ -25,8 +25,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.net.ssl.KeyManager; +import javax.net.ssl.TrustManager; + import java.net.InetSocketAddress; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.time.Instant; @@ -35,7 +40,6 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.ServiceLoader; @@ -43,9 +47,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.net.ssl.KeyManager; -import javax.net.ssl.TrustManager; - public class HttpClientUtils { private static final class HeaderInterceptor implements Interceptor { @@ -79,18 +80,13 @@ public void before(BasicBuilder builder, HttpRequest request, RequestTags tags) private HttpClientUtils() { } - public static URL getProxyUrl(Config config) throws MalformedURLException { - URL master = new URL(config.getMasterUrl()); - String host = master.getHost(); - if (isHostMatchedByNoProxy(host, config.getNoProxy())) { - return null; - } + static URI getProxyUri(URL master, Config config) throws URISyntaxException { String proxy = config.getHttpsProxy(); if (master.getProtocol().equals("http")) { proxy = config.getHttpProxy(); } if (proxy != null) { - URL proxyUrl = new URL(proxy); + URI proxyUrl = new URI(proxy); if (proxyUrl.getPort() < 0) { throw new IllegalArgumentException("Failure in creating proxy URL. Proxy port is required!"); } @@ -186,42 +182,68 @@ public static void applyCommonConfiguration(Config config, HttpClient.Builder bu } try { - - // Only check proxy if it's a full URL with protocol - if (config.getMasterUrl().toLowerCase(Locale.ROOT).startsWith(Config.HTTP_PROTOCOL_PREFIX) - || config.getMasterUrl().startsWith(Config.HTTPS_PROTOCOL_PREFIX)) { - try { - URL proxyUrl = HttpClientUtils.getProxyUrl(config); - if (proxyUrl != null) { - builder.proxyAddress(new InetSocketAddress(proxyUrl.getHost(), proxyUrl.getPort())); - - if (config.getProxyUsername() != null) { - builder.proxyAuthorization(basicCredentials(config.getProxyUsername(), config.getProxyPassword())); - } - } else { - builder.proxyAddress(null); - } - } catch (MalformedURLException e) { - throw new KubernetesClientException("Invalid proxy server configuration", e); - } - } + configureProxy(config, builder); TrustManager[] trustManagers = SSLUtils.trustManagers(config); KeyManager[] keyManagers = SSLUtils.keyManagers(config); - builder.sslContext(keyManagers, trustManagers); - - if (config.getTlsVersions() != null && config.getTlsVersions().length > 0) { - builder.tlsVersions(config.getTlsVersions()); - } - } catch (Exception e) { throw KubernetesClientException.launderThrowable(e); } + + if (config.getTlsVersions() != null && config.getTlsVersions().length > 0) { + builder.tlsVersions(config.getTlsVersions()); + } + HttpClientUtils.createApplicableInterceptors(config, factory).forEach(builder::addOrReplaceInterceptor); } - private static boolean isHostMatchedByNoProxy(String host, String[] noProxies) throws MalformedURLException { + static void configureProxy(Config config, HttpClient.Builder builder) + throws URISyntaxException, MalformedURLException { + URL master; + try { + master = new URL(config.getMasterUrl()); + } catch (MalformedURLException e) { + // Only check proxy if it's a full URL with protocol + return; + } + URI proxyUri = HttpClientUtils.getProxyUri(master, config); + if (proxyUri == null) { + // not configured for a proxy + return; + } + String host = master.getHost(); + if (isHostMatchedByNoProxy(host, config.getNoProxy())) { + builder.proxyType(HttpClient.ProxyType.DIRECT); + } else { + builder.proxyAddress(new InetSocketAddress(proxyUri.getHost(), proxyUri.getPort())); + + if (config.getProxyUsername() != null) { + builder.proxyAuthorization(basicCredentials(config.getProxyUsername(), config.getProxyPassword())); + } + + builder.proxyType(toProxyType(proxyUri.getScheme())); + } + } + + static HttpClient.ProxyType toProxyType(String scheme) throws MalformedURLException { + if (scheme == null) { + throw new MalformedURLException("No protocol specified on proxy URL"); + } + scheme = scheme.toLowerCase(); + if (scheme.startsWith("http")) { + return HttpClient.ProxyType.HTTP; + } + if (scheme.equals("socks4")) { + return HttpClient.ProxyType.SOCKS4; + } + if (scheme.equals("socks5")) { + return HttpClient.ProxyType.SOCKS5; + } + throw new MalformedURLException("Unsupported protocol specified on proxy URL"); + } + + static boolean isHostMatchedByNoProxy(String host, String[] noProxies) throws MalformedURLException { for (String noProxy : noProxies == null ? new String[0] : noProxies) { if (INVALID_HOST_PATTERN.matcher(noProxy).find()) { throw new MalformedURLException("NO_PROXY URL contains invalid entry: '" + noProxy + "'"); diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/HttpClientUtilsTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/HttpClientUtilsTest.java index dcc2bceb51c..c9259fd4c93 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/HttpClientUtilsTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/HttpClientUtilsTest.java @@ -19,6 +19,7 @@ import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.http.HttpClient; +import io.fabric8.kubernetes.client.http.HttpClient.Builder; import io.fabric8.kubernetes.client.http.Interceptor; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -33,7 +34,10 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; +import java.net.InetSocketAddress; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -45,7 +49,9 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.ArgumentMatchers.any; @@ -56,6 +62,32 @@ class HttpClientUtilsTest { + @Test + void toProxyTypeTestUnknown() throws MalformedURLException { + assertThrows(MalformedURLException.class, () -> HttpClientUtils.toProxyType("unknown")); + } + + @Test + void toProxyTypeTestNull() throws MalformedURLException { + assertThrows(MalformedURLException.class, () -> HttpClientUtils.toProxyType(null)); + } + + @Test + void toProxyTypeTestHttps() throws MalformedURLException { + assertEquals(HttpClient.ProxyType.HTTP, HttpClientUtils.toProxyType("https")); + } + + @Test + void testConfigureSocksProxy() throws Exception { + Config config = new ConfigBuilder().withMasterUrl("http://localhost").withHttpProxy("socks5://192.168.0.1:8080").build(); + Builder builder = Mockito.mock(HttpClient.Builder.class, Mockito.RETURNS_SELF); + + HttpClientUtils.configureProxy(config, builder); + + Mockito.verify(builder).proxyType(HttpClient.ProxyType.SOCKS5); + Mockito.verify(builder).proxyAddress(new InetSocketAddress("192.168.0.1", 8080)); + } + @Test void testCreateApplicableInterceptors() { // Given @@ -106,22 +138,17 @@ void setUp() { configBuilder = new ConfigBuilder(); } - @DisplayName("With httpProxy and noProxy matching master url, should return null") - @ParameterizedTest(name = "{index}: Master URL ''{0}'' matched by No Proxy ''{1}'' ") - @MethodSource("masterUrlMatchesNoProxyInput") - void masterUrlMatchesNoProxy(String masterUrl, String[] noProxy) throws MalformedURLException { - // Given - Config config = configBuilder.withHttpProxy("http://proxy.url:8080") - .withMasterUrl(masterUrl).withNoProxy(noProxy).build(); - // When - URL url = HttpClientUtils.getProxyUrl(config); - // Then - assertThat(url).isNull(); + @DisplayName("noProxy matching master hostname") + @ParameterizedTest(name = "{index}: Master hostname ''{0}'' matched by No Proxy ''{1}'' ") + @MethodSource("masterHostnameDoesMatchNoProxyInput") + void masterHostnameDoesMatchNoProxy(String masterHostname, String[] noProxy) + throws MalformedURLException, URISyntaxException { + assertTrue(HttpClientUtils.isHostMatchedByNoProxy(masterHostname, noProxy)); } - Stream masterUrlMatchesNoProxyInput() { + Stream masterHostnameDoesMatchNoProxyInput() { return Stream.of( - arguments("http://192.168.1.100:6443", new String[] { "192.168.1.0/24" }), + arguments("192.168.1.100", new String[] { "192.168.1.0/24" }), arguments("master.example.com", new String[] { "master.example.com" }), arguments("master.example.com", new String[] { ".example.com" }), arguments("master.example.com", @@ -135,22 +162,17 @@ Stream masterUrlMatchesNoProxyInput() { arguments("192.168.1.110", new String[] { "http://192.0.0.0/8" })); } - @DisplayName("With httpProxy and noProxy not matching master url, should return proxy url") - @ParameterizedTest(name = "{index}: Master URL ''{0}'' not matched by No Proxy ''{1}'' ") - @MethodSource("masterUrlDoesNotMatchNoProxyInput") - void masterUrlDoesNotMatchNoProxy(String masterUrl, String[] noProxy) throws MalformedURLException { - // Given - Config config = configBuilder.withHttpProxy("http://proxy.url:8080") - .withMasterUrl(masterUrl).withNoProxy(noProxy).build(); - // When - URL url = HttpClientUtils.getProxyUrl(config); - // Then - assertThat(url).isEqualToWithSortedQueryParameters(new URL("http://proxy.url:8080")); + @DisplayName("noProxy not matching master hostname") + @ParameterizedTest(name = "{index}: Master hostname ''{0}'' not matched by No Proxy ''{1}'' ") + @MethodSource("masterHostnameDoesNotMatchNoProxyInput") + void masterHostnameDoesNotMatchNoProxy(String masterHostname, String[] noProxy) + throws MalformedURLException, URISyntaxException { + assertFalse(HttpClientUtils.isHostMatchedByNoProxy(masterHostname, noProxy)); } - Stream masterUrlDoesNotMatchNoProxyInput() { + Stream masterHostnameDoesNotMatchNoProxyInput() { return Stream.of( - arguments("http://192.168.2.100:6443", new String[] { "192.168.1.0/24" }), + arguments("192.168.2.100", new String[] { "192.168.1.0/24" }), arguments("master.example.com", null), arguments("master.example.com", new String[0]), arguments("master.example.com", new String[] { "master1.example.com" }), @@ -171,11 +193,8 @@ Stream masterUrlDoesNotMatchNoProxyInput() { "*.my.domain.com", "!!!.com", "()-?¿?", "http://proxy url" }) void invalidNoProxyUrlThrowsException(String noProxy) { - // Given - Config config = configBuilder.withHttpProxy("http://proxy.url:8080") - .withMasterUrl("master.url").withNoProxy(noProxy).build(); // When + Then - assertThatThrownBy(() -> HttpClientUtils.getProxyUrl(config)) + assertThatThrownBy(() -> HttpClientUtils.isHostMatchedByNoProxy("http://localhost", new String[] { noProxy })) .isInstanceOf(MalformedURLException.class) .hasMessage("NO_PROXY URL contains invalid entry: '" + noProxy + "'"); } @@ -186,16 +205,16 @@ void whenHttpsProxyUrlWithNoPort_shouldThrowException() { Config config = configBuilder.withMasterUrl("http://localhost").withHttpProxy("http://192.168.0.1").build(); // When + Then assertThatIllegalArgumentException() - .isThrownBy(() -> HttpClientUtils.getProxyUrl(config)) + .isThrownBy(() -> HttpClientUtils.getProxyUri(new URL("http://localhost"), config)) .withMessage("Failure in creating proxy URL. Proxy port is required!"); } @Test - void withNoHttpProxyProvidedReturnsNull() throws MalformedURLException { + void withNoHttpProxyProvidedReturnsNull() throws MalformedURLException, URISyntaxException { // Given - Config config = configBuilder.withMasterUrl("master.url").withNoProxy("other.url").build(); + Config config = configBuilder.withNoProxy("other.url").build(); // When - URL url = HttpClientUtils.getProxyUrl(config); + URI url = HttpClientUtils.getProxyUri(new URL("http://localhost"), config); // Then assertThat(url).isNull(); }