From 42f6e4d1f0eb71a7f1ba215edadef82258602e60 Mon Sep 17 00:00:00 2001 From: andreasevers Date: Mon, 27 Jul 2015 16:48:26 +0200 Subject: [PATCH] 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. 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. --- .../main/asciidoc/spring-cloud-netflix.adoc | 17 ++ .../zuul/filters/ProxyRouteLocator.java | 55 ++++--- .../netflix/zuul/filters/ZuulProperties.java | 2 + .../zuul/filters/ProxyRouteLocatorTests.java | 150 ++++++++++++++++++ 4 files changed, 203 insertions(+), 21 deletions(-) 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,