Support for ServerIntrospector in Feign client
Other Ribbon clients in Spring Cloud use a ServerIntrospector to check if the Server wanted to be secure. This change levels things up (and makes the Spring Feign Client a lit easier to customize). See gh-414
This commit is contained in:
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.feign.ribbon;
|
||||
|
||||
import feign.ribbon.LBClient;
|
||||
import feign.ribbon.LBClientFactory;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* LBClientFactory that caches entries created.
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CachingLBClientFactory implements LBClientFactory {
|
||||
|
||||
private volatile Map<String, LBClient> cache = new ConcurrentReferenceHashMap<>();
|
||||
private final LBClientFactory delegate;
|
||||
|
||||
public CachingLBClientFactory(LBClientFactory delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LBClient create(String clientName) {
|
||||
if (cache.containsKey(clientName)) {
|
||||
return cache.get(clientName);
|
||||
}
|
||||
LBClient client = delegate.create(clientName);
|
||||
cache.put(clientName, client);
|
||||
return client;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.feign.ribbon;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
|
||||
/**
|
||||
* Factory for SpringLoadBalancer instances that caches the entries created.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class CachingSpringLoadBalancerFactory {
|
||||
|
||||
private final SpringClientFactory factory;
|
||||
|
||||
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
public CachingSpringLoadBalancerFactory(SpringClientFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public FeignLoadBalancer create(String clientName) {
|
||||
if (this.cache.containsKey(clientName)) {
|
||||
return this.cache.get(clientName);
|
||||
}
|
||||
IClientConfig config = this.factory.getClientConfig(clientName);
|
||||
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
|
||||
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
|
||||
FeignLoadBalancer client = new FeignLoadBalancer(lb, config, serverIntrospector);
|
||||
this.cache.put(clientName, client);
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2015 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.
|
||||
@@ -21,6 +21,7 @@ import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.netflix.client.AbstractLoadBalancerAwareClient;
|
||||
@@ -32,35 +33,29 @@ import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
|
||||
public class RibbonLoadBalancer
|
||||
extends
|
||||
AbstractLoadBalancerAwareClient<RibbonLoadBalancer.RibbonRequest, RibbonLoadBalancer.RibbonResponse> {
|
||||
|
||||
private final Client delegate;
|
||||
public class FeignLoadBalancer extends
|
||||
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
|
||||
|
||||
private final int connectTimeout;
|
||||
|
||||
private final int readTimeout;
|
||||
|
||||
private final IClientConfig clientConfig;
|
||||
private final ServerIntrospector serverIntrospector;
|
||||
|
||||
private final boolean secure;
|
||||
|
||||
public RibbonLoadBalancer(Client delegate, ILoadBalancer lb,
|
||||
IClientConfig clientConfig) {
|
||||
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
|
||||
ServerIntrospector serverIntrospector) {
|
||||
super(lb, clientConfig);
|
||||
this.setRetryHandler(RetryHandler.DEFAULT);
|
||||
this.clientConfig = clientConfig;
|
||||
this.secure = clientConfig.get(CommonClientConfigKey.IsSecure);
|
||||
this.delegate = delegate;
|
||||
this.connectTimeout = clientConfig.get(CommonClientConfigKey.ConnectTimeout);
|
||||
this.readTimeout = clientConfig.get(CommonClientConfigKey.ReadTimeout);
|
||||
this.serverIntrospector = serverIntrospector;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,31 +63,24 @@ public class RibbonLoadBalancer
|
||||
throws IOException {
|
||||
Request.Options options;
|
||||
if (configOverride != null) {
|
||||
options = new Request.Options(configOverride.get(
|
||||
CommonClientConfigKey.ConnectTimeout, this.connectTimeout),
|
||||
options = new Request.Options(
|
||||
configOverride.get(CommonClientConfigKey.ConnectTimeout,
|
||||
this.connectTimeout),
|
||||
(configOverride.get(CommonClientConfigKey.ReadTimeout,
|
||||
this.readTimeout)));
|
||||
}
|
||||
else {
|
||||
options = new Request.Options(this.connectTimeout, this.readTimeout);
|
||||
}
|
||||
if (isSecure(configOverride)) {
|
||||
URI secureUri = UriComponentsBuilder.fromUri(request.getUri())
|
||||
.scheme("https").build().toUri();
|
||||
request = new RibbonRequest(request.toRequest(), secureUri);
|
||||
}
|
||||
Response response = this.delegate.execute(request.toRequest(), options);
|
||||
Response response = request.client().execute(request.toRequest(), options);
|
||||
return new RibbonResponse(request.getUri(), response);
|
||||
}
|
||||
|
||||
private boolean isSecure(IClientConfig config) {
|
||||
return (config != null) ? config.get(CommonClientConfigKey.IsSecure) : secure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
|
||||
RibbonRequest request, IClientConfig requestConfig) {
|
||||
if (this.clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations, false)) {
|
||||
if (this.clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations,
|
||||
false)) {
|
||||
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
|
||||
requestConfig);
|
||||
}
|
||||
@@ -106,11 +94,24 @@ public class RibbonLoadBalancer
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI reconstructURIWithServer(Server server, URI original) {
|
||||
String scheme = original.getScheme();
|
||||
if (!"https".equals(scheme) && (this.serverIntrospector.isSecure(server)
|
||||
|| this.clientConfig.get(CommonClientConfigKey.IsSecure, false))) {
|
||||
original = UriComponentsBuilder.fromUri(original).scheme("https").build()
|
||||
.toUri();
|
||||
}
|
||||
return super.reconstructURIWithServer(server, original);
|
||||
}
|
||||
|
||||
static class RibbonRequest extends ClientRequest implements Cloneable {
|
||||
|
||||
private final Request request;
|
||||
private final Client client;
|
||||
|
||||
RibbonRequest(Request request, URI uri) {
|
||||
RibbonRequest(Client client, Request request, URI uri) {
|
||||
this.client = client;
|
||||
this.request = request;
|
||||
setUri(uri);
|
||||
}
|
||||
@@ -121,9 +122,13 @@ public class RibbonLoadBalancer
|
||||
.body(this.request.body(), this.request.charset()).request();
|
||||
}
|
||||
|
||||
Client client() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
return new RibbonRequest(this.request, getUri());
|
||||
return new RibbonRequest(this.client, this.request, getUri());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,4 +180,4 @@ public class RibbonLoadBalancer
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
import feign.ribbon.LBClientFactory;
|
||||
import feign.ribbon.RibbonClient;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
@@ -29,12 +26,13 @@ import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Feign;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
|
||||
/**
|
||||
* Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a
|
||||
@@ -47,27 +45,19 @@ import org.springframework.context.annotation.Primary;
|
||||
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||
public class FeignRibbonClientAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private SpringClientFactory factory;
|
||||
|
||||
@Bean
|
||||
public SpringLBClientFactory springLBClientFactory() {
|
||||
return new SpringLBClientFactory(factory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public CachingLBClientFactory cachingLBClientFactory() {
|
||||
return new CachingLBClientFactory(springLBClientFactory());
|
||||
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
|
||||
SpringClientFactory factory) {
|
||||
return new CachingSpringLoadBalancerFactory(factory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Client feignClient() {
|
||||
return RibbonClient.builder().lbClientFactory(cachingLBClientFactory()).build();
|
||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory) {
|
||||
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(ApacheHttpClient.class)
|
||||
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
|
||||
@@ -76,24 +66,19 @@ public class FeignRibbonClientAutoConfiguration {
|
||||
@Autowired(required = false)
|
||||
private HttpClient httpClient;
|
||||
|
||||
@Autowired(required = false)
|
||||
private LBClientFactory lbClientFactory;
|
||||
@Autowired
|
||||
CachingSpringLoadBalancerFactory cachingFactory;
|
||||
|
||||
@Bean
|
||||
public Client feignClient() {
|
||||
RibbonClient.Builder builder = RibbonClient.builder();
|
||||
|
||||
if (httpClient != null) {
|
||||
builder.delegate(new ApacheHttpClient(httpClient));
|
||||
} else {
|
||||
builder.delegate(new ApacheHttpClient());
|
||||
ApacheHttpClient delegate;
|
||||
if (this.httpClient != null) {
|
||||
delegate = new ApacheHttpClient(this.httpClient);
|
||||
}
|
||||
|
||||
if (lbClientFactory != null) {
|
||||
builder.lbClientFactory(lbClientFactory);
|
||||
else {
|
||||
delegate = new ApacheHttpClient();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
return new LoadBalancerFeignClient(delegate, this.cachingFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2015 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.feign.ribbon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class LoadBalancerFeignClient implements Client {
|
||||
|
||||
private final Client delegate;
|
||||
private CachingSpringLoadBalancerFactory lbClientFactory;
|
||||
|
||||
public LoadBalancerFeignClient(Client delegate, CachingSpringLoadBalancerFactory lbClientFactory) {
|
||||
this.delegate = delegate;
|
||||
this.lbClientFactory = lbClientFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
try {
|
||||
URI asUri = URI.create(request.url());
|
||||
String clientName = asUri.getHost();
|
||||
URI uriWithoutHost = cleanUrl(request.url(), clientName);
|
||||
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(this.delegate,
|
||||
request, uriWithoutHost);
|
||||
return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
|
||||
new FeignOptionsClientConfig(options)).toResponse();
|
||||
}
|
||||
catch (ClientException e) {
|
||||
if (e.getCause() instanceof IOException) {
|
||||
throw IOException.class.cast(e.getCause());
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Client getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
static URI cleanUrl(String originalUrl, String host) {
|
||||
return URI.create(originalUrl.replaceFirst(host, ""));
|
||||
}
|
||||
|
||||
private FeignLoadBalancer lbClient(String clientName) {
|
||||
return this.lbClientFactory.create(clientName);
|
||||
}
|
||||
|
||||
static class FeignOptionsClientConfig extends DefaultClientConfigImpl {
|
||||
|
||||
public FeignOptionsClientConfig(Request.Options options) {
|
||||
setProperty(CommonClientConfigKey.ConnectTimeout,
|
||||
options.connectTimeoutMillis());
|
||||
setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadProperties(String clientName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDefaultValues() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.feign.ribbon;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import feign.ribbon.LBClient;
|
||||
import feign.ribbon.LBClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SpringLBClientFactory implements LBClientFactory {
|
||||
|
||||
private final SpringClientFactory factory;
|
||||
|
||||
public SpringLBClientFactory(SpringClientFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LBClient create(String clientName) {
|
||||
IClientConfig config = factory.getClientConfig(clientName);
|
||||
ILoadBalancer lb = factory.getLoadBalancer(clientName);
|
||||
return LBClient.create(lb, config);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.netflix.ribbon;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.ribbon.Ribbon;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
@@ -36,6 +35,7 @@ import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.netflix.client.IClient;
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.ribbon.Ribbon;
|
||||
|
||||
/**
|
||||
* Auto configuration for Ribbon (client side load balancing).
|
||||
@@ -93,8 +93,8 @@ public class RibbonAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
|
||||
return new RibbonClientHttpRequestFactory(springClientFactory,
|
||||
loadBalancerClient);
|
||||
return new RibbonClientHttpRequestFactory(this.springClientFactory,
|
||||
this.loadBalancerClient);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ public class RibbonClientConfiguration {
|
||||
@Override
|
||||
public URI reconstructURIWithServer(Server server, URI original) {
|
||||
String scheme = original.getScheme();
|
||||
if (!"https".equals(scheme) && serverIntrospector.isSecure(server)) {
|
||||
if (!"https".equals(scheme) && this.serverIntrospector.isSecure(server)) {
|
||||
original = UriComponentsBuilder.fromUri(original).scheme("https").build()
|
||||
.toUri();
|
||||
}
|
||||
|
||||
@@ -16,28 +16,30 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import feign.ribbon.LBClient;
|
||||
import feign.ribbon.LBClientFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CachingLBClientFactoryTests {
|
||||
public class CachingSpringLoadBalancerFactoryTests {
|
||||
|
||||
@Mock
|
||||
private LBClientFactory delegate;
|
||||
private SpringClientFactory delegate;
|
||||
|
||||
private CachingLBClientFactory factory;
|
||||
private CachingSpringLoadBalancerFactory factory;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
@@ -47,32 +49,29 @@ public class CachingLBClientFactoryTests {
|
||||
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
|
||||
config.set(CommonClientConfigKey.ReadTimeout, 500);
|
||||
|
||||
LBClient client1 = LBClient.create(null, config);
|
||||
LBClient client2 = LBClient.create(null, config);
|
||||
when(this.delegate.getClientConfig("client1")).thenReturn(config);
|
||||
when(this.delegate.getClientConfig("client2")).thenReturn(config);
|
||||
|
||||
when(delegate.create("client1")).thenReturn(client1);
|
||||
when(delegate.create("client2")).thenReturn(client2);
|
||||
|
||||
factory = new CachingLBClientFactory(delegate);
|
||||
this.factory = new CachingSpringLoadBalancerFactory(this.delegate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delegateCreatesWhenMissing() {
|
||||
LBClient client = factory.create("client1");
|
||||
FeignLoadBalancer client = this.factory.create("client1");
|
||||
assertNotNull("client was null", client);
|
||||
|
||||
verify(delegate, times(1)).create("client1");
|
||||
verify(this.delegate, times(1)).getClientConfig("client1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheWorks() {
|
||||
LBClient client = factory.create("client2");
|
||||
FeignLoadBalancer client = this.factory.create("client2");
|
||||
assertNotNull("client was null", client);
|
||||
|
||||
client = factory.create("client2");
|
||||
client = this.factory.create("client2");
|
||||
assertNotNull("client was null", client);
|
||||
|
||||
verify(delegate, times(1)).create("client2");
|
||||
verify(this.delegate, times(1)).getClientConfig("client2");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,19 +16,27 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign.ribbon;
|
||||
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.netflix.loadbalancer.*;
|
||||
import feign.ribbon.RibbonClient;
|
||||
import org.hamcrest.CustomMatcher;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.AbstractLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.LoadBalancerStats;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerStats;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
@@ -53,6 +61,16 @@ public class FeignRibbonClientTests {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <C> C getInstance(String name, Class<C> type) {
|
||||
if (type.isAssignableFrom(ServerIntrospector.class)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
C instance = (C) new DefaultServerIntrospector();
|
||||
return instance;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILoadBalancer getLoadBalancer(String name) {
|
||||
return FeignRibbonClientTests.this.loadBalancer;
|
||||
@@ -61,10 +79,7 @@ public class FeignRibbonClientTests {
|
||||
|
||||
// Even though we don't maintain FeignRibbonClient, keep these tests
|
||||
// around to make sure the expected behaviour doesn't break
|
||||
private Client client = RibbonClient.builder()
|
||||
.lbClientFactory(new SpringLBClientFactory(factory))
|
||||
.delegate(delegate)
|
||||
.build();
|
||||
private Client client = new LoadBalancerFeignClient(this.delegate, new CachingSpringLoadBalancerFactory(this.factory));
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
|
||||
@@ -13,30 +13,32 @@ import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.RibbonLoadBalancer.RibbonRequest;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.RibbonLoadBalancer.RibbonResponse;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.RibbonRequest;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.RibbonResponse;
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Request.Options;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
public class RibbonLoadBalancerTests {
|
||||
|
||||
@@ -47,7 +49,9 @@ public class RibbonLoadBalancerTests {
|
||||
@Mock
|
||||
private IClientConfig config;
|
||||
|
||||
private RibbonLoadBalancer ribbonLoadBalancer;
|
||||
private FeignLoadBalancer feignLoadBalancer;
|
||||
|
||||
private ServerIntrospector inspector = new DefaultServerIntrospector();
|
||||
|
||||
private Integer defaultConnectTimeout = 10000;
|
||||
private Integer defaultReadTimeout = 10000;
|
||||
@@ -55,29 +59,32 @@ public class RibbonLoadBalancerTests {
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(config.get(MaxAutoRetries, DEFAULT_MAX_AUTO_RETRIES)).thenReturn(1);
|
||||
when(config.get(MaxAutoRetriesNextServer, DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER))
|
||||
.thenReturn(1);
|
||||
when(config.get(OkToRetryOnAllOperations, eq(anyBoolean()))).thenReturn(true);
|
||||
when(config.get(ConnectTimeout)).thenReturn(defaultConnectTimeout);
|
||||
when(config.get(ReadTimeout)).thenReturn(defaultReadTimeout);
|
||||
when(this.config.get(MaxAutoRetries, DEFAULT_MAX_AUTO_RETRIES)).thenReturn(1);
|
||||
when(this.config.get(MaxAutoRetriesNextServer,
|
||||
DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER)).thenReturn(1);
|
||||
when(this.config.get(OkToRetryOnAllOperations, eq(anyBoolean())))
|
||||
.thenReturn(true);
|
||||
when(this.config.get(ConnectTimeout)).thenReturn(this.defaultConnectTimeout);
|
||||
when(this.config.get(ReadTimeout)).thenReturn(this.defaultReadTimeout);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testUriInsecure() {
|
||||
when(config.get(IsSecure)).thenReturn(false);
|
||||
ribbonLoadBalancer = new RibbonLoadBalancer(delegate, lb, config);
|
||||
when(this.config.get(IsSecure)).thenReturn(false);
|
||||
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
|
||||
this.inspector);
|
||||
Request request = new RequestTemplate().method("GET").append("http://foo/")
|
||||
.request();
|
||||
RibbonRequest ribbonRequest = new RibbonRequest(request, new URI(request.url()));
|
||||
RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request,
|
||||
new URI(request.url()));
|
||||
|
||||
Response response = Response.create(200, "Test",
|
||||
Collections.<String, Collection<String>> emptyMap(), new byte[0]);
|
||||
when(delegate.execute(any(Request.class), any(Options.class))).thenReturn(
|
||||
response);
|
||||
when(this.delegate.execute(any(Request.class), any(Options.class)))
|
||||
.thenReturn(response);
|
||||
|
||||
RibbonResponse resp = ribbonLoadBalancer.execute(ribbonRequest, null);
|
||||
RibbonResponse resp = this.feignLoadBalancer.execute(ribbonRequest, null);
|
||||
|
||||
assertThat(resp.getRequestedURI(), is(new URI("http://foo/")));
|
||||
}
|
||||
@@ -85,46 +92,25 @@ public class RibbonLoadBalancerTests {
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testSecureUriFromClientConfig() {
|
||||
when(config.get(IsSecure)).thenReturn(true);
|
||||
ribbonLoadBalancer = new RibbonLoadBalancer(delegate, lb, config);
|
||||
Request request = new RequestTemplate().method("GET").append("http://foo/")
|
||||
.request();
|
||||
RibbonRequest ribbonRequest = new RibbonRequest(request, new URI(request.url()));
|
||||
|
||||
Response response = Response.create(200, "Test",
|
||||
Collections.<String, Collection<String>> emptyMap(), new byte[0]);
|
||||
when(delegate.execute(any(Request.class), any(Options.class))).thenReturn(
|
||||
response);
|
||||
|
||||
RibbonResponse resp = ribbonLoadBalancer.execute(ribbonRequest, null);
|
||||
|
||||
assertThat(resp.getRequestedURI(), is(new URI("https://foo/")));
|
||||
when(this.config.get(IsSecure)).thenReturn(true);
|
||||
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
|
||||
this.inspector);
|
||||
Server server = new Server("foo", 7777);
|
||||
URI uri = this.feignLoadBalancer.reconstructURIWithServer(server,
|
||||
new URI("http://foo/"));
|
||||
assertThat(uri, is(new URI("https://foo:7777/")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testSecureUriFromClientConfigOverride() {
|
||||
when(config.get(IsSecure)).thenReturn(true);
|
||||
ribbonLoadBalancer = new RibbonLoadBalancer(delegate, lb, config);
|
||||
Request request = new RequestTemplate().method("GET").append("http://foo/")
|
||||
.request();
|
||||
RibbonRequest ribbonRequest = new RibbonRequest(request, new URI(request.url()));
|
||||
|
||||
Response response = Response.create(200, "Test",
|
||||
Collections.<String, Collection<String>> emptyMap(), new byte[0]);
|
||||
when(delegate.execute(any(Request.class), any(Options.class))).thenReturn(
|
||||
response);
|
||||
|
||||
IClientConfig override = mock(IClientConfig.class);
|
||||
when(override.get(ConnectTimeout, defaultConnectTimeout)).thenReturn(5000);
|
||||
when(override.get(ReadTimeout, defaultConnectTimeout)).thenReturn(5000);
|
||||
/*
|
||||
* Override secure value.
|
||||
*/
|
||||
when(override.get(IsSecure)).thenReturn(false);
|
||||
|
||||
RibbonResponse resp = ribbonLoadBalancer.execute(ribbonRequest, override);
|
||||
|
||||
assertThat(resp.getRequestedURI(), is(new URI("http://foo/")));
|
||||
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
|
||||
this.inspector);
|
||||
Server server = Mockito.mock(Server.class);
|
||||
when(server.getPort()).thenReturn(443);
|
||||
when(server.getHost()).thenReturn("foo");
|
||||
URI uri = this.feignLoadBalancer.reconstructURIWithServer(server,
|
||||
new URI("http://bar/"));
|
||||
assertThat(uri, is(new URI("https://foo:443/")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,17 +23,12 @@ import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -44,13 +39,13 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.WebIntegrationTest;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
@@ -63,6 +58,9 @@ import com.netflix.loadbalancer.ServerList;
|
||||
import feign.Client;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -221,17 +219,16 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testFeignClientType() throws IllegalAccessException {
|
||||
assertThat(this.feignClient, is(instanceOf(feign.ribbon.RibbonClient.class)));
|
||||
Field field = ReflectionUtils.findField(feign.ribbon.RibbonClient.class, "delegate", Client.class);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
Client delegate = (Client) field.get(this.feignClient);
|
||||
assertThat(this.feignClient, is(instanceOf(LoadBalancerFeignClient.class)));
|
||||
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
|
||||
Client delegate = client.getDelegate();
|
||||
assertThat(delegate, is(instanceOf(feign.Client.Default.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertNotNull("testClientServiceId was null", testClientServiceId);
|
||||
final Hello hello = testClientServiceId.getHello();
|
||||
assertNotNull("testClientServiceId was null", this.testClientServiceId);
|
||||
final Hello hello = this.testClientServiceId.getHello();
|
||||
assertNotNull("The hello response was null", hello);
|
||||
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
|
||||
}
|
||||
|
||||
@@ -24,12 +24,6 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -40,6 +34,7 @@ import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -48,7 +43,6 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
@@ -58,6 +52,9 @@ import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
|
||||
import feign.Client;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -146,18 +143,16 @@ public class FeignHttpClientTests {
|
||||
|
||||
@Test
|
||||
public void testFeignClientType() throws IllegalAccessException {
|
||||
assertThat(this.feignClient, is(instanceOf(feign.ribbon.RibbonClient.class)));
|
||||
Field field = ReflectionUtils.findField(feign.ribbon.RibbonClient.class,
|
||||
"delegate", Client.class);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
Client delegate = (Client) field.get(this.feignClient);
|
||||
assertThat(this.feignClient, is(instanceOf(LoadBalancerFeignClient.class)));
|
||||
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
|
||||
Client delegate = client.getDelegate();
|
||||
assertThat(delegate, is(instanceOf(feign.httpclient.ApacheHttpClient.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFeignInheritanceSupport() {
|
||||
assertNotNull("UserClient was null", userClient);
|
||||
final User user = userClient.getUser(1);
|
||||
assertNotNull("UserClient was null", this.userClient);
|
||||
final User user = this.userClient.getUser(1);
|
||||
assertNotNull("Returned user was null", user);
|
||||
assertEquals("Users were different", user, new User("John Smith"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user