diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index f4a7fe37..39956bdf 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -900,6 +900,23 @@ An application with the `@EnableZuulProxy` could act as a standalone server if you set a default route ("/"), for example `zuul.route.home: /` would route all traffic (i.e. "/**") to the "home" service. +If more fine-grained ignoring is needed, you can specify specific patterns to ignore. +These patterns are being evaluated at the end of the route location process, which +means prefixes should be included in the pattern to warrant a match. Ignored patterns +span all services and supersede any other route specification. + +.application.yml +[source,yaml] +---- + zuul: + ignoredPatterns: */admin/** + routes: + users: /myusers/** +---- + +This means that all calls such as "/myusers/101" will be forwarded to "/101" on the "users" service. +But calls including "/admin/" will not resolve. + === Uploading Files through Zuul If you `@EnableZuulProxy` you can use the proxy paths to diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRouteLocator.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRouteLocator.java index c9115018..857e32b3 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRouteLocator.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRouteLocator.java @@ -108,35 +108,48 @@ public class ProxyRouteLocator implements RouteLocator { } log.debug("path=" + path); Boolean retryable = this.properties.getRetryable(); - for (Entry entry : this.routes.get().entrySet()) { - String pattern = entry.getKey(); - log.debug("Matching pattern:" + pattern); - if (this.pathMatcher.match(pattern, path)) { - ZuulRoute route = entry.getValue(); - id = route.getId(); - location = route.getLocation(); - targetPath = path; - if (path.startsWith(prefix) && this.properties.isStripPrefix()) { - targetPath = path.substring(prefix.length()); - } - if (route.isStripPrefix()) { - int index = route.getPath().indexOf("*") - 1; - if (index > 0) { - String routePrefix = route.getPath().substring(0, index); - targetPath = targetPath.replaceFirst(routePrefix, ""); - prefix = prefix + routePrefix; + if (!matchesIgnoredPatterns(path)) { + for (Entry entry : this.routes.get().entrySet()) { + String pattern = entry.getKey(); + log.debug("Matching pattern:" + pattern); + if (this.pathMatcher.match(pattern, path)) { + ZuulRoute route = entry.getValue(); + id = route.getId(); + location = route.getLocation(); + targetPath = path; + if (path.startsWith(prefix) && this.properties.isStripPrefix()) { + targetPath = path.substring(prefix.length()); } + if (route.isStripPrefix()) { + int index = route.getPath().indexOf("*") - 1; + if (index > 0) { + String routePrefix = route.getPath().substring(0, index); + targetPath = targetPath.replaceFirst(routePrefix, ""); + prefix = prefix + routePrefix; + } + } + if (route.getRetryable() != null) { + retryable = route.getRetryable(); + } + break; } - if (route.getRetryable() != null) { - retryable = route.getRetryable(); - } - break; } } return (location == null ? null : new ProxyRouteSpec(id, targetPath, location, prefix, retryable)); } + protected boolean matchesIgnoredPatterns(String path) { + for (String pattern : this.properties.getIgnoredPatterns()) { + log.debug("Matching ignored pattern:" + pattern); + if (this.pathMatcher.match(pattern, path)) { + log.debug("Path " + path + " matches ignored pattern " + pattern); + return true; + } + } + return false; + } + public void resetRoutes() { this.routes.set(locateRoutes()); } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java index c064f9ee..63a16572 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java @@ -51,6 +51,8 @@ public class ZuulProperties { private List ignoredServices = new ArrayList(); + private List ignoredPatterns = new ArrayList(); + private String servletPath = "/zuul"; @PostConstruct diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRouteLocatorTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRouteLocatorTests.java index 50b8f04f..4e92dfd8 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRouteLocatorTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRouteLocatorTests.java @@ -44,6 +44,8 @@ public class ProxyRouteLocatorTests { public static final String IGNOREDSERVICE = "ignoredservice"; + public static final String IGNOREDPATTERN = "/foo/**"; + public static final String ASERVICE = "aservice"; public static final String MYSERVICE = "myservice"; @@ -151,7 +153,155 @@ public class ProxyRouteLocatorTests { assertEquals("foo", route.getLocation()); assertEquals("/1", route.getPath()); } + + @Test + public void testGetMatchingPathWithoutMatchingIgnoredPattern() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery, + this.properties); + this.properties.setIgnoredPatterns(Collections.singletonList(IGNOREDPATTERN)); + this.properties.getRoutes().put("bar", new ZuulRoute("/bar/**")); + this.properties.init(); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/bar/1"); + assertEquals("bar", route.getLocation()); + assertEquals("bar", route.getId()); + } + + @Test + public void testGetMatchingPathWithMatchingIgnoredPattern() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery, + this.properties); + this.properties.setIgnoredPatterns(Collections.singletonList(IGNOREDPATTERN)); + this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**")); + this.properties.init(); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/foo/1"); + assertNull("routes did not ignore " + IGNOREDPATTERN, route); + } + @Test + public void testGetMatchingPathWithMatchingIgnoredPatternWithPrefix() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery, + this.properties); + this.properties.setIgnoredPatterns(Collections.singletonList(IGNOREDPATTERN)); + this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**")); + this.properties.setPrefix("/proxy"); + this.properties.init(); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/proxy/foo/1"); + assertEquals("foo", route.getLocation()); + assertEquals("/1", route.getPath()); + } + + @Test + public void testGetMatchingPathWithMatchingIgnoredPatternWithServletPath() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/app", this.discovery, + this.properties); + this.properties.setIgnoredPatterns(Collections.singletonList(IGNOREDPATTERN)); + this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**")); + this.properties.init(); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/app/foo/1"); + assertNull("routes did not ignore " + IGNOREDPATTERN, route); + } + + @Test + public void testGetMatchingPathWithoutMatchingIgnoredPatternWithNoPrefixStripping() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery, + this.properties); + this.properties.setIgnoredPatterns(Collections.singletonList(IGNOREDPATTERN)); + this.properties.getRoutes().put("foo", + new ZuulRoute("foo", "/foo/**", "foo", null, false, null)); + this.properties.setStripPrefix(false); + this.properties.setPrefix("/proxy"); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/proxy/foo/1"); + assertEquals("foo", route.getLocation()); + assertEquals("/proxy/foo/1", route.getPath()); + } + + @Test + public void testGetMatchingPathWithMatchingIgnoredPatternWithNoPrefixStripping() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery, + this.properties); + this.properties.setIgnoredPatterns(Collections.singletonList("/proxy" + IGNOREDPATTERN)); + this.properties.getRoutes().put("foo", + new ZuulRoute("foo", "/foo/**", "foo", null, false, null)); + this.properties.setStripPrefix(false); + this.properties.setPrefix("/proxy"); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/proxy/foo/1"); + assertNull("routes did not ignore " + "/proxy" + IGNOREDPATTERN, route); + } + + @Test + public void testGetMatchingPathWithoutMatchingIgnoredPatternWithLocalPrefixStripping() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery, + this.properties); + this.properties.setIgnoredPatterns(Collections.singletonList(IGNOREDPATTERN)); + this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**", "foo")); + this.properties.setStripPrefix(false); + this.properties.setPrefix("/proxy"); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/proxy/foo/1"); + assertEquals("foo", route.getLocation()); + assertEquals("/proxy/1", route.getPath()); + } + + @Test + public void testGetMatchingPathWithMatchingIgnoredPatternWithLocalPrefixStripping() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery, + this.properties); + this.properties.setIgnoredPatterns(Collections.singletonList("/proxy" + IGNOREDPATTERN)); + this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**", "foo")); + this.properties.setStripPrefix(false); + this.properties.setPrefix("/proxy"); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/proxy/foo/1"); + assertNull("routes did not ignore " + "/proxy" + IGNOREDPATTERN, route); + } + + @Test + public void testGetMatchingPathWithoutMatchingIgnoredPatternWithGlobalPrefixStripping() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery, + this.properties); + this.properties.setIgnoredPatterns(Collections.singletonList(IGNOREDPATTERN)); + this.properties.getRoutes().put("foo", + new ZuulRoute("foo", "/foo/**", "foo", null, false, null)); + this.properties.setPrefix("/proxy"); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/proxy/foo/1"); + assertEquals("foo", route.getLocation()); + assertEquals("/foo/1", route.getPath()); + } + + @Test + public void testGetMatchingPathWithMatchingIgnoredPatternWithGlobalPrefixStripping() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery, + this.properties); + this.properties.setIgnoredPatterns(Collections.singletonList("/proxy" + IGNOREDPATTERN)); + this.properties.getRoutes().put("foo", + new ZuulRoute("foo", "/foo/**", "foo", null, false, null)); + this.properties.setPrefix("/proxy"); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/proxy/foo/1"); + assertNull("routes did not ignore " + "/proxy" + IGNOREDPATTERN, route); + } + + @Test + public void testGetMatchingPathWithMatchingIgnoredPatternWithRoutePrefixStripping() throws Exception { + ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery, + this.properties); + ZuulRoute zuulRoute = new ZuulRoute("/foo/**"); + zuulRoute.setStripPrefix(true); + this.properties.setIgnoredPatterns(Collections.singletonList(IGNOREDPATTERN)); + this.properties.getRoutes().put("foo", zuulRoute); + this.properties.init(); + routeLocator.getRoutes(); // force refresh + ProxyRouteSpec route = routeLocator.getMatchingRoute("/foo/1"); + assertNull("routes did not ignore " + IGNOREDPATTERN, route); + } + @Test public void testGetRoutes() { ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,