From 679bc2c2323737a36a6b70500ce7b905e6464b35 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Tue, 15 Oct 2024 13:28:14 +0000 Subject: [PATCH 1/4] Bumping versions --- docs/modules/ROOT/partials/_configprops.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/modules/ROOT/partials/_configprops.adoc b/docs/modules/ROOT/partials/_configprops.adoc index b1a778c9..98a11c38 100644 --- a/docs/modules/ROOT/partials/_configprops.adoc +++ b/docs/modules/ROOT/partials/_configprops.adoc @@ -126,6 +126,8 @@ |spring.cloud.gateway.mvc.remove-hop-by-hop-response-headers-filter.enabled | `+++true+++` | Enables the forwarded-request-headers-filter. |spring.cloud.gateway.mvc.routes | | List of Routes. |spring.cloud.gateway.mvc.routes-map | | Map of Routes. +|spring.cloud.gateway.mvc.streaming-buffer-size | `+++16384+++` | Buffer size for streaming media mime-types. +|spring.cloud.gateway.mvc.streaming-media-types | | Mime-types that are streaming. |spring.cloud.gateway.mvc.transfer-encoding-normalization-request-headers-filter.enabled | `+++true+++` | Enables the transfer-encoding-normalization-request-headers-filter. |spring.cloud.gateway.mvc.weight-calculator-filter.enabled | `+++true+++` | Enables the weight-calculator-filter. |spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.enabled | `+++true+++` | If the XForwardedHeadersFilter is enabled. From 6db4ee18015942ebc94b8b0e666969abd3ce6350 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Fri, 18 Oct 2024 10:50:44 -0400 Subject: [PATCH 2/4] Updates to*Case() to use Locale.ROOT --- .../cloud/gateway/mvc/ProxyExchange.java | 5 +++-- .../mvc/config/ProxyExchangeArgumentResolver.java | 3 ++- .../gateway/sample/GatewaySampleApplication.java | 11 ++++++----- .../gateway/server/mvc/common/HttpStatusHolder.java | 4 +++- .../mvc/config/RouterFunctionHolderFactory.java | 5 +++-- .../filter/RemoveHopByHopRequestHeadersFilter.java | 3 ++- .../gateway/server/mvc/ServerMvcIntegrationTests.java | 5 +++-- .../server/mvc/VanillaRouterFunctionTests.java | 3 ++- .../DiscoveryClientRouteDefinitionLocator.java | 3 ++- .../cloud/gateway/filter/WebsocketRoutingFilter.java | 7 ++++--- .../factory/SecureHeadersGatewayFilterFactory.java | 3 ++- .../filter/headers/RemoveHopByHopHeadersFilter.java | 3 ++- .../cloud/gateway/support/NameUtils.java | 3 ++- .../cloud/gateway/support/ServerWebExchangeUtils.java | 3 ++- .../DiscoveryClientRouteDefinitionLocatorTests.java | 4 ++-- .../ModifyRequestBodyGatewayFilterFactoryTests.java | 6 ++++-- .../headers/RemoveHopByHopHeadersFilterTests.java | 7 ++++--- .../cloud/gateway/webflux/ProxyExchange.java | 5 +++-- 18 files changed, 51 insertions(+), 32 deletions(-) diff --git a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java index 00cbec88..11eb688c 100644 --- a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java +++ b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java @@ -31,6 +31,7 @@ import java.util.Enumeration; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.Vector; import java.util.function.Function; @@ -234,7 +235,7 @@ public class ProxyExchange { this.excluded.clear(); for (String name : names) { - this.excluded.add(name.toLowerCase()); + this.excluded.add(name.toLowerCase(Locale.ROOT)); } return this; } @@ -384,7 +385,7 @@ public class ProxyExchange { private Set filterHeaderKeys(Collection headerNames) { final Set excludedHeaders = this.excluded != null ? this.excluded : Collections.emptySet(); return headerNames.stream() - .filter(header -> !excludedHeaders.contains(header.toLowerCase())) + .filter(header -> !excludedHeaders.contains(header.toLowerCase(Locale.ROOT))) .collect(Collectors.toSet()); } diff --git a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java index 9af97ab2..56335607 100644 --- a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java +++ b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java @@ -20,6 +20,7 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collections; import java.util.Enumeration; +import java.util.Locale; import java.util.Set; import jakarta.servlet.http.HttpServletRequest; @@ -101,7 +102,7 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol HttpHeaders headers = new HttpHeaders(); while (headerNames.hasMoreElements()) { String header = headerNames.nextElement(); - if (this.autoForwardedHeaders.contains(header.toLowerCase())) { + if (this.autoForwardedHeaders.contains(header.toLowerCase(Locale.ROOT))) { headers.addAll(header, Collections.list(nativeRequest.getHeaders(header))); } } diff --git a/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java b/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java index 4ebe38ec..7a7a07d5 100644 --- a/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java +++ b/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.sample; +import java.util.Locale; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -77,7 +78,7 @@ public class GatewaySampleApplication { .addResponseHeader("X-TestHeader", "rewrite_request") .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, (exchange, s) -> { - return Mono.just(new Hello(s.toUpperCase())); + return Mono.just(new Hello(s.toUpperCase(Locale.ROOT))); }) ).uri(uri) ) @@ -86,7 +87,7 @@ public class GatewaySampleApplication { .addResponseHeader("X-TestHeader", "rewrite_request_upper") .modifyRequestBody(String.class, String.class, (exchange, s) -> { - return Mono.just(s.toUpperCase() + s.toUpperCase()); + return Mono.just(s.toUpperCase(Locale.ROOT) + s.toUpperCase(Locale.ROOT)); }) ).uri(uri) ) @@ -95,7 +96,7 @@ public class GatewaySampleApplication { .addResponseHeader("X-TestHeader", "rewrite_response_upper") .modifyResponseBody(String.class, String.class, (exchange, s) -> { - return Mono.just(s.toUpperCase()); + return Mono.just(s.toUpperCase(Locale.ROOT)); }) ).uri(uri) ) @@ -107,7 +108,7 @@ public class GatewaySampleApplication { if (s == null) { return Mono.just("emptybody"); } - return Mono.just(s.toUpperCase()); + return Mono.just(s.toUpperCase(Locale.ROOT)); }) ).uri(uri) @@ -120,7 +121,7 @@ public class GatewaySampleApplication { if (s == null) { return Mono.error(new IllegalArgumentException("this should not happen")); } - return Mono.just(s.toUpperCase()); + return Mono.just(s.toUpperCase(Locale.ROOT)); }) ).uri(uri) ) diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/HttpStatusHolder.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/HttpStatusHolder.java index c19f6d47..f5740f4d 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/HttpStatusHolder.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/HttpStatusHolder.java @@ -16,6 +16,8 @@ package org.springframework.cloud.gateway.server.mvc.common; +import java.util.Locale; + import org.springframework.core.style.ToStringCreator; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; @@ -36,7 +38,7 @@ public class HttpStatusHolder { public static HttpStatusHolder valueOf(String status) { HttpStatusCode httpStatus; try { - httpStatus = HttpStatus.valueOf(status.toUpperCase()); + httpStatus = HttpStatus.valueOf(status.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { httpStatus = null; diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java index e10a06b0..687bdec8 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java @@ -22,6 +22,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; @@ -163,13 +164,13 @@ public class RouterFunctionHolderFactory { String scheme = routeProperties.getUri().getScheme(); Map handlerArgs = new HashMap<>(); Optional handlerOperationMethod = findOperation(handlerOperations, - scheme.toLowerCase(), handlerArgs); + scheme.toLowerCase(Locale.ROOT), handlerArgs); if (handlerOperationMethod.isEmpty()) { // single RouteProperties param handlerArgs.clear(); String routePropsKey = StringUtils.uncapitalize(RouteProperties.class.getSimpleName()); handlerArgs.put(routePropsKey, routeProperties); - handlerOperationMethod = findOperation(handlerOperations, scheme.toLowerCase(), handlerArgs); + handlerOperationMethod = findOperation(handlerOperations, scheme.toLowerCase(Locale.ROOT), handlerArgs); if (handlerOperationMethod.isEmpty()) { throw new IllegalStateException("Unable to find HandlerFunction for scheme: " + scheme); } diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/RemoveHopByHopRequestHeadersFilter.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/RemoveHopByHopRequestHeadersFilter.java index 44f21f90..d57c2213 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/RemoveHopByHopRequestHeadersFilter.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/RemoveHopByHopRequestHeadersFilter.java @@ -19,6 +19,7 @@ package org.springframework.cloud.gateway.server.mvc.filter; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -55,7 +56,7 @@ public class RemoveHopByHopRequestHeadersFilter implements RequestHttpHeadersFil HttpHeaders filtered = new HttpHeaders(); for (Map.Entry> entry : input.entrySet()) { - if (!headersToRemove.contains(entry.getKey().toLowerCase())) { + if (!headersToRemove.contains(entry.getKey().toLowerCase(Locale.ROOT))) { filtered.addAll(entry.getKey(), entry.getValue()); } } diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java index c43fd629..d9a7dc7b 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java @@ -24,6 +24,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -1533,12 +1534,12 @@ public class ServerMvcIntegrationTests { return route("testmodifyrequestbodystring") .POST("/post", host("**.modifyrequestbodystring.org"), http()) .before(new HttpbinUriResolver()) - .before(modifyRequestBody(String.class, String.class, null, (request, s) -> s.toUpperCase() + s.toUpperCase())) + .before(modifyRequestBody(String.class, String.class, null, (request, s) -> s.toUpperCase(Locale.ROOT) + s.toUpperCase(Locale.ROOT))) .build().and( route("testmodifyrequestbodyobject") .POST("/post", host("**.modifyrequestbodyobject.org"), http()) .before(new HttpbinUriResolver()) - .before(modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, (request, s) -> new Hello(s.toUpperCase()))) + .before(modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, (request, s) -> new Hello(s.toUpperCase(Locale.ROOT)))) .build()); // @formatter:on } diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java index 4959d897..fd96c938 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.server.mvc; +import java.util.Locale; import java.util.Map; import org.junit.jupiter.api.Test; @@ -79,7 +80,7 @@ public class VanillaRouterFunctionTests { // @formatter:off return RouterFunctions.route() .POST("/anything/routerfunctionsroute", host("**.routerfunctionsroute.org"), http()) - .before(modifyRequestBody(String.class, String.class, null, (request, s) -> s.toUpperCase())) + .before(modifyRequestBody(String.class, String.class, null, (request, s) -> s.toUpperCase(Locale.ROOT))) .before(new HttpbinUriResolver()) .build(); // @formatter:on diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java index 70050580..4554f248 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java @@ -19,6 +19,7 @@ package org.springframework.cloud.gateway.discovery; import java.net.URI; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Predicate; @@ -170,7 +171,7 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc @Override public String getServiceId() { if (properties.isLowerCaseServiceId()) { - return delegate.getServiceId().toLowerCase(); + return delegate.getServiceId().toLowerCase(Locale.ROOT); } return delegate.getServiceId(); } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilter.java index 04136bf7..04b02e22 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilter.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import org.apache.commons.logging.Log; @@ -79,7 +80,7 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered { /* for testing */ static String convertHttpToWs(String scheme) { - scheme = scheme.toLowerCase(); + scheme = scheme.toLowerCase(Locale.ROOT); return "http".equals(scheme) ? "ws" : "https".equals(scheme) ? "wss" : scheme; } @@ -143,7 +144,7 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered { headersFilters.add((headers, exchange) -> { HttpHeaders filtered = new HttpHeaders(); for (Map.Entry> entry : headers.entrySet()) { - if (!entry.getKey().toLowerCase().startsWith("sec-websocket")) { + if (!entry.getKey().toLowerCase(Locale.ROOT).startsWith("sec-websocket")) { filtered.addAll(entry.getKey(), entry.getValue()); } } @@ -157,7 +158,7 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered { static void changeSchemeIfIsWebSocketUpgrade(ServerWebExchange exchange) { // Check the Upgrade URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - String scheme = requestUrl.getScheme().toLowerCase(); + String scheme = requestUrl.getScheme().toLowerCase(Locale.ROOT); String upgrade = exchange.getRequest().getHeaders().getUpgrade(); // change the scheme if the socket client send a "http" or "https" if ("WebSocket".equalsIgnoreCase(upgrade) && ("http".equals(scheme) || "https".equals(scheme))) { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactory.java index 46d7e780..3a079968 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactory.java @@ -17,6 +17,7 @@ package org.springframework.cloud.gateway.filter.factory; import java.util.List; +import java.util.Locale; import reactor.core.publisher.Mono; @@ -136,7 +137,7 @@ public class SecureHeadersGatewayFilterFactory } private boolean isEnabled(List disabledHeaders, String header) { - return !disabledHeaders.contains(header.toLowerCase()); + return !disabledHeaders.contains(header.toLowerCase(Locale.ROOT)); } public static class Config { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilter.java index 091b7aec..2a43414b 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilter.java @@ -19,6 +19,7 @@ package org.springframework.cloud.gateway.filter.headers; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -74,7 +75,7 @@ public class RemoveHopByHopHeadersFilter implements HttpHeadersFilter, Ordered { headersToRemove.addAll(connectionOptions); for (Map.Entry> entry : originalHeaders.entrySet()) { - if (!headersToRemove.contains(entry.getKey().toLowerCase())) { + if (!headersToRemove.contains(entry.getKey().toLowerCase(Locale.ROOT))) { filtered.addAll(entry.getKey(), entry.getValue()); } } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java index fb7498cb..69e0baad 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.support; +import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -79,7 +80,7 @@ public final class NameUtils { matcher.appendReplacement(stringBuffer, matcher.group(1)); } } - return stringBuffer.toString().toLowerCase(); + return stringBuffer.toString().toLowerCase(Locale.ROOT); } private static String removeGarbage(String s) { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java index 36ee17d9..7585a182 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java @@ -20,6 +20,7 @@ import java.net.URI; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -268,7 +269,7 @@ public final class ServerWebExchangeUtils { } catch (NumberFormatException e) { // try the enum string - httpStatus = HttpStatus.valueOf(statusString.toUpperCase()); + httpStatus = HttpStatus.valueOf(statusString.toUpperCase(Locale.ROOT)); } return httpStatus; } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java index 5c1f5a66..fc26a822 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java @@ -48,9 +48,9 @@ import static org.springframework.cloud.gateway.handler.predicate.RoutePredicate "spring.cloud.gateway.discovery.locator.lower-case-service-id=true" /* * "spring.cloud.gateway.discovery.locator.predicates[0].name=Path", - * "spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]='/'+serviceId.toLowerCase()+'/**'", + * "spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]='/'+serviceId.toLowerCase(Locale.ROOT)+'/**'", * "spring.cloud.gateway.discovery.locator.filters[0].name=RewritePath", - * "spring.cloud.gateway.discovery.locator.filters[0].args[regexp]='/' + serviceId.toLowerCase() + '/(?.*)'" + * "spring.cloud.gateway.discovery.locator.filters[0].args[regexp]='/' + serviceId.toLowerCase(Locale.ROOT) + '/(?.*)'" * , * "spring.cloud.gateway.discovery.locator.filters[0].args[replacement]='/$\\\\{remaining}'", */ diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactoryTests.java index d4440c9b..2eb8b7cc 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactoryTests.java @@ -16,6 +16,8 @@ package org.springframework.cloud.gateway.filter.factory.rewrite; +import java.util.Locale; + import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; @@ -134,7 +136,7 @@ public class ModifyRequestBodyGatewayFilterFactoryTests extends BaseWebClientTes if (body == null) { return Mono.just("modifyrequest"); } - return Mono.just(body.toUpperCase()); + return Mono.just(body.toUpperCase(Locale.ROOT)); })) .uri(uri)) .route("test_modify_request_body_to_large", @@ -152,7 +154,7 @@ public class ModifyRequestBodyGatewayFilterFactoryTests extends BaseWebClientTes .filters(f -> f.modifyRequestBody(new ParameterizedTypeReference() { }, new ParameterizedTypeReference() { }, (swe, body) -> { - return Mono.just(body.replaceAll(" ", "_").toUpperCase()); + return Mono.just(body.replaceAll(" ", "_").toUpperCase(Locale.ROOT)); })) .uri(uri)) .build(); diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java index 0a5445fa..86e4c0bd 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java @@ -19,6 +19,7 @@ package org.springframework.cloud.gateway.filter.headers; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.Locale; import java.util.Set; import org.junit.jupiter.api.Test; @@ -50,7 +51,7 @@ public class RemoveHopByHopHeadersFilterTests { public void caseInsensitive() { MockServerHttpRequest.BaseBuilder builder = MockServerHttpRequest.get("http://localhost/get"); - HEADERS_REMOVED_ON_REQUEST.forEach(header -> builder.header(header.toLowerCase(), header + "1")); + HEADERS_REMOVED_ON_REQUEST.forEach(header -> builder.header(header.toLowerCase(Locale.ROOT), header + "1")); testFilter(MockServerWebExchange.from(builder)); } @@ -60,7 +61,7 @@ public class RemoveHopByHopHeadersFilterTests { MockServerHttpRequest.BaseBuilder builder = MockServerHttpRequest.get("http://localhost/get"); HEADERS_REMOVED_ON_REQUEST - .forEach(header -> builder.header(StringUtils.capitalize(header.toLowerCase()), header + "1")); + .forEach(header -> builder.header(StringUtils.capitalize(header.toLowerCase(Locale.ROOT)), header + "1")); LinkedHashSet customHeaders = new LinkedHashSet<>(); HEADERS_REMOVED_ON_REQUEST.forEach(header -> { @@ -78,7 +79,7 @@ public class RemoveHopByHopHeadersFilterTests { String arbitraryConnectionOption = "xyz"; assumeThat(HEADERS_REMOVED_ON_REQUEST).doesNotContain(arbitraryConnectionOption); - builder.header(HttpHeaders.CONNECTION, "upgrade", "keep-alive", arbitraryConnectionOption.toUpperCase()); + builder.header(HttpHeaders.CONNECTION, "upgrade", "keep-alive", arbitraryConnectionOption.toUpperCase(Locale.ROOT)); builder.header(HttpHeaders.UPGRADE, "WebSocket"); builder.header("Keep-Alive", "timeout=5"); builder.header(arbitraryConnectionOption, ""); diff --git a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/ProxyExchange.java b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/ProxyExchange.java index 67cecf50..47dfb962 100644 --- a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/ProxyExchange.java +++ b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/ProxyExchange.java @@ -22,6 +22,7 @@ import java.net.URISyntaxException; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.Locale; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -221,7 +222,7 @@ public class ProxyExchange { this.excluded.clear(); for (String name : names) { - this.excluded.add(name.toLowerCase()); + this.excluded.add(name.toLowerCase(Locale.ROOT)); } return this; } @@ -407,7 +408,7 @@ public class ProxyExchange { final Set excludedHeaders = this.excluded != null ? this.excluded : Collections.emptySet(); return headers.keySet() .stream() - .filter(header -> !excludedHeaders.contains(header.toLowerCase())) + .filter(header -> !excludedHeaders.contains(header.toLowerCase(Locale.ROOT))) .collect(Collectors.toSet()); } From 26926457c27a1138146b747f7a31d30e02b2cc9b Mon Sep 17 00:00:00 2001 From: spring-builds Date: Fri, 25 Oct 2024 13:35:48 +0000 Subject: [PATCH 3/4] Bumping versions --- .../filter/headers/RemoveHopByHopHeadersFilterTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java index 86e4c0bd..689821f6 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java @@ -79,7 +79,8 @@ public class RemoveHopByHopHeadersFilterTests { String arbitraryConnectionOption = "xyz"; assumeThat(HEADERS_REMOVED_ON_REQUEST).doesNotContain(arbitraryConnectionOption); - builder.header(HttpHeaders.CONNECTION, "upgrade", "keep-alive", arbitraryConnectionOption.toUpperCase(Locale.ROOT)); + builder.header(HttpHeaders.CONNECTION, "upgrade", "keep-alive", + arbitraryConnectionOption.toUpperCase(Locale.ROOT)); builder.header(HttpHeaders.UPGRADE, "WebSocket"); builder.header("Keep-Alive", "timeout=5"); builder.header(arbitraryConnectionOption, ""); From 57c0258b4e5d081d1b8245cb41d3957d49bcfcff Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 28 Oct 2024 14:28:09 -0400 Subject: [PATCH 4/4] Handle URL Connection file not found 404 Fixes gh-3451 --- .../mvc/handler/RestClientProxyExchange.java | 13 ++- .../server/mvc/ServerMvcIntegrationTests.java | 21 ++++- .../mvc/SimpleHttpClientIntegrationTests.java | 80 +++++++++++++++++++ 3 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/SimpleHttpClientIntegrationTests.java diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/RestClientProxyExchange.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/RestClientProxyExchange.java index 46e9d032..60018148 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/RestClientProxyExchange.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/RestClientProxyExchange.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.server.mvc.handler; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -69,9 +70,15 @@ public class RestClientProxyExchange extends AbstractProxyExchange { } private ServerResponse doExchange(Request request, ClientHttpResponse clientResponse) throws IOException { - InputStream body = clientResponse.getBody(); - // put the body input stream in a request attribute so filters can read it. - MvcUtils.putAttribute(request.getServerRequest(), MvcUtils.CLIENT_RESPONSE_INPUT_STREAM_ATTR, body); + try { + InputStream body = clientResponse.getBody(); + // put the body input stream in a request attribute so filters can read it. + MvcUtils.putAttribute(request.getServerRequest(), MvcUtils.CLIENT_RESPONSE_INPUT_STREAM_ATTR, body); + } + catch (FileNotFoundException e) { + // if using SimpleClientHttpRequestFactory + return ServerResponse.notFound().build(); + } ServerResponse serverResponse = GatewayServerResponse.status(clientResponse.getStatusCode()) .build((req, httpServletResponse) -> { try (clientResponse) { diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java index d9a7dc7b..6ceffe35 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java @@ -275,12 +275,12 @@ public class ServerMvcIntegrationTests { public void setStatusGatewayRouterFunctionWorks() { restClient.get() .uri("/status/201") + .header("Host", "www.setstatus.org") .exchange() .expectStatus() .isEqualTo(HttpStatus.TOO_MANY_REQUESTS) .expectHeader() - .valueEquals("x-status", "201"); // .expectBody(String.class).isEqualTo("Failed - // with 201"); + .valueEquals("x-status", "201"); } @Test @@ -981,6 +981,11 @@ public class ServerMvcIntegrationTests { }); } + @Test + public void notFoundWorks() { + restClient.get().uri("/status/404").header("Host", "www.notfound.org").exchange().expectStatus().isNotFound(); + } + @SpringBootConfiguration @EnableAutoConfiguration @LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class) @@ -1032,7 +1037,7 @@ public class ServerMvcIntegrationTests { public RouterFunction gatewayRouterFunctionsSetStatusAndAddRespHeader() { // @formatter:off return route("testsetstatus") - .GET("/status/{status}", http()) + .GET("/status/{status}", host("**.setstatus.org"), http()) .before(new HttpbinUriResolver()) .after(setStatus(HttpStatus.TOO_MANY_REQUESTS)) .after(addResponseHeader("X-Status", "{status}")) @@ -1600,6 +1605,16 @@ public class ServerMvcIntegrationTests { // @formatter:on } + @Bean + public RouterFunction gatewayRouterFunctions404() { + // @formatter:off + return route("testnotfound") + .GET("/status/404", host("**.notfound.org"), http()) + .before(new HttpbinUriResolver()) + .build(); + // @formatter:on + } + @Bean public FilterRegistrationBean myFilter() { FilterRegistrationBean reg = new FilterRegistrationBean<>(new MyFilter()); diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/SimpleHttpClientIntegrationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/SimpleHttpClientIntegrationTests.java new file mode 100644 index 00000000..bd42c04e --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/SimpleHttpClientIntegrationTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2013-2023 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.server.mvc; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.gateway.server.mvc.test.HttpbinTestcontainers; +import org.springframework.cloud.gateway.server.mvc.test.HttpbinUriResolver; +import org.springframework.cloud.gateway.server.mvc.test.TestLoadBalancerConfig; +import org.springframework.cloud.gateway.server.mvc.test.client.TestRestClient; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.web.servlet.function.RouterFunction; +import org.springframework.web.servlet.function.ServerResponse; + +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; + +@SuppressWarnings("unchecked") +@SpringBootTest(properties = { "spring.cloud.gateway.mvc.http-client.type=autodetect" }, + webEnvironment = WebEnvironment.RANDOM_PORT) +@ContextConfiguration(initializers = HttpbinTestcontainers.class) +public class SimpleHttpClientIntegrationTests { + + static { + // if set type to autodetect above + System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); + } + + @LocalServerPort + int port; + + @Autowired + TestRestClient restClient; + + @Test + public void simpleHttpClientNotFoundWorks() { + restClient.get().uri("/status/404").header("Host", "www.notfound.org").exchange().expectStatus().isNotFound(); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + @LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class) + protected static class TestConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctions404() { + // @formatter:off + return route("testnotfound") + .GET("/status/404", host("**.notfound.org"), http()) + .before(new HttpbinUriResolver()) + .build(); + // @formatter:on + } + + } + +}