Merge remote-tracking branch 'Upstream/master' into simplify-retry-logic

This commit is contained in:
Ryan Baxter
2016-10-26 15:05:12 -04:00
38 changed files with 1138 additions and 258 deletions

View File

@@ -970,6 +970,45 @@ This replaces the `SpringMvcContract` with `feign.Contract.Default` and adds a `
Default configurations can be specified in the `@EnableFeignClients` attribute `defaultConfiguration` in a similar manner as described above. The difference is that this configuration will apply to _all_ feign clients.
=== Creating Feign Clients Manually
In some cases it might be necessary to customize your Feign Clients in a way that is not
possible using the methods above. In this case you can create Clients using the
https://github.com/OpenFeign/feign/#basics[Feign Builder API]. Below is an example
which creates two Feign Clients with the same interface but configures each one with
a separate request interceptor.
[source,java,indent=0]
----
@Import(FeignClientsConfiguration.class)
class FooController {
private FooClient fooClient;
private FooClient adminClient;
@Autowired
public FooController(
ResponseEntityDecoder decoder, SpringEncoder encoder, Client client) {
this.fooClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
.target(FooClient.class, "http://PROD-SVC");
this.adminClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
.target(FooClient.class, "http://PROD-SVC");
}
}
----
NOTE: In the above example `FeignClientsConfiguration.class` is the default configuration
provided by Spring Cloud Netflix.
NOTE: `PROD-SVC` is the name of the service the Clients will be making requests to.
[[spring-cloud-feign-hystrix]]
=== Feign Hystrix Support

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.feign;
import feign.Logger;
import feign.slf4j.Slf4jLogger;
/**
* @author Venil Noronha
*/
public class DefaultFeignLoggerFactory implements FeignLoggerFactory {
private Logger logger;
public DefaultFeignLoggerFactory(Logger logger) {
this.logger = logger;
}
@Override
public Logger create(Class<?> type) {
return this.logger != null ? this.logger : new Slf4jLogger(type);
}
}

View File

@@ -39,7 +39,6 @@ import feign.Target.HardCodedTarget;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import feign.slf4j.Slf4jLogger;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -83,11 +82,8 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
protected Feign.Builder feign(FeignContext context) {
Logger logger = getOptional(context, Logger.class);
if (logger == null) {
logger = new Slf4jLogger(this.type);
}
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// @formatter:off
Feign.Builder builder = get(context, Feign.Builder.class)

View File

@@ -19,7 +19,6 @@ package org.springframework.cloud.netflix.feign;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.HttpClient;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -39,16 +38,16 @@ import org.springframework.format.support.FormattingConversionService;
import com.netflix.hystrix.HystrixCommand;
import feign.Client;
import feign.Contract;
import feign.Feign;
import feign.Logger;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.httpclient.ApacheHttpClient;
import feign.hystrix.HystrixFeign;
/**
* @author Dave Syer
* @author Venil Noronha
*/
@Configuration
public class FeignClientsConfiguration {
@@ -62,6 +61,9 @@ public class FeignClientsConfiguration {
@Autowired(required = false)
private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();
@Autowired(required = false)
private Logger logger;
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
@@ -108,4 +110,10 @@ public class FeignClientsConfiguration {
return Feign.builder();
}
@Bean
@ConditionalOnMissingBean(FeignLoggerFactory.class)
public FeignLoggerFactory feignLoggerFactory() {
return new DefaultFeignLoggerFactory(logger);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.feign;
import feign.Logger;
/**
* Allows an application to use a custom Feign {@link Logger}.
*
* @author Venil Noronha
*/
public interface FeignLoggerFactory {
/**
* Factory method to provide a {@link Logger} for a given {@link Class}.
*
* @param type the {@link Class} for which a {@link Logger} instance is to be created
* @return a {@link Logger} instance
*/
public Logger create(Class<?> type);
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.zuul;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Collections;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommandFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* @author Dave Syer
*
*/
public class RibbonCommandFactoryConfiguration {
@Configuration
@ConditionalOnRibbonRestClient
protected static class RestClientRibbonConfiguration {
@Autowired(required = false)
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
@Bean
@ConditionalOnMissingBean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
return new RestClientRibbonCommandFactory(clientFactory, zuulProperties,
zuulFallbackProviders);
}
}
@Configuration
@ConditionalOnRibbonOkHttpClient
@ConditionalOnClass(name = "okhttp3.OkHttpClient")
protected static class OkHttpRibbonConfiguration {
@Autowired(required = false)
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
@Bean
@ConditionalOnMissingBean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
return new OkHttpRibbonCommandFactory(clientFactory, zuulProperties,
zuulFallbackProviders);
}
}
@Configuration
@ConditionalOnRibbonHttpClient
protected static class HttpClientRibbonConfiguration {
@Autowired(required = false)
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
@Bean
@ConditionalOnMissingBean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
return new HttpClientRibbonCommandFactory(clientFactory, zuulProperties, zuulFallbackProviders);
}
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnRibbonHttpClientCondition.class)
@interface ConditionalOnRibbonHttpClient { }
private static class OnRibbonHttpClientCondition extends AnyNestedCondition {
public OnRibbonHttpClientCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Deprecated //remove in Edgware"
@ConditionalOnProperty(name = "zuul.ribbon.httpclient.enabled", matchIfMissing = true)
static class ZuulProperty {}
@ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
static class RibbonProperty {}
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnRibbonOkHttpClientCondition.class)
@interface ConditionalOnRibbonOkHttpClient { }
private static class OnRibbonOkHttpClientCondition extends AnyNestedCondition {
public OnRibbonOkHttpClientCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Deprecated //remove in Edgware"
@ConditionalOnProperty("zuul.ribbon.okhttp.enabled")
static class ZuulProperty {}
@ConditionalOnProperty("ribbon.okhttp.enabled")
static class RibbonProperty {}
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnRibbonRestClientCondition.class)
@interface ConditionalOnRibbonRestClient { }
private static class OnRibbonRestClientCondition extends AnyNestedCondition {
public OnRibbonRestClientCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Deprecated //remove in Edgware"
@ConditionalOnProperty("zuul.ribbon.restclient.enabled")
static class ZuulProperty {}
@ConditionalOnProperty("ribbon.restclient.enabled")
static class RibbonProperty {}
}
}

View File

@@ -16,29 +16,21 @@
package org.springframework.cloud.netflix.zuul;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
@@ -48,26 +40,27 @@ import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientR
import org.springframework.cloud.netflix.zuul.filters.discovery.ServiceRouteMapper;
import org.springframework.cloud.netflix.zuul.filters.discovery.SimpleServiceRouteMapper;
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter;
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter;
import org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter;
import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @author Spencer Gibb
* @author Dave Syer
*/
@Configuration
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class })
public class ZuulProxyConfiguration extends ZuulConfiguration {
@SuppressWarnings("rawtypes")
@Autowired(required = false)
private List<RibbonRequestCustomizer> requestCustomizers = Collections.emptyList();
@@ -86,124 +79,27 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
@Override
@ConditionalOnMissingBean(RouteLocator.class)
public DiscoveryClientRouteLocator routeLocator() {
return new DiscoveryClientRouteLocator(this.server.getServletPrefix(),
this.discovery, this.zuulProperties, this.serviceRouteMapper);
}
@Configuration
@ConditionalOnRibbonHttpClient
protected static class HttpClientRibbonConfiguration {
@Bean
@ConditionalOnMissingBean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
return new HttpClientRibbonCommandFactory(clientFactory, zuulProperties);
}
}
@Configuration
@ConditionalOnRibbonRestClient
protected static class RestClientRibbonConfiguration {
@Bean
@ConditionalOnMissingBean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
return new RestClientRibbonCommandFactory(clientFactory, zuulProperties);
}
}
@Configuration
@ConditionalOnRibbonOkHttpClient
@ConditionalOnClass(name = "okhttp3.OkHttpClient")
protected static class OkHttpRibbonConfiguration {
@Bean
@ConditionalOnMissingBean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
return new OkHttpRibbonCommandFactory(clientFactory, zuulProperties);
}
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnRibbonHttpClientCondition.class)
@interface ConditionalOnRibbonHttpClient { }
private static class OnRibbonHttpClientCondition extends AnyNestedCondition {
public OnRibbonHttpClientCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@Deprecated //remove in Edgware"
@ConditionalOnProperty(name = "zuul.ribbon.httpclient.enabled", matchIfMissing = true)
static class ZuulProperty {}
@ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
static class RibbonProperty {}
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnRibbonOkHttpClientCondition.class)
@interface ConditionalOnRibbonOkHttpClient { }
private static class OnRibbonOkHttpClientCondition extends AnyNestedCondition {
public OnRibbonOkHttpClientCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@Deprecated //remove in Edgware"
@ConditionalOnProperty("zuul.ribbon.okhttp.enabled")
static class ZuulProperty {}
@ConditionalOnProperty("ribbon.okhttp.enabled")
static class RibbonProperty {}
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnRibbonRestClientCondition.class)
@interface ConditionalOnRibbonRestClient { }
private static class OnRibbonRestClientCondition extends AnyNestedCondition {
public OnRibbonRestClientCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@Deprecated //remove in Edgware"
@ConditionalOnProperty("zuul.ribbon.restclient.enabled")
static class ZuulProperty {}
@ConditionalOnProperty("ribbon.restclient.enabled")
static class RibbonProperty {}
return new DiscoveryClientRouteLocator(this.server.getServletPrefix(), this.discovery, this.zuulProperties,
this.serviceRouteMapper);
}
// pre filters
@Bean
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
ProxyRequestHelper proxyRequestHelper) {
return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(),
this.zuulProperties, proxyRequestHelper);
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) {
return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(), this.zuulProperties,
proxyRequestHelper);
}
// route filters
@Bean
public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
RibbonCommandFactory<?> ribbonCommandFactory) {
RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory,
this.requestCustomizers);
RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers);
return filter;
}
@Bean
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper,
ZuulProperties zuulProperties) {
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties) {
return new SimpleHostRoutingFilter(helper, zuulProperties);
}
@@ -256,8 +152,7 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
}
}
private static class ZuulDiscoveryRefreshListener
implements ApplicationListener<ApplicationEvent> {
private static class ZuulDiscoveryRefreshListener implements ApplicationListener<ApplicationEvent> {
private HeartbeatMonitor monitor = new HeartbeatMonitor();

View File

@@ -50,12 +50,11 @@ public class PreDecorationFilter extends ZuulFilter {
private ProxyRequestHelper proxyRequestHelper;
public PreDecorationFilter(RouteLocator routeLocator, String dispatcherServletPath,
ZuulProperties properties, ProxyRequestHelper proxyRequestHelper) {
public PreDecorationFilter(RouteLocator routeLocator, String dispatcherServletPath, ZuulProperties properties,
ProxyRequestHelper proxyRequestHelper) {
this.routeLocator = routeLocator;
this.properties = properties;
this.urlPathHelper
.setRemoveSemicolonContent(properties.isRemoveSemicolonContent());
this.urlPathHelper.setRemoveSemicolonContent(properties.isRemoveSemicolonContent());
this.dispatcherServletPath = dispatcherServletPath;
this.proxyRequestHelper = proxyRequestHelper;
}
@@ -81,8 +80,7 @@ public class PreDecorationFilter extends ZuulFilter {
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
final String requestURI = this.urlPathHelper
.getPathWithinApplication(ctx.getRequest());
final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());
Route route = this.routeLocator.getMatchingRoute(requestURI);
if (route != null) {
String location = route.getLocation();
@@ -90,12 +88,11 @@ public class PreDecorationFilter extends ZuulFilter {
ctx.put("requestURI", route.getPath());
ctx.put("proxy", route.getId());
if (!route.isCustomSensitiveHeaders()) {
this.proxyRequestHelper.addIgnoredHeaders(
this.properties.getSensitiveHeaders().toArray(new String[0]));
this.proxyRequestHelper
.addIgnoredHeaders(this.properties.getSensitiveHeaders().toArray(new String[0]));
}
else {
this.proxyRequestHelper.addIgnoredHeaders(
route.getSensitiveHeaders().toArray(new String[0]));
this.proxyRequestHelper.addIgnoredHeaders(route.getSensitiveHeaders().toArray(new String[0]));
}
if (route.getRetryable() != null) {
@@ -107,8 +104,8 @@ public class PreDecorationFilter extends ZuulFilter {
ctx.addOriginResponseHeader("X-Zuul-Service", location);
}
else if (location.startsWith("forward:")) {
ctx.set("forward.to", StringUtils.cleanPath(
location.substring("forward:".length()) + route.getPath()));
ctx.set("forward.to",
StringUtils.cleanPath(location.substring("forward:".length()) + route.getPath()));
ctx.setRouteHost(null);
return null;
}
@@ -119,35 +116,7 @@ public class PreDecorationFilter extends ZuulFilter {
ctx.addOriginResponseHeader("X-Zuul-ServiceId", location);
}
if (this.properties.isAddProxyHeaders()) {
ctx.addZuulRequestHeader("X-Forwarded-Host", toHostHeader(ctx.getRequest()));
ctx.addZuulRequestHeader("X-Forwarded-Port",
String.valueOf(ctx.getRequest().getServerPort()));
ctx.addZuulRequestHeader(ZuulHeaders.X_FORWARDED_PROTO,
ctx.getRequest().getScheme());
String forwardedPrefix =
ctx.getRequest().getHeader("X-Forwarded-Prefix");
String contextPath = ctx.getRequest().getContextPath();
String prefix = StringUtils.hasLength(forwardedPrefix)
? forwardedPrefix
: (StringUtils.hasLength(contextPath) ? contextPath : null);
if (StringUtils.hasText(route.getPrefix())) {
StringBuilder newPrefixBuilder = new StringBuilder();
if (prefix != null) {
if (prefix.endsWith("/")
&& route.getPrefix().startsWith("/")) {
newPrefixBuilder.append(prefix, 0,
prefix.length() - 1);
}
else {
newPrefixBuilder.append(prefix);
}
}
newPrefixBuilder.append(route.getPrefix());
prefix = newPrefixBuilder.toString();
}
if (prefix != null) {
ctx.addZuulRequestHeader("X-Forwarded-Prefix", prefix);
}
addProxyHeaders(ctx, route);
String xforwardedfor = ctx.getRequest().getHeader("X-Forwarded-For");
String remoteAddr = ctx.getRequest().getRemoteAddr();
if (xforwardedfor == null) {
@@ -174,8 +143,7 @@ public class PreDecorationFilter extends ZuulFilter {
if (RequestUtils.isZuulServletRequest()) {
// remove the Zuul servletPath from the requestUri
log.debug("zuulServletPath=" + this.properties.getServletPath());
fallBackUri = fallBackUri.replaceFirst(this.properties.getServletPath(),
"");
fallBackUri = fallBackUri.replaceFirst(this.properties.getServletPath(), "");
log.debug("Replaced Zuul servlet path:" + fallBackUri);
}
else {
@@ -194,11 +162,70 @@ public class PreDecorationFilter extends ZuulFilter {
return null;
}
private void addProxyHeaders(RequestContext ctx, Route route) {
HttpServletRequest request = ctx.getRequest();
String host = toHostHeader(request);
String port = String.valueOf(request.getServerPort());
String proto = request.getScheme();
if (hasHeader(request, "X-Forwarded-Host")) {
host = request.getHeader("X-Forwarded-Host") + "," + host;
if (!hasHeader(request, "X-Forwarded-Port")) {
if (hasHeader(request, "X-Forwarded-Proto")) {
StringBuilder builder = new StringBuilder();
for (String previous : StringUtils.commaDelimitedListToStringArray(request.getHeader("X-Forwarded-Proto"))) {
if (builder.length()>0) {
builder.append(",");
}
builder.append("https".equals(previous) ? "443" : "80");
}
builder.append(",").append(port);
port = builder.toString();
}
} else {
port = request.getHeader("X-Forwarded-Port") + "," + port;
}
proto = request.getHeader("X-Forwarded-Proto") + "," + proto;
}
ctx.addZuulRequestHeader("X-Forwarded-Host", host);
ctx.addZuulRequestHeader("X-Forwarded-Port", port);
ctx.addZuulRequestHeader(ZuulHeaders.X_FORWARDED_PROTO, proto);
addProxyPrefix(ctx, route);
}
private boolean hasHeader(HttpServletRequest request, String name) {
return StringUtils.hasLength(request.getHeader(name));
}
private void addProxyPrefix(RequestContext ctx, Route route) {
String forwardedPrefix = ctx.getRequest().getHeader("X-Forwarded-Prefix");
String contextPath = ctx.getRequest().getContextPath();
String prefix = StringUtils.hasLength(forwardedPrefix) ? forwardedPrefix
: (StringUtils.hasLength(contextPath) ? contextPath : null);
if (StringUtils.hasText(route.getPrefix())) {
StringBuilder newPrefixBuilder = new StringBuilder();
if (prefix != null) {
if (prefix.endsWith("/") && route.getPrefix().startsWith("/")) {
newPrefixBuilder.append(prefix, 0, prefix.length() - 1);
}
else {
newPrefixBuilder.append(prefix);
}
}
newPrefixBuilder.append(route.getPrefix());
prefix = newPrefixBuilder.toString();
}
if (prefix != null) {
ctx.addZuulRequestHeader("X-Forwarded-Prefix", prefix);
}
}
private String toHostHeader(HttpServletRequest request) {
int port = request.getServerPort();
if ((port == 80 && "http".equals(request.getScheme())) || (port == 443 && "https".equals(request.getScheme()))) {
if ((port == 80 && "http".equals(request.getScheme()))
|| (port == 443 && "https".equals(request.getScheme()))) {
return request.getServerName();
} else {
}
else {
return request.getServerName() + ":" + port;
}
}

View File

@@ -17,20 +17,18 @@
package org.springframework.cloud.netflix.zuul.filters.route;
import static org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer.Runner.customize;
import java.io.InputStream;
import java.net.URI;
import java.util.List;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommand;
import org.springframework.util.MultiValueMap;
import com.netflix.client.http.HttpRequest;
import com.netflix.client.http.HttpResponse;
import com.netflix.niws.client.http.RestClient;
import static org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer.Runner.customize;
/**
* Hystrix wrapper around Eureka Ribbon command
*
@@ -44,6 +42,12 @@ public class RestClientRibbonCommand extends AbstractRibbonCommand<RestClient, H
super(commandKey, client, context, zuulProperties);
}
public RestClientRibbonCommand(String commandKey, RestClient client,
RibbonCommandContext context, ZuulProperties zuulProperties,
ZuulFallbackProvider zuulFallbackProvider) {
super(commandKey, client, context, zuulProperties, zuulFallbackProvider);
}
@Deprecated
public RestClientRibbonCommand(String commandKey, RestClient restClient,
HttpRequest.Verb verb, String uri, Boolean retryable,

View File

@@ -17,27 +17,34 @@
package org.springframework.cloud.netflix.zuul.filters.route;
import java.util.Collections;
import java.util.Set;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommandFactory;
import com.netflix.client.http.HttpRequest;
import com.netflix.niws.client.http.RestClient;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
public class RestClientRibbonCommandFactory implements RibbonCommandFactory<RestClientRibbonCommand> {
public class RestClientRibbonCommandFactory extends AbstractRibbonCommandFactory {
private final SpringClientFactory clientFactory;
private SpringClientFactory clientFactory;
private ZuulProperties zuulProperties;
public RestClientRibbonCommandFactory(SpringClientFactory clientFactory) {
this(clientFactory, new ZuulProperties());
this(clientFactory, new ZuulProperties(), Collections.<ZuulFallbackProvider>emptySet());
}
public RestClientRibbonCommandFactory(SpringClientFactory clientFactory,
ZuulProperties zuulProperties) {
ZuulProperties zuulProperties,
Set<ZuulFallbackProvider> zuulFallbackProviders) {
super(zuulFallbackProviders);
this.clientFactory = clientFactory;
this.zuulProperties = zuulProperties;
}
@@ -45,10 +52,12 @@ public class RestClientRibbonCommandFactory implements RibbonCommandFactory<Rest
@Override
@SuppressWarnings("deprecation")
public RestClientRibbonCommand create(RibbonCommandContext context) {
RestClient restClient = this.clientFactory.getClient(context.getServiceId(),
String serviceId = context.getServiceId();
ZuulFallbackProvider fallbackProvider = getFallbackProvider(serviceId);
RestClient restClient = this.clientFactory.getClient(serviceId,
RestClient.class);
return new RestClientRibbonCommand(context.getServiceId(), restClient, context,
this.zuulProperties);
this.zuulProperties, fallbackProvider);
}
public SpringClientFactory getClientFactory() {

View File

@@ -0,0 +1,40 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.zuul.filters.route;
import org.springframework.http.client.ClientHttpResponse;
/**
* Provides fallback when a failure occurs on a route.
* @author Ryan Baxter
*/
public interface ZuulFallbackProvider {
/**
* The route this fallback will be used for.
* @return The route the fallback will be used for.
*/
public String getRoute();
/**
* Provides a fallback response.
* @return The fallback response.
*/
public ClientHttpResponse fallbackResponse();
}

View File

@@ -22,10 +22,12 @@ import org.springframework.cloud.netflix.ribbon.apache.RibbonApacheHttpResponse;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommand;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
public class HttpClientRibbonCommand extends AbstractRibbonCommand<RibbonLoadBalancingHttpClient, RibbonApacheHttpRequest, RibbonApacheHttpResponse> {
@@ -36,6 +38,14 @@ public class HttpClientRibbonCommand extends AbstractRibbonCommand<RibbonLoadBal
super(commandKey, client, context, zuulProperties);
}
public HttpClientRibbonCommand(final String commandKey,
final RibbonLoadBalancingHttpClient client,
final RibbonCommandContext context,
final ZuulProperties zuulProperties,
final ZuulFallbackProvider zuulFallbackProvider) {
super(commandKey, client, context, zuulProperties, zuulFallbackProvider);
}
@Override
protected RibbonApacheHttpRequest createRequest() throws Exception {
return new RibbonApacheHttpRequest(this.context);

View File

@@ -16,33 +16,46 @@
package org.springframework.cloud.netflix.zuul.filters.route.apache;
import java.util.Collections;
import java.util.Set;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
/**
* @author Christian Lohmann
* @author Ryan Baxter
*/
@RequiredArgsConstructor
public class HttpClientRibbonCommandFactory implements
RibbonCommandFactory<HttpClientRibbonCommand> {
public class HttpClientRibbonCommandFactory extends AbstractRibbonCommandFactory {
private final SpringClientFactory clientFactory;
private final ZuulProperties zuulProperties;
public HttpClientRibbonCommandFactory(SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
this(clientFactory, zuulProperties, Collections.<ZuulFallbackProvider>emptySet());
}
public HttpClientRibbonCommandFactory(SpringClientFactory clientFactory, ZuulProperties zuulProperties,
Set<ZuulFallbackProvider> fallbackProviders) {
super(fallbackProviders);
this.clientFactory = clientFactory;
this.zuulProperties = zuulProperties;
}
@Override
public HttpClientRibbonCommand create(final RibbonCommandContext context) {
ZuulFallbackProvider zuulFallbackProvider = getFallbackProvider(context.getServiceId());
final String serviceId = context.getServiceId();
final RibbonLoadBalancingHttpClient client = this.clientFactory.getClient(
serviceId, RibbonLoadBalancingHttpClient.class);
client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties);
return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties, zuulFallbackProvider);
}
}

View File

@@ -22,20 +22,30 @@ import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpRibbonRequest;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpRibbonResponse;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommand;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
public class OkHttpRibbonCommand extends AbstractRibbonCommand<OkHttpLoadBalancingClient, OkHttpRibbonRequest, OkHttpRibbonResponse> {
public OkHttpRibbonCommand(final String commandKey,
final OkHttpLoadBalancingClient client,
final RibbonCommandContext context,
final ZuulProperties zuulProperties) {
final OkHttpLoadBalancingClient client,
final RibbonCommandContext context,
final ZuulProperties zuulProperties) {
super(commandKey, client, context, zuulProperties);
}
public OkHttpRibbonCommand(final String commandKey,
final OkHttpLoadBalancingClient client,
final RibbonCommandContext context,
final ZuulProperties zuulProperties,
final ZuulFallbackProvider zuulFallbackProvider) {
super(commandKey, client, context, zuulProperties, zuulFallbackProvider);
}
@Override
protected OkHttpRibbonRequest createRequest() throws Exception {
return new OkHttpRibbonRequest(this.context);

View File

@@ -16,33 +16,46 @@
package org.springframework.cloud.netflix.zuul.filters.route.okhttp;
import java.util.Collections;
import java.util.Set;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommandFactory;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
@RequiredArgsConstructor
public class OkHttpRibbonCommandFactory implements
RibbonCommandFactory<OkHttpRibbonCommand> {
public class OkHttpRibbonCommandFactory extends AbstractRibbonCommandFactory {
private final SpringClientFactory clientFactory;
private SpringClientFactory clientFactory;
private final ZuulProperties zuulProperties;
private ZuulProperties zuulProperties;
public OkHttpRibbonCommandFactory(SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
this(clientFactory, zuulProperties, Collections.<ZuulFallbackProvider>emptySet());
}
public OkHttpRibbonCommandFactory(SpringClientFactory clientFactory, ZuulProperties zuulProperties,
Set<ZuulFallbackProvider> zuulFallbackProviders) {
super(zuulFallbackProviders);
this.clientFactory = clientFactory;
this.zuulProperties = zuulProperties;
}
@Override
public OkHttpRibbonCommand create(final RibbonCommandContext context) {
final String serviceId = context.getServiceId();
ZuulFallbackProvider fallbackProvider = getFallbackProvider(serviceId);
final OkHttpLoadBalancingClient client = this.clientFactory.getClient(
serviceId, OkHttpLoadBalancingClient.class);
client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
return new OkHttpRibbonCommand(serviceId, client, context, zuulProperties);
return new OkHttpRibbonCommand(serviceId, client, context, zuulProperties, fallbackProvider);
}
}

View File

@@ -21,8 +21,8 @@ import org.springframework.cloud.netflix.ribbon.RibbonHttpResponse;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.http.client.ClientHttpResponse;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.ClientRequest;
import com.netflix.client.http.HttpResponse;
@@ -44,6 +44,7 @@ public abstract class AbstractRibbonCommand<LBC extends AbstractLoadBalancerAwar
protected final LBC client;
protected RibbonCommandContext context;
protected ZuulFallbackProvider zuulFallbackProvider;
public AbstractRibbonCommand(LBC client, RibbonCommandContext context,
ZuulProperties zuulProperties) {
@@ -52,9 +53,16 @@ public abstract class AbstractRibbonCommand<LBC extends AbstractLoadBalancerAwar
public AbstractRibbonCommand(String commandKey, LBC client,
RibbonCommandContext context, ZuulProperties zuulProperties) {
this(commandKey, client, context, zuulProperties, null);
}
public AbstractRibbonCommand(String commandKey, LBC client,
RibbonCommandContext context, ZuulProperties zuulProperties,
ZuulFallbackProvider fallbackProvider) {
super(getSetter(commandKey, zuulProperties));
this.client = client;
this.context = context;
this.zuulFallbackProvider = fallbackProvider;
}
protected static Setter getSetter(final String commandKey,
@@ -101,6 +109,14 @@ public abstract class AbstractRibbonCommand<LBC extends AbstractLoadBalancerAwar
return new RibbonHttpResponse(response);
}
@Override
protected ClientHttpResponse getFallback() {
if(zuulFallbackProvider != null) {
return zuulFallbackProvider.fallbackResponse();
}
return super.getFallback();
}
public LBC getClient() {
return client;
}

View File

@@ -0,0 +1,44 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.zuul.filters.route.support;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
/**
* @author Ryan Baxter
*/
public abstract class AbstractRibbonCommandFactory implements RibbonCommandFactory {
private Map<String, ZuulFallbackProvider> fallbackProviderCache;
public AbstractRibbonCommandFactory(Set<ZuulFallbackProvider> fallbackProviders){
this.fallbackProviderCache = new HashMap<String, ZuulFallbackProvider>();
for(ZuulFallbackProvider provider : fallbackProviders) {
fallbackProviderCache.put(provider.getRoute(), provider);
}
}
protected ZuulFallbackProvider getFallbackProvider(String route) {
return fallbackProviderCache.get(route);
}
}

View File

@@ -37,9 +37,10 @@ public class ZuulController extends ServletWrappingController {
}
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
// We don't care about the other features of the base class, just want to
// handle the request
return super.handleRequestInternal(request, response);
}
finally {

View File

@@ -25,6 +25,8 @@ import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
import com.netflix.zuul.context.RequestContext;
@@ -51,6 +53,16 @@ public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {
setOrder(-200);
}
@Override
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, CorsConfiguration config) {
if (config == null) {
// Allow CORS requests to go to the backend
return chain;
}
return super.getCorsHandlerExecutionChain(request, chain, config);
}
public void setErrorController(ErrorController errorController) {
this.errorController = errorController;
}
@@ -63,10 +75,8 @@ public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {
}
@Override
protected Object lookupHandler(String urlPath, HttpServletRequest request)
throws Exception {
if (this.errorController != null
&& urlPath.equals(this.errorController.getErrorPath())) {
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) {
return null;
}
String[] ignored = this.routeLocator.getIgnoredPaths().toArray(new String[0]);

View File

@@ -20,7 +20,11 @@ import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.springframework.cloud.netflix.zuul.filters.route.restclient.RestClientRibbonCommandIntegrationTests;
import org.springframework.cloud.netflix.feign.encoding.FeignAcceptEncodingTests;
import org.springframework.cloud.netflix.metrics.servo.ServoMetricReaderTests;
import org.springframework.cloud.netflix.ribbon.RibbonInterceptorTests;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClientTests;
import org.springframework.cloud.netflix.zuul.ZuulProxyConfigurationTests;
/**
* A test suite for probing weird ordering problems in the tests.
@@ -28,10 +32,8 @@ import org.springframework.cloud.netflix.zuul.filters.route.restclient.RestClien
* @author Dave Syer
*/
@RunWith(Suite.class)
@SuiteClasses({
org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelperTests.class,
RestClientRibbonCommandIntegrationTests.class,
org.springframework.cloud.netflix.zuul.FormZuulProxyApplicationTests.class })
@SuiteClasses({ RibbonLoadBalancerClientTests.class, RibbonInterceptorTests.class, FeignAcceptEncodingTests.class,
ServoMetricReaderTests.class, ZuulProxyConfigurationTests.class })
@Ignore
public class AdhocTestSuite {

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.feign;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
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 feign.Logger;
import feign.slf4j.Slf4jLogger;
/**
* @author Venil Noronha
*/
public class FeignLoggerFactoryTests {
@Test
public void testDefaultLogger() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration1.class);
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
assertNotNull(loggerFactory);
Logger logger = loggerFactory.create(Object.class);
assertNotNull(logger);
assertTrue(logger instanceof Slf4jLogger);
context.close();
}
@Configuration
@Import(FeignClientsConfiguration.class)
protected static class SampleConfiguration1 {
}
@Test
public void testCustomLogger() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration2.class);
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
assertNotNull(loggerFactory);
Logger logger = loggerFactory.create(Object.class);
assertNotNull(logger);
assertTrue(logger instanceof LoggerImpl1);
context.close();
}
@Configuration
@Import(FeignClientsConfiguration.class)
protected static class SampleConfiguration2 {
@Bean
public Logger logger() {
return new LoggerImpl1();
}
}
static class LoggerImpl1 extends Logger {
@Override
protected void log(String arg0, String arg1, Object... arg2) {
// noop
}
}
@Test
public void testCustomLoggerFactory() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration3.class);
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
assertNotNull(loggerFactory);
assertTrue(loggerFactory instanceof LoggerFactoryImpl);
Logger logger = loggerFactory.create(Object.class);
assertNotNull(logger);
assertTrue(logger instanceof LoggerImpl2);
context.close();
}
@Configuration
@Import(FeignClientsConfiguration.class)
protected static class SampleConfiguration3 {
@Bean
public FeignLoggerFactory feignLoggerFactory() {
return new LoggerFactoryImpl();
}
}
static class LoggerFactoryImpl implements FeignLoggerFactory {
@Override
public Logger create(Class<?> type) {
return new LoggerImpl2();
}
}
static class LoggerImpl2 extends Logger {
@Override
protected void log(String arg0, String arg1, Object... arg2) {
// noop
}
}
}

View File

@@ -47,7 +47,7 @@ import static org.junit.Assert.assertTrue;
@SpringBootTest(classes = RestTemplateRetryTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
"spring.application.name=resttemplatetest", "logging.level.com.netflix=DEBUG",
"logging.level.org.springframework.cloud.netflix.resttemplate=DEBUG",
"logging.level.com.netflix=DEBUG", "badClients.ribbon.MaxAutoRetries=0",
"logging.level.com.netflix=DEBUG", "badClients.ribbon.MaxAutoRetries=25",
"badClients.ribbon.OkToRetryOnAllOperations=true", "ribbon.http.client.enabled" })
@DirtiesContext
public class RestTemplateRetryTests {

View File

@@ -18,6 +18,9 @@ package org.springframework.cloud.netflix.zuul;
import static org.junit.Assert.assertEquals;
import java.net.URI;
import java.net.URISyntaxException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -33,13 +36,16 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClientException;
import com.netflix.zuul.context.RequestContext;
@@ -68,9 +74,38 @@ public class ServletPathZuulProxyApplicationTests {
public void getOnSelfViaSimpleHostRoutingFilter() {
this.routes.addRoute("/self/**", "http://localhost:" + this.port + "/app/local");
this.endpoint.reset();
ResponseEntity<String> result = new TestRestTemplate().exchange("http://localhost:" + this.port + "/app/self/1",
HttpMethod.GET, new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("Gotten 1!", result.getBody());
}
@Test
public void optionsOnRawEndpoint() throws Exception {
ResponseEntity<String> result = new TestRestTemplate().exchange(RequestEntity
.options(new URI("http://localhost:" + this.port + "/app/local/1"))
.header("Origin", "http://localhost:9000").header("Access-Control-Request-Method", "GET").build(),
String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("http://localhost:9000", result.getHeaders().getFirst("Access-Control-Allow-Origin"));
}
@Test
public void optionsOnSelf() throws Exception {
this.routes.addRoute("/self/**", "http://localhost:" + this.port + "/app/local");
this.endpoint.reset();
ResponseEntity<String> result = new TestRestTemplate().exchange(RequestEntity
.options(new URI("http://localhost:" + this.port + "/app/self/1"))
.header("Origin", "http://localhost:9000").header("Access-Control-Request-Method", "GET").build(),
String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("http://localhost:9000", result.getHeaders().getFirst("Access-Control-Allow-Origin"));
}
@Test
public void contentOnRawEndpoint() throws Exception {
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/app/self/1", HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
RequestEntity.get(new URI("http://localhost:" + this.port + "/app/local/1")).build(), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("Gotten 1!", result.getBody());
}
@@ -80,9 +115,8 @@ public class ServletPathZuulProxyApplicationTests {
this.routes.addRoute(new ZuulRoute("strip", "/strip/**", "strip",
"http://localhost:" + this.port + "/app/local", false, false, null));
this.endpoint.reset();
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/app/strip", HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
ResponseEntity<String> result = new TestRestTemplate().exchange("http://localhost:" + this.port + "/app/strip",
HttpMethod.GET, new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
// Prefix not stripped to it goes to /local/strip
assertEquals("Gotten strip!", result.getBody());
@@ -96,6 +130,7 @@ public class ServletPathZuulProxyApplicationTests {
static class ServletPathZuulProxyApplication {
@RequestMapping(value = "/local/{id}", method = RequestMethod.GET)
@CrossOrigin(origins = "*")
public String get(@PathVariable String id) {
return "Gotten " + id + "!";
}

View File

@@ -103,6 +103,23 @@ public class PreDecorationFilterTests {
assertEquals("localhost:8080", ctx.getZuulRequestHeaders().get("x-forwarded-host"));
}
@Test
public void xForwardedHostAppends() throws Exception {
this.properties.setPrefix("/api");
this.request.setRequestURI("/api/foo/1");
this.request.setRemoteAddr("5.6.7.8");
this.request.setServerPort(8080);
this.request.addHeader("X-Forwarded-Host", "example.com");
this.request.addHeader("X-Forwarded-Proto", "https");
this.routeLocator.addRoute(
new ZuulRoute("foo", "/foo/**", "foo", null, false, null, null));
this.filter.run();
RequestContext ctx = RequestContext.getCurrentContext();
assertEquals("example.com,localhost:8080", ctx.getZuulRequestHeaders().get("x-forwarded-host"));
assertEquals("443,8080", ctx.getZuulRequestHeaders().get("x-forwarded-port"));
assertEquals("https,http", ctx.getZuulRequestHeaders().get("x-forwarded-proto"));
}
@Test
public void hostHeaderSet() throws Exception {
this.properties.setPrefix("/api");

View File

@@ -0,0 +1,46 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.zuul.filters.route.apache;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonCommandFallbackTests;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.netflix.zuul.context.RequestContext;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = HttpClientRibbonCommandIntegrationTests.TestConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = {
"zuul.routes.simple: /simple/**", "zuul.routes.another: /another/twolevel/**",
"ribbon.ReadTimeout: 1"})
@DirtiesContext
public class HttpClientRibbonCommandFallbackTests extends RibbonCommandFallbackTests {
@Before
public void init() {
RequestContext.testSetCurrentContext(null);
RequestContext.getCurrentContext().unset();
}
}

View File

@@ -22,6 +22,9 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.http.HttpHeaders.SET_COOKIE;
import java.util.Collections;
import java.util.Set;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -29,6 +32,7 @@ import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
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.autoconfigure.web.ErrorAttributes;
@@ -44,6 +48,7 @@ import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpCl
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.ZuulProxyTestBase;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -155,6 +160,9 @@ public class HttpClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
@RibbonClient(name = "singleton", configuration = SingletonRibbonClientConfiguration.class) })
static class TestConfig extends ZuulProxyTestBase.AbstractZuulProxyApplication {
@Autowired(required = false)
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
@RequestMapping(value = "/local/{id}", method = RequestMethod.PATCH)
public String patch(@PathVariable final String id,
@RequestBody final String body) {
@@ -176,7 +184,8 @@ public class HttpClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
@Bean
public RibbonCommandFactory<?> ribbonCommandFactory(
final SpringClientFactory clientFactory) {
return new HttpClientRibbonCommandFactory(clientFactory, new ZuulProperties());
return new HttpClientRibbonCommandFactory(clientFactory, new ZuulProperties(),
zuulFallbackProviders);
}
@Bean

View File

@@ -0,0 +1,44 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.zuul.filters.route.okhttp;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonCommandFallbackTests;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.netflix.zuul.context.RequestContext;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = OkHttpRibbonCommandIntegrationTests.TestConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = {
"zuul.routes.simple: /simple/**", "zuul.routes.another: /another/twolevel/**",
"ribbon.ReadTimeout: 1"})
@DirtiesContext
public class OkHttpRibbonCommandFallbackTests extends RibbonCommandFallbackTests {
@Before
public void init() {
RequestContext.testSetCurrentContext(null);
RequestContext.getCurrentContext().unset();
}
}

View File

@@ -20,9 +20,13 @@ package org.springframework.cloud.netflix.zuul.filters.route.okhttp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import java.util.Set;
import org.junit.Before;
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.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.test.context.SpringBootTest;
@@ -34,6 +38,7 @@ import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.ZuulProxyTestBase;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -100,10 +105,14 @@ public class OkHttpRibbonCommandIntegrationTests extends ZuulProxyTestBase {
@RibbonClient(name = "another", configuration = AnotherRibbonClientConfiguration.class) })
static class TestConfig extends ZuulProxyTestBase.AbstractZuulProxyApplication {
@Autowired(required = false)
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
@Bean
public RibbonCommandFactory<?> ribbonCommandFactory(
final SpringClientFactory clientFactory) {
return new OkHttpRibbonCommandFactory(clientFactory, new ZuulProperties());
return new OkHttpRibbonCommandFactory(clientFactory, new ZuulProperties(),
zuulFallbackProviders);
}
@Bean

View File

@@ -0,0 +1,44 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.zuul.filters.route.restclient;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonCommandFallbackTests;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.netflix.zuul.context.RequestContext;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RestClientRibbonCommandIntegrationTests.TestConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = {
"zuul.routes.simple: /simple/**", "zuul.routes.another: /another/twolevel/**",
"ribbon.ReadTimeout: 1"})
@DirtiesContext
public class RestClientRibbonCommandFallbackTests extends RibbonCommandFallbackTests {
@Before
public void init() {
RequestContext.testSetCurrentContext(null);
RequestContext.getCurrentContext().unset();
}
}

View File

@@ -26,10 +26,14 @@ import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -52,6 +56,7 @@ import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonComm
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.NoEncodingFormHttpMessageConverter;
import org.springframework.cloud.netflix.zuul.filters.route.support.ZuulProxyTestBase;
import org.springframework.context.annotation.Bean;
@@ -80,8 +85,6 @@ import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import com.netflix.niws.client.http.RestClient;
import lombok.SneakyThrows;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RestClientRibbonCommandIntegrationTests.TestConfig.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
"zuul.routes.other: /test/**=http://localhost:7777/local",
@@ -285,6 +288,9 @@ public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
@RibbonClient(name = "another", configuration = ZuulProxyTestBase.AnotherRibbonClientConfiguration.class) })
static class TestConfig extends ZuulProxyTestBase.AbstractZuulProxyApplication {
@Autowired(required = false)
private Set<ZuulFallbackProvider> fallbackProviders = Collections.emptySet();
@RequestMapping("/trailing-slash")
public String trailingSlash(HttpServletRequest request) {
return request.getRequestURI();
@@ -327,7 +333,7 @@ public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
@Bean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory) {
return new MyRibbonCommandFactory(clientFactory);
return new MyRibbonCommandFactory(clientFactory, fallbackProviders);
}
@Bean
@@ -350,8 +356,9 @@ public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
private SpringClientFactory clientFactory;
public MyRibbonCommandFactory(SpringClientFactory clientFactory) {
super(clientFactory, new ZuulProperties());
public MyRibbonCommandFactory(SpringClientFactory clientFactory,
Set<ZuulFallbackProvider> fallbackProviders) {
super(clientFactory, new ZuulProperties(), fallbackProviders);
this.clientFactory = clientFactory;
}

View File

@@ -0,0 +1,58 @@
/*
*
* * Copyright 2013-2016 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.cloud.netflix.zuul.filters.route.support;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.junit.Assert.assertEquals;
/**
* @author Ryan Baxter
*/
public abstract class RibbonCommandFallbackTests {
@Value("${local.server.port}")
protected int port;
@Test
public void fallback() {
String uri = "/simple/slow";
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + uri, HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("fallback", result.getBody());
}
@Test
public void noFallback() {
String uri = "/another/twolevel/slow";
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + uri, HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
System.out.println("no fallback body: " + result.getBody());
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, result.getStatusCode());
}
}

View File

@@ -17,11 +17,9 @@
package org.springframework.cloud.netflix.zuul.filters.route.support;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assume.assumeThat;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
@@ -29,9 +27,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.http.HttpServletRequest;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -46,13 +42,16 @@ import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
@@ -66,14 +65,19 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assume.assumeThat;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
public abstract class ZuulProxyTestBase {
@@ -363,6 +367,22 @@ public abstract class ZuulProxyTestBase {
return "Hello space";
}
@RequestMapping("/slow")
public String slow() {
try {
Thread.sleep(80000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "slow";
}
@Bean
public ZuulFallbackProvider fallbackProvider() {
return new FallbackProvider();
}
@Bean
public ZuulFilter sampleFilter() {
return new ZuulFilter() {
@@ -393,6 +413,7 @@ public abstract class ZuulProxyTestBase {
return 0;
}
};
}
@Override
@@ -401,6 +422,53 @@ public abstract class ZuulProxyTestBase {
mapping.setRemoveSemicolonContent(false);
return mapping;
}
}
public static class FallbackProvider implements ZuulFallbackProvider {
@Override
public String getRoute() {
return "simple";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return null;
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("fallback".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_HTML);
return headers;
}
};
}
}
@Configuration

View File

@@ -64,6 +64,7 @@ import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceI
* @author Spencer Gibb
* @author Jon Schneider
* @author Matt Jenkins
* @author Ryan Baxter
*/
@Configuration
@EnableConfigurationProperties
@@ -107,9 +108,16 @@ public class EurekaClientAutoConfiguration {
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) {
RelaxedPropertyResolver relaxedPropertyResolver = new RelaxedPropertyResolver(env, "eureka.instance.");
RelaxedPropertyResolver springPropertyResolver = new RelaxedPropertyResolver(env, "spring.application.");
String springAppName = springPropertyResolver.getProperty("name");
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
instance.setNonSecurePort(this.nonSecurePort);
instance.setInstanceId(getDefaultInstanceId(this.env));
if(StringUtils.hasText(springAppName)) {
instance.setAppname(springAppName);
instance.setVirtualHostName(springAppName);
instance.setSecureVirtualHostName(springAppName);
}
if (this.managementPort != this.nonSecurePort && this.managementPort != 0) {
if (StringUtils.hasText(this.hostname)) {
instance.setHostname(this.hostname);

View File

@@ -16,31 +16,31 @@
package org.springframework.cloud.netflix.eureka;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.commons.util.InetUtils.HostInfo;
import com.netflix.appinfo.DataCenterInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.appinfo.MyDataCenterInfo;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.commons.util.InetUtils.HostInfo;
import com.netflix.appinfo.DataCenterInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.appinfo.MyDataCenterInfo;
/**
* @author Dave Syer
* @author Spencer Gibb
* @author Ryan Baxter
*/
@Data
@ConfigurationProperties("eureka.instance")
public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig {
private static final String UNKNOWN = "unknown";
@Getter(AccessLevel.PRIVATE)
@Setter(AccessLevel.PRIVATE)
private HostInfo hostInfo;
@@ -52,8 +52,7 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig {
/**
* Get the name of the application to be registered with eureka.
*/
@Value("${spring.application.name:unknown}")
private String appname = "unknown";
private String appname = UNKNOWN;
/**
* Get the name of the application group to be registered with eureka.
@@ -119,8 +118,7 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig {
* virtual host name.Think of this as similar to the fully qualified domain name, that
* the users of your services will need to find this instance.
*/
@Value("${spring.application.name:unknown}")
private String virtualHostName;
private String virtualHostName = UNKNOWN;
/**
* Get the unique Id (within the scope of the appName) of this instance to be
@@ -135,8 +133,7 @@ public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig {
* secure virtual host name.Think of this as similar to the fully qualified domain
* name, that the users of your services will need to find this instance.
*/
@Value("${spring.application.name:unknown}")
private String secureVirtualHostName;
private String secureVirtualHostName = UNKNOWN;
/**
* Gets the AWS autoscaling group name associated with this instance. This information

View File

@@ -189,6 +189,23 @@ public class EurekaClientAutoConfigurationTests {
// Mockito.verify(http).addFilter(Matchers.any(HTTPBasicAuthFilter.class));
}
@Test
public void testDefaultAppName() throws Exception {
setupContext();
assertEquals("unknown", getInstanceConfig().getAppname());
assertEquals("unknown", getInstanceConfig().getVirtualHostName());
assertEquals("unknown", getInstanceConfig().getSecureVirtualHostName());
}
@Test
public void testAppName() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context, "spring.application.name=mytest");
setupContext();
assertEquals("mytest", getInstanceConfig().getAppname());
assertEquals("mytest", getInstanceConfig().getVirtualHostName());
assertEquals("mytest", getInstanceConfig().getSecureVirtualHostName());
}
private void testNonSecurePort(String propName) {
addEnvironment(this.context, propName + ":8888");
setupContext();

View File

@@ -20,15 +20,18 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.commons.util.InetUtilsProperties;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.StringUtils;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import static org.junit.Assert.assertEquals;
@@ -38,6 +41,7 @@ import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnviron
/**
* @author Dave Syer
* @author Spencer Gibb
* @author Ryan Baxter
*/
public class EurekaInstanceConfigBeanTests {
@@ -185,6 +189,14 @@ public class EurekaInstanceConfigBeanTests {
}
@Test
public void testDefaultAppName() throws Exception {
setupContext();
assertEquals("default app name is wrong", "unknown", getInstanceConfig().getAppname());
assertEquals("default virtual hostname is wrong", "unknown", getInstanceConfig().getVirtualHostName());
assertEquals("default secure virtual hostname is wrong", "unknown", getInstanceConfig().getSecureVirtualHostName());
}
private void setupContext() {
this.context.register(PropertyPlaceholderAutoConfiguration.class,
TestConfiguration.class);
@@ -198,9 +210,19 @@ public class EurekaInstanceConfigBeanTests {
@Configuration
@EnableConfigurationProperties
protected static class TestConfiguration {
@Autowired
ConfigurableEnvironment env;
@Bean
public EurekaInstanceConfigBean eurekaInstanceConfigBean() {
return new EurekaInstanceConfigBean(new InetUtils(new InetUtilsProperties()));
EurekaInstanceConfigBean configBean = new EurekaInstanceConfigBean(new InetUtils(new InetUtilsProperties()));
RelaxedPropertyResolver springPropertyResolver = new RelaxedPropertyResolver(env, "spring.application.");
String springAppName = springPropertyResolver.getProperty("name");
if(StringUtils.hasText(springAppName)) {
configBean.setSecureVirtualHostName(springAppName);
configBean.setVirtualHostName(springAppName);
configBean.setAppname(springAppName);
}
return configBean;
}
}

View File

@@ -28,12 +28,11 @@ import javax.ws.rs.ext.Provider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EurekaConstants;

View File

@@ -16,10 +16,13 @@
package org.springframework.cloud.netflix.sidecar;
import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.commons.util.InetUtils;
@@ -32,10 +35,9 @@ import org.springframework.util.StringUtils;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.discovery.EurekaClientConfig;
import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
@Configuration
@EnableConfigurationProperties
@@ -73,9 +75,16 @@ public class SidecarConfiguration {
@Bean
public EurekaInstanceConfigBean eurekaInstanceConfigBean() {
EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils);
RelaxedPropertyResolver springPropertyResolver = new RelaxedPropertyResolver(env, "spring.application.");
String springAppName = springPropertyResolver.getProperty("name");
int port = this.sidecarProperties.getPort();
config.setNonSecurePort(port);
config.setInstanceId(getDefaultInstanceId(this.env));
if(StringUtils.hasText(springAppName)) {
config.setAppname(springAppName);
config.setVirtualHostName(springAppName);
config.setSecureVirtualHostName(springAppName);
}
if (StringUtils.hasText(this.hostname)) {
config.setHostname(this.hostname);
}