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

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


The following commit(s) were added to refs/heads/dev by this push:
     new a2f818afd2 [Improve][Connector-V2] Validate HTTP sink URL is not blank 
(#12773)
a2f818afd2 is described below

commit a2f818afd2d98c1bc6b281ee2aa11f9138b6851d
Author: Supidan <[email protected]>
AuthorDate: Thu Oct 15 06:53:50 2026 +0000

    [Improve][Connector-V2] Validate HTTP sink URL is not blank (#12773)
    
    Co-authored-by: Supidan <[email protected]>
---
 docs/en/connectors/sink/Feishu.md                  |  2 +-
 docs/en/connectors/sink/Http.md                    |  2 +-
 docs/zh/connectors/sink/Feishu.md                  |  2 +-
 docs/zh/connectors/sink/Http.md                    |  2 +-
 .../seatunnel/http/sink/HttpSinkFactory.java       |  3 +-
 .../connectors/seatunnel/http/HttpFactoryTest.java | 54 +++++++++++++++++++++-
 6 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/docs/en/connectors/sink/Feishu.md 
b/docs/en/connectors/sink/Feishu.md
index 155e9dc753..d5d420f7ba 100644
--- a/docs/en/connectors/sink/Feishu.md
+++ b/docs/en/connectors/sink/Feishu.md
@@ -56,7 +56,7 @@ Feishu webhook URLs and custom authentication headers are 
sensitive. Do not prin
 
 | Name                        | Type    | Required | Default | Description     
                                                                                
                        |
 
|-----------------------------|---------|----------|---------|-------------------------------------------------------------------------------------------------------------------------|
-| url                         | String  | Yes      | -       | Feishu webhook 
URL. The current sink writer sends requests to this fixed URL and does not 
replace table-name placeholders. |
+| url                         | String  | Yes      | -       | Feishu webhook 
URL. Must not be empty or whitespace-only. The current sink writer sends 
requests to this fixed URL and does not replace table-name placeholders. |
 | headers                     | Map     | No       | -       | HTTP request 
headers. Use it when the webhook gateway requires extra headers.                
                           |
 | params                      | Map     | No       | -       | Accepted by the 
option rule, but the current sink writer does not pass it to requests. Put 
non-sensitive query parameters directly in `url` when needed. |
 | retry                       | Int     | No       | -       | The maximum 
retry times when the HTTP request fails with an `IOException`.                  
                            |
diff --git a/docs/en/connectors/sink/Http.md b/docs/en/connectors/sink/Http.md
index 3e83b2fc63..9cf60a28a3 100644
--- a/docs/en/connectors/sink/Http.md
+++ b/docs/en/connectors/sink/Http.md
@@ -38,7 +38,7 @@ They can be downloaded via install-plugin.sh or from the 
Maven central repositor
 
 |            Name             |  Type  | Required | Default |                  
                               Description                                      
           |
 
|-----------------------------|--------|----------|---------|-------------------------------------------------------------------------------------------------------------|
-| url                         | String | Yes      | -       | Http request url 
                                                                                
           |
+| url                         | String | Yes      | -       | HTTP request 
URL. Must not be empty or whitespace-only.                                      
                |
 | headers                     | Map    | No       | -       | Http headers     
                                                                                
           |
 | params                      | Map    | No       | -       | Accepted by the 
option rule. For the current sink writer, put query parameters directly in 
`url`; rows are posted to the final URL as the request body. |
 | retry                       | Int    | No       | -       | The max retry 
times if request http return to `IOException`                                   
              |
diff --git a/docs/zh/connectors/sink/Feishu.md 
b/docs/zh/connectors/sink/Feishu.md
index e0735cb579..f3b7543852 100644
--- a/docs/zh/connectors/sink/Feishu.md
+++ b/docs/zh/connectors/sink/Feishu.md
@@ -56,7 +56,7 @@ import ChangeLog from '../changelog/connector-http-feishu.md';
 
 | 名称                          | 类型      | 是否必需 | 默认值   | 描述                    
                                                                   |
 
|-----------------------------|---------|------|-------|------------------------------------------------------------------------------------------|
-| url                         | String  | 是    | -     | 飞书 Webhook URL。当前 
Sink 写入器会向这个固定 URL 发送请求,不会替换表名相关占位符。                             |
+| url                         | String  | 是    | -     | 飞书 Webhook 
URL,不能为空或仅包含空白字符。当前 Sink 写入器会向这个固定 URL 发送请求,不会替换表名相关占位符。 |
 | headers                     | Map     | 否    | -     | HTTP 请求头。Webhook 
网关需要额外请求头时使用。                                                            |
 | params                      | Map     | 否    | -     | 该参数会通过参数校验,但当前 Sink 
写入器不会把它传入请求。如需查询参数,请把非敏感参数直接写在 `url` 中。                         |
 | retry                       | Int     | 否    | -     | HTTP 请求发生 
`IOException` 时的最大重试次数。                                                        |
diff --git a/docs/zh/connectors/sink/Http.md b/docs/zh/connectors/sink/Http.md
index e18718a15b..53140c02ca 100644
--- a/docs/zh/connectors/sink/Http.md
+++ b/docs/zh/connectors/sink/Http.md
@@ -37,7 +37,7 @@ import ChangeLog from '../changelog/connector-http.md';
 
 |             名称              |   类型   | 是否必须 |  默认值  |                        
     描述                             |
 
|-----------------------------|--------|------|-------|------------------------------------------------------------|
-| url                         | String | 是    | -     | Http 请求链接              
                                    |
+| url                         | String | 是    | -     | HTTP 
请求链接,不能为空或仅包含空白字符。                                      |
 | headers                     | Map    | 否    | -     | Http 请求头               
                                     |
 | params                      | Map    | 否    | -     | 该参数会通过参数校验。当前 Sink 
写入器会把数据作为请求体 POST 到最终 URL,如需查询参数,建议直接写在 `url` 中。 |
 | retry                       | Int    | 否    | -     | 
如果请求http返回`IOException`的最大重试次数                             |
diff --git 
a/seatunnel-connectors-v2/connector-http/connector-http-base/src/main/java/org/apache/seatunnel/connectors/seatunnel/http/sink/HttpSinkFactory.java
 
b/seatunnel-connectors-v2/connector-http/connector-http-base/src/main/java/org/apache/seatunnel/connectors/seatunnel/http/sink/HttpSinkFactory.java
index b0a192893b..4fb64a4fdd 100644
--- 
a/seatunnel-connectors-v2/connector-http/connector-http-base/src/main/java/org/apache/seatunnel/connectors/seatunnel/http/sink/HttpSinkFactory.java
+++ 
b/seatunnel-connectors-v2/connector-http/connector-http-base/src/main/java/org/apache/seatunnel/connectors/seatunnel/http/sink/HttpSinkFactory.java
@@ -17,6 +17,7 @@
 
 package org.apache.seatunnel.connectors.seatunnel.http.sink;
 
+import org.apache.seatunnel.api.configuration.util.Conditions;
 import org.apache.seatunnel.api.configuration.util.OptionRule;
 import org.apache.seatunnel.api.options.SinkConnectorCommonOptions;
 import org.apache.seatunnel.api.table.connector.TableSink;
@@ -42,7 +43,7 @@ public class HttpSinkFactory implements TableSinkFactory {
     @Override
     public OptionRule optionRule() {
         return OptionRule.builder()
-                .required(HttpSinkOptions.URL)
+                .required(HttpSinkOptions.URL, 
Conditions.notBlank(HttpSinkOptions.URL))
                 .optional(HttpSinkOptions.HEADERS)
                 .optional(HttpSinkOptions.PARAMS)
                 .optional(HttpSinkOptions.RETRY)
diff --git 
a/seatunnel-connectors-v2/connector-http/connector-http-base/src/test/java/org/apache/seatunnel/connectors/seatunnel/http/HttpFactoryTest.java
 
b/seatunnel-connectors-v2/connector-http/connector-http-base/src/test/java/org/apache/seatunnel/connectors/seatunnel/http/HttpFactoryTest.java
index 44b73c8757..dfca204deb 100644
--- 
a/seatunnel-connectors-v2/connector-http/connector-http-base/src/test/java/org/apache/seatunnel/connectors/seatunnel/http/HttpFactoryTest.java
+++ 
b/seatunnel-connectors-v2/connector-http/connector-http-base/src/test/java/org/apache/seatunnel/connectors/seatunnel/http/HttpFactoryTest.java
@@ -17,17 +17,69 @@
 
 package org.apache.seatunnel.connectors.seatunnel.http;
 
+import org.apache.seatunnel.api.configuration.ReadonlyConfig;
+import org.apache.seatunnel.api.configuration.util.ConfigValidator;
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.api.configuration.util.OptionValidationException;
+import org.apache.seatunnel.connectors.seatunnel.http.config.HttpSinkOptions;
 import org.apache.seatunnel.connectors.seatunnel.http.sink.HttpSinkFactory;
 import org.apache.seatunnel.connectors.seatunnel.http.source.HttpSourceFactory;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.Collections;
 
 class HttpFactoryTest {
 
+    private final OptionRule sinkOptionRule = new 
HttpSinkFactory().optionRule();
+
     @Test
     void optionRule() {
         Assertions.assertNotNull((new HttpSourceFactory()).optionRule());
-        Assertions.assertNotNull((new HttpSinkFactory()).optionRule());
+        Assertions.assertNotNull(sinkOptionRule);
+    }
+
+    @ParameterizedTest
+    @ValueSource(
+            strings = {
+                "http://localhost/test/webhook";,
+                "https://example.com/webhook";,
+                
"http://localhost/test/${database_name}_test/${table_name}_test";,
+                "http://localhost/test/${schema_name}_test/${table_name}_test";,
+                " \thttp://localhost/test/webhook\r\n "
+            })
+    void testNonBlankSinkUrlAcceptedWithoutModification(String url) {
+        ReadonlyConfig config =
+                
ReadonlyConfig.fromMap(Collections.singletonMap(HttpSinkOptions.URL.key(), 
url));
+
+        Assertions.assertDoesNotThrow(() -> 
ConfigValidator.of(config).validate(sinkOptionRule));
+        Assertions.assertEquals(url, config.get(HttpSinkOptions.URL));
+    }
+
+    @Test
+    void testMissingSinkUrlRejected() {
+        ReadonlyConfig config = ReadonlyConfig.fromMap(Collections.emptyMap());
+
+        OptionValidationException error =
+                Assertions.assertThrows(
+                        OptionValidationException.class,
+                        () -> 
ConfigValidator.of(config).validate(sinkOptionRule));
+        
Assertions.assertTrue(error.getMessage().contains(HttpSinkOptions.URL.key()));
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {"", " ", "\t", "\n", "\r", " \t\r\n "})
+    void testBlankSinkUrlRejected(String url) {
+        ReadonlyConfig config =
+                
ReadonlyConfig.fromMap(Collections.singletonMap(HttpSinkOptions.URL.key(), 
url));
+
+        OptionValidationException error =
+                Assertions.assertThrows(
+                        OptionValidationException.class,
+                        () -> 
ConfigValidator.of(config).validate(sinkOptionRule));
+        
Assertions.assertTrue(error.getMessage().contains(HttpSinkOptions.URL.key()));
     }
 }

Reply via email to