diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java index 59e14869..3570c8e0 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java @@ -87,8 +87,8 @@ public class ZuulProxyConfiguration extends ZuulConfiguration { @Bean public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator) { return new PreDecorationFilter(routeLocator, - this.zuulProperties.isAddProxyHeaders(), - this.zuulProperties.isRemoveSemicolonContent()); + this.server.getServletPrefix(), + this.zuulProperties); } // route filters diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/SimpleRouteLocator.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/SimpleRouteLocator.java index 3c6a6d5b..f9265290 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/SimpleRouteLocator.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/SimpleRouteLocator.java @@ -90,6 +90,9 @@ public class SimpleRouteLocator implements RouteLocator { log.debug("servletPath=" + this.dispatcherServletPath); log.debug("zuulServletPath=" + this.zuulServletPath); + log.debug("RequestUtils.isDispatcherServletRequest()=" + RequestUtils.isDispatcherServletRequest()); + log.debug("RequestUtils.isZuulServletRequest()=" + RequestUtils.isZuulServletRequest()); + String adjustedPath = adjustPath(path); @@ -173,12 +176,14 @@ public class SimpleRouteLocator implements RouteLocator { && StringUtils.hasText(this.dispatcherServletPath)) { if (!this.dispatcherServletPath.equals("/")) { adjustedPath = path.substring(this.dispatcherServletPath.length()); + log.debug("Stripped dispatcherServletPath"); } } else if (RequestUtils.isZuulServletRequest()) { if (StringUtils.hasText(this.zuulServletPath) && !this.zuulServletPath.equals("/")) { adjustedPath = path.substring(this.zuulServletPath.length()); + log.debug("Stripped zuulServletPath"); } } else { diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre/PreDecorationFilter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre/PreDecorationFilter.java index 517b405b..5cd16f30 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre/PreDecorationFilter.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre/PreDecorationFilter.java @@ -21,6 +21,8 @@ import java.net.URL; import org.springframework.cloud.netflix.zuul.filters.Route; import org.springframework.cloud.netflix.zuul.filters.RouteLocator; +import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; +import org.springframework.cloud.netflix.zuul.util.RequestUtils; import org.springframework.util.StringUtils; import org.springframework.web.util.UrlPathHelper; @@ -36,14 +38,20 @@ public class PreDecorationFilter extends ZuulFilter { private RouteLocator routeLocator; private boolean addProxyHeaders; + + private String dispatcherServletPath; + private String zuulServletPath; private UrlPathHelper urlPathHelper = new UrlPathHelper(); - public PreDecorationFilter(RouteLocator routeLocator, boolean addProxyHeaders, - boolean removeSemicolonContent) { + public PreDecorationFilter(RouteLocator routeLocator, + String dispatcherServletPath, ZuulProperties zuulProperties) { this.routeLocator = routeLocator; - this.addProxyHeaders = addProxyHeaders; - this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent); + this.addProxyHeaders = zuulProperties.isAddProxyHeaders(); + this.urlPathHelper.setRemoveSemicolonContent(zuulProperties.isRemoveSemicolonContent()); + this.dispatcherServletPath = dispatcherServletPath; + this.zuulServletPath = zuulProperties.getServletPath(); + } @Override @@ -121,7 +129,27 @@ public class PreDecorationFilter extends ZuulFilter { } else { log.warn("No route found for uri: " + requestURI); - ctx.set("forward.to", requestURI); + + String fallBackUri = requestURI; + String fallbackPrefix = dispatcherServletPath; //default fallback servlet is DispatcherServlet + + if (RequestUtils.isZuulServletRequest()) { + //remove the Zuul servletPath from the requestUri + log.debug("zuulServletPath=" + zuulServletPath); + fallBackUri = fallBackUri.replaceFirst(zuulServletPath, ""); + log.debug("Replaced Zuul servlet path:" + fallBackUri); + } else { + //remove the DispatcherServlet servletPath from the requestUri + log.debug("dispatcherServletPath=" + dispatcherServletPath); + fallBackUri = fallBackUri.replaceFirst(dispatcherServletPath, ""); + log.debug("Replaced DispatcherServlet servlet path:" + fallBackUri); + } + if (!fallBackUri.startsWith("/")) { + fallBackUri = "/" + fallBackUri; + } + String forwardURI = fallbackPrefix + fallBackUri; + forwardURI = forwardURI.replaceAll("//", "/"); + ctx.set("forward.to", forwardURI); } return null; } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/pre/PreDecorationFilterTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/pre/PreDecorationFilterTests.java index 5caf62da..13349969 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/pre/PreDecorationFilterTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/pre/PreDecorationFilterTests.java @@ -52,10 +52,10 @@ public class PreDecorationFilterTests { @Before public void init() { initMocks(this); + this.properties = new ZuulProperties(); this.routeLocator = new DiscoveryClientRouteLocator("/", this.discovery, this.properties); - this.filter = new PreDecorationFilter(this.routeLocator, true, - this.properties.isRemoveSemicolonContent()); + this.filter = new PreDecorationFilter(this.routeLocator, "/", this.properties); RequestContext ctx = RequestContext.getCurrentContext(); ctx.clear(); ctx.setRequest(this.request); @@ -138,6 +138,105 @@ public class PreDecorationFilterTests { assertEquals("foo", getHeader(ctx.getOriginResponseHeaders(), "x-zuul-serviceid")); } + + @Test + public void routeNotFound() throws Exception { + this.properties.setPrefix("/api"); + this.properties.setStripPrefix(true); + this.routeLocator.addRoute( + new ZuulRoute("foo", "/foo/**", null, "forward:/foo", true, null, null)); + + this.request.setRequestURI("/api/bar/1"); + + this.filter.run(); + RequestContext ctx = RequestContext.getCurrentContext(); + assertEquals("/api/bar/1", ctx.get("forward.to")); + } + + @Test + public void routeNotFoundDispatcherServletSpecialPath() throws Exception { + this.properties.setPrefix("/api"); + this.properties.setStripPrefix(true); + this.properties.setAddProxyHeaders(true); + this.routeLocator.addRoute( + new ZuulRoute("foo", "/foo/**", null, "forward:/foo", true, null, null)); + + this.filter = new PreDecorationFilter(this.routeLocator, + "/special", this.properties); + + this.request.setRequestURI("/api/bar/1"); + + this.filter.run(); + RequestContext ctx = RequestContext.getCurrentContext(); + assertEquals("/special/api/bar/1", ctx.get("forward.to")); + } + + @Test + public void routeNotFoundZuulRequest() throws Exception { + setTestRequestContext(); + RequestContext ctx = RequestContext.getCurrentContext(); + ctx.getCurrentContext().setZuulEngineRan(); + this.request.setRequestURI("/zuul/api/bar/1"); + ctx.setRequest(this.request); + + this.properties.setPrefix("/api"); + this.properties.setStripPrefix(true); + this.properties.setServletPath("/zuul"); + this.routeLocator.addRoute( + new ZuulRoute("foo", "/foo/**", null, "forward:/foo", true, null, null)); + + + this.filter.run(); + + assertEquals("/api/bar/1", ctx.get("forward.to")); + } + + @Test + public void routeNotFoundZuulRequestDispatcherServletSpecialPath() throws Exception { + setTestRequestContext(); + RequestContext ctx = RequestContext.getCurrentContext(); + ctx.getCurrentContext().setZuulEngineRan(); + this.request.setRequestURI("/zuul/api/bar/1"); + ctx.setRequest(this.request); + + this.properties.setPrefix("/api"); + this.properties.setStripPrefix(true); + this.properties.setServletPath("/zuul"); + this.properties.setAddProxyHeaders(true); + this.routeLocator.addRoute( + new ZuulRoute("foo", "/foo/**", null, "forward:/foo", true, null, null)); + this.filter = new PreDecorationFilter(this.routeLocator, + "/special", this.properties); + + + this.filter.run(); + + assertEquals("/special/api/bar/1", ctx.get("forward.to")); + } + + + @Test + public void routeNotFoundZuulRequestZuulHomeMapping() throws Exception { + setTestRequestContext(); + RequestContext ctx = RequestContext.getCurrentContext(); + ctx.getCurrentContext().setZuulEngineRan(); + this.request.setRequestURI("/api/bar/1"); + ctx.setRequest(this.request); + + this.properties.setPrefix("/api"); + this.properties.setStripPrefix(true); + this.properties.setServletPath("/"); + this.properties.setAddProxyHeaders(true); + this.routeLocator.addRoute( + new ZuulRoute("foo", "/foo/**", null, "forward:/foo", true, null, null)); + + this.filter = new PreDecorationFilter(this.routeLocator, + "/special", this.properties); + + this.filter.run(); + + assertEquals("/special/api/bar/1", ctx.get("forward.to")); + } private Object getHeader(List> headers, String key) { String value = null; @@ -149,5 +248,11 @@ public class PreDecorationFilterTests { } return value; } + + private void setTestRequestContext() { + RequestContext context = new RequestContext(); + RequestContext.testSetCurrentContext(context); + + } }