Detect Zuul vs DispatcherServlet execution
Change SimpleRouteLocator to work with both Zuul and DispatcherServlet Fixes gh-794
This commit is contained in:
@@ -36,6 +36,7 @@ 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.pre.ServletDetectionFilter;
|
||||
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;
|
||||
@@ -91,6 +92,11 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
|
||||
}
|
||||
|
||||
// pre filters
|
||||
@Bean
|
||||
public ServletDetectionFilter servletDetectionFilter() {
|
||||
return new ServletDetectionFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator) {
|
||||
return new PreDecorationFilter(routeLocator,
|
||||
|
||||
@@ -24,12 +24,15 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
|
||||
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import com.netflix.zuul.http.ZuulServlet;
|
||||
|
||||
/**
|
||||
* Simple {@link RouteLocator} based on configuration data held in {@link ZuulProperties}.
|
||||
@@ -43,20 +46,18 @@ public class SimpleRouteLocator implements RouteLocator {
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
private String servletPath;
|
||||
private String dispatcherServletPath = "/";
|
||||
private String zuulServletPath;
|
||||
|
||||
private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>();
|
||||
|
||||
public SimpleRouteLocator(String servletPath, ZuulProperties properties) {
|
||||
this.properties = properties;
|
||||
if (StringUtils.hasText(servletPath)) { // a servletPath is passed explicitly
|
||||
this.servletPath = servletPath;
|
||||
}
|
||||
else {
|
||||
// set Zuul servlet path
|
||||
this.servletPath = properties.getServletPath() != null
|
||||
? properties.getServletPath() : "";
|
||||
if (servletPath != null && StringUtils.hasText(servletPath)) {
|
||||
this.dispatcherServletPath = servletPath;
|
||||
}
|
||||
|
||||
this.zuulServletPath = properties.getServletPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +80,7 @@ public class SimpleRouteLocator implements RouteLocator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Route getMatchingRoute(String path) {
|
||||
public Route getMatchingRoute(final String path) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Finding route for path: " + path);
|
||||
@@ -89,25 +90,25 @@ public class SimpleRouteLocator implements RouteLocator {
|
||||
this.routes.set(locateRoutes());
|
||||
}
|
||||
|
||||
log.debug("servletPath=" + this.servletPath);
|
||||
if (StringUtils.hasText(this.servletPath) && !this.servletPath.equals("/")
|
||||
&& path.startsWith(this.servletPath)) {
|
||||
path = path.substring(this.servletPath.length());
|
||||
}
|
||||
log.debug("path=" + path);
|
||||
log.debug("servletPath=" + this.dispatcherServletPath);
|
||||
log.debug("zuulServletPath=" + this.zuulServletPath);
|
||||
|
||||
String adjustedPath = adjustPath(path);
|
||||
|
||||
ZuulRoute route = null;
|
||||
if (!matchesIgnoredPatterns(path)) {
|
||||
if (!matchesIgnoredPatterns(adjustedPath)) {
|
||||
for (Entry<String, ZuulRoute> entry : this.routes.get().entrySet()) {
|
||||
String pattern = entry.getKey();
|
||||
log.debug("Matching pattern:" + pattern);
|
||||
if (this.pathMatcher.match(pattern, path)) {
|
||||
if (this.pathMatcher.match(pattern, adjustedPath)) {
|
||||
route = entry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getRoute(route, path);
|
||||
log.debug("route matched=" + route);
|
||||
|
||||
return getRoute(route, adjustedPath);
|
||||
|
||||
}
|
||||
|
||||
@@ -166,5 +167,27 @@ public class SimpleRouteLocator implements RouteLocator {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String adjustPath(final String path) {
|
||||
String adjustedPath = path;
|
||||
|
||||
if (RequestUtils.isDispatcherServletRequest()
|
||||
&& StringUtils.hasText(dispatcherServletPath)) {
|
||||
if (!dispatcherServletPath.equals("/")) {
|
||||
adjustedPath = path.substring(this.dispatcherServletPath.length());
|
||||
}
|
||||
} else if (RequestUtils.isZuulServletRequest()){
|
||||
if (StringUtils.hasText(zuulServletPath)
|
||||
&& !zuulServletPath.equals("/")) {
|
||||
adjustedPath = path.substring(this.zuulServletPath.length());
|
||||
}
|
||||
} else {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
log.debug("adjustedPath=" + path);
|
||||
return adjustedPath;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import java.lang.reflect.Field;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
@@ -71,16 +71,11 @@ public class Servlet30WrapperFilter extends ZuulFilter {
|
||||
request);
|
||||
ctx.setRequest(new Servlet30RequestWrapper(request));
|
||||
}
|
||||
else if (isDispatcherServletRequest(request)) {
|
||||
else if (RequestUtils.isDispatcherServletRequest()) {
|
||||
// If it's going through the dispatcher we need to buffer the body
|
||||
ctx.setRequest(new Servlet30RequestWrapper(request));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isDispatcherServletRequest(HttpServletRequest request) {
|
||||
return request.getAttribute(
|
||||
DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.zuul.filters.pre;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.http.HttpServletRequestWrapper;
|
||||
import com.netflix.zuul.http.ZuulServlet;
|
||||
|
||||
/**
|
||||
* Detects whether a request is ran through the {@link DispatcherServlet} or {@link ZuulServlet}.
|
||||
* The purpose was to detect this up-front at the very beginning of Zuul filter processing
|
||||
* and rely on this information in all filters.
|
||||
* RequestContext is used such that the information is accessible to classes
|
||||
* which do not have a request reference.
|
||||
* @author Adrian Ivan
|
||||
*/
|
||||
public class ServletDetectionFilter extends ZuulFilter {
|
||||
|
||||
|
||||
public ServletDetectionFilter() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String filterType() {
|
||||
return "pre";
|
||||
}
|
||||
|
||||
/**
|
||||
* Must run before other filters that rely on the difference between
|
||||
* DispatcherServlet and ZuulServlet.
|
||||
*/
|
||||
@Override
|
||||
public int filterOrder() {
|
||||
return -3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldFilter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
HttpServletRequest request = ctx.getRequest();
|
||||
if (!(request instanceof HttpServletRequestWrapper)
|
||||
&& isDispatcherServletRequest(request)) {
|
||||
ctx.set(RequestUtils.IS_DISPATCHERSERVLETREQUEST, true);
|
||||
} else {
|
||||
ctx.set(RequestUtils.IS_DISPATCHERSERVLETREQUEST, false);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isDispatcherServletRequest(HttpServletRequest request) {
|
||||
return request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.springframework.cloud.netflix.zuul.util;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
public class RequestUtils {
|
||||
|
||||
public static final String IS_DISPATCHERSERVLETREQUEST = "isDispatcherServletRequest";
|
||||
|
||||
public static boolean isDispatcherServletRequest() {
|
||||
return RequestContext.getCurrentContext().getBoolean(IS_DISPATCHERSERVLETREQUEST);
|
||||
}
|
||||
|
||||
public static boolean isZuulServletRequest() {
|
||||
//extra check for dispatcher since ZuulServlet can run from ZuulController
|
||||
return !isDispatcherServletRequest() && RequestContext.getCurrentContext().getZuulEngineRan();
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,11 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
|
||||
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -63,6 +66,7 @@ public class DiscoveryClientRouteLocatorTests {
|
||||
@Before
|
||||
public void init() {
|
||||
initMocks(this);
|
||||
setTestRequestcontext(); //re-initialize Zuul context for each test
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -92,6 +96,8 @@ public class DiscoveryClientRouteLocatorTests {
|
||||
|
||||
@Test
|
||||
public void testGetMatchingPathWithServletPath() throws Exception {
|
||||
setTestRequestcontext();
|
||||
RequestContext.getCurrentContext().set(RequestUtils.IS_DISPATCHERSERVLETREQUEST, true);
|
||||
DiscoveryClientRouteLocator routeLocator = new DiscoveryClientRouteLocator("/app",
|
||||
this.discovery, this.properties);
|
||||
this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**"));
|
||||
@@ -101,6 +107,20 @@ public class DiscoveryClientRouteLocatorTests {
|
||||
assertEquals("foo", route.getLocation());
|
||||
assertEquals("/1", route.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMatchingPathWithZuulServletPath() throws Exception {
|
||||
RequestContext.getCurrentContext().setZuulEngineRan();
|
||||
DiscoveryClientRouteLocator routeLocator = new DiscoveryClientRouteLocator("/app",
|
||||
this.discovery, this.properties);
|
||||
this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**"));
|
||||
this.properties.init();
|
||||
routeLocator.getRoutes(); // force refresh
|
||||
Route route = routeLocator.getMatchingRoute("/zuul/foo/1");
|
||||
assertEquals("foo", route.getLocation());
|
||||
assertEquals("/1", route.getPath());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMatchingPathWithNoPrefixStripping() throws Exception {
|
||||
@@ -141,6 +161,34 @@ public class DiscoveryClientRouteLocatorTests {
|
||||
assertEquals("foo", route.getLocation());
|
||||
assertEquals("/foo/1", route.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMatchingPathWithGlobalPrefixStrippingAndServletPath() throws Exception {
|
||||
RequestContext.getCurrentContext().set(RequestUtils.IS_DISPATCHERSERVLETREQUEST, true);
|
||||
DiscoveryClientRouteLocator routeLocator = new DiscoveryClientRouteLocator("/app",
|
||||
this.discovery, this.properties);
|
||||
this.properties.getRoutes().put("foo",
|
||||
new ZuulRoute("foo", "/foo/**", "foo", null, false, null));
|
||||
this.properties.setPrefix("/proxy");
|
||||
routeLocator.getRoutes(); // force refresh
|
||||
Route route = routeLocator.getMatchingRoute("/app/proxy/foo/1");
|
||||
assertEquals("foo", route.getLocation());
|
||||
assertEquals("/foo/1", route.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMatchingPathWithGlobalPrefixStrippingAndZuulServletPath() throws Exception {
|
||||
RequestContext.getCurrentContext().setZuulEngineRan();
|
||||
DiscoveryClientRouteLocator routeLocator = new DiscoveryClientRouteLocator("/",
|
||||
this.discovery, this.properties);
|
||||
this.properties.getRoutes().put("foo",
|
||||
new ZuulRoute("foo", "/foo/**", "foo", null, false, null));
|
||||
this.properties.setPrefix("/proxy");
|
||||
routeLocator.getRoutes(); // force refresh
|
||||
Route route = routeLocator.getMatchingRoute("/zuul/proxy/foo/1");
|
||||
assertEquals("foo", route.getLocation());
|
||||
assertEquals("/foo/1", route.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMatchingPathWithRoutePrefixStripping() throws Exception {
|
||||
@@ -596,4 +644,10 @@ public class DiscoveryClientRouteLocatorTests {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setTestRequestcontext() {
|
||||
RequestContext context = new RequestContext();
|
||||
RequestContext.testSetCurrentContext(context);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user