diff --git a/pom.xml b/pom.xml index e9e3f4bd..e0fb91c2 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,7 @@ 3.0.1-SNAPSHOT 4.0.1-SNAPSHOT 1.17.3 + 1.6.21 diff --git a/spring-cloud-gateway-sample/pom.xml b/spring-cloud-gateway-sample/pom.xml index 06456cf8..5ffe37d8 100644 --- a/spring-cloud-gateway-sample/pom.xml +++ b/spring-cloud-gateway-sample/pom.xml @@ -95,6 +95,7 @@ org.jetbrains.kotlin kotlin-maven-plugin + ${kotlin-maven-plugin.version} -Xjsr305=strict diff --git a/spring-cloud-gateway-server/pom.xml b/spring-cloud-gateway-server/pom.xml index b0afd78f..f49720cc 100644 --- a/spring-cloud-gateway-server/pom.xml +++ b/spring-cloud-gateway-server/pom.xml @@ -230,6 +230,7 @@ kotlin-maven-plugin org.jetbrains.kotlin + ${kotlin-maven-plugin.version} 17 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 45c7ff72..57a041b4 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 @@ -369,14 +369,19 @@ public final class ServerWebExchangeUtils { if (log.isTraceEnabled()) { log.trace("retaining body in exchange attribute"); } - exchange.getAttributes().put(CACHED_REQUEST_BODY_ATTR, dataBuffer); + + Object cachedDataBuffer = exchange.getAttribute(CACHED_REQUEST_BODY_ATTR); + // don't cache if body is already cached + if (!(cachedDataBuffer instanceof DataBuffer)) { + exchange.getAttributes().put(CACHED_REQUEST_BODY_ATTR, dataBuffer); + } } ServerHttpRequest decorator = new ServerHttpRequestDecorator(exchange.getRequest()) { @Override public Flux getBody() { return Mono.fromSupplier(() -> { - if (exchange.getAttributeOrDefault(CACHED_REQUEST_BODY_ATTR, null) == null) { + if (exchange.getAttribute(CACHED_REQUEST_BODY_ATTR) == null) { // probably == downstream closed or no body return null; } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtilsTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtilsTests.java index d3dce34b..2531370b 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtilsTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtilsTests.java @@ -16,14 +16,17 @@ package org.springframework.cloud.gateway.support; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBuffer; +import org.springframework.http.HttpMethod; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.reactive.function.server.HandlerStrategies; @@ -70,8 +73,42 @@ public class ServerWebExchangeUtilsTests { .block(); } + @Test + public void duplicatedCachingDataBufferHandling() { + MockServerWebExchange exchange = mockExchange(HttpMethod.POST, Collections.emptyMap()); + DataBuffer dataBufferBeforeCaching = exchange.getResponse().bufferFactory() + .wrap("Cached buffer".getBytes(StandardCharsets.UTF_8)); + exchange.getAttributes().put(CACHED_REQUEST_BODY_ATTR, dataBufferBeforeCaching); + + ServerWebExchangeUtils + .cacheRequestBodyAndRequest(exchange, + (serverHttpRequest) -> ServerRequest + .create(exchange.mutate().request(serverHttpRequest).build(), + HandlerStrategies.withDefaults().messageReaders()) + .bodyToMono(DefaultDataBuffer.class)) + .block(); + + DataBuffer dataBufferAfterCached = exchange.getAttribute(CACHED_REQUEST_BODY_ATTR); + + Assertions.assertThat(dataBufferBeforeCaching).isEqualTo(dataBufferAfterCached); + } + private MockServerWebExchange mockExchange(Map vars) { - MockServerHttpRequest request = MockServerHttpRequest.get("/get").build(); + return mockExchange(HttpMethod.GET, vars); + } + + private MockServerWebExchange mockExchange(HttpMethod method, Map vars) { + + MockServerHttpRequest request = null; + if (HttpMethod.GET.equals(method)) { + request = MockServerHttpRequest.get("/get").build(); + } + else if (HttpMethod.POST.equals(method)) { + request = MockServerHttpRequest.post("/post").body("post body"); + } + + Assertions.assertThat(request).as("Method was not one of GET or POST").isNotNull(); + MockServerWebExchange exchange = MockServerWebExchange.from(request); ServerWebExchangeUtils.putUriTemplateVariables(exchange, vars); return exchange;