diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 98bf13c3..9f483e55 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -29,7 +29,7 @@ jobs: restore-keys: | ${{ runner.os }}-maven- - name: Build with Maven - run: ./mvnw clean install -B -U -Pspring -Dmaven.test.redirectTestOutputToFile=true + run: ./mvnw clean install -B -U -Pspring -Dmaven.test.redirectTestOutputToFile=true -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 - name: Publish Test Report uses: mikepenz/action-junit-report@v2 if: always() # always run even if the previous step fails diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactory.java index 43945426..a71829c0 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,10 @@ package org.springframework.cloud.gateway.filter.factory; +import java.net.URI; import java.util.Arrays; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -27,11 +29,13 @@ import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.util.UriTemplate; import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ALREADY_PREFIXED_ATTR; 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.getUriTemplateVariables; /** * @author Spencer Gibb @@ -58,6 +62,8 @@ public class PrefixPathGatewayFilterFactory @Override public GatewayFilter apply(Config config) { return new GatewayFilter() { + final UriTemplate uriTemplate = new UriTemplate(config.prefix); + @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { boolean alreadyPrefixed = exchange.getAttributeOrDefault(GATEWAY_ALREADY_PREFIXED_ATTR, false); @@ -68,12 +74,14 @@ public class PrefixPathGatewayFilterFactory ServerHttpRequest req = exchange.getRequest(); addOriginalRequestUrl(exchange, req.getURI()); - String newPath = config.prefix + req.getURI().getRawPath(); + Map uriVariables = getUriTemplateVariables(exchange); + URI uri = uriTemplate.expand(uriVariables); + + String newPath = uri.getRawPath() + req.getURI().getRawPath(); + exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri); ServerHttpRequest request = req.mutate().path(newPath).build(); - exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI()); - if (log.isTraceEnabled()) { log.trace("Prefixed URI with: " + config.prefix + " -> " + request.getURI()); } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactoryTest.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactoryTest.java index 1311696a..a42eed2c 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactoryTest.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.cloud.gateway.filter.factory; import java.net.URI; +import java.util.HashMap; import java.util.LinkedHashSet; import org.junit.Test; @@ -26,6 +27,7 @@ import reactor.core.publisher.Mono; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.factory.PrefixPathGatewayFilterFactory.Config; +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.web.server.ServerWebExchange; @@ -46,11 +48,33 @@ public class PrefixPathGatewayFilterFactoryTest { testPrefixPathFilter("/foo", "/hello%20world", "/foo/hello%20world"); } + @Test + public void testPrefixPathWithVariable() { + HashMap variables = new HashMap<>(); + variables.put("id", "foo"); + testPrefixPathFilter("/{id}", "/bar", "/foo/bar", variables); + } + + @Test + public void testPrefixPathWithMultipleVariables() { + HashMap variables = new HashMap<>(); + variables.put("id", "foo"); + variables.put("hello", "world"); + variables.put("product", "bar"); + testPrefixPathFilter("/{id}/v1/{hello}/{product}", "/test", "/foo/v1/world/bar/test", variables); + } + private void testPrefixPathFilter(String prefix, String path, String expectedPath) { + testPrefixPathFilter(prefix, path, expectedPath, new HashMap<>()); + } + + private void testPrefixPathFilter(String prefix, String path, String expectedPath, + HashMap variables) { GatewayFilter filter = new PrefixPathGatewayFilterFactory().apply(c -> c.setPrefix(prefix)); MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost" + path).build(); ServerWebExchange exchange = MockServerWebExchange.from(request); + ServerWebExchangeUtils.putUriTemplateVariables(exchange, variables); GatewayFilterChain filterChain = mock(GatewayFilterChain.class);