From 3d2be39670a6f8bbc81684fd26acf736aa4a10ef Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Tue, 3 Mar 2020 14:03:04 -0500 Subject: [PATCH] Adds support for spring.codec.* in modify request body filter. Also adds test for support added in modify response. Fixes gh-1596 --- .../config/GatewayAutoConfiguration.java | 9 ++--- ...ModifyRequestBodyGatewayFilterFactory.java | 8 ++++- ...odifyResponseBodyGatewayFilterFactory.java | 33 +++++++---------- ...yRequestBodyGatewayFilterFactoryTests.java | 23 +++++++++++- ...ResponseBodyGatewayFilterFactoryTests.java | 35 ++++++++++++++++++- ...onseBodyGatewayFilterFactoryUnitTests.java | 3 +- 6 files changed, 83 insertions(+), 28 deletions(-) diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index e0bda2e8..5ba878ee 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -434,8 +434,9 @@ public class GatewayAutoConfiguration { } @Bean - public ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory() { - return new ModifyRequestBodyGatewayFilterFactory(); + public ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory( + ServerCodecConfigurer codecConfigurer) { + return new ModifyRequestBodyGatewayFilterFactory(codecConfigurer.getReaders()); } @Bean @@ -447,8 +448,8 @@ public class GatewayAutoConfiguration { public ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory( ServerCodecConfigurer codecConfigurer, Set bodyDecoders, Set bodyEncoders) { - return new ModifyResponseBodyGatewayFilterFactory(codecConfigurer, bodyDecoders, - bodyEncoders); + return new ModifyResponseBodyGatewayFilterFactory(codecConfigurer.getReaders(), + bodyDecoders, bodyEncoders); } @Bean diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactory.java index 128887b3..3c4b2dfc 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactory.java @@ -53,9 +53,15 @@ public class ModifyRequestBodyGatewayFilterFactory extends this.messageReaders = HandlerStrategies.withDefaults().messageReaders(); } + public ModifyRequestBodyGatewayFilterFactory( + List> messageReaders) { + super(Config.class); + this.messageReaders = messageReaders; + } + @Deprecated public ModifyRequestBodyGatewayFilterFactory(ServerCodecConfigurer codecConfigurer) { - this(); + this(codecConfigurer.getReaders()); } @Override diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java index 3352e234..2830c0e1 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java @@ -41,6 +41,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; import org.springframework.http.client.reactive.ClientHttpResponse; +import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponseDecorator; @@ -49,6 +50,7 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.server.HandlerStrategies; import org.springframework.web.server.ServerWebExchange; import static java.util.function.Function.identity; @@ -61,17 +63,16 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.O public class ModifyResponseBodyGatewayFilterFactory extends AbstractGatewayFilterFactory { - @Nullable - private final ServerCodecConfigurer codecConfigurer; - private final Map messageBodyDecoders; private final Map messageBodyEncoders; + private final List> messageReaders; + @Deprecated public ModifyResponseBodyGatewayFilterFactory() { super(Config.class); - this.codecConfigurer = null; + messageReaders = HandlerStrategies.withDefaults().messageReaders(); messageBodyDecoders = Collections.emptyMap(); messageBodyEncoders = Collections.emptyMap(); } @@ -79,16 +80,17 @@ public class ModifyResponseBodyGatewayFilterFactory extends @Deprecated public ModifyResponseBodyGatewayFilterFactory(ServerCodecConfigurer codecConfigurer) { super(Config.class); - this.codecConfigurer = codecConfigurer; + this.messageReaders = codecConfigurer.getReaders(); messageBodyDecoders = Collections.emptyMap(); messageBodyEncoders = Collections.emptyMap(); } - public ModifyResponseBodyGatewayFilterFactory(ServerCodecConfigurer codecConfigurer, + public ModifyResponseBodyGatewayFilterFactory( + List> messageReaders, Set messageBodyDecoders, Set messageBodyEncoders) { super(Config.class); - this.codecConfigurer = codecConfigurer; + this.messageReaders = messageReaders; this.messageBodyDecoders = messageBodyDecoders.stream() .collect(Collectors.toMap(MessageBodyDecoder::encodingType, identity())); this.messageBodyEncoders = messageBodyEncoders.stream() @@ -98,7 +100,7 @@ public class ModifyResponseBodyGatewayFilterFactory extends @Override public GatewayFilter apply(Config config) { ModifyResponseGatewayFilter gatewayFilter = new ModifyResponseGatewayFilter( - config, codecConfigurer); + config); gatewayFilter.setFactory(this); return gatewayFilter; } @@ -185,20 +187,16 @@ public class ModifyResponseBodyGatewayFilterFactory extends private final Config config; - @Nullable - private final ServerCodecConfigurer codecConfigurer; - private GatewayFilterFactory gatewayFilterFactory; - @Deprecated public ModifyResponseGatewayFilter(Config config) { this(config, null); } + @Deprecated public ModifyResponseGatewayFilter(Config config, @Nullable ServerCodecConfigurer codecConfigurer) { this.config = config; - this.codecConfigurer = codecConfigurer; } @Override @@ -299,13 +297,8 @@ public class ModifyResponseBodyGatewayFilterFactory extends private ClientResponse prepareClientResponse(Publisher body, HttpHeaders httpHeaders) { ClientResponse.Builder builder; - if (codecConfigurer != null) { - builder = ClientResponse.create(exchange.getResponse().getStatusCode(), - codecConfigurer.getReaders()); - } - else { - builder = ClientResponse.create(exchange.getResponse().getStatusCode()); - } + builder = ClientResponse.create(exchange.getResponse().getStatusCode(), + messageReaders); return builder.headers(headers -> headers.putAll(httpHeaders)) .body(Flux.from(body)).build(); } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactoryTests.java index 230dc692..3010e5dc 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactoryTests.java @@ -42,7 +42,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen * @author Junghoon Song */ @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = RANDOM_PORT) +@SpringBootTest(webEnvironment = RANDOM_PORT, + properties = "spring.codec.max-in-memory-size=13") @DirtiesContext public class ModifyRequestBodyGatewayFilterFactoryTests extends BaseWebClientTests { @@ -66,6 +67,17 @@ public class ModifyRequestBodyGatewayFilterFactoryTests extends BaseWebClientTes .isEqualTo("modifyrequest"); } + @Test + public void modifyRequestBodyToLarge() { + testClient.post().uri("/post") + .header("Host", "www.modifyrequestbodyemptytolarge.org") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) + .body(BodyInserters.fromValue("request")).exchange().expectStatus() + .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectBody() + .jsonPath("message") + .isEqualTo("Exceeded limit on max bytes to buffer : 13"); + } + @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) @@ -95,6 +107,15 @@ public class ModifyRequestBodyGatewayFilterFactoryTests extends BaseWebClientTes return Mono.just(body.toUpperCase()); })) .uri(uri)) + .route("test_modify_request_body_to_large", r -> r.order(-1) + .host("**.modifyrequestbodyemptytolarge.org") + .filters(f -> f.modifyRequestBody(String.class, String.class, + MediaType.APPLICATION_JSON_VALUE, + (serverWebExchange, body) -> { + return Mono.just( + "tolarge-tolarge-tolarge-tolarge-tolarge-tolarge-tolarge-tolarge-tolarge-tolarge-tolarge-tolarge-tolarge-tolarge-tolarge-tolarge"); + })) + .uri(uri)) .build(); } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactoryTests.java index dc668c67..5c1d351f 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactoryTests.java @@ -33,18 +33,32 @@ import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.cloud.gateway.test.BaseWebClientTests; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.util.UriComponentsBuilder; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = RANDOM_PORT) +@SpringBootTest(webEnvironment = RANDOM_PORT, + properties = "spring.codec.max-in-memory-size=40") @DirtiesContext public class ModifyResponseBodyGatewayFilterFactoryTests extends BaseWebClientTests { + private static final String toLarge; + + static { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + sb.append("to-large-"); + } + toLarge = sb.toString(); + } + @Test public void testModificationOfResponseBody() { URI uri = UriComponentsBuilder.fromUriString(this.baseUri + "/").build(true) @@ -55,6 +69,17 @@ public class ModifyResponseBodyGatewayFilterFactoryTests extends BaseWebClientTe .json("{\"value\": \"httpbin compatible home\", \"length\": 23}"); } + @Test + public void modifyResponeBodyToLarge() { + testClient.post().uri("/post") + .header("Host", "www.modifyresponsebodyjavatoolarge.org") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .body(BodyInserters.fromValue(toLarge)).exchange().expectStatus() + .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectBody() + .jsonPath("message") + .isEqualTo("Exceeded limit on max bytes to buffer : 40"); + } + @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) @@ -77,6 +102,14 @@ public class ModifyResponseBodyGatewayFilterFactoryTests extends BaseWebClientTe return Mono.just(modifiedResponse); })) .uri(uri)) + .route("modify_response_java_test_to_large", + r -> r.path("/").and().host("www.modifyresponsebodyjavatoolarge.org") + .filters(f -> f.prefixPath("/httpbin").modifyResponseBody( + String.class, String.class, + (webExchange, originalResponse) -> { + return Mono.just(toLarge); + })) + .uri(uri)) .build(); } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactoryUnitTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactoryUnitTests.java index 2ccfbe7b..4f191602 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactoryUnitTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactoryUnitTests.java @@ -34,7 +34,8 @@ public class ModifyResponseBodyGatewayFilterFactoryUnitTests { config.setOutClass(Integer.class); config.setNewContentType("mycontenttype"); GatewayFilter filter = new ModifyResponseBodyGatewayFilterFactory( - new DefaultServerCodecConfigurer(), emptySet(), emptySet()).apply(config); + new DefaultServerCodecConfigurer().getReaders(), emptySet(), emptySet()) + .apply(config); assertThat(filter.toString()).contains("String").contains("Integer") .contains("mycontenttype"); }