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 d689e4e541 fix: honor retain flag in mqtt publish message (#6922)
d689e4e541 is described below

commit d689e4e541e6a9d128a4a866b163aa3a0436e535
Author: wy471x <[email protected]>
AuthorDate: Mon Aug 17 16:16:57 2026 +0800

    fix: honor retain flag in mqtt publish message (#6922)
    
    Only store the message in TopicRepository when the RETAIN flag is set,
    and remove the retained message when a zero-byte retained PUBLISH is
    received (MQTT-3.3.1).
    
    Co-authored-by: Claude Opus 4.7 <[email protected]>
    Co-authored-by: aias00 <[email protected]>
---
 .../org/apache/shenyu/protocol/mqtt/Publish.java   |  8 +-
 .../apache/shenyu/protocol/mqtt/PublishTest.java   | 88 ++++++++++++++++++++++
 2 files changed, 95 insertions(+), 1 deletion(-)

diff --git 
a/shenyu-protocol/shenyu-protocol-mqtt/src/main/java/org/apache/shenyu/protocol/mqtt/Publish.java
 
b/shenyu-protocol/shenyu-protocol-mqtt/src/main/java/org/apache/shenyu/protocol/mqtt/Publish.java
index b8804cf6fa..bd7f48ef96 100644
--- 
a/shenyu-protocol/shenyu-protocol-mqtt/src/main/java/org/apache/shenyu/protocol/mqtt/Publish.java
+++ 
b/shenyu-protocol/shenyu-protocol-mqtt/src/main/java/org/apache/shenyu/protocol/mqtt/Publish.java
@@ -52,7 +52,13 @@ public class Publish extends MessageType {
         String message = byteBufToString(payload);
         //// todo qos
         MqttQoS mqttQoS = msg.fixedHeader().qosLevel();
-        Singleton.INST.get(TopicRepository.class).add(topic, message);
+        if (msg.fixedHeader().isRetain()) {
+            if (payload.isReadable()) {
+                Singleton.INST.get(TopicRepository.class).add(topic, message);
+            } else {
+                Singleton.INST.get(TopicRepository.class).remove(topic);
+            }
+        }
         int packetId = msg.variableHeader().packetId();
         CompletableFuture.runAsync(() -> send(topic, payload, packetId));
 
diff --git 
a/shenyu-protocol/shenyu-protocol-mqtt/src/test/java/org/apache/shenyu/protocol/mqtt/PublishTest.java
 
b/shenyu-protocol/shenyu-protocol-mqtt/src/test/java/org/apache/shenyu/protocol/mqtt/PublishTest.java
new file mode 100644
index 0000000000..6d25ff0352
--- /dev/null
+++ 
b/shenyu-protocol/shenyu-protocol-mqtt/src/test/java/org/apache/shenyu/protocol/mqtt/PublishTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.mqtt.MqttFixedHeader;
+import io.netty.handler.codec.mqtt.MqttMessageType;
+import io.netty.handler.codec.mqtt.MqttPublishMessage;
+import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
+import io.netty.handler.codec.mqtt.MqttQoS;
+import io.netty.util.CharsetUtil;
+import org.apache.shenyu.common.utils.Singleton;
+import org.apache.shenyu.protocol.mqtt.repositories.SubscribeRepository;
+import org.apache.shenyu.protocol.mqtt.repositories.TopicRepository;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Test cases for {@link Publish}.
+ */
+public final class PublishTest {
+
+    private static final String RETAINED_TOPIC = "test/retained";
+
+    private static final String NON_RETAINED_TOPIC = "test/non-retained";
+
+    private static final String CLEARED_TOPIC = "test/cleared";
+
+    private static TopicRepository topicRepository;
+
+    @BeforeAll
+    static void setUp() {
+        topicRepository = new TopicRepository();
+        Singleton.INST.single(TopicRepository.class, topicRepository);
+        Singleton.INST.single(SubscribeRepository.class, new 
SubscribeRepository());
+    }
+
+    @Test
+    public void retainedPublishStoresMessage() {
+        new Publish().publish(mock(ChannelHandlerContext.class), 
publishMessage(RETAINED_TOPIC, "hello", true));
+        await().atMost(Duration.ofSeconds(5))
+                .until(() -> 
"hello".equals(topicRepository.get(RETAINED_TOPIC)));
+    }
+
+    @Test
+    public void nonRetainedPublishDoesNotStoreMessage() {
+        new Publish().publish(mock(ChannelHandlerContext.class), 
publishMessage(NON_RETAINED_TOPIC, "hello", false));
+        assertNull(topicRepository.get(NON_RETAINED_TOPIC));
+    }
+
+    @Test
+    public void zeroByteRetainedPublishClearsRetainedMessage() {
+        Publish publish = new Publish();
+        publish.publish(mock(ChannelHandlerContext.class), 
publishMessage(CLEARED_TOPIC, "hello", true));
+        await().atMost(Duration.ofSeconds(5))
+                .until(() -> 
"hello".equals(topicRepository.get(CLEARED_TOPIC)));
+        publish.publish(mock(ChannelHandlerContext.class), 
publishMessage(CLEARED_TOPIC, "", true));
+        assertNull(topicRepository.get(CLEARED_TOPIC));
+    }
+
+    private MqttPublishMessage publishMessage(final String topic, final String 
payload, final boolean retain) {
+        MqttFixedHeader fixedHeader = new 
MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_MOST_ONCE, retain, 
0);
+        MqttPublishVariableHeader variableHeader = new 
MqttPublishVariableHeader(topic, 1);
+        return new MqttPublishMessage(fixedHeader, variableHeader, 
Unpooled.copiedBuffer(payload, CharsetUtil.UTF_8));
+    }
+}

Reply via email to