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;
+ }
+}