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

Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new 2eeaa5da4c fix: accept MQTT 3.1.1 and 5.0 connect protocol versions 
(#6938)
2eeaa5da4c is described below

commit 2eeaa5da4c984b32d5dee81e63702c8d651f1588
Author: wy471x <[email protected]>
AuthorDate: Sat Aug 22 11:42:43 2026 +0800

    fix: accept MQTT 3.1.1 and 5.0 connect protocol versions (#6938)
    
    Co-authored-by: Claude Opus 4.7 <[email protected]>
    Co-authored-by: aias00 <[email protected]>
---
 .../org/apache/shenyu/protocol/mqtt/Connect.java   |   6 +-
 .../apache/shenyu/protocol/mqtt/ConnectTest.java   | 134 +++++++++++++++++++++
 2 files changed, 139 insertions(+), 1 deletion(-)

diff --git 
a/shenyu-protocol/shenyu-protocol-mqtt/src/main/java/org/apache/shenyu/protocol/mqtt/Connect.java
 
b/shenyu-protocol/shenyu-protocol-mqtt/src/main/java/org/apache/shenyu/protocol/mqtt/Connect.java
index 8a9fa005a4..bca27d001d 100644
--- 
a/shenyu-protocol/shenyu-protocol-mqtt/src/main/java/org/apache/shenyu/protocol/mqtt/Connect.java
+++ 
b/shenyu-protocol/shenyu-protocol-mqtt/src/main/java/org/apache/shenyu/protocol/mqtt/Connect.java
@@ -54,6 +54,7 @@ public class Connect extends MessageType {
         if (!allowedProtocolVersion(msg)) {
             LOG.info("MQTT protocol version is not supported. clientId: {}", 
clientId);
             close(ctx, CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION);
+            return;
         }
 
         String userName = msg.payload().userName();
@@ -88,6 +89,9 @@ public class Connect extends MessageType {
     }
 
     private boolean allowedProtocolVersion(final MqttConnectMessage msg) {
-        return msg.variableHeader().version() == 
MqttVersion.MQTT_3_1.protocolLevel();
+        int protocolLevel = msg.variableHeader().version();
+        return protocolLevel == MqttVersion.MQTT_3_1.protocolLevel()
+                || protocolLevel == MqttVersion.MQTT_3_1_1.protocolLevel()
+                || protocolLevel == MqttVersion.MQTT_5.protocolLevel();
     }
 }
diff --git 
a/shenyu-protocol/shenyu-protocol-mqtt/src/test/java/org/apache/shenyu/protocol/mqtt/ConnectTest.java
 
b/shenyu-protocol/shenyu-protocol-mqtt/src/test/java/org/apache/shenyu/protocol/mqtt/ConnectTest.java
new file mode 100644
index 0000000000..0be443f7c9
--- /dev/null
+++ 
b/shenyu-protocol/shenyu-protocol-mqtt/src/test/java/org/apache/shenyu/protocol/mqtt/ConnectTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+package org.apache.shenyu.protocol.mqtt;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.mqtt.MqttConnAckMessage;
+import io.netty.handler.codec.mqtt.MqttConnectMessage;
+import io.netty.handler.codec.mqtt.MqttConnectPayload;
+import io.netty.handler.codec.mqtt.MqttConnectVariableHeader;
+import io.netty.handler.codec.mqtt.MqttFixedHeader;
+import io.netty.handler.codec.mqtt.MqttMessageType;
+import io.netty.handler.codec.mqtt.MqttQoS;
+import io.netty.handler.codec.mqtt.MqttVersion;
+import org.apache.shenyu.common.utils.Singleton;
+import org.apache.shenyu.protocol.mqtt.repositories.ChannelRepository;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+
+import static 
io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED;
+import static 
io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION;
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test cases for {@link Connect}.
+ */
+public final class ConnectTest {
+
+    private static final String CLIENT_ID = "test-client";
+
+    private static final String USER_NAME = "test-user";
+
+    private static final String PASSWORD = "test-password";
+
+    private static ChannelRepository channelRepository;
+
+    @BeforeAll
+    static void setUp() {
+        channelRepository = new ChannelRepository();
+        Singleton.INST.single(ChannelRepository.class, channelRepository);
+        new MqttContext().setUserName(USER_NAME);
+        new MqttContext().setPassword(PASSWORD);
+    }
+
+    @AfterAll
+    static void tearDown() {
+        new MqttContext().setUserName(null);
+        new MqttContext().setPassword(null);
+    }
+
+    @Test
+    public void mqtt31ConnectIsAccepted() {
+        connectIsAccepted(MqttVersion.MQTT_3_1);
+    }
+
+    @Test
+    public void mqtt311ConnectIsAccepted() {
+        connectIsAccepted(MqttVersion.MQTT_3_1_1);
+    }
+
+    @Test
+    public void mqtt5ConnectIsAccepted() {
+        connectIsAccepted(MqttVersion.MQTT_5);
+    }
+
+    @Test
+    public void unsupportedProtocolVersionIsRejected() {
+        ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+        Channel channel = mock(Channel.class);
+        when(ctx.channel()).thenReturn(channel);
+        when(ctx.close()).thenReturn(mock(ChannelFuture.class));
+
+        new Connect().connect(ctx, connectMessage("MQTT", 6));
+
+        ArgumentCaptor<MqttConnAckMessage> captor = 
ArgumentCaptor.forClass(MqttConnAckMessage.class);
+        verify(ctx, times(1)).writeAndFlush(captor.capture());
+        assertEquals(CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION,
+                captor.getValue().variableHeader().connectReturnCode());
+        verify(ctx).close();
+        assertNull(channelRepository.get(channel));
+    }
+
+    private void connectIsAccepted(final MqttVersion version) {
+        ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+        Channel channel = mock(Channel.class);
+        when(ctx.channel()).thenReturn(channel);
+
+        new Connect().connect(ctx, connectMessage(version.protocolName(), 
version.protocolLevel()));
+
+        ArgumentCaptor<MqttConnAckMessage> captor = 
ArgumentCaptor.forClass(MqttConnAckMessage.class);
+        verify(ctx).writeAndFlush(captor.capture());
+        assertEquals(CONNECTION_ACCEPTED, 
captor.getValue().variableHeader().connectReturnCode());
+        assertTrue(captor.getValue().variableHeader().isSessionPresent());
+        await().atMost(Duration.ofSeconds(5))
+                .until(() -> CLIENT_ID.equals(channelRepository.get(channel)));
+    }
+
+    private MqttConnectMessage connectMessage(final String protocolName, final 
int protocolLevel) {
+        MqttFixedHeader fixedHeader = new 
MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
+        MqttConnectVariableHeader variableHeader = new 
MqttConnectVariableHeader(protocolName, protocolLevel,
+                true, true, false, 0, false, false, 60);
+        MqttConnectPayload payload = new MqttConnectPayload(CLIENT_ID, null, 
null,
+                USER_NAME, PASSWORD.getBytes(StandardCharsets.UTF_8));
+        return new MqttConnectMessage(fixedHeader, variableHeader, payload);
+    }
+}

Reply via email to