Merge remote-tracking branch 'Upstream/master' into eureka-app-name-default
This commit is contained in:
@@ -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 {}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,30 +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 java.util.Set;
|
||||
|
||||
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;
|
||||
@@ -49,27 +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.ZuulFallbackProvider;
|
||||
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();
|
||||
|
||||
@@ -88,135 +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 {
|
||||
|
||||
@Autowired(required = false)
|
||||
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RibbonCommandFactory<?> ribbonCommandFactory(
|
||||
SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
|
||||
return new HttpClientRibbonCommandFactory(clientFactory, zuulProperties, zuulFallbackProviders);
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@@ -269,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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 + "!";
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user