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

yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new 62cb1df3a7f branch-4.2: [improvement](s3) Default scheme-less S3 
endpoints to HTTPS (#65379) (#69151)
62cb1df3a7f is described below

commit 62cb1df3a7f8124ad338e8788e4faaf71f030b07
Author: Xin Liao <[email protected]>
AuthorDate: Tue Oct 20 10:14:53 2026 +0800

    branch-4.2: [improvement](s3) Default scheme-less S3 endpoints to HTTPS 
(#65379) (#69151)
    
    Pick apache/doris#65379 to branch-4.2.
    
    Branch adaptation: retain the 4.2 S3 property and container credential
    implementations. Update the 4.2 FE S3 client call sites so scheme-less
    endpoints reach the shared HTTPS default. Keep persisted S3 resource
    endpoints as supplied and normalize a copy for connectivity checks.
    
    SSRF surface: the existing FE catalog connectivity test can contact an
    S3 host supplied by a user with CREATE privilege on the catalog. This
    change lets a scheme-less endpoint use HTTPS for that request; it adds
    no new host source or request site.
    
    Validation: `build-support/clang-format.sh`,
    `build-support/check-format.sh`, and `git diff --check` passed.
    `DORIS_GCC_HOME=/usr ./run-fe-ut.sh --run
    
org.apache.doris.common.util.S3UtilTest,org.apache.doris.catalog.S3ResourceTest`
    was attempted but stopped during generated-code build because
    `thirdparty/installed/bin/protoc` is missing from this worktree. BE,
    Cloud, and regression tests were not run.
    
    ---------
    
    Co-authored-by: Refrain <[email protected]>
---
 be/src/common/config.cpp                           |  4 +-
 be/src/common/config.h                             |  2 +-
 be/src/util/s3_util.cpp                            |  4 +-
 be/test/io/s3_client_factory_test.cpp              | 29 +++++++++++
 cloud/src/common/config.h                          |  4 +-
 cloud/src/recycler/s3_accessor.cpp                 |  4 +-
 cloud/test/util_test.cpp                           | 30 ++++++++++++
 common/cpp/aws_common.cpp                          | 10 ++++
 common/cpp/aws_common.h                            | 10 +++-
 .../main/java/org/apache/doris/common/Config.java  |  5 ++
 .../java/org/apache/doris/catalog/S3Resource.java  | 29 ++++++-----
 .../java/org/apache/doris/common/util/S3Util.java  | 22 ++++++---
 .../AbstractS3CompatibleConnectivityTester.java    |  2 +-
 .../doris/datasource/iceberg/dlf/DLFCatalog.java   |  7 +--
 .../java/org/apache/doris/fs/obj/S3ObjStorage.java |  8 +---
 .../org/apache/doris/catalog/S3ResourceTest.java   | 18 +++++--
 .../org/apache/doris/common/util/S3UtilTest.java   | 56 ++++++++++++++++++++++
 .../pipeline/vault_p0/conf/be_custom.conf          |  1 +
 .../pipeline/vault_p0/conf/fe_custom.conf          |  1 +
 .../pipeline/vault_p0/conf/recycler_custom.conf    |  1 +
 .../cold_heat_separation/policy/alter.groovy       |  2 +-
 21 files changed, 200 insertions(+), 49 deletions(-)

diff --git a/be/src/common/config.cpp b/be/src/common/config.cpp
index bd9eff1e435..705fcdd35db 100644
--- a/be/src/common/config.cpp
+++ b/be/src/common/config.cpp
@@ -1805,8 +1805,8 @@ DEFINE_mInt32(thrift_client_open_num_tries, "1");
 
 DEFINE_Bool(enable_index_compaction, "false");
 
-// http scheme in S3Client to use. E.g. http or https
-DEFINE_String(s3_client_http_scheme, "http");
+// Default HTTP scheme used by S3Client when the endpoint has no scheme.
+DEFINE_String(s3_client_http_scheme, "https");
 DEFINE_Validator(s3_client_http_scheme, [](const std::string& config) -> bool {
     return config == "http" || config == "https";
 });
diff --git a/be/src/common/config.h b/be/src/common/config.h
index 70c6519efef..5c7ff731260 100644
--- a/be/src/common/config.h
+++ b/be/src/common/config.h
@@ -1799,7 +1799,7 @@ DECLARE_mInt64(paimon_jni_writer_memory_pool_limit_bytes);
 // Retry the Open num_retries time waiting 100 milliseconds between retries.
 DECLARE_mInt32(thrift_client_open_num_tries);
 
-// http scheme in S3Client to use. E.g. http or https
+// Default HTTP scheme used by S3Client when the endpoint has no scheme.
 DECLARE_String(s3_client_http_scheme);
 
 DECLARE_mBool(ignore_schema_change_check);
diff --git a/be/src/util/s3_util.cpp b/be/src/util/s3_util.cpp
index e3d2ac9be2b..0cae6f74cc2 100644
--- a/be/src/util/s3_util.cpp
+++ b/be/src/util/s3_util.cpp
@@ -444,9 +444,7 @@ std::shared_ptr<io::ObjStorageClient> 
S3ClientFactory::_create_s3_client(
         aws_config.connectTimeoutMs = s3_conf.connect_timeout_ms;
     }
 
-    if (config::s3_client_http_scheme == "http") {
-        aws_config.scheme = Aws::Http::Scheme::HTTP;
-    }
+    set_s3_client_default_http_scheme(aws_config, 
config::s3_client_http_scheme);
 
     aws_config.retryStrategy = std::make_shared<S3CustomRetryStrategy>(
             config::max_s3_client_retry /*scaleFactor = 25*/, 
/*retry_slow_down=*/true);
diff --git a/be/test/io/s3_client_factory_test.cpp 
b/be/test/io/s3_client_factory_test.cpp
index 0882fd7847c..fc6413a0f04 100644
--- a/be/test/io/s3_client_factory_test.cpp
+++ b/be/test/io/s3_client_factory_test.cpp
@@ -18,6 +18,7 @@
 #include <aws/core/auth/AWSCredentialsProviderChain.h>
 #include <aws/core/auth/GeneralHTTPCredentialsProvider.h>
 #include <aws/core/auth/STSCredentialsProvider.h>
+#include <aws/core/client/ClientConfiguration.h>
 #include <aws/identity-management/auth/STSAssumeRoleCredentialsProvider.h>
 #include <gtest/gtest.h>
 
@@ -29,6 +30,7 @@
 #include <vector>
 
 #include "cloud/config.h"
+#include "cpp/aws_common.h"
 #include "cpp/custom_aws_credentials_provider_chain.h"
 #include "io/fs/rate_limited_obj_storage_client.h"
 #include "io/fs/s3_obj_storage_client.h"
@@ -236,6 +238,32 @@ TEST_F(S3ClientFactoryTest, AwsCredentialsProvider) {
     config::aws_credentials_provider_version = "v2";
 }
 
+TEST_F(S3ClientFactoryTest, SetS3ClientDefaultHttpScheme) {
+    S3ClientFactory::instance();
+    Aws::Client::ClientConfiguration client_config;
+    client_config.endpointOverride = "example.com:9000";
+
+    set_s3_client_default_http_scheme(client_config, "http");
+    EXPECT_EQ(client_config.endpointOverride, "example.com:9000");
+    EXPECT_EQ(client_config.scheme, Aws::Http::Scheme::HTTP);
+
+    set_s3_client_default_http_scheme(client_config, "https");
+    EXPECT_EQ(client_config.endpointOverride, "example.com:9000");
+    EXPECT_EQ(client_config.scheme, Aws::Http::Scheme::HTTPS);
+
+    client_config.endpointOverride = "http://example.com:9000";;
+    client_config.scheme = Aws::Http::Scheme::HTTP;
+    set_s3_client_default_http_scheme(client_config, "https");
+    EXPECT_EQ(client_config.endpointOverride, "http://example.com:9000";);
+    EXPECT_EQ(client_config.scheme, Aws::Http::Scheme::HTTP);
+
+    client_config.endpointOverride = "https://example.com:9000";;
+    client_config.scheme = Aws::Http::Scheme::HTTPS;
+    set_s3_client_default_http_scheme(client_config, "http");
+    EXPECT_EQ(client_config.endpointOverride, "https://example.com:9000";);
+    EXPECT_EQ(client_config.scheme, Aws::Http::Scheme::HTTPS);
+}
+
 TEST_F(S3ClientFactoryTest, ConvertPropertiesToS3ConfRoleArnProviderType) {
     std::map<std::string, std::string> properties {
             {"AWS_ENDPOINT", "s3.us-west-2.amazonaws.com"},
@@ -248,6 +276,7 @@ TEST_F(S3ClientFactoryTest, 
ConvertPropertiesToS3ConfRoleArnProviderType) {
 
     S3Conf s3_conf;
     ASSERT_TRUE(S3ClientFactory::convert_properties_to_s3_conf(properties, 
s3_uri, &s3_conf).ok());
+    ASSERT_EQ(s3_conf.client_conf.endpoint, properties.at("AWS_ENDPOINT"));
     ASSERT_EQ(s3_conf.client_conf.cred_provider_type, 
CredProviderType::Default);
 
     properties["AWS_CREDENTIALS_PROVIDER_TYPE"] = "WEB_IDENTITY";
diff --git a/cloud/src/common/config.h b/cloud/src/common/config.h
index e6558756465..f851b4303d3 100644
--- a/cloud/src/common/config.h
+++ b/cloud/src/common/config.h
@@ -322,8 +322,8 @@ CONF_String(priority_networks, "");
 
 CONF_Bool(enable_cluster_name_check, "false");
 
-// http scheme in S3Client to use. E.g. http or https
-CONF_String(s3_client_http_scheme, "http");
+// Default HTTP scheme used by S3Client when the endpoint has no scheme.
+CONF_String(s3_client_http_scheme, "https");
 CONF_Validator(s3_client_http_scheme, [](const std::string& config) -> bool {
     return config == "http" || config == "https";
 });
diff --git a/cloud/src/recycler/s3_accessor.cpp 
b/cloud/src/recycler/s3_accessor.cpp
index 08effdf17fc..e5fb445a45d 100644
--- a/cloud/src/recycler/s3_accessor.cpp
+++ b/cloud/src/recycler/s3_accessor.cpp
@@ -426,9 +426,7 @@ int S3Accessor::init() {
                                                     
config::instance_recycler_worker_pool_size),
                                              (long)aws_config.maxConnections);
 
-        if (config::s3_client_http_scheme == "http") {
-            aws_config.scheme = Aws::Http::Scheme::HTTP;
-        }
+        set_s3_client_default_http_scheme(aws_config, 
config::s3_client_http_scheme);
         // Recycler should fail fast on S3 SlowDown instead of retrying and 
blocking worker threads.
         aws_config.retryStrategy = std::make_shared<S3CustomRetryStrategy>(
                 config::max_s3_client_retry, /*retry_slow_down=*/false);
diff --git a/cloud/test/util_test.cpp b/cloud/test/util_test.cpp
index 0b20ae08171..abe91c65199 100644
--- a/cloud/test/util_test.cpp
+++ b/cloud/test/util_test.cpp
@@ -17,8 +17,10 @@
 
 #include "cpp/util.h"
 
+#include <aws/core/client/ClientConfiguration.h>
 #include <bthread/countdown_event.h>
 
+#include <chrono>
 #include <stdexcept>
 #include <string>
 #include <string_view>
@@ -33,6 +35,7 @@
 #include "cpp/sync_point.h"
 #include "gtest/gtest.h"
 #include "recycler/recycler.h"
+#include "recycler/s3_accessor.h"
 #include "recycler/sync_executor.h"
 
 using namespace doris::cloud;
@@ -456,3 +459,30 @@ TEST(UtilTest, test_long_normalize_http_uri) {
 
     EXPECT_EQ(doris::normalize_http_uri(longPath), expected);
 }
+
+TEST(UtilTest, test_set_s3_client_default_http_scheme) {
+    doris::cloud::S3Environment::getInstance();
+    Aws::Client::ClientConfiguration client_config =
+            doris::cloud::S3Environment::getClientConfiguration();
+
+    client_config.endpointOverride = "example.com:9000";
+    doris::set_s3_client_default_http_scheme(client_config, "http");
+    EXPECT_EQ(client_config.endpointOverride, "example.com:9000");
+    EXPECT_EQ(client_config.scheme, Aws::Http::Scheme::HTTP);
+
+    doris::set_s3_client_default_http_scheme(client_config, "https");
+    EXPECT_EQ(client_config.endpointOverride, "example.com:9000");
+    EXPECT_EQ(client_config.scheme, Aws::Http::Scheme::HTTPS);
+
+    client_config.endpointOverride = "http://example.com:9000";;
+    client_config.scheme = Aws::Http::Scheme::HTTP;
+    doris::set_s3_client_default_http_scheme(client_config, "https");
+    EXPECT_EQ(client_config.endpointOverride, "http://example.com:9000";);
+    EXPECT_EQ(client_config.scheme, Aws::Http::Scheme::HTTP);
+
+    client_config.endpointOverride = "https://example.com:9000";;
+    client_config.scheme = Aws::Http::Scheme::HTTPS;
+    doris::set_s3_client_default_http_scheme(client_config, "http");
+    EXPECT_EQ(client_config.endpointOverride, "https://example.com:9000";);
+    EXPECT_EQ(client_config.scheme, Aws::Http::Scheme::HTTPS);
+}
diff --git a/common/cpp/aws_common.cpp b/common/cpp/aws_common.cpp
index 885f7e7421e..0f2d900d8cd 100644
--- a/common/cpp/aws_common.cpp
+++ b/common/cpp/aws_common.cpp
@@ -18,6 +18,7 @@
 #include "aws_common.h"
 
 #include <aws/core/auth/GeneralHTTPCredentialsProvider.h>
+#include <aws/core/client/ClientConfiguration.h>
 #include <aws/core/platform/Environment.h>
 #include <aws/core/utils/memory/AWSMemory.h>
 #include <glog/logging.h>
@@ -142,4 +143,13 @@ std::string get_valid_ca_cert_path(const 
std::vector<std::string>& ca_cert_file_
     }
     return "";
 }
+
+void set_s3_client_default_http_scheme(Aws::Client::ClientConfiguration& 
client_config,
+                                       const std::string& scheme) {
+    if (client_config.endpointOverride.starts_with("http://";) ||
+        client_config.endpointOverride.starts_with("https://";)) {
+        return;
+    }
+    client_config.scheme = scheme == "http" ? Aws::Http::Scheme::HTTP : 
Aws::Http::Scheme::HTTPS;
+}
 }
diff --git a/common/cpp/aws_common.h b/common/cpp/aws_common.h
index 5caff27bec1..275d35889b6 100644
--- a/common/cpp/aws_common.h
+++ b/common/cpp/aws_common.h
@@ -26,6 +26,10 @@ namespace Aws::Auth {
 class AWSCredentialsProvider;
 }
 
+namespace Aws::Client {
+struct ClientConfiguration;
+}
+
 namespace doris {
     //AWS Credentials Provider Type
     enum class CredProviderType {
@@ -72,4 +76,8 @@ namespace doris {
 
     std::string get_valid_ca_cert_path(const std::vector<std::string>& 
ca_cert_file_paths);
 
-    } // namespace doris
\ No newline at end of file
+    // Configures the default S3 client transport scheme for endpoints without 
an explicit scheme.
+    void set_s3_client_default_http_scheme(Aws::Client::ClientConfiguration& 
client_config,
+                                           const std::string& scheme);
+
+    } // namespace doris
diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java 
b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
index 95752e6c3ed..c311f4c55ec 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
@@ -3688,6 +3688,11 @@ public class Config extends ConfigBase {
                     + "it is intentionally not modifiable at runtime via ADMIN 
SET FRONTEND CONFIG."})
     public static String[] s3_load_endpoint_white_list = {};
 
+    @ConfField(description = {
+            "The default scheme for S3 endpoints without an explicit scheme. 
Valid values are http and https."},
+            options = {"http", "https"})
+    public static String s3_client_http_scheme = "https";
+
     @ConfField(mutable = true, description = {
             "对于确定性的 S3 路径(无通配符如 *, ?),使用 HEAD 请求代替 ListObjects 来避免需要 
ListBucket 权限。"
             + "花括号模式 {1,2,3} 和非否定方括号模式 [abc] 会展开为具体路径。"
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/S3Resource.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/S3Resource.java
index 4e4d5b7b63b..948bfe470cc 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/S3Resource.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/S3Resource.java
@@ -22,6 +22,7 @@ import org.apache.doris.common.DdlException;
 import org.apache.doris.common.credentials.CloudCredentialWithEndpoint;
 import org.apache.doris.common.proc.BaseProcResult;
 import org.apache.doris.common.util.PrintableMap;
+import org.apache.doris.common.util.S3Util;
 import 
org.apache.doris.datasource.property.storage.AbstractS3CompatibleProperties;
 import org.apache.doris.datasource.property.storage.S3Properties;
 import org.apache.doris.datasource.property.storage.StorageProperties;
@@ -101,20 +102,19 @@ public class S3Resource extends Resource {
             LOG.debug("s3 info need check validity : {}", needCheck);
         }
 
-        // the endpoint for ping need add uri scheme.
-        String pingEndpoint = properties.get(S3Properties.ENDPOINT);
-        if (!pingEndpoint.startsWith("http://";) && 
!pingEndpoint.startsWith("https://";)) {
-            pingEndpoint = "http://"; + properties.get(S3Properties.ENDPOINT);
-            properties.put(S3Properties.ENDPOINT, pingEndpoint);
-            properties.put(S3Properties.Env.ENDPOINT, pingEndpoint);
-        }
-        String region = S3Properties.getRegionOfEndpoint(pingEndpoint);
+        String endpoint = properties.get(S3Properties.ENDPOINT);
+        properties.put(S3Properties.Env.ENDPOINT, endpoint);
+        String region = S3Properties.getRegionOfEndpoint(endpoint);
         properties.putIfAbsent(S3Properties.REGION, region);
 
         if (needCheck) {
+            Map<String, String> pingProperties = new HashMap<>(properties);
+            String pingEndpoint = S3Util.buildEndpointUrl(endpoint);
+            pingProperties.put(S3Properties.ENDPOINT, pingEndpoint);
+            pingProperties.put(S3Properties.Env.ENDPOINT, pingEndpoint);
             String bucketName = properties.get(S3Properties.BUCKET);
             String rootPath = properties.get(S3Properties.ROOT_PATH);
-            pingS3(bucketName, rootPath, properties);
+            pingS3(bucketName, rootPath, pingProperties);
         }
         // optional
         S3Properties.optionalS3Property(properties);
@@ -188,6 +188,9 @@ public class S3Resource extends Resource {
         }
         // compatible with old version, Need convert if modified properties 
map uses old properties.
         S3Properties.convertToStdProperties(properties);
+        if (!Strings.isNullOrEmpty(properties.get(S3Properties.ENDPOINT))) {
+            properties.put(S3Properties.Env.ENDPOINT, 
properties.get(S3Properties.ENDPOINT));
+        }
         boolean needCheck = isNeedCheck(properties);
         if (LOG.isDebugEnabled()) {
             LOG.debug("s3 info need check validity : {}", needCheck);
@@ -196,6 +199,9 @@ public class S3Resource extends Resource {
             S3Properties.requiredS3PingProperties(this.properties);
             Map<String, String> changedProperties = new 
HashMap<>(this.properties);
             changedProperties.putAll(properties);
+            String endpoint = 
S3Util.buildEndpointUrl(changedProperties.get(S3Properties.ENDPOINT));
+            changedProperties.put(S3Properties.ENDPOINT, endpoint);
+            changedProperties.put(S3Properties.Env.ENDPOINT, endpoint);
             String bucketName = properties.getOrDefault(S3Properties.BUCKET, 
this.properties.get(S3Properties.BUCKET));
             String rootPath = properties.getOrDefault(S3Properties.ROOT_PATH,
                     this.properties.get(S3Properties.ROOT_PATH));
@@ -240,8 +246,8 @@ public class S3Resource extends Resource {
         String token = properties.getOrDefault(S3Properties.SESSION_TOKEN,
                 this.properties.get(S3Properties.SESSION_TOKEN));
         String endpoint = properties.getOrDefault(S3Properties.ENDPOINT, 
this.properties.get(S3Properties.ENDPOINT));
-        String pingEndpoint = "http://"; + endpoint;
-        String region = S3Properties.getRegionOfEndpoint(pingEndpoint);
+        String pingEndpoint = S3Util.buildEndpointUrl(endpoint);
+        String region = S3Properties.getRegionOfEndpoint(endpoint);
         properties.putIfAbsent(S3Properties.REGION, region);
         return new CloudCredentialWithEndpoint(pingEndpoint, region, ak, sk, 
token);
     }
@@ -284,4 +290,3 @@ public class S3Resource extends Resource {
         readUnlock();
     }
 }
-
diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/util/S3Util.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/S3Util.java
index 7b2d213697d..be75e961fde 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/util/S3Util.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/S3Util.java
@@ -94,7 +94,7 @@ public class S3Util {
     }
 
     @Deprecated
-    public static S3Client buildS3Client(URI endpoint, String region, 
CloudCredential credential,
+    public static S3Client buildS3Client(String endpoint, String region, 
CloudCredential credential,
             boolean isUsePathStyle) {
         EqualJitterBackoffStrategy backoffStrategy = EqualJitterBackoffStrategy
                 .builder()
@@ -117,7 +117,7 @@ public class S3Util {
         return S3Client.builder()
                 
.httpClient(UrlConnectionHttpClient.builder().socketTimeout(Duration.ofSeconds(30))
                         .connectionTimeout(Duration.ofSeconds(30)).build())
-                .endpointOverride(endpoint)
+                .endpointOverride(URI.create(buildEndpointUrl(endpoint)))
                 .credentialsProvider(getAwsCredencialsProvider(credential))
                 .region(Region.of(region))
                 .overrideConfiguration(clientConf)
@@ -220,7 +220,7 @@ public class S3Util {
                 sessionToken, roleArn, externalId);
     }
 
-    public static S3Client buildS3Client(URI endpoint, String region, boolean 
isUsePathStyle,
+    public static S3Client buildS3Client(String endpoint, String region, 
boolean isUsePathStyle,
                                          AwsCredentialsProvider credential) {
         EqualJitterBackoffStrategy backoffStrategy = EqualJitterBackoffStrategy
                 .builder()
@@ -243,7 +243,7 @@ public class S3Util {
         return S3Client.builder()
                 
.httpClient(UrlConnectionHttpClient.builder().socketTimeout(Duration.ofSeconds(30))
                         .connectionTimeout(Duration.ofSeconds(30)).build())
-                .endpointOverride(endpoint)
+                .endpointOverride(URI.create(buildEndpointUrl(endpoint)))
                 .credentialsProvider(credential)
                 .region(Region.of(region))
                 .overrideConfiguration(clientConf)
@@ -255,7 +255,7 @@ public class S3Util {
                 .build();
     }
 
-    public static S3Client buildS3Client(URI endpoint, String region, boolean 
isUsePathStyle, String accessKey,
+    public static S3Client buildS3Client(String endpoint, String region, 
boolean isUsePathStyle, String accessKey,
             String secretKey, String sessionToken, String roleArn, String 
externalId) {
         EqualJitterBackoffStrategy backoffStrategy = EqualJitterBackoffStrategy
                 .builder()
@@ -275,11 +275,12 @@ public class S3Util {
                 // using AwsS3V4Signer
                 .putAdvancedOption(SdkAdvancedClientOption.SIGNER, 
AwsS3V4Signer.create())
                 .build();
+        URI endpointUri = URI.create(buildEndpointUrl(endpoint));
         return S3Client.builder()
                 
.httpClient(UrlConnectionHttpClient.builder().socketTimeout(Duration.ofSeconds(30))
                         .connectionTimeout(Duration.ofSeconds(30)).build())
-                .endpointOverride(endpoint)
-                .credentialsProvider(getAwsCredencialsProvider(endpoint, 
region, accessKey, secretKey,
+                .endpointOverride(endpointUri)
+                .credentialsProvider(getAwsCredencialsProvider(endpointUri, 
region, accessKey, secretKey,
                         sessionToken, roleArn, externalId))
                 .region(Region.of(region))
                 .overrideConfiguration(clientConf)
@@ -697,6 +698,13 @@ public class S3Util {
         }
     }
 
+    public static String buildEndpointUrl(String endpoint) {
+        if (endpoint.startsWith("http://";) || endpoint.startsWith("https://";)) 
{
+            return endpoint;
+        }
+        return Config.s3_client_http_scheme + "://" + endpoint;
+    }
+
     /**
      * Check if a path pattern is deterministic, meaning all file paths can be 
determined
      * without listing. A pattern is deterministic if it contains no true 
wildcard characters
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/connectivity/AbstractS3CompatibleConnectivityTester.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/connectivity/AbstractS3CompatibleConnectivityTester.java
index 551448dc767..25b0e5e2995 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/connectivity/AbstractS3CompatibleConnectivityTester.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/connectivity/AbstractS3CompatibleConnectivityTester.java
@@ -62,7 +62,7 @@ public abstract class AbstractS3CompatibleConnectivityTester 
implements StorageC
         String endpoint = properties.getEndpoint();
 
         try (S3Client client = S3Util.buildS3Client(
-                URI.create(endpoint),
+                endpoint,
                 properties.getRegion(),
                 Boolean.parseBoolean(properties.getUsePathStyle()),
                 properties.getAwsCredentialsProvider())) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/dlf/DLFCatalog.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/dlf/DLFCatalog.java
index fb90260f09a..d5dc5795315 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/dlf/DLFCatalog.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/dlf/DLFCatalog.java
@@ -30,7 +30,6 @@ import org.apache.iceberg.aws.s3.S3FileIO;
 import org.apache.iceberg.catalog.TableIdentifier;
 import org.apache.iceberg.io.FileIO;
 
-import java.net.URI;
 import java.util.Map;
 
 public class DLFCatalog extends HiveCompatibleCatalog {
@@ -61,11 +60,7 @@ public class DLFCatalog extends HiveCompatibleCatalog {
         boolean isUsePathStyle = 
Boolean.parseBoolean(ossProperties.getUsePathStyle());
         // s3 file io just supports s3-like endpoint
         String s3Endpoint = endpoint.replace("oss-" + region, "s3.oss-" + 
region);
-        if (!s3Endpoint.contains("://")) {
-            s3Endpoint = "http://"; + s3Endpoint;
-        }
-        URI endpointUri = URI.create(s3Endpoint);
-        FileIO io = new S3FileIO(() -> S3Util.buildS3Client(endpointUri, 
region, credential, isUsePathStyle));
+        FileIO io = new S3FileIO(() -> S3Util.buildS3Client(s3Endpoint, 
region, credential, isUsePathStyle));
         io.initialize(properties);
         return io;
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/fs/obj/S3ObjStorage.java 
b/fe/fe-core/src/main/java/org/apache/doris/fs/obj/S3ObjStorage.java
index 5f14506c4de..de342349022 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/fs/obj/S3ObjStorage.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/fs/obj/S3ObjStorage.java
@@ -83,7 +83,6 @@ import software.amazon.awssdk.services.sts.model.Credentials;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.InputStream;
-import java.net.URI;
 import java.nio.file.FileSystems;
 import java.nio.file.PathMatcher;
 import java.nio.file.Paths;
@@ -116,12 +115,7 @@ public class S3ObjStorage implements ObjStorage<S3Client> {
     @Override
     public S3Client getClient() {
         if (client == null) {
-            String endpointStr = s3Properties.getEndpoint();
-            if (!endpointStr.contains("://")) {
-                endpointStr = "http://"; + endpointStr;
-            }
-            URI endpoint = URI.create(endpointStr);
-            client = S3Util.buildS3Client(endpoint, s3Properties.getRegion(),
+            client = S3Util.buildS3Client(s3Properties.getEndpoint(), 
s3Properties.getRegion(),
                     isUsePathStyle, s3Properties.getAwsCredentialsProvider());
         }
         return client;
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/catalog/S3ResourceTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/catalog/S3ResourceTest.java
index 8a8d23d2a64..e725b967315 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/catalog/S3ResourceTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/S3ResourceTest.java
@@ -196,7 +196,8 @@ public class S3ResourceTest {
         Assert.assertEquals("s3_1", rS3Resource1.getName());
         Assert.assertEquals("s3_2", rS3Resource2.getName());
 
-        Assert.assertEquals(rS3Resource2.getProperty(S3Properties.ENDPOINT), 
"http://aaa";);
+        Assert.assertEquals(rS3Resource2.getProperty(S3Properties.ENDPOINT), 
"aaa");
+        
Assert.assertEquals(rS3Resource2.getProperty(S3Properties.Env.ENDPOINT), "aaa");
         Assert.assertEquals(rS3Resource2.getProperty(S3Properties.REGION), 
"bbb");
         Assert.assertEquals(rS3Resource2.getProperty(S3Properties.ROOT_PATH), 
"/path/to/root");
         Assert.assertEquals(rS3Resource2.getProperty(S3Properties.ACCESS_KEY), 
"xxx");
@@ -228,11 +229,22 @@ public class S3ResourceTest {
         Map<String, String> modify = new HashMap<>();
         modify.put("s3.access_key", "aaa");
         s3Resource.modifyProperties(modify);
+
+        modify.clear();
+        modify.put(S3Properties.ENDPOINT, "new-endpoint");
+        s3Resource.modifyProperties(modify);
+        Assert.assertEquals("new-endpoint", 
s3Resource.getProperty(S3Properties.ENDPOINT));
+        Assert.assertEquals("new-endpoint", 
s3Resource.getProperty(S3Properties.Env.ENDPOINT));
+
+        modify.clear();
+        modify.put(S3Properties.Env.ENDPOINT, "http://other-endpoint";);
+        s3Resource.modifyProperties(modify);
+        Assert.assertEquals("http://other-endpoint";, 
s3Resource.getProperty(S3Properties.ENDPOINT));
+        Assert.assertEquals("http://other-endpoint";, 
s3Resource.getProperty(S3Properties.Env.ENDPOINT));
     }
 
     @Test
-    public void testHttpScheme() throws DdlException {
-        // if https:// is set, it should be replaced with http://
+    public void testExplicitSchemeIsPreserved() throws DdlException {
         ImmutableMap<String, String> properties = ImmutableMap.of(
                 "AWS_ENDPOINT", "https://aaa";,
                 "AWS_REGION", "bbb",
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/common/util/S3UtilTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/common/util/S3UtilTest.java
index ed3cdd56328..764196ae8b8 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/common/util/S3UtilTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/common/util/S3UtilTest.java
@@ -17,9 +17,17 @@
 
 package org.apache.doris.common.util;
 
+import org.apache.doris.common.Config;
+
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.services.s3.S3Client;
 
+import java.net.URI;
 import java.nio.file.FileSystems;
 import java.nio.file.PathMatcher;
 import java.nio.file.Paths;
@@ -27,6 +35,54 @@ import java.util.Arrays;
 import java.util.List;
 
 public class S3UtilTest {
+    private String originalS3ClientHttpScheme;
+
+    @Before
+    public void setUp() {
+        originalS3ClientHttpScheme = Config.s3_client_http_scheme;
+    }
+
+    @After
+    public void tearDown() {
+        Config.s3_client_http_scheme = originalS3ClientHttpScheme;
+    }
+
+    @Test
+    public void testBuildEndpointUrlDefaultsToHttps() {
+        Config.s3_client_http_scheme = "https";
+        Assert.assertEquals("https://s3.us-east-1.amazonaws.com";,
+                S3Util.buildEndpointUrl("s3.us-east-1.amazonaws.com"));
+    }
+
+    @Test
+    public void testBuildEndpointUrlUsesConfiguredHttpScheme() {
+        Config.s3_client_http_scheme = "http";
+        Assert.assertEquals("http://127.0.0.1:9000";,
+                S3Util.buildEndpointUrl("127.0.0.1:9000"));
+    }
+
+    @Test
+    public void testBuildEndpointUrlPreservesExplicitSchemes() {
+        Config.s3_client_http_scheme = "https";
+        Assert.assertEquals("http://127.0.0.1:9000";,
+                S3Util.buildEndpointUrl("http://127.0.0.1:9000";));
+
+        Config.s3_client_http_scheme = "http";
+        Assert.assertEquals("https://s3.us-east-1.amazonaws.com";,
+                S3Util.buildEndpointUrl("https://s3.us-east-1.amazonaws.com";));
+    }
+
+    @Test
+    public void testBuildS3ClientAppliesDefaultSchemeAtClientCreation() {
+        Config.s3_client_http_scheme = "https";
+        try (S3Client client = S3Util.buildS3Client(
+                "127.0.0.1:9000", "us-east-1", true,
+                
StaticCredentialsProvider.create(AwsBasicCredentials.create("ak", "sk")))) {
+            Assert.assertEquals(URI.create("https://127.0.0.1:9000";),
+                    
client.serviceClientConfiguration().endpointOverride().get());
+        }
+    }
+
 
     @Test
     public void testExtendGlobNumberRange_simpleRange() {
diff --git a/regression-test/pipeline/vault_p0/conf/be_custom.conf 
b/regression-test/pipeline/vault_p0/conf/be_custom.conf
index 779eb5a77a5..3a2a0e16213 100644
--- a/regression-test/pipeline/vault_p0/conf/be_custom.conf
+++ b/regression-test/pipeline/vault_p0/conf/be_custom.conf
@@ -11,6 +11,7 @@ enable_file_cache=true
 enable_file_cache_query_limit=true
 file_cache_max_file_segment_size=1048576
 s3_write_buffer_whole_size=52428800
+s3_client_http_scheme = http
 enable_vertical_compaction=true
 fuzzy_vertical_compaction=true
 vacuum_stale_rowsets_interval_seconds=60
diff --git a/regression-test/pipeline/vault_p0/conf/fe_custom.conf 
b/regression-test/pipeline/vault_p0/conf/fe_custom.conf
index f62ffa19fc7..9b622795168 100644
--- a/regression-test/pipeline/vault_p0/conf/fe_custom.conf
+++ b/regression-test/pipeline/vault_p0/conf/fe_custom.conf
@@ -12,6 +12,7 @@ enable_mtmv = true
 remote_fragment_exec_timeout_ms=60000
 dynamic_partition_check_interval_seconds=10
 use_fuzzy_session_variable=true
+s3_client_http_scheme = http
 
 enable_cloud_snapshot_version = true
 enable_auto_collect_statistics = false
diff --git a/regression-test/pipeline/vault_p0/conf/recycler_custom.conf 
b/regression-test/pipeline/vault_p0/conf/recycler_custom.conf
index 62deff2b870..3c3dc627122 100644
--- a/regression-test/pipeline/vault_p0/conf/recycler_custom.conf
+++ b/regression-test/pipeline/vault_p0/conf/recycler_custom.conf
@@ -1,2 +1,3 @@
 # below lines will be appended to the default doris_cloud.conf when deploying 
recycler
 brpc_listen_port = 6000
+s3_client_http_scheme = http
diff --git a/regression-test/suites/cold_heat_separation/policy/alter.groovy 
b/regression-test/suites/cold_heat_separation/policy/alter.groovy
index cfd8e0da6fc..22903e672ba 100644
--- a/regression-test/suites/cold_heat_separation/policy/alter.groovy
+++ b/regression-test/suites/cold_heat_separation/policy/alter.groovy
@@ -174,7 +174,7 @@ suite("alter_policy") {
         // AWS_CONNECTION_TIMEOUT_MS
         assertEquals(show_alter_result[5][3], "2222")
         // AWS_ENDPOINT
-        assertEquals(show_alter_result[6][3], "http://bj.s3.comaaaa";)
+        assertEquals(show_alter_result[6][3], "bj.s3.comaaaa")
         // AWS_REGION
         assertEquals(show_alter_result[7][3], "bj")
         // s3_rootpath


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to