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 687c51233f95..cd5dcb3d2d12 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.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 @@ protected Mono doExecute( 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 @@ protected Mono doExecute( 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 30e84abc0809..f31150c699d3 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.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 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 void testExecutorServiceError() { 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 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