Fix potential memory leak when using ReadBodyRoutePredicateFactory

Fixes gh-3465
This commit is contained in:
qnnn
2024-07-20 02:02:16 +08:00
committed by spencergibb
parent 4aa7c08867
commit 3bb69ec39c
3 changed files with 26 additions and 22 deletions

View File

@@ -16,37 +16,17 @@
package org.springframework.cloud.gateway.filter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.PooledDataBuffer;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR;
public class RemoveCachedBodyFilter implements GlobalFilter, Ordered {
private static final Log log = LogFactory.getLog(RemoveCachedBodyFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).doFinally(s -> {
Object attribute = exchange.getAttributes().remove(CACHED_REQUEST_BODY_ATTR);
if (attribute != null && attribute instanceof PooledDataBuffer) {
PooledDataBuffer dataBuffer = (PooledDataBuffer) attribute;
if (dataBuffer.isAllocated()) {
if (log.isTraceEnabled()) {
log.trace("releasing cached body in exchange attribute");
}
// ensure proper release
while (!dataBuffer.release()) {
// release() counts down until zero, will never be infinite loop
}
}
}
});
return chain.filter(exchange).doFinally(s -> ServerWebExchangeUtils.clearCachedRequestBody(exchange));
}
@Override

View File

@@ -24,6 +24,7 @@ import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.config.GlobalCorsProperties;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.env.Environment;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
@@ -99,6 +100,7 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
})
.switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
ServerWebExchangeUtils.clearCachedRequestBody(exchange);
if (logger.isTraceEnabled()) {
logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");
}

View File

@@ -40,6 +40,7 @@ import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.NettyDataBuffer;
import org.springframework.core.io.buffer.PooledDataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.AbstractServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
@@ -379,6 +380,27 @@ public final class ServerWebExchangeUtils {
.flatMap(function);
}
/**
* clear the request body in a ServerWebExchange attribute. The attribute is
* {@link #CACHED_REQUEST_BODY_ATTR}.
* @param exchange the available ServerWebExchange.
*/
public static void clearCachedRequestBody(ServerWebExchange exchange) {
Object attribute = exchange.getAttributes().remove(CACHED_REQUEST_BODY_ATTR);
if (attribute != null && attribute instanceof PooledDataBuffer) {
PooledDataBuffer dataBuffer = (PooledDataBuffer) attribute;
if (dataBuffer.isAllocated()) {
if (log.isTraceEnabled()) {
log.trace("releasing cached body in exchange attribute");
}
// ensure proper release
while (!dataBuffer.release()) {
// release() counts down until zero, will never be infinite loop
}
}
}
}
private static ServerHttpRequest decorate(ServerWebExchange exchange, DataBuffer dataBuffer,
boolean cacheDecoratedRequest) {
if (dataBuffer.readableByteCount() > 0) {