diff --git a/docs/src/main/asciidoc/spring-cloud-gateway.adoc b/docs/src/main/asciidoc/spring-cloud-gateway.adoc index 42a01db3..5fe5fbfc 100644 --- a/docs/src/main/asciidoc/spring-cloud-gateway.adoc +++ b/docs/src/main/asciidoc/spring-cloud-gateway.adoc @@ -149,8 +149,12 @@ spring: - Host=**.somehost.org,**.anotherhost.org ---- +URI template variables are supported as well, such as `{sub}.myhost.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`. +This predicate extracts the URI template variables (like `sub` defined in the example above) as a map of names and values and places it in the `ServerWebExchange.getAttributes()` with a key defined in `ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE`. Those values are then available for use by <> + === Method Route Predicate Factory The Method Route Predicate Factory takes one parameter: the HTTP method to match. @@ -188,7 +192,7 @@ spring: This route would match if the request path was, for example: `/foo/1` or `/foo/bar` or `/bar/baz`. -This predicate extracts the URI template variables (like `segment` defined in the example above) as a map of names and values and places it in the `ServerWebExchange.getAttributes()` with a key defined in `PathRoutePredicate.URL_PREDICATE_VARS_ATTR`. Those values are then available for use by <> +This predicate extracts the URI template variables (like `segment` defined in the example above) as a map of names and values and places it in the `ServerWebExchange.getAttributes()` with a key defined in `ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE`. Those values are then available for use by <> A utility method is available to make access to these variables easier. diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactory.java index d9ca270f..facb9e26 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactory.java @@ -28,7 +28,7 @@ import org.springframework.web.util.UriTemplate; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.getPathPredicateVariables; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.getUriTemplateVariables; /** * @author Spencer Gibb @@ -54,7 +54,7 @@ public class SetPathGatewayFilterFactory extends AbstractGatewayFilterFactory uriVariables = getPathPredicateVariables(exchange); + Map uriVariables = getUriTemplateVariables(exchange); URI uri = uriTemplate.expand(uriVariables); String newPath = uri.getRawPath(); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateFactory.java index 8481c603..f6c3216c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateFactory.java @@ -20,8 +20,10 @@ package org.springframework.cloud.gateway.handler.predicate; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.function.Predicate; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; import org.springframework.core.style.ToStringCreator; import org.springframework.util.AntPathMatcher; import org.springframework.util.CollectionUtils; diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java index 47c7f7bc..bca4909b 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java @@ -35,7 +35,7 @@ import org.springframework.web.util.pattern.PathPattern; import org.springframework.web.util.pattern.PathPattern.PathMatchInfo; import org.springframework.web.util.pattern.PathPatternParser; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.putUriTemplateVariables; import static org.springframework.http.server.PathContainer.parsePath; /** @@ -85,8 +85,8 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory Mono.just(predicate.test(t)); } - public static Map getPathPredicateVariables(ServerWebExchange exchange) { - return getPathMatchVariables(exchange, URI_TEMPLATE_VARIABLES_ATTRIBUTE); + @SuppressWarnings("unchecked") + public static void putUriTemplateVariables(ServerWebExchange exchange, Map uriVariables) { + if (exchange.getAttributes().containsKey(URI_TEMPLATE_VARIABLES_ATTRIBUTE)) { + Map existingVariables = (Map) exchange.getAttributes().get(URI_TEMPLATE_VARIABLES_ATTRIBUTE); + HashMap newVariables = new HashMap<>(); + newVariables.putAll(existingVariables); + newVariables.putAll(uriVariables); + exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, newVariables); + } else { + exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables); + } } - public static Map getPathMatchVariables(ServerWebExchange exchange, String attr) { - PathPattern.PathMatchInfo variables = exchange.getAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE); - - if (variables != null) { - return variables.getUriVariables(); - } - return Collections.emptyMap(); + public static Map getUriTemplateVariables(ServerWebExchange exchange) { + return exchange.getAttributeOrDefault(URI_TEMPLATE_VARIABLES_ATTRIBUTE, new HashMap<>()); } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryIntegrationTests.java index 10b6c72e..34b72437 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryIntegrationTests.java @@ -40,7 +40,18 @@ public class SetPathGatewayFilterFactoryIntegrationTests extends BaseWebClientTe .uri("/foo/get") .header("Host", "www.setpath.org") .exchange() - .expectStatus().isOk(); + .expectStatus().isOk() + .expectHeader().valueEquals(ROUTE_ID_HEADER, "set_path_test"); + } + + @Test + public void setPathViaHostFilterWork() { + testClient.get() + .uri("/") + .header("Host", "get.setpathhost.org") + .exchange() + .expectStatus().isOk() + .expectHeader().valueEquals(ROUTE_ID_HEADER, "set_path_host_test"); } @EnableAutoConfiguration diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryTests.java index 91979bf7..4b840c35 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryTests.java @@ -17,30 +17,25 @@ package org.springframework.cloud.gateway.filter.factory; -import java.lang.reflect.Constructor; import java.net.URI; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; -import java.util.Map; import org.junit.Test; import org.mockito.ArgumentCaptor; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; -import org.springframework.util.ReflectionUtils; import org.springframework.web.server.ServerWebExchange; -import org.springframework.cloud.gateway.filter.GatewayFilterChain; -import org.springframework.web.util.pattern.PathPattern.PathMatchInfo; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE; - -import reactor.core.publisher.Mono; /** * @author Spencer Gibb @@ -89,15 +84,7 @@ public class SetPathGatewayFilterFactoryTests { .build(); ServerWebExchange exchange = MockServerWebExchange.from(request); - - try { - Constructor constructor = ReflectionUtils.accessibleConstructor(PathMatchInfo.class, Map.class, Map.class); - constructor.setAccessible(true); - PathMatchInfo pathMatchInfo = constructor.newInstance(variables, Collections.emptyMap()); - exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathMatchInfo); - } catch (Exception e) { - ReflectionUtils.rethrowRuntimeException(e); - } + ServerWebExchangeUtils.putUriTemplateVariables(exchange, variables); GatewayFilterChain filterChain = mock(GatewayFilterChain.class); diff --git a/spring-cloud-gateway-core/src/test/resources/application.yml b/spring-cloud-gateway-core/src/test/resources/application.yml index 31e846f4..6a598243 100644 --- a/spring-cloud-gateway-core/src/test/resources/application.yml +++ b/spring-cloud-gateway-core/src/test/resources/application.yml @@ -245,6 +245,14 @@ spring: filters: - SetPath=/{segment} + # ===================================== + - id: set_path_host_test + uri: ${test.uri} + predicates: + - Host={subdomain}.setpathhost.org + filters: + - SetPath=/{subdomain} + # ===================================== - id: strip_prefix_test uri: ${test.uri}