This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 26a700a8a3 fix: enforce request body size limit during buffering in
AiProxyPlugin (#6897)
26a700a8a3 is described below
commit 26a700a8a34a5babd993df1a65a6f0b22c0ee34b
Author: wy471x <[email protected]>
AuthorDate: Sun Aug 16 21:20:54 2026 +0800
fix: enforce request body size limit during buffering in AiProxyPlugin
(#6897)
* fix: enforce request body size limit during buffering in AiProxyPlugin
Use DataBufferUtils.join with maxSize to prevent OOM from large request
bodies. Previously the full body was buffered into memory before the
size check ran, allowing a multi-GB payload to exhaust heap. Now
DataBufferLimitException is raised during buffering and returned as 413.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
* test: add unit test for DataBufferLimitException → 413 in AiProxyPlugin
Co-Authored-By: Claude Opus 4.7 <[email protected]>
---------
Co-authored-by: Claude Opus 4.7 <[email protected]>
Co-authored-by: aias00 <[email protected]>
---
.../plugin/ai/proxy/enhanced/AiProxyPlugin.java | 21 +++++++++------------
.../plugin/ai/proxy/enhanced/AiProxyPluginTest.java | 20 ++++++++++++++++++++
2 files changed, 29 insertions(+), 12 deletions(-)
diff --git
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPlugin.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPlugin.java
index 687c51233f..cd5dcb3d2d 100644
---
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPlugin.java
@@ -41,6 +41,7 @@ import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferLimitException;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -63,7 +64,7 @@ public class AiProxyPlugin extends AbstractShenyuPlugin {
/**
* Maximum request body size: 5MB.
*/
- private static final long MAX_REQUEST_BODY_SIZE_BYTES = 5 * 1024 * 1024L;
+ private static final int MAX_REQUEST_BODY_SIZE_BYTES = 5 * 1024 * 1024;
private final AiModelFactoryRegistry aiModelFactoryRegistry;
@@ -100,18 +101,8 @@ public class AiProxyPlugin extends AbstractShenyuPlugin {
CacheKeyUtils.INST.getKey(
selector.getId(), Constants.DEFAULT_RULE));
- return DataBufferUtils.join(exchange.getRequest().getBody())
+ return DataBufferUtils.join(exchange.getRequest().getBody(),
MAX_REQUEST_BODY_SIZE_BYTES)
.flatMap(dataBuffer -> {
- // Validate actual body size after reading, not just
Content-Length header
- final int actualSize = dataBuffer.readableByteCount();
- if (actualSize > MAX_REQUEST_BODY_SIZE_BYTES) {
- DataBufferUtils.release(dataBuffer);
- LOG.warn("[AiProxy] Request body size {} exceeds
maximum allowed size {}",
- actualSize, MAX_REQUEST_BODY_SIZE_BYTES);
-
exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
- return exchange.getResponse().setComplete();
- }
-
final String requestBody =
dataBuffer.toString(StandardCharsets.UTF_8);
DataBufferUtils.release(dataBuffer);
@@ -152,6 +143,12 @@ public class AiProxyPlugin extends AbstractShenyuPlugin {
return handleStreamRequest(exchange, selector,
requestBody, primaryConfig, selectorHandle);
}
return handleNonStreamRequest(exchange, selector,
requestBody, primaryConfig, selectorHandle);
+ })
+ .onErrorResume(DataBufferLimitException.class, e -> {
+ LOG.warn("[AiProxy] Request body exceeds maximum allowed
size {} bytes",
+ MAX_REQUEST_BODY_SIZE_BYTES);
+
exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
+ return exchange.getResponse().setComplete();
});
}
diff --git
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/test/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPluginTest.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/test/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPluginTest.java
index 30e84abc08..f31150c699 100644
---
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/test/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPluginTest.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/test/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPluginTest.java
@@ -47,6 +47,8 @@ import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.buffer.DataBufferLimitException;
+import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
@@ -57,6 +59,7 @@ import reactor.test.StepVerifier;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -339,4 +342,21 @@ public class AiProxyPluginTest {
verify(configService).resolveAdminFallbackConfig(primaryConfig,
handle);
verify(executorService).execute(any(), any(), any());
}
+
+ @Test
+ public void testRequestBodyExceedsMaxSize() {
+ final AiProxyHandle handle = new AiProxyHandle();
+ aiProxyPluginHandler.getSelectorCachedHandle()
+ .cachedHandle(CacheKeyUtils.INST.getKey(SELECTOR_ID,
Constants.DEFAULT_RULE), handle);
+
+ try (MockedStatic<DataBufferUtils> dataBufferUtilsMock =
mockStatic(DataBufferUtils.class)) {
+ dataBufferUtilsMock.when(() -> DataBufferUtils.join(any(),
anyInt()))
+ .thenReturn(Mono.error(new
DataBufferLimitException("Request body exceeds limit")));
+
+ StepVerifier.create(plugin.doExecute(exchange,
mock(ShenyuPluginChain.class), selector, rule))
+ .verifyComplete();
+
+ assertEquals(HttpStatus.PAYLOAD_TOO_LARGE,
exchange.getResponse().getStatusCode());
+ }
+ }
}
\ No newline at end of file