Updates cacheRequestBody() to handle the empty body case.

This allows the AdaptCahcedBodyGlobalFilter to not worry about handling empty.

Fixes gh-1315
This commit is contained in:
Spencer Gibb
2019-10-01 16:05:56 -04:00
parent e285b841de
commit 856a2417b6
6 changed files with 72 additions and 70 deletions

View File

@@ -70,18 +70,13 @@ public class AdaptCachedBodyGlobalFilter
return chain.filter(exchange);
}
final Mono<String> requestProcessingFinishedFlag = Mono
.just("requestProcessingFinishedFlag");
return ServerWebExchangeUtils
.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();
return ServerWebExchangeUtils.cacheRequestBody(exchange, (serverHttpRequest) -> {
// don't mutate and build if same request object
if (serverHttpRequest == exchange.getRequest()) {
return chain.filter(exchange);
}
return chain.filter(exchange.mutate().request(serverHttpRequest).build());
});
}
@Override

View File

@@ -322,7 +322,8 @@ public final class ServerWebExchangeUtils {
* @param exchange the available ServerWebExchange.
* @param cacheDecoratedRequest if true, the ServerHttpRequestDecorator will be
* cached.
* @param function a function that accepts the created ServerHttpRequestDecorator.
* @param function a function that accepts a ServerHttpRequest. It can be the created
* ServerHttpRequestDecorator or the originial if there is no body.
* @param <T> generic type for the return {@link Mono}.
* @return Mono of type T created by the function parameter.
*/
@@ -330,39 +331,37 @@ public final class ServerWebExchangeUtils {
boolean cacheDecoratedRequest,
Function<ServerHttpRequest, Mono<T>> function) {
// Join all the DataBuffers so we have a single DataBuffer for the body
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
if (dataBuffer.readableByteCount() > 0) {
if (log.isTraceEnabled()) {
log.trace("retaining body in exchange attribute");
}
exchange.getAttributes().put(CACHED_REQUEST_BODY_ATTR,
dataBuffer);
}
return DataBufferUtils.join(exchange.getRequest().getBody()).map(dataBuffer -> {
if (dataBuffer.readableByteCount() > 0) {
if (log.isTraceEnabled()) {
log.trace("retaining body in exchange attribute");
}
exchange.getAttributes().put(CACHED_REQUEST_BODY_ATTR, dataBuffer);
}
ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(
exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return Mono.<DataBuffer>fromSupplier(() -> {
if (exchange.getAttributeOrDefault(
CACHED_REQUEST_BODY_ATTR, null) == null) {
// probably == downstream closed
return null;
}
// TODO: deal with Netty
NettyDataBuffer pdb = (NettyDataBuffer) dataBuffer;
return pdb.factory()
.wrap(pdb.getNativeBuffer().retainedSlice());
}).flux();
ServerHttpRequest decorator = new ServerHttpRequestDecorator(
exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return Mono.<DataBuffer>fromSupplier(() -> {
if (exchange.getAttributeOrDefault(CACHED_REQUEST_BODY_ATTR,
null) == null) {
// probably == downstream closed
return null;
}
};
if (cacheDecoratedRequest) {
exchange.getAttributes().put(
CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR, decorator);
}
return function.apply(decorator);
});
// TODO: deal with Netty
NettyDataBuffer pdb = (NettyDataBuffer) dataBuffer;
return pdb.factory().wrap(pdb.getNativeBuffer().retainedSlice());
}).flux();
}
};
if (cacheDecoratedRequest) {
exchange.getAttributes().put(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR,
decorator);
}
return decorator;
// return function.apply(decorator)/*.then(monoVoid())*/;
}).switchIfEmpty(Mono.just(exchange.getRequest())).flatMap(function);
}
}

View File

@@ -70,7 +70,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
// 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")
@ActiveProfiles("retrytests")
public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTests {
@Rule
@@ -135,7 +135,7 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest
public void retryFilterPostOneTime() {
testClient.post().uri(
"/retrypost?key=retryFilterPostOneTime&expectedbody=HelloGateway&count=1")
.header(HttpHeaders.HOST, "www.retrypostconfig.org")
.header(HttpHeaders.HOST, "www.retrypostonceconfig.org")
.syncBody("HelloGateway").exchange().expectStatus().isOk();
assertThat(this.capture.toString()).contains("setting new iteration in attr 0");
assertThat(this.capture.toString())
@@ -220,7 +220,7 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest
}
@RequestMapping("/httpbin/retrypost")
public ResponseEntity<String> retry(@RequestParam("key") String key,
public ResponseEntity<String> retrypost(@RequestParam("key") String key,
@RequestParam(name = "count", defaultValue = "3") int count,
@RequestParam("expectedbody") String expectedbody,
@RequestBody String body) {

View File

@@ -1,5 +0,0 @@
spring:
cloud:
gateway:
default-filters:
- PrefixPath=/httpbin

View File

@@ -0,0 +1,31 @@
spring:
cloud:
gateway:
default-filters:
- PrefixPath=/httpbin
routes:
# =====================================
- id: retry_test
uri: ${test.uri}
predicates:
- Path=/retry
filters:
- Retry
# =====================================
- id: retry_post_once_test
uri: ${test.uri}
predicates:
- Host=**.retrypostonceconfig.org
filters:
- name: Retry
args:
methods: GET,POST
# =====================================
- id: retry_post_test
uri: ${test.uri}
predicates:
- Host=**.retrypostconfig.org
filters:
- name: Retry
args:
methods: GET,POST

View File

@@ -253,24 +253,6 @@ spring:
- AddResponseHeader=X-Request-Foo, Bar
- RemoveResponseHeader=X-Request-Foo
# =====================================
- id: retry_test
uri: ${test.uri}
predicates:
- Path=/retry
filters:
- Retry
# =====================================
- id: retry_post_test
uri: ${test.uri}
predicates:
- Host=**.retrypostconfig.org
filters:
- name: Retry
args:
methods: GET,POST
# =====================================
- id: secure_headers_test
uri: ${test.uri}