Allows multiple patterns in host route predicate.

Each pattern is checked if it matches in order.

fixes gh-589
This commit is contained in:
Spencer Gibb
2018-12-03 18:38:02 -05:00
parent 7505050072
commit 5dd0c81839
5 changed files with 100 additions and 14 deletions

View File

@@ -134,7 +134,7 @@ spring:
This route matches if the request has a header named `X-Request-Id` whos value matches the `\d+` regular expression (has a value of one or more digits).
=== Host Route Predicate Factory
The Host Route Predicate Factory takes one parameter: the host name pattern. The pattern is an Ant style pattern with `.` as the separator. This predicates matches the `Host` header that matches the pattern.
The Host Route Predicate Factory takes one parameter: a list of host name patterns. The pattern is an Ant style pattern with `.` as the separator. This predicates matches the `Host` header that matches the pattern.
.application.yml
[source,yaml]
@@ -146,10 +146,10 @@ spring:
- id: host_route
uri: http://example.org
predicates:
- Host=**.somehost.org
- Host=**.somehost.org,**.anotherhost.org
----
This route would match if the request has a `Host` header has the value `www.somehost.org` or `beta.somehost.org`.
This route would match if the request has a `Host` header has the value `www.somehost.org` or `beta.somehost.org` or `www.anotherhost.org`.
=== Method Route Predicate Factory

View File

@@ -17,12 +17,14 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
@@ -44,34 +46,54 @@ public class HostRoutePredicateFactory extends AbstractRoutePredicateFactory<Hos
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList(PATTERN_KEY);
return Collections.singletonList("patterns");
}
@Override
public ShortcutType shortcutType() {
return ShortcutType.GATHER_LIST;
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
String host = exchange.getRequest().getHeaders().getFirst("Host");
return this.pathMatcher.match(config.getPattern(), host);
return config.getPatterns().stream()
.anyMatch(pattern -> this.pathMatcher.match(pattern, host));
};
}
@Validated
public static class Config {
private String pattern;
private List<String> patterns = new ArrayList<>();
@Deprecated
public String getPattern() {
return pattern;
if (!CollectionUtils.isEmpty(this.patterns)) {
return patterns.get(0);
}
return null;
}
@Deprecated
public Config setPattern(String pattern) {
this.pattern = pattern;
this.patterns = new ArrayList<>();
this.patterns.add(pattern);
return this;
}
public List<String> getPatterns() {
return patterns;
}
public void setPatterns(List<String> patterns) {
this.patterns = patterns;
}
@Override
public String toString() {
return new ToStringCreator(this)
.append("pattern", pattern)
.append("patterns", patterns)
.toString();
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.gateway.route.builder;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.function.Predicate;
import org.springframework.cloud.gateway.handler.AsyncPredicate;
@@ -134,9 +135,9 @@ public class PredicateSpec extends UriSpec {
* @param pattern the pattern to check against. The pattern is an Ant style pattern with {@code .} as a separator
* @return a {@link BooleanSpec} to be used to add logical operators
*/
public BooleanSpec host(String pattern) {
public BooleanSpec host(String... pattern) {
return asyncPredicate(getBean(HostRoutePredicateFactory.class)
.applyAsync(c-> c.setPattern(pattern)));
.applyAsync(c-> c.setPatterns(Arrays.asList(pattern))));
}
/**

View File

@@ -19,11 +19,15 @@ package org.springframework.cloud.gateway.handler.predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
@@ -37,18 +41,57 @@ public class HostRoutePredicateFactoryTests extends BaseWebClientTests {
@Test
public void hostRouteWorks() {
expectHostRoute("www.example.org", "host_example_to_httpbin");
}
public void expectHostRoute(String host, String routeId) {
testClient.get()
.uri("/get")
.header("Host", "www.example.org")
.header("Host", host)
.exchange()
.expectStatus().isOk()
.expectHeader().valueEquals(HANDLER_MAPPER_HEADER, RoutePredicateHandlerMapping.class.getSimpleName())
.expectHeader().valueEquals(ROUTE_ID_HEADER, "host_example_to_httpbin");
.expectHeader().valueEquals(ROUTE_ID_HEADER, routeId);
}
@Test
public void hostRouteBackwardsCompatiblePatternWorks() {
expectHostRoute("www.hostpatternarg.org", "host_backwards_compatible_test");
}
@Test
public void hostRouteBackwardsCompatibleShortcutWorks() {
expectHostRoute("www.hostpatternshortcut.org", "host_backwards_compatible_shortcut_test");
}
@Test
public void mulitHostRouteWorks() {
expectHostRoute("www.hostmulti1.org", "host_multi_test");
expectHostRoute("www.hostmulti2.org", "host_multi_test");
}
@Test
public void mulitHostRouteDslWorks() {
expectHostRoute("www.hostmultidsl1.org", "host_multi_dsl");
expectHostRoute("www.hostmultidsl2.org", "host_multi_dsl");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)
public static class TestConfig { }
public static class TestConfig {
@Value("${test.uri}")
String uri;
@Bean
public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("host_multi_dsl", r -> r.host("**.hostmultidsl1.org", "**.hostmultidsl2.org")
.filters(f -> f.prefixPath("/httpbin"))
.uri(uri))
.build();
}
}
}

View File

@@ -81,6 +81,26 @@ spring:
predicates:
- Host=**.forward.org
# =====================================
- id: host_backwards_compatible_test
uri: ${test.uri}
predicates:
- name: Host
args:
pattern: '**.hostpatternarg.org'
# =====================================
- id: host_backwards_compatible_shortcut_test
uri: ${test.uri}
predicates:
- Host=**.hostpatternshortcut.org
# =====================================
- id: host_multi_test
uri: ${test.uri}
predicates:
- Host=**.hostmulti1.org,**.hostmulti2.org
# =====================================
- id: hystrix_failure_test
uri: ${test.uri}