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 478dcfe4cf refactor: use try-with-resources for GZIP decompression in
AiResponseTransformerPlugin and add unit test for resource cleanup (#6915)
478dcfe4cf is described below
commit 478dcfe4cfa99670cf2f7556f3c346378f421691
Author: Limbo <[email protected]>
AuthorDate: Fri Aug 14 14:48:50 2026 +0800
refactor: use try-with-resources for GZIP decompression in
AiResponseTransformerPlugin and add unit test for resource cleanup (#6915)
Co-authored-by: aias00 <[email protected]>
---
.../response/AiResponseTransformerPlugin.java | 11 ++++----
.../response/AiResponseTransformerPluginTest.java | 30 ++++++++++++++++++++++
2 files changed, 35 insertions(+), 6 deletions(-)
diff --git
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPlugin.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPlugin.java
index 033ee402fd..6dfea7c12d 100644
---
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPlugin.java
@@ -48,6 +48,8 @@ import reactor.core.publisher.Mono;
import org.reactivestreams.Publisher;
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
@@ -57,6 +59,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import java.util.zip.GZIPInputStream;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -309,17 +312,13 @@ public class AiResponseTransformerPlugin extends
AbstractShenyuPlugin {
String contentEncoding =
exchange.getResponse().getHeaders().getFirst("Content-Encoding");
if ("gzip".equalsIgnoreCase(contentEncoding)) {
LOG.debug("Detected gzip encoding, attempting to
decompress");
- try {
- java.io.ByteArrayInputStream bis = new
java.io.ByteArrayInputStream(bytes);
- java.util.zip.GZIPInputStream gis = new
java.util.zip.GZIPInputStream(bis);
- java.io.ByteArrayOutputStream bos = new
java.io.ByteArrayOutputStream();
+ try (GZIPInputStream gis = new GZIPInputStream(new
ByteArrayInputStream(bytes));
+ ByteArrayOutputStream bos = new
ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = gis.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
- gis.close();
- bos.close();
originalResponseBody =
bos.toString(StandardCharsets.UTF_8.name());
LOG.debug("Decompressed response body: {}",
originalResponseBody);
} catch (Exception e) {
diff --git
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPluginTest.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPluginTest.java
index fcaa60beb1..aa9d4d50fb 100644
---
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPluginTest.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPluginTest.java
@@ -26,13 +26,17 @@ import org.apache.shenyu.common.utils.Singleton;
import org.apache.shenyu.plugin.ai.common.config.AiCommonConfig;
import org.apache.shenyu.plugin.ai.common.spring.ai.AiModelFactory;
import
org.apache.shenyu.plugin.ai.common.spring.ai.registry.AiModelFactoryRegistry;
+import
org.apache.shenyu.plugin.ai.transformer.response.template.AiResponseTransformerTemplate;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
+import org.mockito.MockedConstruction;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
@@ -43,13 +47,18 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.Collections;
+import java.util.zip.GZIPInputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.lenient;
@@ -139,6 +148,27 @@ class AiResponseTransformerPluginTest {
.verifyComplete();
}
+ @Test
+ void testGzipInputStreamClosedWhenDecompressionFails() throws IOException {
+ MockServerHttpResponse response = (MockServerHttpResponse)
exchange.getResponse();
+ response.getHeaders().set(HttpHeaders.CONTENT_ENCODING, "gzip");
+ AiResponseTransformerTemplate template =
mock(AiResponseTransformerTemplate.class);
+ ChatClient chatClient = mock(ChatClient.class);
+ when(template.assembleMessage(exchange)).thenReturn(Mono.empty());
+ AiResponseTransformerPlugin.AiResponseTransformerDecorator decorator =
+ new
AiResponseTransformerPlugin.AiResponseTransformerDecorator(exchange, template,
chatClient);
+ DataBuffer responseBody =
response.bufferFactory().wrap("gzip".getBytes(StandardCharsets.UTF_8));
+
+ try (MockedConstruction<GZIPInputStream> gzipStreams =
mockConstruction(GZIPInputStream.class,
+ (gzipInputStream, context) ->
when(gzipInputStream.read(any(byte[].class))).thenThrow(new IOException()))) {
+ StepVerifier.create(decorator.writeWith(Mono.just(responseBody)))
+ .verifyComplete();
+
+ assertEquals(1, gzipStreams.constructed().size());
+ verify(gzipStreams.constructed().get(0)).close();
+ }
+ }
+
@Test
void testExtractBodyFromAiResponse() {
String aiResponse = "HTTP/1.1 200 OK\nContent-Type:
application/json\n\n{\"status\":\"success\"}";