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

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


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 4413477105c [fix](fe) Fix HTTP system forwarding on follower FE 
(#68827)
4413477105c is described below

commit 4413477105c1743e9401dbdb3708c20f9e13e0c2
Author: Calvin Kirs <[email protected]>
AuthorDate: Sat Oct 10 09:32:32 2026 +0800

    [fix](fe) Fix HTTP system forwarding on follower FE (#68827)
    
    https://github.com/apache/doris/pull/68823
---
 .../java/org/apache/doris/qe/FEOpExecutor.java     |  9 +++--
 .../doris/qe/FEOpExecutorMysqlProtocolTest.java    | 24 ++++++++++++
 .../test_http_rest_v1_system_follower.groovy       | 45 ++++++++++++++++++++++
 3 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/FEOpExecutor.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/FEOpExecutor.java
index fe0de92857d..21420b31c22 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/FEOpExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/FEOpExecutor.java
@@ -23,6 +23,7 @@ import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.ClientPool;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.ErrorCode;
+import org.apache.doris.mysql.DummyMysqlChannel;
 import org.apache.doris.mysql.MysqlCommand;
 import org.apache.doris.mysql.MysqlCursorFetchCompatibility;
 import org.apache.doris.mysql.MysqlProto;
@@ -222,10 +223,10 @@ public class FEOpExecutor {
 
         // Propagate the client's CLIENT_DEPRECATE_EOF capability so the 
master FE
         // generates packets matching the original client's protocol 
expectations.
-        // Only a MySQL connection negotiates this capability and owns a 
MysqlChannel;
-        // an Arrow Flight SQL session has none, and leaving the field unset 
keeps the
-        // master on its default packet layout.
-        if (ctx.getConnectType() == ConnectType.MYSQL) {
+        // HTTP contexts use a dummy MySQL channel without a negotiated 
capability.
+        // Leave their protocol fields unset so the master uses its default 
packet layout.
+        if (ctx.getConnectType() == ConnectType.MYSQL
+                && !(ctx.getMysqlChannel() instanceof DummyMysqlChannel && 
ctx.getCapability() == null)) {
             
params.setClientDeprecatedEOF(ctx.getMysqlChannel().clientDeprecatedEOF());
             params.setMysqlCapability(ctx.getCapability().getFlags());
         }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/qe/FEOpExecutorMysqlProtocolTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/qe/FEOpExecutorMysqlProtocolTest.java
index a3e235f56f5..de0c3e2ee8e 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/qe/FEOpExecutorMysqlProtocolTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/qe/FEOpExecutorMysqlProtocolTest.java
@@ -60,6 +60,7 @@ public class FEOpExecutorMysqlProtocolTest {
             TMasterOpRequest request = new TestFEOpExecutor(context).build();
 
             Assertions.assertTrue(request.isClientDeprecatedEOF());
+            Assertions.assertEquals(context.getCapability().getFlags(), 
request.getMysqlCapability());
             Assertions.assertTrue(request.isCursorFetchRequested());
             Assertions.assertEquals("8.2.0", 
request.getConnectAttributes().get("_client_version"));
 
@@ -107,6 +108,29 @@ public class FEOpExecutorMysqlProtocolTest {
         }
     }
 
+    @Test
+    public void testHttpForwardRequestUsesDefaultMysqlCapability() throws 
Exception {
+        Env env = Mockito.mock(Env.class);
+        Mockito.when(env.getSelfNode()).thenReturn(new 
SystemInfoService.HostInfo("127.0.0.1", 9010));
+        try (MockedStatic<Env> mockedEnv = Mockito.mockStatic(Env.class)) {
+            mockedEnv.when(Env::getCurrentEnv).thenReturn(env);
+            ConnectContext context = new ConnectContext();
+            
context.setCurrentUserIdentity(UserIdentity.createAnalyzedUserIdentWithIp("alice",
 "%"));
+            context.setRemoteIP("127.0.0.1");
+            Assertions.assertNull(context.getCapability());
+
+            TMasterOpRequest request = new TestFEOpExecutor(context).build();
+            Assertions.assertFalse(request.isSetClientDeprecatedEOF());
+            Assertions.assertFalse(request.isSetMysqlCapability());
+
+            ConnectContext forwardedContext = createContext();
+            ConnectProcessor.restoreForwardedMysqlContext(forwardedContext, 
request);
+            int expectedFlags = MysqlCapability.DEFAULT_CAPABILITY.getFlags()
+                    & ~MysqlCapability.Flag.CLIENT_DEPRECATE_EOF.getFlagBit();
+            Assertions.assertEquals(expectedFlags, 
forwardedContext.getCapability().getFlags());
+        }
+    }
+
     @Test
     public void testForwardedCapabilityAndMissingCursorFlag() throws Exception 
{
         int legacyFlags = MysqlCapability.DEFAULT_CAPABILITY.getFlags()
diff --git 
a/regression-test/suites/auth_p0/test_http_rest_v1_system_follower.groovy 
b/regression-test/suites/auth_p0/test_http_rest_v1_system_follower.groovy
new file mode 100644
index 00000000000..b928969f2f9
--- /dev/null
+++ b/regression-test/suites/auth_p0/test_http_rest_v1_system_follower.groovy
@@ -0,0 +1,45 @@
+// 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.
+
+import org.apache.doris.regression.suite.ClusterOptions
+
+suite("test_http_rest_v1_system_follower", "docker") {
+    def options = new ClusterOptions()
+    options.setFeNum(2)
+    options.setBeNum(1)
+    docker(options) {
+        def follower = cluster.getAllFrontends(true).find { !it.isMaster }
+        assertNotNull(follower)
+
+        // Wait for the follower to replay the journal before authenticating 
over HTTP.
+        String followerJdbcUrl = 
"jdbc:mysql://${follower.host}:${follower.queryPort}/"
+        connect(context.config.jdbcUser, context.config.jdbcPassword, 
followerJdbcUrl) {
+            sql "SYNC"
+        }
+
+        httpTest {
+            basicAuthorization "${context.config.jdbcUser}", 
"${context.config.jdbcPassword}"
+            endpoint "${follower.host}:${follower.httpPort}"
+            uri "/rest/v1/system?path=/"
+            op "get"
+            check { respCode, body ->
+                assertEquals(200, respCode)
+                assertEquals(0, parseJson(body).code)
+            }
+        }
+    }
+}


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

Reply via email to