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 0e41bcc4e4 Codex/fix sandbox response leak (#6359)
0e41bcc4e4 is described below

commit 0e41bcc4e46a561c3a942613b25409a5aa7b2a8d
Author: SUPERSAIYAN <[email protected]>
AuthorDate: Mon Jun 8 11:09:13 2026 +0800

    Codex/fix sandbox response leak (#6359)
    
    * Fix sandbox proxy response leak
    
    * Restore static sandbox HTTP utility
    
    * Close sandbox test exchange
    
    ---------
    
    Co-authored-by: aias00 <[email protected]>
---
 .../admin/service/impl/SandboxServiceImpl.java     |  25 ++---
 .../admin/service/impl/SandboxServiceImplTest.java | 103 +++++++++++++++++++++
 2 files changed, 116 insertions(+), 12 deletions(-)

diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SandboxServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SandboxServiceImpl.java
index bd50263f80..e0315584f0 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SandboxServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SandboxServiceImpl.java
@@ -107,19 +107,20 @@ public class SandboxServiceImpl implements SandboxService 
{
         // Public request parameters.
         Map<String, Object> reqParams = 
this.buildReqBizParams(proxyGatewayDTO);
         List<HttpUtils.UploadFile> files = this.uploadFiles(request);
-        Response resp = HTTP_UTILS.requestCall(uriComponents.toUriString(), 
reqParams, header, 
HttpUtils.HTTPMethod.fromValue(proxyGatewayDTO.getHttpMethod()), files);
-        ResponseBody body = resp.body();
-
-        if (Objects.isNull(body)) {
-            return;
-        }
-        if (StringUtils.isNotEmpty(appKey)) {
-            response.addHeader("sandbox-beforesign", 
UriUtils.encode(signContent, StandardCharsets.UTF_8));
-            response.addHeader("sandbox-sign", UriUtils.encode(sign, 
StandardCharsets.UTF_8));
+        try (Response resp = 
HTTP_UTILS.requestCall(uriComponents.toUriString(), reqParams, header, 
HttpUtils.HTTPMethod.fromValue(proxyGatewayDTO.getHttpMethod()), files)) {
+            ResponseBody body = resp.body();
+
+            if (Objects.isNull(body)) {
+                return;
+            }
+            if (StringUtils.isNotEmpty(appKey)) {
+                response.addHeader("sandbox-beforesign", 
UriUtils.encode(signContent, StandardCharsets.UTF_8));
+                response.addHeader("sandbox-sign", UriUtils.encode(sign, 
StandardCharsets.UTF_8));
+            }
+
+            IOUtils.copy(body.byteStream(), response.getOutputStream());
+            response.flushBuffer();
         }
-
-        IOUtils.copy(body.byteStream(), response.getOutputStream());
-        response.flushBuffer();
     }
 
     private Set<String> getPermitHostPorts() {
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/impl/SandboxServiceImplTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/impl/SandboxServiceImplTest.java
new file mode 100644
index 0000000000..a0d1db0f4d
--- /dev/null
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/impl/SandboxServiceImplTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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.apache.shenyu.admin.service.impl;
+
+import com.sun.net.httpserver.HttpServer;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+
+import org.apache.shenyu.admin.model.dto.ProxyGatewayDTO;
+import org.apache.shenyu.admin.model.vo.ShenyuDictVO;
+import org.apache.shenyu.admin.service.AppAuthService;
+import org.apache.shenyu.admin.service.ShenyuDictService;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test case for {@link SandboxServiceImpl}.
+ */
+@ExtendWith(MockitoExtension.class)
+final class SandboxServiceImplTest {
+
+    @Mock
+    private AppAuthService appAuthService;
+
+    @Mock
+    private ShenyuDictService shenyuDictService;
+
+    @Test
+    void requestProxyGatewayShouldCopyBody() throws IOException {
+        HttpServer server = startHttpServer("proxied response");
+        int port = server.getAddress().getPort();
+        SandboxServiceImpl sandboxService = new 
SandboxServiceImpl(appAuthService, shenyuDictService);
+
+        
when(shenyuDictService.list(anyString())).thenReturn(Collections.singletonList(buildDict(port)));
+
+        try {
+            MockHttpServletResponse response = new MockHttpServletResponse();
+            sandboxService.requestProxyGateway(buildProxyGatewayDTO(port), new 
MockHttpServletRequest(), response);
+
+            assertThat(response.getContentAsString()).isEqualTo("proxied 
response");
+        } finally {
+            server.stop(0);
+        }
+    }
+
+    private ProxyGatewayDTO buildProxyGatewayDTO(final int port) {
+        ProxyGatewayDTO proxyGatewayDTO = new ProxyGatewayDTO();
+        proxyGatewayDTO.setRequestUrl("http://localhost:"; + port + "/proxy");
+        proxyGatewayDTO.setHeaders(Collections.singletonMap("Content-Type", 
"application/x-www-form-urlencoded"));
+        proxyGatewayDTO.setBizParam(Collections.emptyMap());
+        return proxyGatewayDTO;
+    }
+
+    private ShenyuDictVO buildDict(final int port) {
+        ShenyuDictVO dictVO = new ShenyuDictVO();
+        dictVO.setDictValue("http://localhost:"; + port);
+        dictVO.setEnabled(Boolean.TRUE);
+        return dictVO;
+    }
+
+    private HttpServer startHttpServer(final String content) throws 
IOException {
+        byte[] responseBody = content.getBytes(StandardCharsets.UTF_8);
+        HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);
+        server.createContext("/proxy", exchange -> {
+            try {
+                exchange.sendResponseHeaders(200, responseBody.length);
+                try (OutputStream outputStream = exchange.getResponseBody()) {
+                    outputStream.write(responseBody);
+                }
+            } finally {
+                exchange.close();
+            }
+        });
+        server.start();
+        return server;
+    }
+}

Reply via email to