Repository: netty/netty
Issue: why stomp.js can not get the message body

### Expected behavior
Expect the stomp.js can use netty stomp send a messge to a topic and other stomp.js can receive the message.
### Actual behavior
when Iuse netty stomp send a messge to a topic and other stomp.js  receive the message,I found the stomp.js client can not recevice the message the message body is empty. In the web brower network console chosse ws you will find the message is split to two message .
### Steps to reproduce
use the code in package io.netty.example.stomp  and start the server, use stomp.js send a messge to a topic and other stomp.js receive the message.
### Minimal yet complete reproducer code (or URL to code)

### Netty version
4.1.49.Final

### JVM version (e.g. `java -version`)
java version "1.8.0_231"
### OS version (e.g. `uname -a`)
windows 10

Issue comments:
Hi, I take a look 

Pull request review comments:
@amizurov unfortunately this is a a breaking change as it changes the method signature of some methods 
Yes but this is only example, maybe you can advice another way ?
ah sorry missed that... Changing an example is of course fine.
hmm... is this really correct for the case where `msg` isn't an instance of `StompFrame ` ? Seems like you may loose items here then.
We use this encoder inside `StompWebSocketProtocolCodec` so we accept only `StompSubframe` and don't put it to pipeline, just delegate.
still this seems very error-prone. At least we should add an assert imho
Sorry could you please explain what kind of assertion you mean ? 
never mind 

Patch that resolved the issue:
diff --git a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketFrameEncoder.java b/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketFrameEncoder.java
new file mode 100644
index 00000000000..68b92e6992f
--- /dev/null
+++ b/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketFrameEncoder.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Netty Project
+ *
+ * The Netty Project 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 io.netty.example.stomp.websocket;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.CompositeByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
+import io.netty.handler.codec.http.websocketx.WebSocketFrame;
+import io.netty.handler.codec.stomp.LastStompContentSubframe;
+import io.netty.handler.codec.stomp.StompFrame;
+import io.netty.handler.codec.stomp.StompHeadersSubframe;
+import io.netty.handler.codec.stomp.StompSubframe;
+import io.netty.handler.codec.stomp.StompSubframeEncoder;
+
+import java.util.List;
+
+public class StompWebSocketFrameEncoder extends StompSubframeEncoder {
+
+    @Override
+    public void encode(ChannelHandlerContext ctx, StompSubframe msg, List<Object> out) throws Exception {
+        super.encode(ctx, msg, out);
+
+        if (out.isEmpty()) {
+            return;
+        }
+
+        final WebSocketFrame webSocketFrame;
+        if (msg instanceof StompFrame) {
+            if (out.size() == 1) {
+                webSocketFrame = new TextWebSocketFrame(getFirst(out));
+            } else {
+                CompositeByteBuf content = ctx.alloc().compositeBuffer(out.size());
+                for (Object byteBuf : out) {
+                    content.addComponent(true, (ByteBuf) byteBuf);
+                }
+                webSocketFrame = new TextWebSocketFrame(content);
+            }
+        } else if (msg instanceof StompHeadersSubframe) {
+            webSocketFrame = new TextWebSocketFrame(false, 0, getFirst(out));
+        } else if (msg instanceof LastStompContentSubframe) {
+            webSocketFrame = new ContinuationWebSocketFrame(true, 0, getFirst(out));
+        } else {
+            webSocketFrame = new ContinuationWebSocketFrame(false, 0, getFirst(out));
+        }
+
+        out.clear();
+        out.add(webSocketFrame);
+    }
+
+    private static ByteBuf getFirst(List<Object> container) {
+        return (ByteBuf) container.get(0);
+    }
+}
diff --git a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketProtocolCodec.java b/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketProtocolCodec.java
index 97870787748..8d8ed4b7f98 100644
--- a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketProtocolCodec.java
+++ b/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketProtocolCodec.java
@@ -15,24 +15,25 @@
  */
 package io.netty.example.stomp.websocket;
 
-import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.MessageToMessageCodec;
 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
 import io.netty.handler.codec.http.websocketx.WebSocketFrame;
+import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator;
 import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
 import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.HandshakeComplete;
+import io.netty.handler.codec.stomp.StompSubframe;
 import io.netty.handler.codec.stomp.StompSubframeAggregator;
 import io.netty.handler.codec.stomp.StompSubframeDecoder;
-import io.netty.handler.codec.stomp.StompSubframeEncoder;
 
 import java.util.List;
 
 @Sharable
-public class StompWebSocketProtocolCodec extends MessageToMessageCodec<WebSocketFrame, ByteBuf> {
+public class StompWebSocketProtocolCodec extends MessageToMessageCodec<WebSocketFrame, StompSubframe> {
 
     private final StompChatHandler stompChatHandler = new StompChatHandler();
+    private final StompWebSocketFrameEncoder stompWebSocketFrameEncoder = new StompWebSocketFrameEncoder();
 
     @Override
     public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
@@ -40,8 +41,8 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc
             StompVersion stompVersion = StompVersion.findBySubProtocol(((HandshakeComplete) evt).selectedSubprotocol());
             ctx.channel().attr(StompVersion.CHANNEL_ATTRIBUTE_KEY).set(stompVersion);
             ctx.pipeline()
+               .addLast(new WebSocketFrameAggregator(65536))
                .addLast(new StompSubframeDecoder())
-               .addLast(new StompSubframeEncoder())
                .addLast(new StompSubframeAggregator(65536))
                .addLast(stompChatHandler)
                .remove(StompWebSocketClientPageHandler.INSTANCE);
@@ -51,8 +52,8 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc
     }
 
     @Override
-    protected void encode(ChannelHandlerContext ctx, ByteBuf stompFrame, List<Object> out) {
-        out.add(new TextWebSocketFrame(stompFrame.retain()));
+    protected void encode(ChannelHandlerContext ctx, StompSubframe stompFrame, List<Object> out) throws Exception {
+        stompWebSocketFrameEncoder.encode(ctx, stompFrame, out);
     }
 
     @Override


Test patch: