Merge branch '2.1.x'
This commit is contained in:
@@ -45,7 +45,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint;
|
||||
import org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.AlwaysRetainBodyGlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.ForwardPathFilter;
|
||||
import org.springframework.cloud.gateway.filter.ForwardRoutingFilter;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
@@ -126,7 +125,6 @@ import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
@@ -265,11 +263,6 @@ public class GatewayAutoConfiguration {
|
||||
return new AdaptCachedBodyGlobalFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AlwaysRetainBodyGlobalFilter alwaysRetainBodyGlobalFilter() {
|
||||
return new AlwaysRetainBodyGlobalFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RemoveCachedBodyFilter removeCachedBodyFilter() {
|
||||
return new RemoveCachedBodyFilter();
|
||||
@@ -405,9 +398,8 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory(
|
||||
ServerCodecConfigurer codecConfigurer) {
|
||||
return new ModifyRequestBodyGatewayFilterFactory(codecConfigurer);
|
||||
public ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory() {
|
||||
return new ModifyRequestBodyGatewayFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -416,9 +408,8 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory(
|
||||
ServerCodecConfigurer codecConfigurer) {
|
||||
return new ModifyResponseBodyGatewayFilterFactory(codecConfigurer);
|
||||
public ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory() {
|
||||
return new ModifyResponseBodyGatewayFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -16,39 +16,65 @@
|
||||
|
||||
package org.springframework.cloud.gateway.filter;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.gateway.event.EnableBodyCachingEvent;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
public class AdaptCachedBodyGlobalFilter implements GlobalFilter, Ordered {
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
|
||||
public class AdaptCachedBodyGlobalFilter
|
||||
implements GlobalFilter, Ordered, ApplicationListener<EnableBodyCachingEvent> {
|
||||
|
||||
private ConcurrentMap<String, Boolean> routesToCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Cached request body key.
|
||||
*/
|
||||
public static final String CACHED_REQUEST_BODY_KEY = "cachedRequestBody";
|
||||
@Deprecated
|
||||
public static final String CACHED_REQUEST_BODY_KEY = CACHED_REQUEST_BODY_ATTR;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EnableBodyCachingEvent event) {
|
||||
this.routesToCache.putIfAbsent(event.getRouteId(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
|
||||
Flux<DataBuffer> body = exchange.getAttributeOrDefault(CACHED_REQUEST_BODY_KEY,
|
||||
null);
|
||||
if (body != null) {
|
||||
ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(
|
||||
exchange.getRequest()) {
|
||||
@Override
|
||||
public Flux<DataBuffer> getBody() {
|
||||
return body;
|
||||
}
|
||||
};
|
||||
exchange.getAttributes().remove(CACHED_REQUEST_BODY_KEY);
|
||||
return chain.filter(exchange.mutate().request(decorator).build());
|
||||
// the cached ServerHttpRequest is used when the ServerWebExchange can not be
|
||||
// mutated, for example, during a predicate where the body is read, but still
|
||||
// needs to be cached.
|
||||
ServerHttpRequest cachedRequest = exchange
|
||||
.getAttributeOrDefault(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR, null);
|
||||
if (cachedRequest != null) {
|
||||
exchange.getAttributes().remove(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR);
|
||||
return chain.filter(exchange.mutate().request(cachedRequest).build());
|
||||
}
|
||||
|
||||
return chain.filter(exchange);
|
||||
//
|
||||
DataBuffer body = exchange.getAttributeOrDefault(CACHED_REQUEST_BODY_ATTR, null);
|
||||
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||
|
||||
if (body != null || !this.routesToCache.containsKey(route.getId())) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
return ServerWebExchangeUtils
|
||||
.cacheRequestBody(exchange,
|
||||
(serverHttpRequest) -> chain.filter(
|
||||
exchange.mutate().request(serverHttpRequest).build()))
|
||||
.switchIfEmpty(chain.filter(exchange));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.filter;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.gateway.event.EnableBodyCachingEvent;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.NettyDataBuffer;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
|
||||
public class AlwaysRetainBodyGlobalFilter
|
||||
implements GlobalFilter, Ordered, ApplicationListener<EnableBodyCachingEvent> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(AlwaysRetainBodyGlobalFilter.class);
|
||||
|
||||
private ConcurrentMap<String, Boolean> routesToCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Request body cache key.
|
||||
*/
|
||||
public static final String ALWAYS_CACHE_REQUEST_BODY_KEY = "alwaysCacheRequestBody";
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EnableBodyCachingEvent event) {
|
||||
this.routesToCache.putIfAbsent(event.getRouteId(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
Object body = exchange.getAttributeOrDefault(ALWAYS_CACHE_REQUEST_BODY_KEY, null);
|
||||
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||
|
||||
if (body != null || !this.routesToCache.containsKey(route.getId())) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
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(ALWAYS_CACHE_REQUEST_BODY_KEY,
|
||||
dataBuffer);
|
||||
}
|
||||
|
||||
ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(
|
||||
exchange.getRequest()) {
|
||||
@Override
|
||||
public Flux<DataBuffer> getBody() {
|
||||
return Mono.<DataBuffer>fromSupplier(() -> {
|
||||
if (exchange.getAttributeOrDefault(
|
||||
ALWAYS_CACHE_REQUEST_BODY_KEY, null) == null) {
|
||||
// probably == downstream closed
|
||||
return null;
|
||||
}
|
||||
// TODO: deal with Netty
|
||||
NettyDataBuffer pdb = (NettyDataBuffer) dataBuffer;
|
||||
return pdb.factory()
|
||||
.wrap(pdb.getNativeBuffer().retainedSlice());
|
||||
}).flux();
|
||||
}
|
||||
};
|
||||
return chain.filter(exchange.mutate().request(decorator).build());
|
||||
}).switchIfEmpty(chain.filter(exchange));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return -10;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.buffer.PooledDataBuffer;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.springframework.cloud.gateway.filter.AlwaysRetainBodyGlobalFilter.ALWAYS_CACHE_REQUEST_BODY_KEY;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR;
|
||||
|
||||
public class RemoveCachedBodyFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@@ -33,13 +33,13 @@ public class RemoveCachedBodyFilter implements GlobalFilter, Ordered {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return chain.filter(exchange).doFinally(s -> {
|
||||
PooledDataBuffer b = (PooledDataBuffer) exchange.getAttributes()
|
||||
.remove(ALWAYS_CACHE_REQUEST_BODY_KEY);
|
||||
if (b != null && b.isAllocated()) {
|
||||
PooledDataBuffer dataBuffer = (PooledDataBuffer) exchange.getAttributes()
|
||||
.remove(CACHED_REQUEST_BODY_ATTR);
|
||||
if (dataBuffer != null && dataBuffer.isAllocated()) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("releasing cached body in exchange attribute");
|
||||
}
|
||||
b.release();
|
||||
dataBuffer.release();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,21 +22,15 @@ import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.gateway.handler.AsyncPredicate;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
||||
import org.springframework.web.reactive.function.server.HandlerStrategies;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter.CACHED_REQUEST_BODY_KEY;
|
||||
|
||||
/**
|
||||
* Predicate that reads the body and applies a user provided predicate to run on the body.
|
||||
* The body is cached in memory so that possible subsequent calls to the predicate do not
|
||||
@@ -45,7 +39,7 @@ import static org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilt
|
||||
public class ReadBodyPredicateFactory
|
||||
extends AbstractRoutePredicateFactory<ReadBodyPredicateFactory.Config> {
|
||||
|
||||
protected static final Log LOGGER = LogFactory.getLog(ReadBodyPredicateFactory.class);
|
||||
protected static final Log log = LogFactory.getLog(ReadBodyPredicateFactory.class);
|
||||
|
||||
private static final String TEST_ATTRIBUTE = "read_body_predicate_test_attribute";
|
||||
|
||||
@@ -78,46 +72,23 @@ public class ReadBodyPredicateFactory
|
||||
return Mono.just(test);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Predicate test failed because class in predicate "
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Predicate test failed because class in predicate "
|
||||
+ "does not match the cached body object", e);
|
||||
}
|
||||
}
|
||||
return Mono.just(false);
|
||||
}
|
||||
else {
|
||||
// Join all the DataBuffers so we have a single DataBuffer for the body
|
||||
return DataBufferUtils.join(exchange.getRequest().getBody())
|
||||
.flatMap(dataBuffer -> {
|
||||
byte[] bytes = new byte[dataBuffer.readableByteCount()];
|
||||
dataBuffer.read(bytes);
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
|
||||
DataBuffer buffer = exchange.getResponse().bufferFactory()
|
||||
.wrap(bytes);
|
||||
return Mono.just(buffer);
|
||||
});
|
||||
|
||||
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
|
||||
exchange.getRequest()) {
|
||||
@Override
|
||||
public Flux<DataBuffer> getBody() {
|
||||
return cachedFlux;
|
||||
}
|
||||
};
|
||||
return ServerRequest
|
||||
.create(exchange.mutate().request(mutatedRequest)
|
||||
.build(), messageReaders)
|
||||
.bodyToMono(inClass).doOnNext(objectValue -> {
|
||||
exchange.getAttributes().put(
|
||||
CACHE_REQUEST_BODY_OBJECT_KEY,
|
||||
objectValue);
|
||||
exchange.getAttributes()
|
||||
.put(CACHED_REQUEST_BODY_KEY, cachedFlux);
|
||||
}).map(objectValue -> config.predicate
|
||||
.test(objectValue));
|
||||
});
|
||||
|
||||
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
|
||||
(serverHttpRequest) -> ServerRequest
|
||||
.create(exchange.mutate().request(serverHttpRequest)
|
||||
.build(), messageReaders)
|
||||
.bodyToMono(inClass)
|
||||
.doOnNext(objectValue -> exchange.getAttributes()
|
||||
.put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
|
||||
.map(objectValue -> config.getPredicate()
|
||||
.test(objectValue)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,15 +20,24 @@ import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.AsyncPredicate;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.NettyDataBuffer;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.AbstractServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
@@ -38,6 +47,8 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
*/
|
||||
public final class ServerWebExchangeUtils {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ServerWebExchangeUtils.class);
|
||||
|
||||
/**
|
||||
* Preserve-Host header attribute name.
|
||||
*/
|
||||
@@ -130,6 +141,18 @@ public final class ServerWebExchangeUtils {
|
||||
public static final String GATEWAY_ALREADY_PREFIXED_ATTR = qualify(
|
||||
"gatewayAlreadyPrefixed");
|
||||
|
||||
/**
|
||||
* Cached ServerHttpRequestDecorator attribute name. Used when
|
||||
* {@link #cacheRequestBodyAndRequest(ServerWebExchange, Function)} is called.
|
||||
*/
|
||||
public static final String CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR = "cachedServerHttpRequestDecorator";
|
||||
|
||||
/**
|
||||
* Cached request body key. Used when {@link #cacheRequestBodyAndRequest(ServerWebExchange, Function)}
|
||||
* or {@link #cacheRequestBody(ServerWebExchange, Function)} are called.
|
||||
*/
|
||||
public static final String CACHED_REQUEST_BODY_ATTR = "cachedRequestBody";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ServerWebExchangeUtils.class);
|
||||
|
||||
private ServerWebExchangeUtils() {
|
||||
@@ -247,4 +270,84 @@ public final class ServerWebExchangeUtils {
|
||||
new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the request body and the created {@link ServerHttpRequestDecorator} in
|
||||
* ServerWebExchange attributes. Those attributes are {@link #CACHED_REQUEST_BODY_ATTR}
|
||||
* and {@link #CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR} respectively. This method
|
||||
* is useful when the {@link ServerWebExchange} can not be modified, such as a
|
||||
* {@link RoutePredicateFactory}.
|
||||
* @param exchange the available ServerWebExchange.
|
||||
* @param function a function that accepts the created ServerHttpRequestDecorator.
|
||||
* @param <T> generic type for the return {@link Mono}.
|
||||
* @return Mono of type T created by the function parameter.
|
||||
*/
|
||||
public static <T> Mono<T> cacheRequestBodyAndRequest(ServerWebExchange exchange,
|
||||
Function<ServerHttpRequest, Mono<T>> function) {
|
||||
return cacheRequestBody(exchange, true, function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the request body in a ServerWebExchange attributes. The attribute is
|
||||
* {@link #CACHED_REQUEST_BODY_ATTR}. This method is useful when the
|
||||
* {@link ServerWebExchange} can be mutated, such as a {@link GatewayFilterFactory}/
|
||||
* @param exchange the available ServerWebExchange.
|
||||
* @param function a function that accepts the created ServerHttpRequestDecorator.
|
||||
* @param <T> generic type for the return {@link Mono}.
|
||||
* @return Mono of type T created by the function parameter.
|
||||
*/
|
||||
public static <T> Mono<T> cacheRequestBody(ServerWebExchange exchange,
|
||||
Function<ServerHttpRequest, Mono<T>> function) {
|
||||
return cacheRequestBody(exchange, false, function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the request body in a ServerWebExchange attribute. The attribute is
|
||||
* {@link #CACHED_REQUEST_BODY_ATTR}. If this method is called from a location
|
||||
* that can not mutate the ServerWebExchange (such as a Predicate), setting
|
||||
* cacheDecoratedRequest to true will put a {@link ServerHttpRequestDecorator} in
|
||||
* an attribute {@link #CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR} for adaptation
|
||||
* later.
|
||||
* @param exchange the available ServerWebExchange.
|
||||
* @param cacheDecoratedRequest if true, the ServerHttpRequestDecorator will be cached.
|
||||
* @param function a function that accepts the created ServerHttpRequestDecorator.
|
||||
* @param <T> generic type for the return {@link Mono}.
|
||||
* @return Mono of type T created by the function parameter.
|
||||
*/
|
||||
private static <T> Mono<T> cacheRequestBody(ServerWebExchange exchange,
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
if (cacheDecoratedRequest) {
|
||||
exchange.getAttributes().put(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR,
|
||||
decorator);
|
||||
}
|
||||
return function.apply(decorator);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,36 +130,36 @@ public class ReadBodyPredicateFactoryTest {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
static class Event {
|
||||
|
||||
class Event {
|
||||
private String foo;
|
||||
|
||||
private String foo;
|
||||
private String bar;
|
||||
|
||||
private String bar;
|
||||
Event() {
|
||||
}
|
||||
|
||||
Event() {
|
||||
}
|
||||
Event(String foo, String bar) {
|
||||
this.foo = foo;
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
Event(String foo, String bar) {
|
||||
this.foo = foo;
|
||||
this.bar = bar;
|
||||
}
|
||||
public String getFoo() {
|
||||
return foo;
|
||||
}
|
||||
|
||||
public String getFoo() {
|
||||
return foo;
|
||||
}
|
||||
public void setFoo(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
|
||||
public void setFoo(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
public String getBar() {
|
||||
return bar;
|
||||
}
|
||||
|
||||
public String getBar() {
|
||||
return bar;
|
||||
}
|
||||
public void setBar(String bar) {
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
public void setBar(String bar) {
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user