This is an automated email from the ASF dual-hosted git repository.
CalvinKirs pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 626b24c1ca9 [fix](fe) Fix HTTP system requests forwarded to the master
(#68823)
626b24c1ca9 is described below
commit 626b24c1ca99ce0ec63ae18051e34100e2611185
Author: Calvin Kirs <[email protected]>
AuthorDate: Sat Oct 10 11:04:05 2026 +0800
[fix](fe) Fix HTTP system requests forwarded to the master (#68823)
### What problem does this PR solve?
An authorized `/rest/v1/system` request on a non-master FE forwards
`SHOW PROC` with an HTTP context that has no negotiated MySQL
capability. Reading that capability returns JSON business code 500. Skip
MySQL protocol fields for this context while preserving forwarding for
connections with an explicit capability.
### Release note
Fix `/rest/v1/system` on non-master FEs.
### Check List (For Author)
- Test: 21 targeted FE unit tests passed; Checkstyle passed. HTTP
regression coverage added but not run locally (no cluster).
- Behavior changed: Yes, authorized HTTP system requests can be
forwarded successfully.
- Does this need documentation: No.
---
.../doris/mysql/protocol/MysqlProtocolAdapter.java | 8 +++++-
.../mysql/protocol/MysqlProtocolAdapterTest.java | 29 ++++++++++++++++++++--
.../suites/auth_p0/test_http_rest_v1_auth.groovy | 17 +++++++++++++
3 files changed, 51 insertions(+), 3 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/protocol/MysqlProtocolAdapter.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/protocol/MysqlProtocolAdapter.java
index 5f76d55772f..4279566e15c 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/mysql/protocol/MysqlProtocolAdapter.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/mysql/protocol/MysqlProtocolAdapter.java
@@ -18,6 +18,7 @@
package org.apache.doris.mysql.protocol;
import org.apache.doris.common.util.DebugUtil;
+import org.apache.doris.mysql.DummyMysqlChannel;
import org.apache.doris.mysql.MysqlCapability;
import org.apache.doris.mysql.MysqlChannel;
import org.apache.doris.mysql.MysqlCommand;
@@ -168,10 +169,15 @@ public class MysqlProtocolAdapter implements
ProtocolAdapter {
* The master encodes the response of a forwarded statement for this
connection's client, so
* it needs the client's negotiated capabilities, and for a forwarded
COM_STMT_EXECUTE the
* execute packet and whether it asked for a cursor. {@link
#restoreFromForwardRequest} is the
- * master's side.
+ * master's side. HTTP and internal contexts have no client protocol state
to forward.
*/
@Override
public void fillForwardRequest(ConnectContext ctx, TMasterOpRequest
request) {
+ // HTTP and internal contexts have no negotiated capability. A dummy
channel with an
+ // explicitly supplied capability still carries that state when
forwarding.
+ if (channel instanceof DummyMysqlChannel && capability == null) {
+ return;
+ }
if (ctx.getCommand() == MysqlCommand.COM_STMT_EXECUTE) {
if (prepareExecuteBuffer != null) {
request.setPrepareExecuteBuffer(prepareExecuteBuffer);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/mysql/protocol/MysqlProtocolAdapterTest.java
b/fe/fe-core/src/test/java/org/apache/doris/mysql/protocol/MysqlProtocolAdapterTest.java
index bc4fcb7ea71..22886f63941 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/mysql/protocol/MysqlProtocolAdapterTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/mysql/protocol/MysqlProtocolAdapterTest.java
@@ -277,9 +277,30 @@ public class MysqlProtocolAdapterTest {
Assertions.assertEquals(1, channel.getOutbound().size());
}
+ @Test
+ public void testHttpContextForwardsWithoutMysqlCapabilities() {
+ // HTTP controllers create an internal context without a MySQL
handshake.
+ ConnectContext ctx = new ConnectContext();
+ Assertions.assertNull(ctx.getCapability());
+ TMasterOpRequest request = new TMasterOpRequest();
+
+ ctx.getProtocolAdapter().fillForwardRequest(ctx, request);
+
+ Assertions.assertFalse(request.isSetMysqlCapability());
+ Assertions.assertFalse(request.isSetClientDeprecatedEOF());
+ Assertions.assertFalse(request.isSetPrepareExecuteBuffer());
+ Assertions.assertFalse(request.isSetCursorFetchRequested());
+
+ // The master can execute the request using its default proxy
capabilities.
+ ConnectContext proxy = ConnectContext.forMysqlProxy("http-session");
+ MysqlProtocolAdapter.of(proxy).restoreFromForwardRequest(proxy,
request);
+ Assertions.assertNotNull(proxy.getCapability());
+ Assertions.assertFalse(proxy.getCapability().isDeprecatedEOF());
+ }
+
@Test
public void testForwardRequestCarriesWhatTheMasterNeedsToAnswerTheClient()
{
- RecordingMysqlChannel channel = new RecordingMysqlChannel();
+ MysqlChannel channel = Mockito.mock(MysqlChannel.class);
ConnectContext ctx = new ConnectContext(new
MysqlProtocolAdapter(channel));
MysqlProtocolAdapter protocol = MysqlProtocolAdapter.of(ctx);
int flags = MysqlCapability.DEFAULT_CAPABILITY.getFlags()
@@ -296,19 +317,23 @@ public class MysqlProtocolAdapterTest {
// A COM_STMT_EXECUTE from a client that deprecated EOF: the execute
packet and the cursor
// flag travel too.
- channel.setClientDeprecatedEOF();
+ flags |= MysqlCapability.Flag.CLIENT_DEPRECATE_EOF.getFlagBit();
+ ctx.setCapability(new MysqlCapability(flags));
+ Mockito.when(channel.clientDeprecatedEOF()).thenReturn(true);
ctx.setCommand(MysqlCommand.COM_STMT_EXECUTE);
ctx.setPrepareExecuteBuffer(ByteBuffer.wrap(new byte[] {7, 0, 0, 0}));
ctx.setCursorFetchRequested(true);
request = new TMasterOpRequest();
protocol.fillForwardRequest(ctx, request);
Assertions.assertTrue(request.isClientDeprecatedEOF());
+ Assertions.assertEquals(flags, request.getMysqlCapability());
Assertions.assertArrayEquals(new byte[] {7, 0, 0, 0},
request.getPrepareExecuteBuffer());
Assertions.assertTrue(request.isCursorFetchRequested());
// The master's proxy context takes them over.
ConnectContext proxy = ConnectContext.forMysqlProxy("session-1");
MysqlProtocolAdapter.of(proxy).restoreFromForwardRequest(proxy,
request);
+ Assertions.assertEquals(flags, proxy.getCapability().getFlags());
Assertions.assertTrue(proxy.getCapability().isDeprecatedEOF());
Assertions.assertTrue(proxy.getMysqlChannel().clientDeprecatedEOF());
Assertions.assertTrue(proxy.isCursorFetchRequested());
diff --git a/regression-test/suites/auth_p0/test_http_rest_v1_auth.groovy
b/regression-test/suites/auth_p0/test_http_rest_v1_auth.groovy
index f6cdd0444c8..66ad68320e2 100644
--- a/regression-test/suites/auth_p0/test_http_rest_v1_auth.groovy
+++ b/regression-test/suites/auth_p0/test_http_rest_v1_auth.groovy
@@ -87,4 +87,21 @@ suite("test_http_rest_v1_auth", "p0,auth") {
assertEquals(200, respCode)
assertEquals(200, parseJson(body).code)
}
+
+ // Exercise both local execution on the master and SHOW PROC forwarding
from other FEs.
+ sql_return_maparray("SHOW FRONTENDS").findAll {
+ "${it.Alive}".equalsIgnoreCase("true")
+ }.each { frontend ->
+ httpTest {
+ basicAuthorization "${user}", "${pwd}"
+ endpoint "${frontend.Host}:${frontend.HttpPort}"
+ uri "/rest/v1/system?path=/"
+ op "get"
+ check { respCode, body ->
+ log.info("system (${frontend.Host}:${frontend.HttpPort})
body:${body}")
+ assertEquals(200, respCode)
+ assertEquals(0, parseJson(body).code)
+ }
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]