This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/dev/pr-12072-c2bd543ffccacd50f7d55425f6118f7771ba00a1
in repository https://gitbox.apache.org/repos/asf/seatunnel.git

commit 76c22220d7e6611495f174678bfbcc8191bb8727
Author: SEZ <[email protected]>
AuthorDate: Sat Sep 5 13:40:05 2026 +0000

    [Improve][Zeta] Bound the size of a config file uploaded to the REST server 
(#12072)
---
 docs/en/engines/zeta/rest-api-v2.md                 | 21 ++++++++++++++++++++-
 docs/zh/engines/zeta/rest-api-v2.md                 | 19 ++++++++++++++++++-
 .../config/YamlSeaTunnelDomConfigProcessor.java     | 18 ++++++++++++++++++
 .../engine/common/config/server/HttpConfig.java     |  8 ++++++++
 .../common/config/server/ServerConfigOptions.java   | 14 ++++++++++++++
 .../seatunnel/engine/server/JettyService.java       | 13 ++++++++++++-
 6 files changed, 90 insertions(+), 3 deletions(-)

diff --git a/docs/en/engines/zeta/rest-api-v2.md 
b/docs/en/engines/zeta/rest-api-v2.md
index 47bc667fdf..efe4d04a3e 100644
--- a/docs/en/engines/zeta/rest-api-v2.md
+++ b/docs/en/engines/zeta/rest-api-v2.md
@@ -16,7 +16,7 @@ The v2 API and the Web UI are both served by the embedded 
Jetty server. Jetty st
 
 There are two different "default" sources that are easy to mix up:
 
-- Code defaults: `enable-http = false`, `enable-https = false`, `port = 8080`, 
`context-path = ""`, `enable-dynamic-port = false`, `port-range = 100`
+- Code defaults: `enable-http = false`, `enable-https = false`, `port = 8080`, 
`context-path = ""`, `enable-dynamic-port = false`, `port-range = 100`, 
`upload-max-file-size-mb = 10`, `upload-max-request-size-mb = 10`
 - The packaged `seatunnel.yaml` example: it already sets `enable-http: true` 
and `port: 8080`
 
 As a result, if you start SeaTunnel with the packaged configuration, the Web 
UI and REST API usually
@@ -60,6 +60,21 @@ seatunnel:
       context-path: /seatunnel
 ```
 
+The size of an uploaded config file is bounded, so a single request cannot 
fill the master's temp
+directory or exhaust its heap. Both limits apply to `/submit-job/upload` only, 
and a value of `0`
+or below means unlimited:
+
+```yaml
+
+seatunnel:
+  engine:
+    http:
+      enable-http: true
+      port: 8080
+      upload-max-file-size-mb: 10
+      upload-max-request-size-mb: 10
+```
+
 ## Web UI and Port 8080 Troubleshooting
 
 - If `http://<host>:8080/` is unreachable, first check whether 
`seatunnel.engine.http.enable-http` or `enable-https` is actually enabled. The 
`network.rest-api.enabled` setting in `hazelcast.yaml` does not replace the 
Jetty switch.
@@ -1028,6 +1043,10 @@ The name of the uploaded file key is config_file, and 
supports the following for
 - `.conf` or `.config` files: parsed in HOCON format
 - `.sql` files: parsed in SQL format, supports CREATE TABLE and INSERT INTO 
syntax
 
+The upload is limited to `seatunnel.engine.http.upload-max-file-size-mb` (10 
MB by default) per
+file and `upload-max-request-size-mb` (10 MB by default) per request. A larger 
upload is rejected
+before the config is parsed.
+
 curl Example :
 ```bash
 # Upload HOCON config file
diff --git a/docs/zh/engines/zeta/rest-api-v2.md 
b/docs/zh/engines/zeta/rest-api-v2.md
index d5eda567bd..c2e4fabd88 100644
--- a/docs/zh/engines/zeta/rest-api-v2.md
+++ b/docs/zh/engines/zeta/rest-api-v2.md
@@ -13,7 +13,7 @@ v2 版本的 API 和 Web UI 都由内嵌 Jetty 提供,与 v1 版本保持相
 
 这里需要区分两个容易混淆的“默认值”来源:
 
-- 代码默认值:`enable-http = false`、`enable-https = false`、`port = 
8080`、`context-path = ""`、`enable-dynamic-port = false`、`port-range = 100`
+- 代码默认值:`enable-http = false`、`enable-https = false`、`port = 
8080`、`context-path = ""`、`enable-dynamic-port = false`、`port-range = 
100`、`upload-max-file-size-mb = 10`、`upload-max-request-size-mb = 10`
 - 发行包自带的 `seatunnel.yaml` 示例:默认写入了 `enable-http: true` 和 `port: 8080`
 
 因此,直接使用发行包自带配置启动时,Web UI 和 REST API 通常会监听
@@ -56,6 +56,20 @@ seatunnel:
       context-path: /seatunnel
 ```
 
+上传的配置文件有大小上限,避免单个请求把 Master 的临时目录写满或把堆内存吃光。这两个配置只作用于
+`/submit-job/upload`,取值小于等于 `0` 表示不限制:
+
+```yaml
+
+seatunnel:
+  engine:
+    http:
+      enable-http: true
+      port: 8080
+      upload-max-file-size-mb: 10
+      upload-max-request-size-mb: 10
+```
+
 ## Web UI 与 8080 排查
 
 - 如果 `http://<host>:8080/` 打不开,先检查 `seatunnel.engine.http.enable-http` 或 
`enable-https` 是否真的开启;仅配置 `hazelcast.yaml` 中的 `network.rest-api.enabled` 不能替代 
Jetty 开关。
@@ -1002,6 +1016,9 @@ INSERT INTO console_sink SELECT * FROM fake_source;
 - `.conf` 或 `.config` 文件:按照 HOCON 格式解析
 - `.sql` 文件:按照 SQL 格式解析,支持 CREATE TABLE 和 INSERT INTO 语法
 
+单个文件大小受 `seatunnel.engine.http.upload-max-file-size-mb`(默认 10 MB)限制,单个请求的总大小受
+`upload-max-request-size-mb`(默认 10 MB)限制。超出上限的请求会在解析配置之前被拒绝。
+
 curl Example
 
 ```bash
diff --git 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/YamlSeaTunnelDomConfigProcessor.java
 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/YamlSeaTunnelDomConfigProcessor.java
index 5e69c3ecbf..f2826a8e0a 100644
--- 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/YamlSeaTunnelDomConfigProcessor.java
+++ 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/YamlSeaTunnelDomConfigProcessor.java
@@ -658,6 +658,24 @@ public class YamlSeaTunnelDomConfigProcessor extends 
AbstractDomConfigProcessor
                     .key()
                     .equals(name)) {
                 httpConfig.setBasicAuthPassword(getTextContent(node));
+            } else if 
(ServerConfigOptions.MasterServerConfigOptions.UPLOAD_MAX_FILE_SIZE_MB
+                    .key()
+                    .equals(name)) {
+                httpConfig.setUploadMaxFileSizeMb(
+                        getIntegerValue(
+                                ServerConfigOptions.MasterServerConfigOptions
+                                        .UPLOAD_MAX_FILE_SIZE_MB
+                                        .key(),
+                                getTextContent(node)));
+            } else if 
(ServerConfigOptions.MasterServerConfigOptions.UPLOAD_MAX_REQUEST_SIZE_MB
+                    .key()
+                    .equals(name)) {
+                httpConfig.setUploadMaxRequestSizeMb(
+                        getIntegerValue(
+                                ServerConfigOptions.MasterServerConfigOptions
+                                        .UPLOAD_MAX_REQUEST_SIZE_MB
+                                        .key(),
+                                getTextContent(node)));
             } else {
                 LOGGER.warning("Unrecognized element: " + name);
             }
diff --git 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/HttpConfig.java
 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/HttpConfig.java
index fe7324bf31..36e860dfac 100644
--- 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/HttpConfig.java
+++ 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/HttpConfig.java
@@ -78,6 +78,14 @@ public class HttpConfig implements Serializable {
     private String basicAuthPassword =
             
ServerConfigOptions.MasterServerConfigOptions.BASIC_AUTH_PASSWORD.defaultValue();
 
+    /** The maximum size in MB of a single uploaded file. A value <= 0 means 
unlimited. */
+    private int uploadMaxFileSizeMb =
+            
ServerConfigOptions.MasterServerConfigOptions.UPLOAD_MAX_FILE_SIZE_MB.defaultValue();
+
+    /** The maximum total size in MB of a multipart request. A value <= 0 
means unlimited. */
+    private int uploadMaxRequestSizeMb =
+            
ServerConfigOptions.MasterServerConfigOptions.UPLOAD_MAX_REQUEST_SIZE_MB.defaultValue();
+
     public void setPort(int port) {
         checkPositive(port, ServerConfigOptions.MasterServerConfigOptions.HTTP 
+ " must be > 0");
         this.port = port;
diff --git 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/ServerConfigOptions.java
 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/ServerConfigOptions.java
index a99596331d..903e1d9e1a 100644
--- 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/ServerConfigOptions.java
+++ 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/ServerConfigOptions.java
@@ -374,6 +374,20 @@ public class ServerConfigOptions {
                         .defaultValue("admin")
                         .withDescription("The password for basic 
authentication.");
 
+        public static final Option<Integer> UPLOAD_MAX_FILE_SIZE_MB =
+                Options.key("upload-max-file-size-mb")
+                        .intType()
+                        .defaultValue(10)
+                        .withDescription(
+                                "The maximum size in MB of a single file 
uploaded to the http server. A value <= 0 means unlimited.");
+
+        public static final Option<Integer> UPLOAD_MAX_REQUEST_SIZE_MB =
+                Options.key("upload-max-request-size-mb")
+                        .intType()
+                        .defaultValue(10)
+                        .withDescription(
+                                "The maximum total size in MB of a multipart 
request sent to the http server. A value <= 0 means unlimited.");
+
         public static final Option<HttpConfig> HTTP =
                 Options.key("http")
                         .type(new TypeReference<HttpConfig>() {})
diff --git 
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/JettyService.java
 
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/JettyService.java
index 8734e12ba8..a7866cad48 100644
--- 
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/JettyService.java
+++ 
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/JettyService.java
@@ -160,6 +160,12 @@ public class JettyService {
         log.info("SeaTunnel REST service will start on https port {}", 
httpsPort);
     }
 
+    private static long toBytes(int megabytes) {
+        // Jetty reads a negative limit as unlimited, which is what this 
server did before the
+        // limit became configurable. Keep that available instead of scaling 
it into bytes.
+        return megabytes <= 0 ? -1L : megabytes * 1024L * 1024L;
+    }
+
     public void createJettyServer() {
 
         ServletContextHandler context = new 
ServletContextHandler(ServletContextHandler.SESSIONS);
@@ -238,7 +244,12 @@ public class JettyService {
         context.addServlet(jobInfoHolder, convertUrlToPath(REST_URL_JOB_INFO));
         context.addServlet(jobInfoHolder, 
convertUrlToPath(REST_URL_RUNNING_JOB));
         context.addServlet(threadDumpHolder, 
convertUrlToPath(REST_URL_THREAD_DUMP));
-        MultipartConfigElement multipartConfigElement = new 
MultipartConfigElement("");
+        // Bound the upload: the whole part is buffered by Jetty and then read 
into a String, so an
+        // unbounded body lets a single request fill the temp directory and 
exhaust the master heap.
+        long maxFileSize = toBytes(httpConfig.getUploadMaxFileSizeMb());
+        long maxRequestSize = toBytes(httpConfig.getUploadMaxRequestSizeMb());
+        MultipartConfigElement multipartConfigElement =
+                new MultipartConfigElement("", maxFileSize, maxRequestSize, 0);
         
submitJobByUploadFileHolder.getRegistration().setMultipartConfig(multipartConfigElement);
         context.addServlet(
                 submitJobByUploadFileHolder, 
convertUrlToPath(REST_URL_SUBMIT_JOB_BY_UPLOAD_FILE));

Reply via email to