From e285b841dec371908bf68bc41bc998252a3ee115 Mon Sep 17 00:00:00 2001 From: Nikita Konev Date: Tue, 1 Oct 2019 01:51:01 +0300 Subject: [PATCH] fix post request with body processed twice in retry filter https://github.com/spring-cloud/spring-cloud-gateway/issues/1315 --- .../filter/AdaptCachedBodyGlobalFilter.java | 15 ++++++++--- ...yGatewayFilterFactoryIntegrationTests.java | 27 +++++++++++++++++++ .../application-only-prefix-filter.yml | 5 ++++ 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 spring-cloud-gateway-core/src/test/resources/application-only-prefix-filter.yml diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/AdaptCachedBodyGlobalFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/AdaptCachedBodyGlobalFilter.java index f505f406..1ed0da77 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/AdaptCachedBodyGlobalFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/AdaptCachedBodyGlobalFilter.java @@ -70,11 +70,18 @@ public class AdaptCachedBodyGlobalFilter return chain.filter(exchange); } + final Mono requestProcessingFinishedFlag = Mono + .just("requestProcessingFinishedFlag"); return ServerWebExchangeUtils - .cacheRequestBody(exchange, - (serverHttpRequest) -> chain.filter( - exchange.mutate().request(serverHttpRequest).build())) - .switchIfEmpty(chain.filter(exchange)); + .cacheRequestBody(exchange, (serverHttpRequest) -> chain + .filter(exchange.mutate().request(serverHttpRequest).build()) + // suppress empty mono response from FilteringWebHandler, see + // https://github.com/spring-cloud/spring-cloud-gateway/issues/1315 + .then(requestProcessingFinishedFlag)) + // when request body is empty - we return from cacheRequestBody() with + // empty mono - so we will process that request + .switchIfEmpty(chain.filter(exchange).then(requestProcessingFinishedFlag)) + .then(); } @Override diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java index aa084f2f..0fbd0add 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java @@ -27,6 +27,8 @@ import com.netflix.loadbalancer.ServerList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,6 +36,7 @@ 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.boot.test.rule.OutputCapture; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory.RetryConfig; @@ -49,6 +52,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -63,8 +67,20 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen "spring.cloud.gateway.httpclient.connect-timeout=500", "spring.cloud.gateway.httpclient.response-timeout=2s" }) @DirtiesContext +// default filter AddResponseHeader suppresses bug +// https://github.com/spring-cloud/spring-cloud-gateway/issues/1315, +// so we use only PrefixPath filter +@ActiveProfiles("only-prefix-filter") public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTests { + @Rule + public final OutputCapture capture = new OutputCapture(); + + @Before + public void before() { + capture.reset(); + } + @Test public void retryFilterGet() { testClient.get().uri("/retry?key=get").exchange().expectStatus().isOk() @@ -115,6 +131,17 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest .exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("3"); } + @Test + public void retryFilterPostOneTime() { + testClient.post().uri( + "/retrypost?key=retryFilterPostOneTime&expectedbody=HelloGateway&count=1") + .header(HttpHeaders.HOST, "www.retrypostconfig.org") + .syncBody("HelloGateway").exchange().expectStatus().isOk(); + assertThat(this.capture.toString()).contains("setting new iteration in attr 0"); + assertThat(this.capture.toString()) + .doesNotContain("setting new iteration in attr 1"); + } + @Test public void retriesSleepyRequest() throws Exception { testClient.mutate().responseTimeout(Duration.ofSeconds(10)).build().get() diff --git a/spring-cloud-gateway-core/src/test/resources/application-only-prefix-filter.yml b/spring-cloud-gateway-core/src/test/resources/application-only-prefix-filter.yml new file mode 100644 index 00000000..70790b9e --- /dev/null +++ b/spring-cloud-gateway-core/src/test/resources/application-only-prefix-filter.yml @@ -0,0 +1,5 @@ +spring: + cloud: + gateway: + default-filters: + - PrefixPath=/httpbin