diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 3a3dc484..5768bf6c 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -1357,6 +1357,12 @@ file as the ordering will be lost using a properties file. For example: If you were to use a properties file, the `legacy` path may end up in front of the `users` path rendering the `users` path unreachable. +=== Zuul Http Client + +The default HTTP client used by zuul is now backed by the Apache HTTP Client instead of the +deprecated Ribbon `RestClient. To use `RestClient` or to use the `okhttp3.OkHttpClient` set +`ribbon.restclient.enabled=true` or `ribbon.okhttp.enabled=true` respectively. + === Cookies and Sensitive Headers It's OK to share headers between services in the same system, but you diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java index e1bffacb..a9729321 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java @@ -16,12 +16,18 @@ package org.springframework.cloud.netflix.ribbon; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -30,6 +36,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfigurati import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @@ -79,7 +86,7 @@ public class RibbonAutoConfiguration { @Configuration @ConditionalOnClass(HttpRequest.class) - @ConditionalOnProperty(value = "ribbon.http.client.enabled", matchIfMissing = false) + @ConditionalOnRibbonRestClient protected static class RibbonClientConfig { @Autowired @@ -102,4 +109,24 @@ public class RibbonAutoConfiguration { } } + //TODO: support for autoconfiguring restemplate to use apache http client or okhttp + + @Target({ ElementType.TYPE, ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Conditional(OnRibbonRestClientCondition.class) + @interface ConditionalOnRibbonRestClient { } + + private static class OnRibbonRestClientCondition extends AnyNestedCondition { + public OnRibbonRestClientCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @Deprecated //remove in Edgware" + @ConditionalOnProperty("ribbon.http.client.enabled") + static class ZuulProperty {} + + @ConditionalOnProperty("ribbon.restclient.enabled") + static class RibbonProperty {} + } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java index 3292d6a4..7e43ba7a 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java @@ -24,13 +24,17 @@ import org.apache.http.client.params.ClientPNames; import org.apache.http.client.params.CookiePolicy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient; +import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; -import org.springframework.web.util.UriComponentsBuilder; +import com.netflix.client.AbstractLoadBalancerAwareClient; import com.netflix.client.DefaultLoadBalancerRetryHandler; import com.netflix.client.RetryHandler; import com.netflix.client.config.DefaultClientConfigImpl; @@ -112,27 +116,75 @@ public class RibbonClientConfiguration { return serverList; } - /** - * Create a Netflix {@link RestClient} integrated with Ribbon if none already exists - * in the application context. It is not required for Ribbon to work properly and is - * therefore created lazily if ever another component requires it. - * - * @param config the configuration to use by the underlying Ribbon instance - * @param loadBalancer the load balancer to use by the underlying Ribbon instance - * @param serverIntrospector server introspector to use by the underlying Ribbon instance - * @param retryHandler retry handler to use by the underlying Ribbon instance - * @return a {@link RestClient} instances backed by Ribbon - */ - @Bean - @Lazy - @ConditionalOnMissingBean - public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer, - ServerIntrospector serverIntrospector, RetryHandler retryHandler) { - RestClient client = new OverrideRestClient(config, serverIntrospector); - client.setLoadBalancer(loadBalancer); - client.setRetryHandler(retryHandler); - Monitors.registerObject("Client_" + this.name, client); - return client; + @Configuration + @ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true) + protected static class HttpClientRibbonConfiguration { + @Value("${ribbon.client.name}") + private String name = "client"; + + @Bean + @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class) + public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient( + IClientConfig config, ServerIntrospector serverIntrospector, + ILoadBalancer loadBalancer, RetryHandler retryHandler) { + RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient( + config, serverIntrospector); + client.setLoadBalancer(loadBalancer); + client.setRetryHandler(retryHandler); + Monitors.registerObject("Client_" + this.name, client); + return client; + } + } + + @Configuration + @ConditionalOnProperty("ribbon.okhttp.enabled") + @ConditionalOnClass(name = "okhttp3.OkHttpClient") + protected static class OkHttpRibbonConfiguration { + @Value("${ribbon.client.name}") + private String name = "client"; + + @Bean + @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class) + public OkHttpLoadBalancingClient okHttpLoadBalancingClient(IClientConfig config, + ServerIntrospector serverIntrospector, ILoadBalancer loadBalancer, + RetryHandler retryHandler) { + OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(config, + serverIntrospector); + client.setLoadBalancer(loadBalancer); + client.setRetryHandler(retryHandler); + Monitors.registerObject("Client_" + this.name, client); + return client; + } + } + + @Configuration + @RibbonAutoConfiguration.ConditionalOnRibbonRestClient + protected static class RestClientRibbonConfiguration { + @Value("${ribbon.client.name}") + private String name = "client"; + + /** + * Create a Netflix {@link RestClient} integrated with Ribbon if none already exists + * in the application context. It is not required for Ribbon to work properly and is + * therefore created lazily if ever another component requires it. + * + * @param config the configuration to use by the underlying Ribbon instance + * @param loadBalancer the load balancer to use by the underlying Ribbon instance + * @param serverIntrospector server introspector to use by the underlying Ribbon instance + * @param retryHandler retry handler to use by the underlying Ribbon instance + * @return a {@link RestClient} instances backed by Ribbon + */ + @Bean + @Lazy + @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class) + public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer, + ServerIntrospector serverIntrospector, RetryHandler retryHandler) { + RestClient client = new OverrideRestClient(config, serverIntrospector); + client.setLoadBalancer(loadBalancer); + client.setRetryHandler(retryHandler); + Monitors.registerObject("Client_" + this.name, client); + return client; + } } @Bean diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java index cbe55202..1fa68564 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java @@ -16,33 +16,55 @@ package org.springframework.cloud.netflix.ribbon.apache; -import com.netflix.client.config.CommonClientConfigKey; -import com.netflix.client.config.IClientConfig; -import com.netflix.loadbalancer.ILoadBalancer; +import java.net.URI; + import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.HttpClientBuilder; +import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient; import org.springframework.web.util.UriComponentsBuilder; -import java.net.URI; +import com.netflix.client.config.CommonClientConfigKey; +import com.netflix.client.config.DefaultClientConfigImpl; +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; + +import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded; /** * @author Christian Lohmann */ +//TODO: rename (ie new class that extends this in Dalston) to ApacheHttpLoadBalancingClient public class RibbonLoadBalancingHttpClient extends AbstractLoadBalancingClient { private final HttpClient delegate = HttpClientBuilder.create().disableCookieManagement().build(); + private final IClientConfig config; + private final ServerIntrospector serverIntrospector; + @Deprecated public RibbonLoadBalancingHttpClient() { super(); + this.config = new DefaultClientConfigImpl(); + this.serverIntrospector = new DefaultServerIntrospector(); } + @Deprecated public RibbonLoadBalancingHttpClient(final ILoadBalancer lb) { super(lb); + this.config = new DefaultClientConfigImpl(); + this.serverIntrospector = new DefaultServerIntrospector(); + } + + public RibbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector) { + this.config = config; + this.serverIntrospector = serverIntrospector; + initWithNiwsConfig(config); } @Override @@ -76,4 +98,10 @@ public class RibbonLoadBalancingHttpClient return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI()); } + @Override + public URI reconstructURIWithServer(Server server, URI original) { + URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector, server); + return super.reconstructURIWithServer(server, uri); + } + } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/okhttp/OkHttpLoadBalancingClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/okhttp/OkHttpLoadBalancingClient.java index d96b9b7d..001e57e2 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/okhttp/OkHttpLoadBalancingClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/okhttp/OkHttpLoadBalancingClient.java @@ -19,15 +19,22 @@ package org.springframework.cloud.netflix.ribbon.okhttp; import java.net.URI; import java.util.concurrent.TimeUnit; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; +import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; import org.springframework.cloud.netflix.ribbon.support.AbstractLoadBalancingClient; import org.springframework.web.util.UriComponentsBuilder; import com.netflix.client.config.CommonClientConfigKey; +import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded; /** * @author Spencer Gibb @@ -35,13 +42,28 @@ import com.netflix.loadbalancer.ILoadBalancer; public class OkHttpLoadBalancingClient extends AbstractLoadBalancingClient { private final OkHttpClient delegate = new OkHttpClient(); + private final IClientConfig config; + private final ServerIntrospector serverIntrospector; + @Deprecated public OkHttpLoadBalancingClient() { super(); + config = new DefaultClientConfigImpl(); + serverIntrospector = new DefaultServerIntrospector(); } + @Deprecated public OkHttpLoadBalancingClient(final ILoadBalancer lb) { super(lb); + config = new DefaultClientConfigImpl(); + serverIntrospector = new DefaultServerIntrospector(); + } + + public OkHttpLoadBalancingClient(IClientConfig config, + ServerIntrospector serverIntrospector) { + this.config = config; + this.serverIntrospector = serverIntrospector; + initWithNiwsConfig(config); } @Override @@ -87,4 +109,11 @@ public class OkHttpLoadBalancingClient return builder.build(); } + + @Override + public URI reconstructURIWithServer(Server server, URI original) { + URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector, + server); + return super.reconstructURIWithServer(server, uri); + } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java index b21d4d40..8777cd92 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java @@ -16,12 +16,18 @@ package org.springframework.cloud.netflix.zuul; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Collections; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.trace.TraceRepository; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; @@ -52,6 +58,7 @@ import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; /** @@ -84,7 +91,7 @@ public class ZuulProxyConfiguration extends ZuulConfiguration { } @Configuration - @ConditionalOnProperty(name = "zuul.ribbon.httpclient.enabled", matchIfMissing = true) + @ConditionalOnRibbonHttpClient protected static class HttpClientRibbonConfiguration { @Bean @@ -96,7 +103,7 @@ public class ZuulProxyConfiguration extends ZuulConfiguration { } @Configuration - @ConditionalOnProperty("zuul.ribbon.restclient.enabled") + @ConditionalOnRibbonRestClient protected static class RestClientRibbonConfiguration { @Bean @@ -108,7 +115,7 @@ public class ZuulProxyConfiguration extends ZuulConfiguration { } @Configuration - @ConditionalOnProperty("zuul.ribbon.okhttp.enabled") + @ConditionalOnRibbonOkHttpClient @ConditionalOnClass(name = "okhttp3.OkHttpClient") protected static class OkHttpRibbonConfiguration { @@ -120,6 +127,63 @@ public class ZuulProxyConfiguration extends ZuulConfiguration { } } + @Target({ ElementType.TYPE, ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Conditional(OnRibbonHttpClientCondition.class) + @interface ConditionalOnRibbonHttpClient { } + + private static class OnRibbonHttpClientCondition extends AnyNestedCondition { + public OnRibbonHttpClientCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @Deprecated //remove in Edgware" + @ConditionalOnProperty(name = "zuul.ribbon.httpclient.enabled", matchIfMissing = true) + static class ZuulProperty {} + + @ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true) + static class RibbonProperty {} + } + + @Target({ ElementType.TYPE, ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Conditional(OnRibbonOkHttpClientCondition.class) + @interface ConditionalOnRibbonOkHttpClient { } + + private static class OnRibbonOkHttpClientCondition extends AnyNestedCondition { + public OnRibbonOkHttpClientCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @Deprecated //remove in Edgware" + @ConditionalOnProperty("zuul.ribbon.okhttp.enabled") + static class ZuulProperty {} + + @ConditionalOnProperty("ribbon.okhttp.enabled") + static class RibbonProperty {} + } + + @Target({ ElementType.TYPE, ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Conditional(OnRibbonRestClientCondition.class) + @interface ConditionalOnRibbonRestClient { } + + private static class OnRibbonRestClientCondition extends AnyNestedCondition { + public OnRibbonRestClientCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @Deprecated //remove in Edgware" + @ConditionalOnProperty("zuul.ribbon.restclient.enabled") + static class ZuulProperty {} + + @ConditionalOnProperty("ribbon.restclient.enabled") + static class RibbonProperty {} + } + // pre filters @Bean public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfigurationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfigurationTests.java index 4e6b6a93..e6d05f7c 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfigurationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfigurationTests.java @@ -17,17 +17,27 @@ package org.springframework.cloud.netflix.ribbon; import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import com.netflix.client.AbstractLoadBalancerAwareClient; +import com.netflix.niws.client.http.RestClient; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration.OverrideRestClient; import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.Server; +import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient; +import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; @@ -82,11 +92,11 @@ public class RibbonClientConfigurationTests { Server server = new Server("foo", 7777); when(this.inspector.isSecure(server)).thenReturn(true); - OverrideRestClient overrideRestClient = new OverrideRestClient(this.config, - this.inspector); - URI uri = overrideRestClient.reconstructURIWithServer(server, - new URI("http://foo/")); - assertThat(uri, is(new URI("https://foo:7777/"))); + for (AbstractLoadBalancerAwareClient client : clients()) { + URI uri = client.reconstructURIWithServer(server, + new URI("http://foo/")); + assertThat(getReason(client), uri, is(new URI("https://foo:7777/"))); + } } @Test @@ -94,11 +104,15 @@ public class RibbonClientConfigurationTests { Server server = new Server("foo", 7777); when(this.inspector.isSecure(server)).thenReturn(false); - OverrideRestClient overrideRestClient = new OverrideRestClient(this.config, - this.inspector); - URI uri = overrideRestClient.reconstructURIWithServer(server, - new URI("http://foo/")); - assertThat(uri, is(new URI("http://foo:7777/"))); + for (AbstractLoadBalancerAwareClient client : clients()) { + URI uri = client.reconstructURIWithServer(server, + new URI("http://foo/")); + assertThat(getReason(client), uri, is(new URI("http://foo:7777/"))); + } + } + + String getReason(AbstractLoadBalancerAwareClient client) { + return client.getClass().getSimpleName()+" failed"; } @Test @@ -106,11 +120,57 @@ public class RibbonClientConfigurationTests { Server server = new Server("foo", 7777); when(this.inspector.isSecure(server)).thenReturn(true); - OverrideRestClient overrideRestClient = new OverrideRestClient(this.config, - this.inspector); - URI uri = overrideRestClient.reconstructURIWithServer(server, - new URI("http://foo/%20bar")); - assertThat(uri, is(new URI("https://foo:7777/%20bar"))); + for (AbstractLoadBalancerAwareClient client : clients()) { + URI uri = client.reconstructURIWithServer(server, + new URI("http://foo/%20bar")); + assertThat(getReason(client), uri, is(new URI("https://foo:7777/%20bar"))); + } + } + + private List clients() { + ArrayList clients = new ArrayList<>(); + clients.add(new OverrideRestClient(this.config, this.inspector)); + clients.add(new RibbonLoadBalancingHttpClient(this.config, this.inspector)); + clients.add(new OkHttpLoadBalancingClient(this.config, this.inspector)); + return clients; + } + + @Test + public void testDefaultsToApacheHttpClient() { + testClient(RibbonLoadBalancingHttpClient.class, null, RestClient.class, OkHttpLoadBalancingClient.class); + testClient(RibbonLoadBalancingHttpClient.class, "ribbon.httpclient.enabled", RestClient.class, OkHttpLoadBalancingClient.class); + } + + @Test + public void testEnableRestClient() { + testClient(RestClient.class, "ribbon.restclient.enabled", RibbonLoadBalancingHttpClient.class, + OkHttpLoadBalancingClient.class); + } + + @Test + public void testEnableOkHttpClient() { + testClient(OkHttpLoadBalancingClient.class, "ribbon.okhttp.enabled", RibbonLoadBalancingHttpClient.class, + RestClient.class); + } + + void testClient(Class clientType, String property, Class... excludedTypes) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(RibbonAutoConfiguration.class, + RibbonClientConfiguration.class); + if (property != null) { + EnvironmentTestUtils.addEnvironment(context, property); + } + context.refresh(); + context.getBean(clientType); + for (Class excludedType : excludedTypes) { + assertThat("has "+excludedType.getSimpleName()+ " instance", hasInstance(context, excludedType), is(false)); + } + context.close(); + } + + private boolean hasInstance(ListableBeanFactory lbf, Class requiredType) { + return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(lbf, + requiredType).length > 0; } static class TestRestClient extends OverrideRestClient { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringClientFactoryTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringClientFactoryTests.java index 531a231e..bdcff563 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringClientFactoryTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringClientFactoryTests.java @@ -19,7 +19,6 @@ package org.springframework.cloud.netflix.ribbon; import org.apache.http.client.params.ClientPNames; import org.apache.http.client.params.CookiePolicy; import org.junit.Test; -import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -28,6 +27,7 @@ import com.netflix.niws.client.http.RestClient; import com.sun.jersey.client.apache4.ApacheHttpClient4; import static org.junit.Assert.assertEquals; +import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment; /** * @author Dave Syer @@ -41,7 +41,7 @@ public class SpringClientFactoryTests { SpringClientFactory factory = new SpringClientFactory(); AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext( RibbonAutoConfiguration.class, ArchaiusAutoConfiguration.class); - EnvironmentTestUtils.addEnvironment(parent, "foo.ribbon.MaxAutoRetries:2"); + addEnvironment(parent, "foo.ribbon.MaxAutoRetries:2"); factory.setApplicationContext(parent); DefaultLoadBalancerRetryHandler retryHandler = (DefaultLoadBalancerRetryHandler) factory .getLoadBalancerContext("foo").getRetryHandler(); @@ -54,8 +54,10 @@ public class SpringClientFactoryTests { @Test public void testCookiePolicy() { SpringClientFactory factory = new SpringClientFactory(); - AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext( - RibbonAutoConfiguration.class, ArchaiusAutoConfiguration.class); + AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(); + addEnvironment(parent, "ribbon.restclient.enabled=true"); + parent.register(RibbonAutoConfiguration.class, ArchaiusAutoConfiguration.class); + parent.refresh(); factory.setApplicationContext(parent); RestClient client = factory.getClient("foo", RestClient.class); ApacheHttpClient4 jerseyClient = (ApacheHttpClient4) client.getJerseyClient(); diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfigurationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfigurationTests.java new file mode 100644 index 00000000..88745b59 --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfigurationTests.java @@ -0,0 +1,90 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.cloud.netflix.zuul; + +import org.junit.Test; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory; +import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory; +import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory; +import org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommandFactory; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; + +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + +/** + * @author Spencer Gibb + */ +public class ZuulProxyConfigurationTests { + + @Test + public void testDefaultsToApacheHttpClient() { + testClient(HttpClientRibbonCommandFactory.class, null); + testClient(HttpClientRibbonCommandFactory.class, "zuul.ribbon.httpclient.enabled=true"); + testClient(HttpClientRibbonCommandFactory.class, "ribbon.httpclient.enabled=true"); + } + + @Test + public void testEnableRestClient() { + testClient(RestClientRibbonCommandFactory.class, "zuul.ribbon.restclient.enabled=true"); + testClient(RestClientRibbonCommandFactory.class, "ribbon.restclient.enabled=true"); + } + + @Test + public void testEnableOkHttpClient() { + testClient(OkHttpRibbonCommandFactory.class, "zuul.ribbon.okhttp.enabled=true"); + testClient(OkHttpRibbonCommandFactory.class, "ribbon.okhttp.enabled=true"); + } + + void testClient(Class clientType, String property) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(TestConfig.class, ZuulProxyConfiguration.class); + if (property != null) { + EnvironmentTestUtils.addEnvironment(context, property); + } + context.refresh(); + RibbonCommandFactory factory = context.getBean(RibbonCommandFactory.class); + assertThat("RibbonCommandFactory is wrong type for property: "+property, factory, is(instanceOf(clientType))); + context.close(); + } + + static class TestConfig { + @Bean + ServerProperties serverProperties() { + return new ServerProperties(); + } + + @Bean + SpringClientFactory springClientFactory() { + return mock(SpringClientFactory.class); + } + + @Bean + DiscoveryClient discoveryClient() { + return mock(DiscoveryClient.class); + } + } + +} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java index f8ff51f5..b41940e7 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java @@ -35,6 +35,7 @@ import org.springframework.boot.autoconfigure.web.ErrorAttributes; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.RibbonClients; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; @@ -200,10 +201,10 @@ public class HttpClientRibbonCommandIntegrationTests extends ZuulProxyTestBase { @Bean public RibbonLoadBalancingHttpClient ribbonClient(IClientConfig config, ILoadBalancer loadBalancer, RetryHandler retryHandler) { - final RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(); + final RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(config, + new DefaultServerIntrospector()); client.setLoadBalancer(loadBalancer); client.setRetryHandler(retryHandler); - client.initWithNiwsConfig(config); return client; } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/restclient/RestClientRibbonCommandIntegrationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/restclient/RestClientRibbonCommandIntegrationTests.java index c70e49fc..826a574b 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/restclient/RestClientRibbonCommandIntegrationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/restclient/RestClientRibbonCommandIntegrationTests.java @@ -88,7 +88,7 @@ import lombok.SneakyThrows; "zuul.routes.another: /another/twolevel/**", "zuul.routes.simple: /simple/**", "zuul.routes.badhost: /badhost/**", "zuul.ignoredHeaders: X-Header", "zuul.routes.rnd: /rnd/**", "rnd.ribbon.listOfServers: ${random.value}", - "zuul.removeSemicolonContent: false" }) + "zuul.removeSemicolonContent: false", "ribbon.restclient.enabled=true"}) @DirtiesContext public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {