Removes ribbon and hystrix support

This commit is contained in:
Spencer Gibb
2020-01-17 19:28:33 -05:00
parent bf1a5e6d33
commit 8a08e1ec4b
58 changed files with 226 additions and 4348 deletions

View File

@@ -79,19 +79,6 @@ public class FeignAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean

View File

@@ -16,8 +16,6 @@
package org.springframework.cloud.openfeign;
import feign.hystrix.FallbackFactory;
import org.springframework.context.ApplicationContext;
/**
@@ -88,13 +86,6 @@ public class FeignClientBuilder {
return this;
}
public Builder<T> fallbackFactory(
final Class<? extends FallbackFactory<? extends T>> fallbackFactory) {
FeignClientsRegistrar.validateFallbackFactory(fallbackFactory);
this.feignClientFactoryBean.setFallbackFactory(fallbackFactory);
return this;
}
/**
* @return the created Feign client
*/

View File

@@ -39,7 +39,6 @@ import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
@@ -288,11 +287,6 @@ class FeignClientFactoryBean
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not load balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient) client).getDelegate();
}
if (client instanceof FeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap

View File

@@ -20,14 +20,12 @@ import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.databind.Module;
import com.netflix.hystrix.HystrixCommand;
import feign.Contract;
import feign.Feign;
import feign.Logger;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.hystrix.HystrixFeign;
import feign.optionals.OptionalDecoder;
import org.springframework.beans.factory.ObjectFactory;
@@ -35,7 +33,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.PageJacksonModule;
@@ -144,18 +141,4 @@ public class FeignClientsConfiguration {
return new PageJacksonModule();
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
}
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign;
import feign.Feign;
import feign.Target;
import feign.hystrix.FallbackFactory;
import feign.hystrix.HystrixFeign;
import feign.hystrix.SetterFactory;
import org.springframework.util.StringUtils;
/**
* @author Spencer Gibb
* @author Erik Kringen
*/
@SuppressWarnings("unchecked")
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
: factory.getContextId();
SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(name, context, target, builder, fallback);
}
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(name, context, target, builder,
fallbackFactory);
}
return feign.target(target);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
FallbackFactory.class);
return builder.target(target, fallbackFactory);
}
private <T> T targetWithFallback(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallback) {
T fallbackInstance = getFromContext("fallback", feignClientName, context,
fallback, target.type());
return builder.target(target, fallbackInstance);
}
private <T> T getFromContext(String fallbackMechanism, String feignClientName,
FeignContext context, Class<?> beanType, Class<T> targetType) {
Object fallbackInstance = context.getInstance(feignClientName, beanType);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No " + fallbackMechanism
+ " instance of type %s found for feign client %s",
beanType, feignClientName));
}
if (!targetType.isAssignableFrom(beanType)) {
throw new IllegalStateException(String.format("Incompatible "
+ fallbackMechanism
+ " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
beanType, targetType, feignClientName));
}
return (T) fallbackInstance;
}
private <T> T getOptional(String feignClientName, FeignContext context,
Class<T> beanType) {
return context.getInstance(feignClientName, beanType);
}
}

View File

@@ -19,14 +19,12 @@ package org.springframework.cloud.openfeign.loadbalancer;
import feign.Client;
import feign.Feign;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -43,7 +41,6 @@ import org.springframework.context.annotation.Import;
@ConditionalOnClass(Feign.class)
@ConditionalOnBean(BlockingLoadBalancerClient.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter(FeignRibbonClientAutoConfiguration.class)
@EnableConfigurationProperties(FeignHttpClientProperties.class)
@Configuration(proxyBeanMethods = false)
// Order is important here, last should be the default, first should be optional

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.util.Map;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
* Factory for SpringLoadBalancer instances that caches the entries created.
*
* @author Spencer Gibb
* @author Dave Syer
* @author Ryan Baxter
* @author Gang Li
*/
public class CachingSpringLoadBalancerFactory {
protected final SpringClientFactory factory;
protected LoadBalancedRetryFactory loadBalancedRetryFactory = null;
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
public CachingSpringLoadBalancerFactory(SpringClientFactory factory) {
this.factory = factory;
}
public CachingSpringLoadBalancerFactory(SpringClientFactory factory,
LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) {
this.factory = factory;
this.loadBalancedRetryFactory = loadBalancedRetryPolicyFactory;
}
public FeignLoadBalancer create(String clientName) {
FeignLoadBalancer client = this.cache.get(clientName);
if (client != null) {
return client;
}
IClientConfig config = this.factory.getClientConfig(clientName);
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,
ServerIntrospector.class);
client = this.loadBalancedRetryFactory != null
? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
this.loadBalancedRetryFactory)
: new FeignLoadBalancer(lb, config, serverIntrospector);
this.cache.put(clientName, client);
return client;
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import feign.Client;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Spencer Gibb
*/
@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
clientFactory);
}
}

View File

@@ -1,246 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.ClientException;
import com.netflix.client.ClientRequest;
import com.netflix.client.IResponse;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import feign.Client;
import feign.Request;
import feign.Response;
import org.springframework.cloud.netflix.ribbon.RibbonProperties;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToSecureConnectionIfNeeded;
/**
* @author Dave Syer
* @author Spencer Gibb
* @author Ryan Baxter
* @author Tim Ysewyn
* @author Olga Maciaszek-Sharma
*/
public class FeignLoadBalancer extends
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
private final RibbonProperties ribbon;
protected int connectTimeout;
protected int readTimeout;
protected IClientConfig clientConfig;
protected ServerIntrospector serverIntrospector;
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
ServerIntrospector serverIntrospector) {
super(lb, clientConfig);
this.setRetryHandler(RetryHandler.DEFAULT);
this.clientConfig = clientConfig;
this.ribbon = RibbonProperties.from(clientConfig);
RibbonProperties ribbon = this.ribbon;
this.connectTimeout = ribbon.getConnectTimeout();
this.readTimeout = ribbon.getReadTimeout();
this.serverIntrospector = serverIntrospector;
}
@Override
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
throws IOException {
Request.Options options;
if (configOverride != null) {
RibbonProperties override = RibbonProperties.from(configOverride);
options = new Request.Options(override.connectTimeout(this.connectTimeout),
override.readTimeout(this.readTimeout));
}
else {
options = new Request.Options(this.connectTimeout, this.readTimeout);
}
Response response = request.client().execute(request.toRequest(), options);
return new RibbonResponse(request.getUri(), response);
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
RibbonRequest request, IClientConfig requestConfig) {
if (this.ribbon.isOkToRetryOnAllOperations()) {
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
requestConfig);
}
if (!request.toRequest().httpMethod().name().equals("GET")) {
return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
requestConfig);
}
else {
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
requestConfig);
}
}
@Override
public URI reconstructURIWithServer(Server server, URI original) {
URI uri = updateToSecureConnectionIfNeeded(original, this.clientConfig,
this.serverIntrospector, server);
return super.reconstructURIWithServer(server, uri);
}
protected static class RibbonRequest extends ClientRequest implements Cloneable {
private final Request request;
private final Client client;
protected RibbonRequest(Client client, Request request, URI uri) {
this.client = client;
setUri(uri);
this.request = toRequest(request);
}
private Request toRequest(Request request) {
Map<String, Collection<String>> headers = new LinkedHashMap<>(
request.headers());
return Request.create(request.httpMethod(), getUri().toASCIIString(), headers,
request.requestBody().asBytes(), request.charset());
}
Request toRequest() {
return toRequest(this.request);
}
Client client() {
return this.client;
}
HttpRequest toHttpRequest() {
return new HttpRequest() {
@Override
public HttpMethod getMethod() {
return HttpMethod
.resolve(RibbonRequest.this.toRequest().httpMethod().name());
}
@Override
public String getMethodValue() {
return getMethod().name();
}
@Override
public URI getURI() {
return RibbonRequest.this.getUri();
}
@Override
public HttpHeaders getHeaders() {
Map<String, List<String>> headers = new HashMap<>();
Map<String, Collection<String>> feignHeaders = RibbonRequest.this
.toRequest().headers();
for (String key : feignHeaders.keySet()) {
headers.put(key, new ArrayList<String>(feignHeaders.get(key)));
}
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(headers);
return httpHeaders;
}
};
}
public Request getRequest() {
return this.request;
}
public Client getClient() {
return this.client;
}
@Override
public Object clone() {
return new RibbonRequest(this.client, this.request, getUri());
}
}
protected static class RibbonResponse implements IResponse {
private final URI uri;
private final Response response;
protected RibbonResponse(URI uri, Response response) {
this.uri = uri;
this.response = response;
}
@Override
public Object getPayload() throws ClientException {
return this.response.body();
}
@Override
public boolean hasPayload() {
return this.response.body() != null;
}
@Override
public boolean isSuccess() {
return this.response.status() == 200;
}
@Override
public URI getRequestedURI() {
return this.uri;
}
@Override
public Map<String, Collection<String>> getHeaders() {
return this.response.headers();
}
Response toResponse() {
return this.response;
}
@Override
public void close() throws IOException {
if (this.response != null && this.response.body() != null) {
this.response.body().close();
}
}
}
}

View File

@@ -1,129 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
import org.springframework.http.HttpRequest;
import org.springframework.retry.RetryContext;
/**
* @author Ryan Baxter
*/
public class FeignRetryPolicy extends InterceptorRetryPolicy {
private HttpRequest request;
private String serviceId;
public FeignRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy,
ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
super(request, policy, serviceInstanceChooser, serviceName);
this.request = request;
this.serviceId = serviceName;
}
@Override
public boolean canRetry(RetryContext context) {
/*
* In InterceptorRetryPolicy.canRetry we ask the LoadBalancer to choose a server
* if one is not set in the retry context and then return true. RetryTemplat calls
* the canRetry method of the policy even on its first execution. So the fact that
* we didnt have a service instance set in the RetryContext signaled that it was
* the first execution and we should return true.
*
* In the Feign scenario, Feign as actually already queried the load balancer for
* a service instance and we set that service instance in the context when we call
* the open method of the policy. So in the Feign case we just return true if the
* retry count is 0 indicating we haven't yet made a failed request.
*/
if (context.getRetryCount() == 0) {
return true;
}
return super.canRetry(context);
}
@Override
public RetryContext open(RetryContext parent) {
/*
* With Feign (unlike Ribbon) the request already has the URI for the service
* instance we are going to make the request to, so extract that information and
* set the service instance in the context. In the Ribbon scenario the URI in the
* request object still has the service id so we choose and set the service
* instance later on.
*/
LoadBalancedRetryContext context = new LoadBalancedRetryContext(parent,
this.request);
context.setServiceInstance(
new FeignRetryPolicyServiceInstance(this.serviceId, this.request));
return context;
}
class FeignRetryPolicyServiceInstance implements ServiceInstance {
private String serviceId;
private HttpRequest request;
private Map<String, String> metadata;
FeignRetryPolicyServiceInstance(String serviceId, HttpRequest request) {
this.serviceId = serviceId;
this.request = request;
this.metadata = new HashMap<>();
}
@Override
public String getServiceId() {
return this.serviceId;
}
@Override
public String getHost() {
return this.request.getURI().getHost();
}
@Override
public int getPort() {
return this.request.getURI().getPort();
}
@Override
public boolean isSecure() {
return "https".equals(this.request.getURI().getScheme());
}
@Override
public URI getUri() {
return this.request.getURI();
}
@Override
public Map<String, String> getMetadata() {
return this.metadata;
}
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import com.netflix.loadbalancer.ILoadBalancer;
import feign.Feign;
import feign.Request;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
/**
* Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a
* load balancer.
*
* @author Dave Syer
* @author Olga Maciaszek-Sharma
*/
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
// Order is important here, last should be the default, first should be optional
// see
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
SpringClientFactory factory) {
return new CachingSpringLoadBalancerFactory(factory);
}
@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
return new CachingSpringLoadBalancerFactory(factory, retryFactory);
}
@Bean
@ConditionalOnMissingBean
public Request.Options feignRequestOptions() {
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import org.apache.http.client.HttpClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.clientconfig.HttpClientFeignConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @author Spencer Gibb
* @author Olga Maciaszek-Sharma
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
@Import(HttpClientFeignConfiguration.class)
class HttpClientFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}

View File

@@ -1,143 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.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 com.netflix.client.config.IClientConfig;
import feign.Client;
import feign.Request;
import feign.Response;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
/**
* @author Dave Syer
*
*/
public class LoadBalancerFeignClient implements Client {
static final Request.Options DEFAULT_OPTIONS = new Request.Options();
private final Client delegate;
private CachingSpringLoadBalancerFactory lbClientFactory;
private SpringClientFactory clientFactory;
public LoadBalancerFeignClient(Client delegate,
CachingSpringLoadBalancerFactory lbClientFactory,
SpringClientFactory clientFactory) {
this.delegate = delegate;
this.lbClientFactory = lbClientFactory;
this.clientFactory = clientFactory;
}
static URI cleanUrl(String originalUrl, String host) {
String newUrl = originalUrl;
if (originalUrl.startsWith("https://")) {
newUrl = originalUrl.substring(0, 8)
+ originalUrl.substring(8 + host.length());
}
else if (originalUrl.startsWith("http")) {
newUrl = originalUrl.substring(0, 7)
+ originalUrl.substring(7 + host.length());
}
StringBuffer buffer = new StringBuffer(newUrl);
if ((newUrl.startsWith("https://") && newUrl.length() == 8)
|| (newUrl.startsWith("http://") && newUrl.length() == 7)) {
buffer.append("/");
}
return URI.create(buffer.toString());
}
@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);
IClientConfig requestConfig = getClientConfig(options, clientName);
return lbClient(clientName)
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}
IClientConfig getClientConfig(Request.Options options, String clientName) {
IClientConfig requestConfig;
if (options == DEFAULT_OPTIONS) {
requestConfig = this.clientFactory.getClientConfig(clientName);
}
else {
requestConfig = new FeignOptionsClientConfig(options);
}
return requestConfig;
}
protected IOException findIOException(Throwable t) {
if (t == null) {
return null;
}
if (t instanceof IOException) {
return (IOException) t;
}
return findIOException(t.getCause());
}
public Client getDelegate() {
return this.delegate;
}
private FeignLoadBalancer lbClient(String clientName) {
return this.lbClientFactory.create(clientName);
}
static class FeignOptionsClientConfig extends DefaultClientConfigImpl {
FeignOptionsClientConfig(Request.Options options) {
setProperty(CommonClientConfigKey.ConnectTimeout,
options.connectTimeoutMillis());
setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
}
@Override
public void loadProperties(String clientName) {
}
@Override
public void loadDefaultValues() {
}
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import feign.Client;
import feign.okhttp.OkHttpClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.clientconfig.OkHttpFeignConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @author Spencer Gibb
* @author Olga Maciaszek-Sharma
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
@Import(OkHttpFeignConfiguration.class)
class OkHttpFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}

View File

@@ -1,148 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.io.IOException;
import java.net.URI;
import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import feign.Request;
import feign.Response;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRecoveryCallback;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.RibbonProperties;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.StreamUtils;
/**
* A {@link FeignLoadBalancer} that leverages Spring Retry to retry failed requests.
*
* @author Ryan Baxter
* @author Gang Li
*/
public class RetryableFeignLoadBalancer extends FeignLoadBalancer
implements ServiceInstanceChooser {
private final LoadBalancedRetryFactory loadBalancedRetryFactory;
public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
ServerIntrospector serverIntrospector,
LoadBalancedRetryFactory loadBalancedRetryFactory) {
super(lb, clientConfig, serverIntrospector);
this.loadBalancedRetryFactory = loadBalancedRetryFactory;
this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig));
}
@Override
public RibbonResponse execute(final RibbonRequest request,
IClientConfig configOverride) throws IOException {
final Request.Options options;
if (configOverride != null) {
RibbonProperties ribbon = RibbonProperties.from(configOverride);
options = new Request.Options(ribbon.connectTimeout(this.connectTimeout),
ribbon.readTimeout(this.readTimeout));
}
else {
options = new Request.Options(this.connectTimeout, this.readTimeout);
}
final LoadBalancedRetryPolicy retryPolicy = this.loadBalancedRetryFactory
.createRetryPolicy(this.getClientName(), this);
RetryTemplate retryTemplate = new RetryTemplate();
BackOffPolicy backOffPolicy = this.loadBalancedRetryFactory
.createBackOffPolicy(this.getClientName());
retryTemplate.setBackOffPolicy(
backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
RetryListener[] retryListeners = this.loadBalancedRetryFactory
.createRetryListeners(this.getClientName());
if (retryListeners != null && retryListeners.length != 0) {
retryTemplate.setListeners(retryListeners);
}
retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy()
: new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this,
this.getClientName()));
return retryTemplate.execute(new RetryCallback<RibbonResponse, IOException>() {
@Override
public RibbonResponse doWithRetry(RetryContext retryContext)
throws IOException {
Request feignRequest = null;
// on retries the policy will choose the server and set it in the context
// extract the server and update the request being made
if (retryContext instanceof LoadBalancedRetryContext) {
ServiceInstance service = ((LoadBalancedRetryContext) retryContext)
.getServiceInstance();
if (service != null) {
feignRequest = ((RibbonRequest) request
.replaceUri(reconstructURIWithServer(
new Server(service.getHost(), service.getPort()),
request.getUri()))).toRequest();
}
}
if (feignRequest == null) {
feignRequest = request.toRequest();
}
Response response = request.client().execute(feignRequest, options);
if (retryPolicy != null
&& retryPolicy.retryableStatusCode(response.status())) {
byte[] byteArray = response.body() == null ? new byte[] {}
: StreamUtils
.copyToByteArray(response.body().asInputStream());
response.close();
throw new RibbonResponseStatusCodeException(
RetryableFeignLoadBalancer.this.clientName, response,
byteArray, request.getUri());
}
return new RibbonResponse(request.getUri(), response);
}
}, new LoadBalancedRecoveryCallback<RibbonResponse, Response>() {
@Override
protected RibbonResponse createResponse(Response response, URI uri) {
return new RibbonResponse(uri, response);
}
});
}
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
FeignLoadBalancer.RibbonRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(),
requestConfig);
}
@Override
public ServiceInstance choose(String serviceId) {
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
this.getLoadBalancer().chooseServer(serviceId));
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.io.ByteArrayInputStream;
import java.net.URI;
import feign.Response;
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
/**
* A {@link RetryableStatusCodeException} for {@link Response}s.
*
* @author Ryan Baxter
*/
public class RibbonResponseStatusCodeException extends RetryableStatusCodeException {
private final Response response;
public RibbonResponseStatusCodeException(String serviceId, Response response,
byte[] body, URI uri) {
super(serviceId, response.status(), response, uri);
this.response = Response.builder()
.body(new ByteArrayInputStream(body), body.length)
.headers(response.headers()).reason(response.reason())
.status(response.status()).request(response.request()).build();
}
@Override
public Response getResponse() {
return this.response;
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.support;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixThreadPoolKey;
/**
* Convenience class for implementing feign fallbacks that return {@link HystrixCommand}.
* Also useful for return types of {@link rx.Observable} and
* {@link java.util.concurrent.Future}. For those return types, just call
* {@link FallbackCommand#observe()} or {@link FallbackCommand#queue()} respectively.
*
* @param <T> result type
* @author Spencer Gibb
*/
public class FallbackCommand<T> extends HystrixCommand<T> {
private T result;
public FallbackCommand(T result) {
this(result, "fallback");
}
protected FallbackCommand(T result, String groupname) {
super(HystrixCommandGroupKey.Factory.asKey(groupname));
this.result = result;
}
public FallbackCommand(T result, HystrixCommandGroupKey group) {
super(group);
this.result = result;
}
public FallbackCommand(T result, HystrixCommandGroupKey group,
int executionIsolationThreadTimeoutInMilliseconds) {
super(group, executionIsolationThreadTimeoutInMilliseconds);
this.result = result;
}
public FallbackCommand(T result, HystrixCommandGroupKey group,
HystrixThreadPoolKey threadPool) {
super(group, threadPool);
this.result = result;
}
public FallbackCommand(T result, HystrixCommandGroupKey group,
HystrixThreadPoolKey threadPool,
int executionIsolationThreadTimeoutInMilliseconds) {
super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds);
this.result = result;
}
public FallbackCommand(T result, Setter setter) {
super(setter);
this.result = result;
}
@Override
protected T run() throws Exception {
return this.result;
}
}

View File

@@ -23,7 +23,6 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -49,8 +48,7 @@ public class EnableFeignClientsSpringDataTests {
}
@Configuration(proxyBeanMethods = false)
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
FeignAutoConfiguration.class })
@Import({ PropertyPlaceholderAutoConfiguration.class, FeignAutoConfiguration.class })
protected static class PlainConfiguration {
}

View File

@@ -30,7 +30,6 @@ import org.junit.runner.RunWith;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.cloud.test.ClassPathExclusions;
@@ -88,7 +87,7 @@ public class EnableFeignClientsTests {
}
@Configuration(proxyBeanMethods = false)
@Import({ ArchaiusAutoConfiguration.class, FeignAutoConfiguration.class })
@Import({ FeignAutoConfiguration.class })
protected static class PlainConfiguration {
}

View File

@@ -24,7 +24,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import feign.hystrix.FallbackFactory;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Rule;
@@ -127,10 +126,8 @@ public class FeignClientBuilderTests {
public void forType_allFieldsSetOnBuilder() {
// when:
final FeignClientBuilder.Builder builder = this.feignClientBuilder
.forType(TestFeignClient.class, "TestClient").decode404(true)
.fallback(TestFeignClientFallback.class)
.fallbackFactory(TestFeignClientFallbackFactory.class).path("Path/")
.url("Url/").contextId("TestContext");
.forType(TestFeignClient.class, "TestClient").decode404(true).url("Url/")
.contextId("TestContext");
// then:
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
@@ -142,9 +139,7 @@ public class FeignClientBuilderTests {
assertFactoryBeanField(builder, "url", "http://Url/");
assertFactoryBeanField(builder, "path", "/Path");
assertFactoryBeanField(builder, "decode404", true);
assertFactoryBeanField(builder, "fallback", TestFeignClientFallback.class);
assertFactoryBeanField(builder, "fallbackFactory",
TestFeignClientFallbackFactory.class);
}
@Test
@@ -167,18 +162,4 @@ public class FeignClientBuilderTests {
}
private class TestFeignClientFallback implements TestFeignClient {
}
private class TestFeignClientFallbackFactory
implements FallbackFactory<TestFeignClient> {
@Override
public TestFeignClientFallback create(Throwable throwable) {
return new TestFeignClientFallback();
}
}
}

View File

@@ -18,7 +18,6 @@ package org.springframework.cloud.openfeign;
import feign.Contract;
import feign.ExceptionPropagationPolicy;
import feign.Feign;
import feign.Logger;
import feign.QueryMapEncoder;
import feign.Request;
@@ -30,7 +29,6 @@ import feign.auth.BasicAuthRequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import feign.hystrix.HystrixFeign;
import feign.optionals.OptionalDecoder;
import feign.querymap.BeanQueryMapEncoder;
import feign.slf4j.Slf4jLogger;
@@ -40,7 +38,6 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.context.annotation.Bean;
@@ -121,13 +118,6 @@ public class FeignClientOverrideDefaultsTests {
.cast(this.context.getInstance("bar", ErrorDecoder.class));
}
@Test
public void overrideBuilder() {
HystrixFeign.Builder.class
.cast(this.context.getInstance("foo", Feign.Builder.class));
Feign.Builder.class.cast(this.context.getInstance("bar", Feign.Builder.class));
}
@Test
public void overrideRequestOptions() {
assertThat(this.context.getInstance("foo", Request.Options.class)).isNull();
@@ -180,8 +170,7 @@ public class FeignClientOverrideDefaultsTests {
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = { FooClient.class, BarClient.class })
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
FeignAutoConfiguration.class })
@Import({ PropertyPlaceholderAutoConfiguration.class, FeignAutoConfiguration.class })
protected static class TestConfiguration {
@Bean
@@ -217,11 +206,6 @@ public class FeignClientOverrideDefaultsTests {
return new Contract.Default();
}
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
@Bean
public QueryMapEncoder queryMapEncoder() {
return new feign.QueryMapEncoder.Default();

View File

@@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration;
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingInterceptor;
import org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration;
@@ -54,8 +53,7 @@ public class FeignCompressionTests {
.withPropertyValues("feign.compression.response.enabled=true",
"feign.compression.request.enabled=true",
"feign.okhttp.enabled=false")
.withConfiguration(AutoConfigurations.of(ArchaiusAutoConfiguration.class,
FeignAutoConfiguration.class,
.withConfiguration(AutoConfigurations.of(FeignAutoConfiguration.class,
FeignContentGzipEncodingAutoConfiguration.class,
FeignAcceptGzipEncodingAutoConfiguration.class,
HttpClientConfiguration.class, PlainConfig.class))

View File

@@ -16,8 +16,6 @@
package org.springframework.cloud.openfeign;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -26,18 +24,10 @@ import org.junit.runner.RunWith;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer;
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer;
import org.springframework.cloud.test.ClassPathExclusions;
import org.springframework.cloud.test.ModifiedClassPathRunner;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@@ -50,11 +40,7 @@ public class SpringRetryDisabledTests {
@Before
public void setUp() {
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.sources(RibbonAutoConfiguration.class,
LoadBalancerAutoConfiguration.class,
RibbonClientConfiguration.class,
FeignRibbonClientAutoConfiguration.class)
.run();
.sources(LoadBalancerAutoConfiguration.class).run();
}
@After
@@ -66,12 +52,15 @@ public class SpringRetryDisabledTests {
@Test
public void testLoadBalancedRetryFactoryBean() throws Exception {
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = this.context
.getBeansOfType(CachingSpringLoadBalancerFactory.class);
assertThat(lbFactorys.values()).hasSize(1);
FeignLoadBalancer lb = lbFactorys.values().iterator().next().create("foo");
assertThat(lb).isInstanceOf(FeignLoadBalancer.class);
assertThat(lb).isNotInstanceOf(RetryableFeignLoadBalancer.class);
// FIXME: 3.0.0
/*
* Map<String, CachingSpringLoadBalancerFactory> lbFactorys = this.context
* .getBeansOfType(CachingSpringLoadBalancerFactory.class);
* assertThat(lbFactorys.values()).hasSize(1); FeignLoadBalancer lb =
* lbFactorys.values().iterator().next().create("foo");
* assertThat(lb).isInstanceOf(FeignLoadBalancer.class);
* assertThat(lb).isNotInstanceOf(RetryableFeignLoadBalancer.class);
*/
}
}

View File

@@ -16,45 +16,37 @@
package org.springframework.cloud.openfeign;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer;
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { RibbonAutoConfiguration.class,
RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class, HttpClientConfiguration.class })
@ContextConfiguration(
classes = { LoadBalancerAutoConfiguration.class, HttpClientConfiguration.class })
public class SpringRetryEnabledTests implements ApplicationContextAware {
private ApplicationContext context;
@Test
public void testLoadBalancedRetryFactoryBean() throws Exception {
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = this.context
.getBeansOfType(CachingSpringLoadBalancerFactory.class);
assertThat(lbFactorys.values()).hasSize(1);
FeignLoadBalancer lb = lbFactorys.values().iterator().next().create("foo");
assertThat(lb).isInstanceOf(RetryableFeignLoadBalancer.class);
// FIXME: 3.0.0
/*
* Map<String, CachingSpringLoadBalancerFactory> lbFactorys = this.context
* .getBeansOfType(CachingSpringLoadBalancerFactory.class);
* assertThat(lbFactorys.values()).hasSize(1); FeignLoadBalancer lb =
* lbFactorys.values().iterator().next().create("foo");
* assertThat(lb).isInstanceOf(RetryableFeignLoadBalancer.class);
*/
}
@Override

View File

@@ -16,12 +16,8 @@
package org.springframework.cloud.openfeign.encoding;
import java.util.Collections;
import java.util.List;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,12 +25,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
@@ -75,7 +69,7 @@ public class FeignAcceptEncodingTests {
}
@EnableFeignClients(clients = InvoiceClient.class)
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
// @RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
@SpringBootApplication(
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
@Import(NoSecurityConfiguration.class)
@@ -89,13 +83,11 @@ public class FeignAcceptEncodingTests {
@Value("${local.server.port}")
private int port = 0;
@Bean
public ILoadBalancer ribbonLoadBalancer() {
BaseLoadBalancer balancer = new BaseLoadBalancer();
balancer.setServersList(
Collections.singletonList(new Server("localhost", this.port)));
return balancer;
}
/*
* @Bean public ILoadBalancer ribbonLoadBalancer() { BaseLoadBalancer balancer =
* new BaseLoadBalancer(); balancer.setServersList( Collections.singletonList(new
* Server("localhost", this.port))); return balancer; }
*/
}

View File

@@ -16,12 +16,8 @@
package org.springframework.cloud.openfeign.encoding;
import java.util.Collections;
import java.util.List;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,12 +25,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
@@ -79,7 +73,7 @@ public class FeignContentEncodingTests {
}
@EnableFeignClients(clients = InvoiceClient.class)
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
// @RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
@SpringBootApplication(
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
@Import(NoSecurityConfiguration.class)
@@ -93,13 +87,11 @@ public class FeignContentEncodingTests {
@Value("${local.server.port}")
private int port = 0;
@Bean
public ILoadBalancer ribbonLoadBalancer() {
BaseLoadBalancer balancer = new BaseLoadBalancer();
balancer.setServersList(
Collections.singletonList(new Server("localhost", this.port)));
return balancer;
}
/*
* @Bean public ILoadBalancer ribbonLoadBalancer() { BaseLoadBalancer balancer =
* new BaseLoadBalancer(); balancer.setServersList( Collections.singletonList(new
* Server("localhost", this.port))); return balancer; }
*/
}

View File

@@ -16,11 +16,6 @@
package org.springframework.cloud.openfeign.encoding;
import java.util.Collections;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,13 +24,11 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.Page;
@@ -85,7 +78,7 @@ public class FeignPageableEncodingTests {
}
@EnableFeignClients(clients = InvoiceClient.class)
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
// @RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
@SpringBootApplication(
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app",
exclude = { RepositoryRestMvcAutoConfiguration.class })
@@ -101,13 +94,11 @@ public class FeignPageableEncodingTests {
@Value("${local.server.port}")
private int port = 0;
@Bean
public ILoadBalancer ribbonLoadBalancer() {
BaseLoadBalancer balancer = new BaseLoadBalancer();
balancer.setServersList(
Collections.singletonList(new Server("localhost", this.port)));
return balancer;
}
/*
* @Bean public ILoadBalancer ribbonLoadBalancer() { BaseLoadBalancer balancer =
* new BaseLoadBalancer(); balancer.setServersList( Collections.singletonList(new
* Server("localhost", this.port))); return balancer; }
*/
}

View File

@@ -18,7 +18,6 @@ package org.springframework.cloud.openfeign.hateoas.app;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Import;
@@ -29,7 +28,6 @@ import org.springframework.context.annotation.Import;
* @author Hector Espert
*/
@EnableFeignClients(clients = FeignHalClient.class)
@RibbonClient(name = "local", configuration = FeignHalRibbonConfiguration.class)
@SpringBootApplication(
scanBasePackages = "org.springframework.cloud.openfeign.hateoas.app",
exclude = RepositoryRestMvcAutoConfiguration.class)

View File

@@ -16,14 +16,7 @@
package org.springframework.cloud.openfeign.hateoas.app;
import java.util.Collections;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
/**
* @author Hector Espert
@@ -33,12 +26,4 @@ public class FeignHalRibbonConfiguration {
@Value("${local.server.port}")
private int serverPort = 0;
@Bean
public ILoadBalancer ribbonLoadBalancer() {
Server server = new Server("localhost", serverPort);
BaseLoadBalancer balancer = new BaseLoadBalancer();
balancer.setServersList(Collections.singletonList(server));
return balancer;
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.hystrix.security;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.hystrix.security.app.CustomConcurrenyStrategy;
import org.springframework.cloud.openfeign.hystrix.security.app.ProxyUsernameController;
import org.springframework.cloud.openfeign.hystrix.security.app.TestInterceptor;
import org.springframework.cloud.openfeign.hystrix.security.app.UsernameClient;
import org.springframework.cloud.openfeign.hystrix.security.app.UsernameController;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @author Daniel Lavoie
*/
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EnableFeignClients(clients = UsernameClient.class)
@Import(NoSecurityConfiguration.class)
public class HystrixSecurityApplication {
@Bean
public CustomConcurrenyStrategy customConcurrenyStrategy() {
return new CustomConcurrenyStrategy();
}
@Bean
public TestInterceptor testInterceptor() {
return new TestInterceptor();
}
@Bean
public ProxyUsernameController proxyUsernameController() {
return new ProxyUsernameController();
}
@Bean
public UsernameController usernameController() {
return new UsernameController();
}
}

View File

@@ -1,134 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.hystrix.security;
import java.util.Base64;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.netflix.hystrix.security.SecurityContextConcurrencyStrategy;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.hystrix.security.app.CustomConcurrenyStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests that a secured web service returning values using a feign client properly access
* the security context from a hystrix command.
*
* @author Daniel Lavoie
*/
@RunWith(SpringRunner.class)
@DirtiesContext
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
properties = { "feign.hystrix.enabled=true" })
@ActiveProfiles("proxysecurity")
public class HystrixSecurityTests {
@Autowired
private CustomConcurrenyStrategy customConcurrenyStrategy;
@LocalServerPort
private String serverPort;
// TODO: move to constants in TestAutoConfiguration
private String username = "user";
private String password = "password";
public static HttpHeaders createBasicAuthHeader(final String username,
final String password) {
return new HttpHeaders() {
private static final long serialVersionUID = 1766341693637204893L;
{
String auth = username + ":" + password;
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
String authHeader = "Basic " + new String(encodedAuth);
this.set("Authorization", authHeader);
}
};
}
@Test
public void testSecurityConcurrencyStrategyInstalled() {
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance()
.getConcurrencyStrategy();
assertThat(concurrencyStrategy)
.isInstanceOf(SecurityContextConcurrencyStrategy.class);
}
@Test
public void testFeignHystrixSecurity() {
HttpHeaders headers = createBasicAuthHeader(this.username, this.password);
ResponseEntity<String> entity = new RestTemplate().exchange(
"http://localhost:" + this.serverPort + "/proxy-username", HttpMethod.GET,
new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody())
.as("Username should have been intercepted by feign interceptor.")
.isEqualTo(this.username);
assertThat(this.customConcurrenyStrategy.isHookCalled())
.as("Custom hook should have been called.").isTrue();
}
@SpringBootConfiguration
@Import(HystrixSecurityApplication.class)
@RibbonClient(name = "username", configuration = LocalRibbonClientConfiguration.class)
protected static class TestConfig {
}
protected static class LocalRibbonClientConfiguration {
@LocalServerPort
private int port = 0;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
}
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.hystrix.security.app;
import java.util.concurrent.Callable;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
public class CustomConcurrenyStrategy extends HystrixConcurrencyStrategy {
private boolean hookCalled;
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
this.hookCalled = true;
return super.wrapCallable(callable);
}
public boolean isHookCalled() {
return this.hookCalled;
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.hystrix.security.app;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Daniel Lavoie
*/
@RestController
@RequestMapping("/proxy-username")
public class ProxyUsernameController {
@Autowired
private UsernameClient usernameClient;
@RequestMapping
public String getUsername() {
return this.usernameClient.getUsername();
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.hystrix.security.app;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* This interceptor should be called from an Hyxtrix command execution thread. It is
* access the SecurityContext and settings an http header from the authentication details.
*
* @author Daniel Lavoie
*/
public class TestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
if (SecurityContextHolder.getContext().getAuthentication() != null) {
template.header("username",
SecurityContextHolder.getContext().getAuthentication().getName());
}
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.hystrix.security.app;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author Daniel Lavoie
*/
@FeignClient("username")
public interface UsernameClient {
@RequestMapping("/username")
String getUsername();
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.hystrix.security.app;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Daniel Lavoie
*/
@RestController
@RequestMapping("/username")
public class UsernameController {
@RequestMapping
public String getUsername(@RequestHeader String username) {
return username;
}
}

View File

@@ -16,22 +16,16 @@
package org.springframework.cloud.openfeign.invalid;
import feign.Feign;
import feign.hystrix.FallbackFactory;
import feign.hystrix.HystrixFeign;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -50,9 +44,7 @@ public class FeignClientValidationTests {
@Test
public void testServiceIdAndValue() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
LoadBalancerAutoConfiguration.class, RibbonAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class,
NameAndServiceIdConfiguration.class);
LoadBalancerAutoConfiguration.class, NameAndServiceIdConfiguration.class);
assertThat(context.getBean(NameAndServiceIdConfiguration.Client.class))
.isNotNull();
context.close();
@@ -63,7 +55,6 @@ public class FeignClientValidationTests {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.setAllowBeanDefinitionOverriding(false);
context.register(LoadBalancerAutoConfiguration.class,
RibbonAutoConfiguration.class, FeignRibbonClientAutoConfiguration.class,
DuplicatedFeignClientNamesConfiguration.class);
context.refresh();
assertThat(
@@ -81,47 +72,6 @@ public class FeignClientValidationTests {
new AnnotationConfigApplicationContext(BadHostnameConfiguration.class);
}
@Test
public void testMissingFallback() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
MissingFallbackConfiguration.class)) {
this.expected.expectMessage("No fallback instance of type");
assertThat(context.getBean(MissingFallbackConfiguration.Client.class))
.isNotNull();
}
}
@Test
public void testWrongFallbackType() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
WrongFallbackTypeConfiguration.class)) {
this.expected.expectMessage("Incompatible fallback instance");
assertThat(context.getBean(WrongFallbackTypeConfiguration.Client.class))
.isNotNull();
}
}
@Test
public void testMissingFallbackFactory() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
MissingFallbackFactoryConfiguration.class)) {
this.expected.expectMessage("No fallbackFactory instance of type");
assertThat(context.getBean(MissingFallbackFactoryConfiguration.Client.class))
.isNotNull();
}
}
@Test
public void testWrongFallbackFactoryType() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
WrongFallbackFactoryTypeConfiguration.class)) {
this.expected.expectMessage("Incompatible fallbackFactory instance");
assertThat(
context.getBean(WrongFallbackFactoryTypeConfiguration.Client.class))
.isNotNull();
}
}
@Configuration(proxyBeanMethods = false)
@Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class })
@EnableFeignClients(clients = NameAndServiceIdConfiguration.Client.class)
@@ -177,123 +127,4 @@ public class FeignClientValidationTests {
}
@Configuration(proxyBeanMethods = false)
@Import(FeignAutoConfiguration.class)
@EnableFeignClients(clients = MissingFallbackConfiguration.Client.class)
protected static class MissingFallbackConfiguration {
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
@FeignClient(name = "foobar", url = "http://localhost",
fallback = ClientFallback.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
class ClientFallback implements Client {
@Override
public String get() {
return null;
}
}
}
@Configuration(proxyBeanMethods = false)
@Import(FeignAutoConfiguration.class)
@EnableFeignClients(clients = WrongFallbackTypeConfiguration.Client.class)
protected static class WrongFallbackTypeConfiguration {
@Bean
Dummy dummy() {
return new Dummy();
}
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
@FeignClient(name = "foobar", url = "http://localhost", fallback = Dummy.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
class Dummy {
}
}
@Configuration(proxyBeanMethods = false)
@Import(FeignAutoConfiguration.class)
@EnableFeignClients(clients = MissingFallbackFactoryConfiguration.Client.class)
protected static class MissingFallbackFactoryConfiguration {
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
@FeignClient(name = "foobar", url = "http://localhost",
fallbackFactory = ClientFallback.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
class ClientFallback implements FallbackFactory<Client> {
@Override
public Client create(Throwable cause) {
return null;
}
}
}
@Configuration(proxyBeanMethods = false)
@Import(FeignAutoConfiguration.class)
@EnableFeignClients(clients = WrongFallbackFactoryTypeConfiguration.Client.class)
protected static class WrongFallbackFactoryTypeConfiguration {
@Bean
Dummy dummy() {
return new Dummy();
}
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
@FeignClient(name = "foobar", url = "http://localhost",
fallbackFactory = Dummy.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
class Dummy {
}
}
}

View File

@@ -18,20 +18,6 @@ package org.springframework.cloud.openfeign.loadbalancer;
import java.util.Map;
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import org.junit.jupiter.api.Test;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration;
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@@ -41,54 +27,46 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
class FeignLoadBalancerAutoConfigurationTests {
@Test
void shouldInstantiateDefaultFeignBlockingLoadBalancerClientWhenHttpClientDisabled() {
ConfigurableApplicationContext context = initContext(
"spring.cloud.loadbalancer.ribbon.enabled=false",
"feign.httpclient.enabled=false");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalanced(context, Client.Default.class);
assertThatBeanNotPresent(context, LoadBalancerFeignClient.class);
}
@Test
void shouldInstantiateHttpFeignClientWhenEnabled() {
ConfigurableApplicationContext context = initContext(
"spring.cloud.loadbalancer.ribbon.enabled=false");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalanced(context, ApacheHttpClient.class);
assertThatBeanNotPresent(context, LoadBalancerFeignClient.class);
}
@Test
void shouldInstantiateOkHttpFeignClientWhenEnabled() {
ConfigurableApplicationContext context = initContext(
"spring.cloud.loadbalancer.ribbon.enabled=false",
"feign.httpclient.enabled=false", "feign.okhttp.enabled=true");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalanced(context, OkHttpClient.class);
assertThatBeanNotPresent(context, LoadBalancerFeignClient.class);
}
@Test
void shouldNotProcessLoadBalancerConfigurationWhenRibbonEnabled() {
ConfigurableApplicationContext context = initContext(
"spring.cloud.loadbalancer.ribbon.enabled=true");
assertThatOneBeanPresent(context, LoadBalancerFeignClient.class);
assertThatBeanNotPresent(context, BlockingLoadBalancerClient.class);
assertThatBeanNotPresent(context, FeignBlockingLoadBalancerClient.class);
}
private ConfigurableApplicationContext initContext(String... properties) {
return new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties(properties)
.sources(HttpClientConfiguration.class, RibbonAutoConfiguration.class,
LoadBalancerAutoConfiguration.class,
BlockingLoadBalancerClientAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class,
FeignLoadBalancerAutoConfiguration.class)
.run();
}
// FIXME: 3.0.0
/*
* @Test void
* shouldInstantiateDefaultFeignBlockingLoadBalancerClientWhenHttpClientDisabled() {
* ConfigurableApplicationContext context = initContext(
* "spring.cloud.loadbalancer.ribbon.enabled=false",
* "feign.httpclient.enabled=false"); assertThatOneBeanPresent(context,
* BlockingLoadBalancerClient.class); assertLoadBalanced(context,
* Client.Default.class); assertThatBeanNotPresent(context,
* LoadBalancerFeignClient.class); }
*
* @Test void shouldInstantiateHttpFeignClientWhenEnabled() {
* ConfigurableApplicationContext context = initContext(
* "spring.cloud.loadbalancer.ribbon.enabled=false");
* assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
* assertLoadBalanced(context, ApacheHttpClient.class);
* assertThatBeanNotPresent(context, LoadBalancerFeignClient.class); }
*
* @Test void shouldInstantiateOkHttpFeignClientWhenEnabled() {
* ConfigurableApplicationContext context = initContext(
* "spring.cloud.loadbalancer.ribbon.enabled=false", "feign.httpclient.enabled=false",
* "feign.okhttp.enabled=true"); assertThatOneBeanPresent(context,
* BlockingLoadBalancerClient.class); assertLoadBalanced(context, OkHttpClient.class);
* assertThatBeanNotPresent(context, LoadBalancerFeignClient.class); }
*
* @Test void shouldNotProcessLoadBalancerConfigurationWhenRibbonEnabled() {
* ConfigurableApplicationContext context = initContext(
* "spring.cloud.loadbalancer.ribbon.enabled=true"); assertThatOneBeanPresent(context,
* LoadBalancerFeignClient.class); assertThatBeanNotPresent(context,
* BlockingLoadBalancerClient.class); assertThatBeanNotPresent(context,
* FeignBlockingLoadBalancerClient.class); }
*
* private ConfigurableApplicationContext initContext(String... properties) { return
* new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties)
* .sources(HttpClientConfiguration.class, RibbonAutoConfiguration.class,
* LoadBalancerAutoConfiguration.class,
* BlockingLoadBalancerClientAutoConfiguration.class,
* FeignRibbonClientAutoConfiguration.class, FeignLoadBalancerAutoConfiguration.class)
* .run(); }
*/
private void assertThatOneBeanPresent(ConfigurableApplicationContext context,
Class<?> beanClass) {

View File

@@ -1,106 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryFactory;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Spencer Gibb
*/
public class CachingSpringLoadBalancerFactoryTests {
@Mock
private SpringClientFactory delegate;
@Mock
private RibbonLoadBalancedRetryFactory loadBalancedRetryFactory;
private CachingSpringLoadBalancerFactory factory;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
IClientConfig config = new DefaultClientConfigImpl();
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 500);
when(this.delegate.getClientConfig("client1")).thenReturn(config);
when(this.delegate.getClientConfig("client2")).thenReturn(config);
this.factory = new CachingSpringLoadBalancerFactory(this.delegate,
this.loadBalancedRetryFactory);
}
@Test
public void delegateCreatesWhenMissing() {
FeignLoadBalancer client = this.factory.create("client1");
assertThat(client).as("client was null").isNotNull();
verify(this.delegate, times(1)).getClientConfig("client1");
}
@Test
public void cacheWorks() {
FeignLoadBalancer client = this.factory.create("client2");
assertThat(client).as("client was null").isNotNull();
client = this.factory.create("client2");
assertThat(client).as("client was null").isNotNull();
verify(this.delegate, times(1)).getClientConfig("client2");
}
@Test
public void delegateCreatesWithNoRetry() {
IClientConfig config = new DefaultClientConfigImpl();
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 500);
when(this.delegate.getClientConfig("retry")).thenReturn(config);
CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory(
this.delegate);
FeignLoadBalancer client = this.factory.create("retry");
assertThat(client).as("client was null").isNotNull();
}
@Test
public void delegateCreatesWithRetry() {
IClientConfig config = new DefaultClientConfigImpl();
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 500);
when(this.delegate.getClientConfig("retry")).thenReturn(config);
CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory(
this.delegate, this.loadBalancedRetryFactory);
FeignLoadBalancer client = this.factory.create("retry");
assertThat(client).as("client was null").isNotNull();
}
}

View File

@@ -1,209 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.RoundRobinRule;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
import feign.Client;
import feign.Request;
import feign.Request.Options;
import feign.RequestTemplate;
import feign.Response;
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.ribbon.DefaultServerIntrospector;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.RibbonRequest;
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.RibbonResponse;
import static com.netflix.client.config.CommonClientConfigKey.ConnectTimeout;
import static com.netflix.client.config.CommonClientConfigKey.IsSecure;
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetries;
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetriesNextServer;
import static com.netflix.client.config.CommonClientConfigKey.OkToRetryOnAllOperations;
import static com.netflix.client.config.CommonClientConfigKey.ReadTimeout;
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES;
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER;
import static feign.Request.HttpMethod.GET;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
public class FeignLoadBalancerTests {
@Mock
private Client delegate;
@Mock
private ILoadBalancer lb;
@Mock
private IClientConfig config;
private FeignLoadBalancer feignLoadBalancer;
private ServerIntrospector inspector = new DefaultServerIntrospector();
private Integer defaultConnectTimeout = 10000;
private Integer defaultReadTimeout = 10000;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
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);
when(this.config.get(OkToRetryOnAllOperations, false)).thenReturn(true);
}
@Test
public void testUriInsecure() throws Exception {
when(this.config.get(IsSecure)).thenReturn(false);
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
this.inspector);
Request request = new RequestTemplate().method(GET).target("https://foo/")
.resolve(new HashMap<>()).request();
RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request,
new URI(request.url()));
Response response = Response.builder().request(request).status(200).reason("Test")
.headers(Collections.emptyMap()).body(new byte[0]).build();
when(this.delegate.execute(any(Request.class), any(Options.class)))
.thenReturn(response);
RibbonResponse resp = this.feignLoadBalancer.execute(ribbonRequest, null);
assertThat(resp.getRequestedURI()).isEqualTo(new URI("https://foo"));
}
@Test
public void testSecureUriFromClientConfig() throws Exception {
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("https://foo/"));
assertThat(uri).isEqualTo(new URI("https://foo:7777/"));
}
@Test
public void testInsecureUriFromInsecureClientConfigToSecureServerIntrospector()
throws Exception {
when(this.config.get(IsSecure)).thenReturn(false);
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
new ServerIntrospector() {
@Override
public boolean isSecure(Server server) {
return true;
}
@Override
public Map<String, String> getMetadata(Server server) {
return null;
}
});
Server server = new Server("foo", 7777);
URI uri = this.feignLoadBalancer.reconstructURIWithServer(server,
new URI("https://foo/"));
assertThat(uri).isEqualTo(new URI("https://foo:7777/"));
}
@Test
public void testSecureUriFromClientConfigOverride() throws Exception {
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("https://bar/"));
assertThat(uri).isEqualTo(new URI("https://foo:443/"));
}
@Test
public void testRibbonRequestURLEncode() throws Exception {
String url = "https://foo/?name=%7bcookie"; // name={cookie
Request request = Request.create(GET, url, new HashMap<>(), null, null, null);
assertThat(request.url()).isEqualTo(url);
RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request,
new URI(request.url()));
Request cloneRequest = ribbonRequest.toRequest();
assertThat(cloneRequest.url()).isEqualTo(url);
}
@Test
public void testOverrideFeignLoadBalancer() throws Exception {
when(this.config.get(IsSecure)).thenReturn(false);
Server server1 = new Server("foo", 6666);
Server server2 = new Server("foo", 7777);
BaseLoadBalancer baseLoadBalancer = new BaseLoadBalancer();
baseLoadBalancer.setRule(new RoundRobinRule() {
@Override
public Server choose(Object loadBalancerKey) {
return loadBalancerKey == null ? server2 : server1;
}
});
this.feignLoadBalancer = new FeignLoadBalancer(baseLoadBalancer, this.config,
this.inspector) {
protected void customizeLoadBalancerCommandBuilder(
final FeignLoadBalancer.RibbonRequest request,
final IClientConfig config,
final LoadBalancerCommand.Builder<FeignLoadBalancer.RibbonResponse> builder) {
builder.withServerLocator(request.getRequest().headers().get("c_ip"));
}
};
Request request = new RequestTemplate().method(GET).resolve(new HashMap<>())
.request();
RibbonResponse resp = this.feignLoadBalancer.executeWithLoadBalancer(
new RibbonRequest(this.delegate, request, new URI(request.url())), null);
assertThat(resp.getRequestedURI().getPort()).isEqualTo(7777);
request = new RequestTemplate().method(GET).header("c_ip", "666")
.resolve(new HashMap<>()).request();
resp = this.feignLoadBalancer.executeWithLoadBalancer(
new RibbonRequest(this.delegate, request, new URI(request.url())), null);
assertThat(resp.getRequestedURI().getPort()).isEqualTo(6666);
}
}

View File

@@ -1,193 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* @author Venil Noronha
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FeignRibbonClientPathTests.Application.class,
webEnvironment = RANDOM_PORT,
value = { "spring.application.name=feignribbonclientpathtest",
"feign.okhttp.enabled=false", "feign.httpclient.enabled=false",
"feign.hystrix.enabled=false", "test.path.prefix=/base/path" // For
// pathWithPlaceholder
// test
})
@DirtiesContext
public class FeignRibbonClientPathTests {
@Value("${local.server.port}")
private int port = 0;
@Autowired
private TestClient1 testClient1;
@Autowired
private TestClient2 testClient2;
@Autowired
private TestClient3 testClient3;
@Autowired
private TestClient4 testClient4;
@Autowired
private TestClient5 testClient5;
@Test
public void pathWithLeadingButNotTrailingSlash() {
testClientPath(this.testClient1);
}
@Test
public void pathWithoutLeadingAndTrailingSlash() {
testClientPath(this.testClient2);
}
@Test
public void pathWithoutLeadingButTrailingSlash() {
testClientPath(this.testClient3);
}
@Test
public void pathWithLeadingAndTrailingSlash() {
testClientPath(this.testClient4);
}
@Test
public void pathWithPlaceholder() {
testClientPath(this.testClient5);
}
private void testClientPath(TestClient testClient) {
Hello hello = testClient.getHello();
assertThat(hello).as("Object returned was null").isNotNull();
assertThat(hello.getMessage()).as("Response object value didn't match")
.isEqualTo("hello world");
}
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
}
@FeignClient(name = "localapp", path = "/base/path")
protected interface TestClient1 extends TestClient {
}
@FeignClient(name = "localapp1", path = "base/path")
protected interface TestClient2 extends TestClient {
}
@FeignClient(name = "localapp2", path = "base/path/")
protected interface TestClient3 extends TestClient {
}
@FeignClient(name = "localapp3", path = "/base/path/")
protected interface TestClient4 extends TestClient {
}
@FeignClient(name = "localapp4", path = "${test.path.prefix}")
protected interface TestClient5 extends TestClient {
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@RestController
@RequestMapping("/base/path")
@EnableFeignClients(clients = { TestClient1.class, TestClient2.class,
TestClient3.class, TestClient4.class, TestClient5.class })
@RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class)
@Import(NoSecurityConfiguration.class)
public static class Application {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
public Hello getHello() {
return new Hello("hello world");
}
}
public static class Hello {
private String message;
public Hello() {
}
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
}
@Configuration(proxyBeanMethods = false)
public static class LocalRibbonClientConfiguration {
@Value("${local.server.port}")
private int port = 0;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
}
}
}

View File

@@ -1,158 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.concurrent.atomic.AtomicInteger;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* Tests the Feign Retryer, not ribbon retry.
*
* @author Spencer Gibb
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FeignRibbonClientRetryTests.Application.class,
webEnvironment = RANDOM_PORT,
value = { "spring.application.name=feignclientretrytest",
"feign.okhttp.enabled=false", "feign.httpclient.enabled=false",
"feign.hystrix.enabled=false", "localapp.ribbon.MaxAutoRetries=2",
"localapp.ribbon.MaxAutoRetriesNextServer=3" })
@DirtiesContext
public class FeignRibbonClientRetryTests {
@Value("${local.server.port}")
private int port = 0;
@Autowired
private TestClient testClient;
@Test
public void testClient() {
assertThat(this.testClient).as("testClient was null").isNotNull();
assertThat(Proxy.isProxyClass(this.testClient.getClass()))
.as("testClient is not a java Proxy").isTrue();
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient);
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
}
@Test
public void testRetries() {
int retryMe = this.testClient.retryMe();
assertThat(1).as("retryCount didn't match").isEqualTo(retryMe);
// TODO: not sure how to verify retry happens. Debugging through it, it works
// maybe the assertEquals above is enough because of the bogus servers
}
@FeignClient("localapp")
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/retryme")
int retryMe();
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@RestController
@EnableFeignClients(clients = TestClient.class)
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
@Import(NoSecurityConfiguration.class)
public static class Application {
private AtomicInteger retries = new AtomicInteger(1);
@RequestMapping(method = RequestMethod.GET, value = "/hello")
public Hello getHello() {
return new Hello("hello world 1");
}
@RequestMapping(method = RequestMethod.GET, value = "/retryme")
public int retryMe() {
return this.retries.getAndIncrement();
}
}
public static class Hello {
private String message;
public Hello() {
}
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
}
}
// Load balancer with fixed server list for "local" pointing to localhost
// some bogus servers are thrown in to test retry
@Configuration(proxyBeanMethods = false)
class LocalRibbonClientConfiguration {
@Value("${local.server.port}")
private int port = 0;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("mybadhost", 80),
new Server("mybadhost2", 10002), new Server("mybadhost3", 10003),
new Server("localhost", this.port));
}
}

View File

@@ -1,152 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.util.HashMap;
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;
import feign.Request.Options;
import feign.RequestTemplate;
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 static feign.Request.HttpMethod.GET;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
/**
* @author Dave Syer
* @author Spencer Gibb
*/
public class FeignRibbonClientTests {
private AbstractLoadBalancer loadBalancer = mock(AbstractLoadBalancer.class);
private Client delegate = mock(Client.class);
private SpringClientFactory factory = new SpringClientFactory() {
@Override
public IClientConfig getClientConfig(String name) {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 500);
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;
}
};
// Even though we don't maintain FeignRibbonClient, keep these tests
// around to make sure the expected behaviour doesn't break
private Client client = new LoadBalancerFeignClient(this.delegate,
new CachingSpringLoadBalancerFactory(this.factory), this.factory);
@Before
public void init() {
when(this.loadBalancer.chooseServer(any()))
.thenReturn(new Server("foo.com", 8000));
// to fix NPE
LoadBalancerStats stats = mock(LoadBalancerStats.class);
when(this.loadBalancer.getLoadBalancerStats()).thenReturn(stats);
when(stats.getSingleServerStat(any(Server.class)))
.thenReturn(mock(ServerStats.class));
}
@Test
public void remoteRequestIsSentAtRoot() throws Exception {
Request request = new RequestTemplate().method(GET).target("https://foo")
.resolve(new HashMap<>()).request();
this.client.execute(request, new Options());
RequestMatcher matcher = new RequestMatcher("https://foo.com:8000/");
verify(this.delegate).execute(argThat(matcher), any(Options.class));
}
@Test
public void remoteRequestIsSent() throws Exception {
Request request = new RequestTemplate().method(GET).target("https://foo/")
.resolve(new HashMap<>()).request();
this.client.execute(request, new Options());
RequestMatcher matcher = new RequestMatcher("https://foo.com:8000/");
verify(this.delegate).execute(argThat(matcher), any(Options.class));
}
@Test
public void verifyCleanUrl() throws Exception {
Request request = new RequestTemplate().method(GET)
.target("https://tp/abc/bcd.json").resolve(new HashMap<>()).request();
this.client.execute(request, new Options());
RequestMatcher matcher = new RequestMatcher("https://foo.com:8000/abc/bcd.json");
verify(this.delegate).execute(argThat(matcher), any(Options.class));
}
@Test
public void remoteRequestIsSecure() throws Exception {
Request request = new RequestTemplate().method(GET).target("https://foo/")
.resolve(new HashMap<>()).request();
this.client.execute(request, new Options());
RequestMatcher matcher = new RequestMatcher("https://foo.com:8000/");
verify(this.delegate).execute(argThat(matcher), any(Options.class));
}
private final static class RequestMatcher extends CustomMatcher<Request> {
private String url;
private RequestMatcher(String url) {
super("request has URI: " + url);
this.url = url;
}
@Override
public boolean matches(Object item) {
Request request = (Request) item;
return request.url().equals(this.url);
}
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.lang.reflect.Field;
import javax.net.ssl.SSLContextSpi;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.http.config.Lookup;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.impl.conn.DefaultHttpClientConnectionOperator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = FeignRibbonHttpClientConfigurationTests.FeignRibbonHttpClientConfigurationTestsApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "debug=true", "feign.httpclient.disableSslValidation=true" })
@DirtiesContext
public class FeignRibbonHttpClientConfigurationTests {
@Autowired
HttpClientConnectionManager connectionManager;
@Test
public void disableSslTest() throws Exception {
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(
this.connectionManager);
assertThat(socketFactoryRegistry.lookup("https")).isNotNull();
assertThat(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers())
.isNull();
}
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(
HttpClientConnectionManager connectionManager) {
DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator) this
.getField(connectionManager, "connectionOperator");
return (Lookup) this.getField(connectionOperator, "socketFactoryRegistry");
}
private X509TrustManager getX509TrustManager(
Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory) socketFactoryRegistry
.lookup("https");
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) this
.getField(connectionSocketFactory, "socketfactory");
SSLContextSpi sslContext = (SSLContextSpi) this.getField(sslSocketFactory,
"context");
return (X509TrustManager) this.getField(sslContext, "trustManager");
}
protected <T> Object getField(Object target, String name) {
Field field = ReflectionUtils.findField(target.getClass(), name);
ReflectionUtils.makeAccessible(field);
Object value = ReflectionUtils.getField(field, target);
return value;
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
static class FeignRibbonHttpClientConfigurationTestsApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class)
.run(args);
}
}
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.lang.reflect.Field;
import javax.net.ssl.HostnameVerifier;
import okhttp3.OkHttpClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = FeignRibbonOkHttpClientConfigurationTests.FeignRibbonOkHttpClientConfigurationTestsApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "debug=true", "feign.httpclient.disableSslValidation=true",
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false" })
@DirtiesContext
public class FeignRibbonOkHttpClientConfigurationTests {
@Autowired
OkHttpClient httpClient;
@Test
public void disableSslTest() throws Exception {
HostnameVerifier hostnameVerifier = (HostnameVerifier) this
.getField(this.httpClient, "hostnameVerifier");
assertThat(
OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier))
.isTrue();
}
protected <T> Object getField(Object target, String name) {
Field field = ReflectionUtils.findField(target.getClass(), name);
ReflectionUtils.makeAccessible(field);
Object value = ReflectionUtils.getField(field, target);
return value;
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
static class FeignRibbonOkHttpClientConfigurationTestsApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class)
.run(args);
}
}
}

View File

@@ -1,131 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import feign.Request;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.FeignContext;
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.web.bind.annotation.RequestMapping;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = LoadBalancerFeignClientOverrideTests.TestConfiguration.class,
webEnvironment = WebEnvironment.RANDOM_PORT,
value = { "spring.application.name=loadBalancerFeignClientTests",
"feign.httpclient.enabled=false", "feign.okhttp.enabled=false" })
@DirtiesContext
public class LoadBalancerFeignClientOverrideTests {
@Autowired
private FeignContext context;
@Test
public void overrideRequestOptions() {
// specific ribbon 'bar' configuration via spring bean
Request.Options barOptions = this.context.getInstance("bar",
Request.Options.class);
assertThat(barOptions.connectTimeoutMillis()).isEqualTo(1);
assertThat(barOptions.readTimeoutMillis()).isEqualTo(2);
assertOptions(barOptions, "bar", 1, 2);
// specific ribbon 'foo' configuration via application.yml
Request.Options fooOptions = this.context.getInstance("foo",
Request.Options.class);
assertThat(fooOptions).isEqualTo(LoadBalancerFeignClient.DEFAULT_OPTIONS);
assertOptions(fooOptions, "foo", 7, 17);
// generic ribbon default configuration
Request.Options bazOptions = this.context.getInstance("baz",
Request.Options.class);
assertThat(bazOptions).isEqualTo(LoadBalancerFeignClient.DEFAULT_OPTIONS);
assertOptions(bazOptions, "baz", 3001, 60001);
}
void assertOptions(Request.Options options, String name, int expectedConnect,
int expectedRead) {
LoadBalancerFeignClient client = this.context.getInstance(name,
LoadBalancerFeignClient.class);
IClientConfig config = client.getClientConfig(options, name);
assertThat(config.get(CommonClientConfigKey.ConnectTimeout, -1).intValue())
.as("connect was wrong for " + name).isEqualTo(expectedConnect);
assertThat(config.get(CommonClientConfigKey.ReadTimeout, -1).intValue())
.as("read was wrong for " + name).isEqualTo(expectedRead);
}
@FeignClient(value = "foo", configuration = FooConfiguration.class)
interface FooClient {
@RequestMapping("/")
String get();
}
@FeignClient(value = "bar", configuration = BarConfiguration.class)
interface BarClient {
@RequestMapping("/")
String get();
}
@FeignClient("baz")
interface BazClient {
@RequestMapping("/")
String get();
}
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = { FooClient.class, BarClient.class, BazClient.class })
@EnableAutoConfiguration
protected static class TestConfiguration {
}
public static class FooConfiguration {
}
public static class BarConfiguration {
@Bean
public Request.Options feignRequestOptions() {
return new Request.Options(1, 2);
}
}
}

View File

@@ -1,702 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.client.RequestSpecificRetryHandler;
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.Response;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryFactory;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicy;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.TerminatedRetryException;
import org.springframework.retry.backoff.BackOffContext;
import org.springframework.retry.backoff.BackOffInterruptedException;
import org.springframework.retry.backoff.BackOffPolicy;
import static com.netflix.client.config.CommonClientConfigKey.ConnectTimeout;
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetries;
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetriesNextServer;
import static com.netflix.client.config.CommonClientConfigKey.OkToRetryOnAllOperations;
import static com.netflix.client.config.CommonClientConfigKey.ReadTimeout;
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES;
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER;
import static feign.Request.HttpMethod.GET;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Ryan Baxter
* @author Gang Li
* @author Olga Maciaszek-Sharma
*/
public class RetryableFeignLoadBalancerTests {
@Mock
private ILoadBalancer lb;
@Mock
private IClientConfig config;
private ServerIntrospector inspector = new DefaultServerIntrospector();
private Integer defaultConnectTimeout = 10000;
private Integer defaultReadTimeout = 10000;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
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);
when(this.config.get(OkToRetryOnAllOperations, false)).thenReturn(true);
}
@Test
public void executeNoFailure() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404,502,foo, ,").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory);
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
Response response = Response.builder().status(200).request(feignRequest)
.headers(new HashMap<>()).build();
doReturn(response).when(client).execute(any(Request.class),
any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
verify(client, times(1)).execute(any(Request.class), any(Request.Options.class));
}
@Test
public void executeNeverRetry() throws Exception {
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
doThrow(new IOException("boom")).when(client).execute(any(Request.class),
any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
this.config, this.inspector, new LoadBalancedRetryFactory() {
@Override
public LoadBalancedRetryPolicy createRetryPolicy(String s,
ServiceInstanceChooser serviceInstanceChooser) {
return null;
}
@Override
public RetryListener[] createRetryListeners(String service) {
return new RetryListener[0];
}
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return null;
}
});
try {
feignLb.execute(request, null);
}
catch (Exception e) {
assertThat(e).isInstanceOf(IOException.class);
}
finally {
verify(client, times(1)).execute(any(Request.class),
any(Request.Options.class));
}
}
@Test
public void executeRetry() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
}
};
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
Response response = Response.builder().status(200).request(feignRequest)
.headers(new HashMap<>()).build();
doThrow(new IOException("boom")).doReturn(response).when(client)
.execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertThat(backOffPolicy.getCount()).isEqualTo(1);
}
@Test
public void executeRetryOnStatusCode() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
}
};
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
Response response = Response.builder().request(feignRequest).status(200)
.headers(new HashMap<>()).build();
Response fourOFourResponse = Response.builder().request(feignRequest).status(404)
.headers(new HashMap<>()).build();
doReturn(fourOFourResponse).doReturn(response).when(client)
.execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertThat(backOffPolicy.getCount()).isEqualTo(1);
}
@Test
public void executeRetryOnStatusCodeWithEmptyBody() throws Exception {
int retriesNextServer = 0;
when(this.config.get(MaxAutoRetriesNextServer,
DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER)).thenReturn(retriesNextServer);
doReturn(new Server("foo", 80)).when(this.lb).chooseServer(any());
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(retriesNextServer).when(config)
.get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
}
};
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
Response response = Response.builder().request(feignRequest).status(404)
.headers(new HashMap<>()).build();
Response fourOFourResponse = Response.builder().request(feignRequest).status(404)
.headers(new HashMap<>()).build();
doReturn(fourOFourResponse).doReturn(response).when(client)
.execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertThat(ribbonResponse.toResponse().status()).isEqualTo(404);
assertThat(ribbonResponse.toResponse().body().length())
.isEqualTo(Integer.valueOf(0));
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertThat(backOffPolicy.getCount()).isEqualTo(1);
}
@Test
public void getRequestSpecificRetryHandler() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory);
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
Response response = Response.builder().request(feignRequest).status(200)
.headers(new HashMap<>()).build();
doReturn(response).when(client).execute(any(Request.class),
any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
this.config, this.inspector, loadBalancedRetryFactory);
RequestSpecificRetryHandler retryHandler = feignLb
.getRequestSpecificRetryHandler(request, this.config);
assertThat(retryHandler.getMaxRetriesOnNextServer()).isEqualTo(1);
assertThat(retryHandler.getMaxRetriesOnSameServer()).isEqualTo(1);
}
@Test
public void choose() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory);
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
Response response = Response.builder().request(feignRequest).status(200)
.headers(new HashMap<>()).build();
doReturn(response).when(client).execute(any(Request.class),
any(Request.Options.class));
final Server server = new Server("foo", 80);
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(
new ILoadBalancer() {
@Override
public void addServers(List<Server> list) {
}
@Override
public Server chooseServer(Object o) {
return server;
}
@Override
public void markServerDown(Server server) {
}
@Override
public List<Server> getServerList(boolean b) {
return null;
}
@Override
public List<Server> getReachableServers() {
return null;
}
@Override
public List<Server> getAllServers() {
return null;
}
}, this.config, this.inspector, loadBalancedRetryFactory);
ServiceInstance serviceInstance = feignLb.choose("foo");
assertThat(serviceInstance.getHost()).isEqualTo("foo");
assertThat(serviceInstance.getPort()).isEqualTo(80);
}
@Test
public void retryListenerTest() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
MyRetryListener myRetryListener = new MyRetryListener();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public RetryListener[] createRetryListeners(String service) {
return new RetryListener[] { myRetryListener };
}
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
}
};
Request feignRequest = Request.create(GET, "https://listener", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://listener"));
Response response = Response.builder().request(feignRequest).status(200)
.headers(new HashMap<>()).build();
doThrow(new IOException("boom")).doReturn(response).when(client)
.execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertThat(backOffPolicy.getCount()).isEqualTo(1);
assertThat(myRetryListener.getOnError()).isEqualTo(1);
}
@Test(expected = TerminatedRetryException.class)
public void retryListenerTestNoRetry() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
MyRetryListenerNotRetry myRetryListenerNotRetry = new MyRetryListenerNotRetry();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public RetryListener[] createRetryListeners(String service) {
return new RetryListener[] { myRetryListenerNotRetry };
}
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
}
};
Request feignRequest = Request.create(GET, "https://listener", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://listener"));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
}
@Test
public void retryWithDefaultConstructorTest() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
}
};
Request feignRequest = Request.create(GET, "https://listener", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://listener"));
Response response = Response.builder().request(feignRequest).status(200)
.headers(new HashMap<>()).build();
doThrow(new IOException("boom")).doReturn(response).when(client)
.execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryPolicyFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertThat(backOffPolicy.getCount()).isEqualTo(1);
}
@Test
public void executeRetryFail() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
lbContext.setRetryHandler(new DefaultLoadBalancerRetryHandler(1, 0, true));
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(0).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
}
};
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("https://foo"));
Response fourOFourResponse = Response.builder().request(feignRequest).status(404)
.headers(new HashMap<>()).body(new Response.Body() { // set content into
// response
@Override
public Integer length() {
return "test".getBytes().length;
}
@Override
public boolean isRepeatable() {
return true;
}
@Override
public InputStream asInputStream() throws IOException {
return new ByteArrayInputStream("test".getBytes());
}
@Override
public Reader asReader() throws IOException {
return new InputStreamReader(asInputStream(), UTF_8);
}
@Override
public Reader asReader(Charset charset) throws IOException {
return new InputStreamReader(asInputStream(), charset);
}
@Override
public void close() throws IOException {
}
}).build();
doReturn(fourOFourResponse).when(client).execute(any(Request.class),
any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertThat(backOffPolicy.getCount()).isEqualTo(1);
InputStream inputStream = ribbonResponse.toResponse().body().asInputStream();
byte[] buf = new byte[100];
int read = inputStream.read(buf);
assertThat(new String(buf, 0, read)).isEqualTo("test");
}
class MyBackOffPolicy implements BackOffPolicy {
private int count = 0;
@Override
public BackOffContext start(RetryContext retryContext) {
return null;
}
@Override
public void backOff(BackOffContext backOffContext)
throws BackOffInterruptedException {
this.count++;
}
public int getCount() {
return this.count;
}
}
class MyRetryListener implements RetryListener {
private int onError = 0;
@Override
public <T, E extends Throwable> boolean open(RetryContext context,
RetryCallback<T, E> callback) {
return true;
}
@Override
public <T, E extends Throwable> void close(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
}
@Override
public <T, E extends Throwable> void onError(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
this.onError++;
}
public int getOnError() {
return this.onError;
}
}
class MyRetryListenerNotRetry implements RetryListener {
@Override
public <T, E extends Throwable> boolean open(RetryContext context,
RetryCallback<T, E> callback) {
return false;
}
@Override
public <T, E extends Throwable> void close(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
}
@Override
public <T, E extends Throwable> void onError(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
}
}
}

View File

@@ -1,66 +0,0 @@
/*
* Copyright 2013-2019 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
*
* https://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.openfeign.ribbon;
import java.io.ByteArrayInputStream;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import feign.Request;
import feign.Response;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.util.StreamUtils;
import static feign.Request.HttpMethod.GET;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@RunWith(MockitoJUnitRunner.class)
public class RibbonResponseStatusCodeExceptionTest {
@Test
public void getResponse() throws Exception {
Map<String, Collection<String>> headers = new HashMap<>();
List<String> fooValues = new ArrayList<>();
fooValues.add("bar");
headers.put("foo", fooValues);
Request request = Request.create(GET, "https://service.com", new HashMap<>(),
new byte[] {}, Charset.defaultCharset());
byte[] body = "foo".getBytes();
ByteArrayInputStream is = new ByteArrayInputStream(body);
Response response = Response.builder().status(200).reason("Success")
.request(request).body(is, body.length).headers(headers).build();
RibbonResponseStatusCodeException ex = new RibbonResponseStatusCodeException(
"service", response, body, new URI(request.url()));
assertThat(ex.getResponse().status()).isEqualTo(200);
assertThat(ex.getResponse().request()).isEqualTo(request);
assertThat(ex.getResponse().reason()).isEqualTo("Success");
assertThat(StreamUtils.copyToString(ex.getResponse().body().asInputStream(),
Charset.defaultCharset())).isEqualTo("foo");
}
}

View File

@@ -20,11 +20,8 @@ import java.io.IOException;
import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import org.apache.http.Header;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.RegistryBuilder;
@@ -35,7 +32,6 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockingDetails;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
@@ -48,7 +44,6 @@ import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientConne
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientFactory;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
@@ -59,7 +54,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockingDetails;
/**
* @author Ryan Baxter
@@ -76,8 +70,9 @@ public class ApacheHttpClientConfigurationTests {
@Autowired
ApacheHttpClientFactory httpClientFactory;
@Autowired
LoadBalancerFeignClient feignClient;
/*
* @Autowired LoadBalancerFeignClient feignClient;
*/
@Test
public void testFactories() {
@@ -92,12 +87,15 @@ public class ApacheHttpClientConfigurationTests {
@Test
public void testHttpClientWithFeign() {
Client delegate = this.feignClient.getDelegate();
assertThat(ApacheHttpClient.class.isInstance(delegate)).isTrue();
ApacheHttpClient apacheHttpClient = (ApacheHttpClient) delegate;
HttpClient httpClient = getField(apacheHttpClient, "client");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertThat(httpClientDetails.isMock()).isTrue();
// FIXME: 3.0.0
/*
* Client delegate = this.feignClient.getDelegate();
* assertThat(ApacheHttpClient.class.isInstance(delegate)).isTrue();
* ApacheHttpClient apacheHttpClient = (ApacheHttpClient) delegate; HttpClient
* httpClient = getField(apacheHttpClient, "client"); MockingDetails
* httpClientDetails = mockingDetails(httpClient);
* assertThat(httpClientDetails.isMock()).isTrue();
*/
}
protected <T> T getField(Object target, String name) {

View File

@@ -19,12 +19,10 @@ package org.springframework.cloud.openfeign.test;
import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;
import feign.Client;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockingDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
@@ -35,7 +33,6 @@ import org.springframework.cloud.commons.httpclient.DefaultOkHttpClientFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
@@ -43,7 +40,6 @@ import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockingDetails;
/**
* @author Ryan Baxter
@@ -63,8 +59,9 @@ public class OkHttpClientConfigurationTests {
@Autowired
OkHttpClientConnectionPoolFactory connectionPoolFactory;
@Autowired
LoadBalancerFeignClient feignClient;
/*
* @Autowired LoadBalancerFeignClient feignClient;
*/
@Test
public void testFactories() {
@@ -79,12 +76,15 @@ public class OkHttpClientConfigurationTests {
@Test
public void testHttpClientWithFeign() {
Client delegate = this.feignClient.getDelegate();
assertThat(feign.okhttp.OkHttpClient.class.isInstance(delegate)).isTrue();
feign.okhttp.OkHttpClient okHttpClient = (feign.okhttp.OkHttpClient) delegate;
OkHttpClient httpClient = getField(okHttpClient, "delegate");
MockingDetails httpClientDetails = mockingDetails(httpClient);
assertThat(httpClientDetails.isMock()).isTrue();
// FIXME: 3.0.0
/*
* Client delegate = this.feignClient.getDelegate();
* assertThat(feign.okhttp.OkHttpClient.class.isInstance(delegate)).isTrue();
* feign.okhttp.OkHttpClient okHttpClient = (feign.okhttp.OkHttpClient) delegate;
* OkHttpClient httpClient = getField(okHttpClient, "delegate"); MockingDetails
* httpClientDetails = mockingDetails(httpClient);
* assertThat(httpClientDetails.isMock()).isTrue();
*/
}
protected <T> T getField(Object target, String name) {

View File

@@ -18,8 +18,6 @@ package org.springframework.cloud.openfeign.valid;
import java.util.List;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import feign.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -28,8 +26,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
@@ -104,7 +100,8 @@ public class FeignClientNotPrimaryTests {
@RestController
@EnableFeignClients(clients = { TestClient.class },
defaultConfiguration = TestDefaultFeignConfig.class)
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
// @RibbonClient(name = "localapp", configuration =
// LocalRibbonClientConfiguration.class)
protected static class Application {
@Bean
@@ -167,10 +164,10 @@ public class FeignClientNotPrimaryTests {
@Value("${local.server.port}")
private int port = 0;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
}
/*
* @Bean public ServerList<Server> ribbonServerList() { return new
* StaticServerList<>(new Server("localhost", this.port)); }
*/
}

View File

@@ -17,7 +17,6 @@
package org.springframework.cloud.openfeign.valid;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.ParseException;
import java.time.LocalDate;
@@ -28,43 +27,24 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import feign.Client;
import feign.Feign;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Target;
import feign.hystrix.FallbackFactory;
import feign.hystrix.SetterFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import rx.Observable;
import rx.Single;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.FeignFormatterRegistrar;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.cloud.openfeign.support.FallbackCommand;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -110,16 +90,6 @@ public class FeignClientTests {
public static final String MYHEADER2 = "myheader2";
@Autowired
HystrixClient hystrixClient;
@Autowired
@Qualifier("localapp3FeignClient")
HystrixClient namedHystrixClient;
@Autowired
HystrixSetterFactoryClient hystrixSetterFactoryClient;
@Value("${local.server.port}")
private int port = 0;
@@ -135,15 +105,6 @@ public class FeignClientTests {
@Autowired
private Client feignClient;
@Autowired
private HystrixClientWithFallBackFactory hystrixClientWithFallBackFactory;
@Autowired
private InvalidTypeHystrixClientWithFallBackFactory invalidTypeHystrixClientWithFallBackFactory;
@Autowired
private NullHystrixClientWithFallBackFactory nullHystrixClientWithFallBackFactory;
private static ArrayList<Hello> getHelloList() {
ArrayList<Hello> hellos = new ArrayList<>();
hellos.add(new Hello(HELLO_WORLD_1));
@@ -205,13 +166,13 @@ public class FeignClientTests {
assertThat(header).as("header was wrong").isEqualTo("myPlaceholderHeaderValue");
}
@Test
public void testFeignClientType() throws IllegalAccessException {
assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
Client delegate = client.getDelegate();
assertThat(delegate).isInstanceOf(Client.Default.class);
}
/*
* @Test FIXME 3.0.0 public void testFeignClientType() throws IllegalAccessException {
* assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
* LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient; Client
* delegate = client.getDelegate();
* assertThat(delegate).isInstanceOf(Client.Default.class); }
*/
@Test
public void testServiceId() {
@@ -240,23 +201,6 @@ public class FeignClientTests {
assertThat(params).as("params not converted correctly").isEqualTo(list);
}
@Test
public void testHystrixCommand() throws NoSuchMethodException {
HystrixCommand<List<Hello>> command = this.testClient.getHellosHystrix();
assertThat(command).as("command was null").isNotNull();
assertThat(command.getCommandGroup().name()).as(
"Hystrix command group name should match the name of the feign client")
.isEqualTo("localapp");
String configKey = Feign.configKey(TestClient.class,
TestClient.class.getMethod("getHellosHystrix", (Class<?>[]) null));
assertThat(command.getCommandKey().name())
.as("Hystrix command key name should match the feign config key")
.isEqualTo(configKey);
List<Hello> hellos = command.execute();
assertThat(hellos).as("hellos was null").isNotNull();
assertThat(getHelloList()).as("hellos didn't match").isEqualTo(hellos);
}
@Test
public void testSingle() {
Single<Hello> single = this.testClient.getHelloSingle();
@@ -331,95 +275,11 @@ public class FeignClientTests {
assertThat(this.testClient.getToString(args)).isEqualTo(expectedResult);
}
@Test
public void testHystrixFallbackWorks() {
Hello hello = this.hystrixClient.fail();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello.getMessage()).as("message was wrong").isEqualTo("fallback");
}
@Test
public void testHystrixFallbackSingle() {
Single<Hello> single = this.hystrixClient.failSingle();
assertThat(single).as("single was null").isNotNull();
Hello hello = single.toBlocking().value();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello.getMessage()).as("message was wrong")
.isEqualTo("fallbacksingle");
}
@Test
public void testHystrixFallbackCommand() {
HystrixCommand<Hello> command = this.hystrixClient.failCommand();
assertThat(command).as("command was null").isNotNull();
Hello hello = command.execute();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello.getMessage()).as("message was wrong")
.isEqualTo("fallbackcommand");
}
@Test
public void testHystrixFallbackObservable() {
Observable<Hello> observable = this.hystrixClient.failObservable();
assertThat(observable).as("observable was null").isNotNull();
Hello hello = observable.toBlocking().first();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello.getMessage()).as("message was wrong")
.isEqualTo("fallbackobservable");
}
@Test
public void testHystrixFallbackFuture() throws Exception {
Future<Hello> future = this.hystrixClient.failFuture();
assertThat(future).as("future was null").isNotNull();
Hello hello = future.get(1, TimeUnit.SECONDS);
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello.getMessage()).as("message was wrong")
.isEqualTo("fallbackfuture");
}
@Test
public void testHystrixClientWithFallBackFactory() throws Exception {
Hello hello = this.hystrixClientWithFallBackFactory.fail();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello.getMessage()).as("hello#message was null").isNotNull();
assertThat(hello.getMessage().contains("500")).as(
"hello#message did not contain the cause (status code) of the fallback invocation")
.isTrue();
}
@Test(expected = HystrixRuntimeException.class)
public void testInvalidTypeHystrixFallbackFactory() throws Exception {
this.invalidTypeHystrixClientWithFallBackFactory.fail();
}
@Test(expected = HystrixRuntimeException.class)
public void testNullHystrixFallbackFactory() throws Exception {
this.nullHystrixClientWithFallBackFactory.fail();
}
@Test
public void namedFeignClientWorks() {
assertThat(this.namedHystrixClient).as("namedHystrixClient was null").isNotNull();
}
@Test
public void testHystrixSetterFactory() {
HystrixCommand<List<Hello>> command = this.hystrixSetterFactoryClient
.getHellosHystrix();
assertThat(command).as("command was null").isNotNull();
String setterPrefix = TestHystrixSetterFactoryClientConfig.SETTER_PREFIX;
assertThat(command.getCommandGroup().name()).as(
"Hystrix command group name should match the name of the feign client with a prefix of "
+ setterPrefix)
.isEqualTo(setterPrefix + "localapp5");
assertThat(command.getCommandKey().name()).as(
"Hystrix command key name should match the request method (space) request path with a prefix of "
+ setterPrefix)
.isEqualTo(setterPrefix + "GET /hellos");
List<Hello> hellos = command.execute();
assertThat(hellos).as("hellos was null").isNotNull();
assertThat(getHelloList()).as("hellos didn't match").isEqualTo(hellos);
// FIXME: 3.0.0
// assertThat(this.namedHystrixClient).as("namedHystrixClient was
// null").isNotNull();
}
protected enum Arg {
@@ -469,9 +329,6 @@ public class FeignClientTests {
List<LocalDate> getFormattedParams(@RequestParam("params") @DateTimeFormat(
pattern = "dd-MM-yyyy") List<LocalDate> params);
@RequestMapping(method = RequestMethod.GET, path = "/hellos")
HystrixCommand<List<Hello>> getHellosHystrix();
@RequestMapping(method = RequestMethod.GET, path = "/noContent")
ResponseEntity<Void> noContent();
@@ -517,61 +374,6 @@ public class FeignClientTests {
}
@FeignClient(name = "localapp3", fallback = HystrixClientFallback.class)
protected interface HystrixClient {
@RequestMapping(method = RequestMethod.GET, path = "/fail")
Single<Hello> failSingle();
@RequestMapping(method = RequestMethod.GET, path = "/fail")
Hello fail();
@RequestMapping(method = RequestMethod.GET, path = "/fail")
HystrixCommand<Hello> failCommand();
@RequestMapping(method = RequestMethod.GET, path = "/fail")
Observable<Hello> failObservable();
@RequestMapping(method = RequestMethod.GET, path = "/fail")
Future<Hello> failFuture();
}
@FeignClient(name = "localapp4", fallbackFactory = HystrixClientFallbackFactory.class)
protected interface HystrixClientWithFallBackFactory {
@RequestMapping(method = RequestMethod.GET, path = "/fail")
Hello fail();
}
@FeignClient(name = "localapp6",
fallbackFactory = InvalidTypeHystrixClientFallbackFactory.class)
protected interface InvalidTypeHystrixClientWithFallBackFactory {
@RequestMapping(method = RequestMethod.GET, path = "/fail")
Hello fail();
}
@FeignClient(name = "localapp7",
fallbackFactory = NullHystrixClientFallbackFactory.class)
protected interface NullHystrixClientWithFallBackFactory {
@RequestMapping(method = RequestMethod.GET, path = "/fail")
Hello fail();
}
@FeignClient(name = "localapp5",
configuration = TestHystrixSetterFactoryClientConfig.class)
protected interface HystrixSetterFactoryClient {
@RequestMapping(method = RequestMethod.GET, path = "/hellos")
HystrixCommand<List<Hello>> getHellosHystrix();
}
protected static class OtherArg {
public final String value;
@@ -611,120 +413,40 @@ public class FeignClientTests {
}
static class HystrixClientFallbackFactory
implements FallbackFactory<HystrixClientWithFallBackFactory> {
@Override
public HystrixClientWithFallBackFactory create(final Throwable cause) {
return new HystrixClientWithFallBackFactory() {
@Override
public Hello fail() {
assertThat(cause).isNotNull().as("Cause was null");
return new Hello(
"Hello from the fallback side: " + cause.getMessage());
}
};
}
}
static class InvalidTypeHystrixClientFallbackFactory
implements FallbackFactory<String> {
@Override
public String create(final Throwable cause) {
return "hello";
}
}
static class NullHystrixClientFallbackFactory implements FallbackFactory<String> {
@Override
public String create(final Throwable cause) {
return null;
}
}
static class HystrixClientFallback implements HystrixClient {
@Override
public Hello fail() {
return new Hello("fallback");
}
@Override
public Single<Hello> failSingle() {
return Single.just(new Hello("fallbacksingle"));
}
@Override
public HystrixCommand<Hello> failCommand() {
return new FallbackCommand<>(new Hello("fallbackcommand"));
}
@Override
public Observable<Hello> failObservable() {
return Observable.just(new Hello("fallbackobservable"));
}
@Override
public Future<Hello> failFuture() {
return new FallbackCommand<>(new Hello("fallbackfuture")).queue();
}
}
public static class TestHystrixSetterFactoryClientConfig {
public static final String SETTER_PREFIX = "SETTER-";
@Bean
public SetterFactory commandKeyIsRequestLineSetterFactory() {
return new SetterFactory() {
@Override
public HystrixCommand.Setter create(Target<?> target, Method method) {
String groupKey = SETTER_PREFIX + target.name();
RequestMapping requestMapping = method
.getAnnotation(RequestMapping.class);
String commandKey = SETTER_PREFIX + requestMapping.method()[0] + " "
+ requestMapping.path()[0];
return HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
}
};
}
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@RestController
@EnableFeignClients(clients = { TestClientServiceId.class, TestClient.class,
DecodingTestClient.class, HystrixClient.class,
HystrixClientWithFallBackFactory.class, HystrixSetterFactoryClient.class,
InvalidTypeHystrixClientWithFallBackFactory.class,
NullHystrixClientWithFallBackFactory.class },
@EnableFeignClients(
clients = { TestClientServiceId.class, TestClient.class,
DecodingTestClient.class },
defaultConfiguration = TestDefaultFeignConfig.class)
@RibbonClients({
@RibbonClient(name = "localapp",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp1",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp2",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp3",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp4",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp5",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp6",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp7",
configuration = LocalRibbonClientConfiguration.class) })
/*
* @RibbonClients({
*
* @RibbonClient(name = "localapp", configuration =
* LocalRibbonClientConfiguration.class),
*
* @RibbonClient(name = "localapp1", configuration =
* LocalRibbonClientConfiguration.class),
*
* @RibbonClient(name = "localapp2", configuration =
* LocalRibbonClientConfiguration.class),
*
* @RibbonClient(name = "localapp3", configuration =
* LocalRibbonClientConfiguration.class),
*
* @RibbonClient(name = "localapp4", configuration =
* LocalRibbonClientConfiguration.class),
*
* @RibbonClient(name = "localapp5", configuration =
* LocalRibbonClientConfiguration.class),
*
* @RibbonClient(name = "localapp6", configuration =
* LocalRibbonClientConfiguration.class),
*
* @RibbonClient(name = "localapp7", configuration =
* LocalRibbonClientConfiguration.class) })
*/
@Import(NoSecurityConfiguration.class)
protected static class Application {
@@ -735,27 +457,6 @@ public class FeignClientTests {
.run(args);
}
// needs to be in parent context to test multiple HystrixClient beans
@Bean
public HystrixClientFallback hystrixClientFallback() {
return new HystrixClientFallback();
}
@Bean
public HystrixClientFallbackFactory hystrixClientFallbackFactory() {
return new HystrixClientFallbackFactory();
}
@Bean
public InvalidTypeHystrixClientFallbackFactory invalidTypeHystrixClientFallbackFactory() {
return new InvalidTypeHystrixClientFallbackFactory();
}
@Bean
public NullHystrixClientFallbackFactory nullHystrixClientFallbackFactory() {
return new NullHystrixClientFallbackFactory();
}
@Bean
FeignFormatterRegistrar feignFormatterRegistrar() {
return new FeignFormatterRegistrar() {
@@ -941,10 +642,10 @@ public class FeignClientTests {
@Value("${local.server.port}")
private int port = 0;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
}
/*
* @Bean public ServerList<Server> ribbonServerList() { return new
* StaticServerList<>(new Server("localhost", this.port)); }
*/
}

View File

@@ -18,13 +18,10 @@ package org.springframework.cloud.openfeign.valid;
import org.junit.Test;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -56,12 +53,15 @@ public class FeignClientValidationTests {
@Test
public void validLoadBalanced() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
LoadBalancerAutoConfiguration.class, RibbonAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class,
GoodServiceIdConfiguration.class);
assertThat(context.getBean(GoodServiceIdConfiguration.Client.class)).isNotNull();
context.close();
// FIXME: 3.0.0
/*
* AnnotationConfigApplicationContext context = new
* AnnotationConfigApplicationContext( LoadBalancerAutoConfiguration.class,
* RibbonAutoConfiguration.class, FeignRibbonClientAutoConfiguration.class,
* GoodServiceIdConfiguration.class);
* assertThat(context.getBean(GoodServiceIdConfiguration.Client.class)).isNotNull(
* ); context.close();
*/
}
@Configuration(proxyBeanMethods = false)

View File

@@ -18,8 +18,6 @@ package org.springframework.cloud.openfeign.valid;
import java.util.Objects;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import feign.Client;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,14 +27,9 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
@@ -93,10 +86,13 @@ public class FeignHttpClientTests {
@Test
public void testFeignClientType() throws IllegalAccessException {
assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
Client delegate = client.getDelegate();
assertThat(delegate).isInstanceOf(feign.httpclient.ApacheHttpClient.class);
// FIXME: 3.0.0
/*
* assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
* LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
* Client delegate = client.getDelegate();
* assertThat(delegate).isInstanceOf(feign.httpclient.ApacheHttpClient.class);
*/
}
@Test
@@ -141,11 +137,15 @@ public class FeignHttpClientTests {
@EnableAutoConfiguration
@RestController
@EnableFeignClients(clients = { TestClient.class, UserClient.class })
@RibbonClients({
@RibbonClient(name = "localapp",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp1",
configuration = LocalRibbonClientConfiguration.class) })
/*
* @RibbonClients({
*
* @RibbonClient(name = "localapp", configuration =
* LocalRibbonClientConfiguration.class),
*
* @RibbonClient(name = "localapp1", configuration =
* LocalRibbonClientConfiguration.class) })
*/
@Import(NoSecurityConfiguration.class)
protected static class Application implements UserService {
@@ -258,10 +258,10 @@ public class FeignHttpClientTests {
@LocalServerPort
private int port = 0;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
}
/*
* @Bean public ServerList<Server> ribbonServerList() { return new
* StaticServerList<>(new Server("localhost", this.port)); }
*/
}

View File

@@ -18,8 +18,6 @@ package org.springframework.cloud.openfeign.valid;
import java.util.Objects;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import feign.Client;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,14 +27,9 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.ResponseEntity;
@@ -94,10 +87,13 @@ public class FeignOkHttpTests {
@Test
public void testFeignClientType() throws IllegalAccessException {
assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
Client delegate = client.getDelegate();
assertThat(delegate).isInstanceOf(feign.okhttp.OkHttpClient.class);
// FIXME: 3.0.0
/*
* assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
* LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
* Client delegate = client.getDelegate();
* assertThat(delegate).isInstanceOf(feign.okhttp.OkHttpClient.class);
*/
}
@Test
@@ -140,11 +136,15 @@ public class FeignOkHttpTests {
@EnableAutoConfiguration
@RestController
@EnableFeignClients(clients = { TestClient.class, UserClient.class })
@RibbonClients({
@RibbonClient(name = "localapp",
configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp1",
configuration = LocalRibbonClientConfiguration.class) })
/*
* @RibbonClients({
*
* @RibbonClient(name = "localapp", configuration =
* LocalRibbonClientConfiguration.class),
*
* @RibbonClient(name = "localapp1", configuration =
* LocalRibbonClientConfiguration.class) })
*/
@Import(NoSecurityConfiguration.class)
protected static class Application implements UserService {
@@ -257,10 +257,10 @@ public class FeignOkHttpTests {
@Value("${local.server.port}")
private int port = 0;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
}
/*
* @Bean public ServerList<Server> ribbonServerList() { return new
* StaticServerList<>(new Server("localhost", this.port)); }
*/
}

View File

@@ -16,8 +16,6 @@
package org.springframework.cloud.openfeign.valid;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import io.vavr.collection.HashSet;
import io.vavr.collection.Set;
import org.junit.Test;
@@ -28,12 +26,9 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
@@ -79,7 +74,8 @@ public class IterableParameterTests {
@EnableAutoConfiguration
@RestController
@EnableFeignClients(clients = TestClient.class)
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
// @RibbonClient(name = "localapp", configuration =
// LocalRibbonClientConfiguration.class)
@Import(NoSecurityConfiguration.class)
protected static class Application {
@@ -96,10 +92,10 @@ public class IterableParameterTests {
@LocalServerPort
private int port = 0;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
}
/*
* @Bean public ServerList<Server> ribbonServerList() { return new
* StaticServerList<>(new Server("localhost", this.port)); }
*/
}

View File

@@ -16,8 +16,6 @@
package org.springframework.cloud.openfeign.valid.scanning;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -25,12 +23,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.cloud.openfeign.testclients.TestClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
@@ -68,7 +63,8 @@ public class FeignClientEnvVarTests {
@EnableAutoConfiguration
@RestController
@EnableFeignClients(basePackages = { "${basepackage}" })
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
// @RibbonClient(name = "localapp", configuration =
// LocalRibbonClientConfiguration.class)
@Import(NoSecurityConfiguration.class)
protected static class Application {
@@ -86,10 +82,10 @@ public class FeignClientEnvVarTests {
@Value("${local.server.port}")
private int port = 0;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
}
/*
* @Bean public ServerList<Server> ribbonServerList() { return new
* StaticServerList<>(new Server("localhost", this.port)); }
*/
}

View File

@@ -16,8 +16,6 @@
package org.springframework.cloud.openfeign.valid.scanning;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import feign.Client;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,12 +24,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
@@ -100,7 +95,7 @@ public class FeignClientScanningTests {
@EnableAutoConfiguration
@RestController
@EnableFeignClients // NO clients attribute. That's what this class is testing!
@RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class)
// @RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class)
@Import(NoSecurityConfiguration.class)
protected static class Application {
@@ -118,10 +113,10 @@ public class FeignClientScanningTests {
@Value("${local.server.port}")
private int port = 0;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
}
/*
* @Bean public ServerList<Server> ribbonServerList() { return new
* StaticServerList<>(new Server("localhost", this.port)); }
*/
}