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

jbertram pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git


The following commit(s) were added to refs/heads/main by this push:
     new c4501f6793 ARTEMIS-4294 support text-encoded WebSocket frames
c4501f6793 is described below

commit c4501f6793ab0e59e88bf6c4dba06b39ade84889
Author: Justin Bertram <[email protected]>
AuthorDate: Fri May 26 10:32:07 2023 -0500

    ARTEMIS-4294 support text-encoded WebSocket frames
---
 .../remoting/impl/netty/TransportConstants.java    |   4 +
 .../artemis/core/protocol/ProtocolHandler.java     |   4 +-
 .../artemis/core/server/ActiveMQMessageBundle.java |   3 +
 .../protocol/websocket/WebSocketFrameEncoder.java  | 145 +++++++++++----------
 .../websocket/WebSocketFrameEncoderType.java       |  35 +++++
 .../protocol/websocket/WebSocketServerHandler.java |  26 ++--
 .../websocket/WebSocketFrameEncoderTest.java       |  87 +++++++++----
 .../websocket/WebSocketServerHandlerTest.java      |   2 +-
 docs/user-manual/en/stomp.md                       |   5 +
 9 files changed, 201 insertions(+), 110 deletions(-)

diff --git 
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java
 
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java
index 3edf16f755..acaa72ad45 100644
--- 
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java
+++ 
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/TransportConstants.java
@@ -337,6 +337,10 @@ public class TransportConstants {
 
    public static final int DEFAULT_WEB_SOCKET_MAX_FRAME_PAYLOAD_LENGTH = 65536;
 
+   public static final String WEB_SOCKET_ENCODER_TYPE = "webSocketEncoderType";
+
+   public static final String DEFAULT_WEB_SOCKET_ENCODER_TYPE = "binary";
+
    public static final String HANDSHAKE_TIMEOUT = "handshake-timeout";
 
    public static final int DEFAULT_HANDSHAKE_TIMEOUT = 10;
diff --git 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/ProtocolHandler.java
 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/ProtocolHandler.java
index 62b09c508b..8d489c0423 100644
--- 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/ProtocolHandler.java
+++ 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/ProtocolHandler.java
@@ -48,6 +48,7 @@ import 
org.apache.activemq.artemis.core.remoting.impl.netty.NettySNIHostnameHand
 import 
org.apache.activemq.artemis.core.remoting.impl.netty.NettyServerConnection;
 import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
 import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+import 
org.apache.activemq.artemis.core.server.protocol.websocket.WebSocketFrameEncoderType;
 import 
org.apache.activemq.artemis.core.server.protocol.websocket.WebSocketServerHandler;
 import org.apache.activemq.artemis.spi.core.protocol.ProtocolManager;
 import org.apache.activemq.artemis.utils.ConfigurationHelper;
@@ -158,7 +159,8 @@ public class ProtocolHandler {
                }
                stompMaxFramePayloadLength = stompMaxFramePayloadLength != -1 ? 
stompMaxFramePayloadLength : 
TransportConstants.DEFAULT_WEB_SOCKET_MAX_FRAME_PAYLOAD_LENGTH;
                int webSocketMaxFramePayloadLength = 
ConfigurationHelper.getIntProperty(TransportConstants.WEB_SOCKET_MAX_FRAME_PAYLOAD_LENGTH,
 -1, nettyAcceptor.getConfiguration());
-               ctx.pipeline().addLast("websocket-handler", new 
WebSocketServerHandler(websocketSubprotocolIds, webSocketMaxFramePayloadLength 
!= -1 ? webSocketMaxFramePayloadLength : stompMaxFramePayloadLength));
+               String encoderConfigType = 
ConfigurationHelper.getStringProperty(TransportConstants.WEB_SOCKET_ENCODER_TYPE,
 TransportConstants.DEFAULT_WEB_SOCKET_ENCODER_TYPE, 
nettyAcceptor.getConfiguration());
+               ctx.pipeline().addLast("websocket-handler", new 
WebSocketServerHandler(websocketSubprotocolIds, webSocketMaxFramePayloadLength 
!= -1 ? webSocketMaxFramePayloadLength : stompMaxFramePayloadLength, 
WebSocketFrameEncoderType.valueOfType(encoderConfigType)));
                ctx.pipeline().addLast(new ProtocolDecoder(false, false));
                ctx.pipeline().remove(this);
                ctx.pipeline().remove(HTTP_HANDLER);
diff --git 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQMessageBundle.java
 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQMessageBundle.java
index 886595ff5e..2018d3f136 100644
--- 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQMessageBundle.java
+++ 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQMessageBundle.java
@@ -533,4 +533,7 @@ public interface ActiveMQMessageBundle {
 
    @Message(id = 229247, value = "Invalid address configuration for '{}'. 
Address must support multicast and/or anycast.")
    IllegalArgumentException addressWithNoRoutingType(String address);
+
+   @Message(id = 229248, value = "Invalid value for webSocketEncoderType: 
'{}'. Supported values: 'binary', 'text'.")
+   IllegalStateException invalidWebSocketEncoderType(String 
webSocketEncoderType);
 }
diff --git 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoder.java
 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoder.java
index 813a2a082e..feed8fee25 100644
--- 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoder.java
+++ 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoder.java
@@ -1,69 +1,76 @@
-/*
- * 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.activemq.artemis.core.server.protocol.websocket;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelOutboundHandlerAdapter;
-import io.netty.channel.ChannelPromise;
-import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
-import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
-
-/**
- * This class uses the maximum frame payload size to packetize/frame outbound 
websocket messages into
- * continuation frames.
- */
-public class WebSocketFrameEncoder extends ChannelOutboundHandlerAdapter {
-
-   private int maxFramePayloadLength;
-
-   /**
-    * @param maxFramePayloadLength
-    */
-   public WebSocketFrameEncoder(int maxFramePayloadLength) {
-      this.maxFramePayloadLength = maxFramePayloadLength;
-   }
-
-   @Override
-   public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise 
promise) throws Exception {
-      if (msg instanceof ByteBuf) {
-         writeContinuationFrame(ctx, (ByteBuf) msg, promise);
-      } else {
-         super.write(ctx, msg, promise);
-      }
-   }
-
-   private void writeContinuationFrame(ChannelHandlerContext ctx, ByteBuf 
byteBuf, ChannelPromise promise) {
-      int count = byteBuf.readableBytes();
-      int length = Math.min(count, maxFramePayloadLength);
-      boolean finalFragment = length == count;
-      ByteBuf fragment = Unpooled.buffer(length);
-      byteBuf.readBytes(fragment, length);
-      ctx.writeAndFlush(new BinaryWebSocketFrame(finalFragment, 0, fragment), 
promise);
-
-      while ((count = byteBuf.readableBytes()) > 0) {
-         length = Math.min(count, maxFramePayloadLength);
-         finalFragment = length == count;
-         fragment = Unpooled.buffer(length);
-         byteBuf.readBytes(fragment, length);
-         ctx.writeAndFlush(new ContinuationWebSocketFrame(finalFragment, 0, 
fragment), promise);
-      }
-
-      byteBuf.release();
-   }
-}
+/*
+ * 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.activemq.artemis.core.server.protocol.websocket;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
+import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
+
+/**
+ * This class uses the maximum frame payload size to packetize/frame outbound 
websocket messages into
+ * continuation frames.
+ */
+public class WebSocketFrameEncoder extends ChannelOutboundHandlerAdapter {
+
+   private int maxFramePayloadLength;
+   private WebSocketFrameEncoderType type;
+
+   /**
+    * @param maxFramePayloadLength
+    */
+   public WebSocketFrameEncoder(int maxFramePayloadLength, 
WebSocketFrameEncoderType type) {
+      this.maxFramePayloadLength = maxFramePayloadLength;
+      this.type = type;
+   }
+
+   @Override
+   public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise 
promise) throws Exception {
+      if (msg instanceof ByteBuf) {
+         writeContinuationFrame(ctx, (ByteBuf) msg, promise);
+      } else {
+         super.write(ctx, msg, promise);
+      }
+   }
+
+   private void writeContinuationFrame(ChannelHandlerContext ctx, ByteBuf 
byteBuf, ChannelPromise promise) {
+      int count = byteBuf.readableBytes();
+      int length = Math.min(count, maxFramePayloadLength);
+      boolean finalFragment = length == count;
+      ByteBuf fragment = Unpooled.buffer(length);
+      byteBuf.readBytes(fragment, length);
+      if (type == WebSocketFrameEncoderType.BINARY) {
+         ctx.writeAndFlush(new BinaryWebSocketFrame(finalFragment, 0, 
fragment), promise);
+      } else {
+         ctx.writeAndFlush(new TextWebSocketFrame(finalFragment, 0, fragment), 
promise);
+      }
+
+      while ((count = byteBuf.readableBytes()) > 0) {
+         length = Math.min(count, maxFramePayloadLength);
+         finalFragment = length == count;
+         fragment = Unpooled.buffer(length);
+         byteBuf.readBytes(fragment, length);
+         ctx.writeAndFlush(new ContinuationWebSocketFrame(finalFragment, 0, 
fragment), promise);
+      }
+
+      byteBuf.release();
+   }
+}
\ No newline at end of file
diff --git 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoderType.java
 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoderType.java
new file mode 100644
index 0000000000..0355d160a8
--- /dev/null
+++ 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoderType.java
@@ -0,0 +1,35 @@
+/**
+ * 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.activemq.artemis.core.server.protocol.websocket;
+
+import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle;
+
+public enum WebSocketFrameEncoderType {
+   BINARY, TEXT;
+
+   public static WebSocketFrameEncoderType valueOfType(String type) {
+      switch (type) {
+         case "binary":
+            return BINARY;
+         case "text":
+            return TEXT;
+         default:
+            throw 
ActiveMQMessageBundle.BUNDLE.invalidWebSocketEncoderType(type);
+      }
+   }
+}
diff --git 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketServerHandler.java
 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketServerHandler.java
index f2b003cabd..e15fe21991 100644
--- 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketServerHandler.java
+++ 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketServerHandler.java
@@ -51,10 +51,12 @@ public class WebSocketServerHandler extends 
SimpleChannelInboundHandler<Object>
    private WebSocketServerHandshaker handshaker;
    private List<String> supportedProtocols;
    private int maxFramePayloadLength;
+   private WebSocketFrameEncoderType encoderType;
 
-   public WebSocketServerHandler(List<String> supportedProtocols, int 
maxFramePayloadLength) {
+   public WebSocketServerHandler(List<String> supportedProtocols, int 
maxFramePayloadLength, WebSocketFrameEncoderType encoderType) {
       this.supportedProtocols = supportedProtocols;
       this.maxFramePayloadLength = maxFramePayloadLength;
+      this.encoderType = encoderType;
    }
 
    @Override
@@ -86,19 +88,15 @@ public class WebSocketServerHandler extends 
SimpleChannelInboundHandler<Object>
          
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
       } else {
          ChannelFuture handshake = this.handshaker.handshake(ctx.channel(), 
req);
-         handshake.addListener(new ChannelFutureListener() {
-
-            @Override
-            public void operationComplete(ChannelFuture future) throws 
Exception {
-               if (future.isSuccess()) {
-                  // we need to insert an encoder that takes the underlying 
ChannelBuffer of a StompFrame.toActiveMQBuffer and
-                  // wrap it in a binary web socket frame before letting the 
wsencoder send it on the wire
-                  WebSocketFrameEncoder  encoder = new 
WebSocketFrameEncoder(maxFramePayloadLength);
-                  future.channel().pipeline().addAfter("wsencoder", 
"websocket-frame-encoder", encoder);
-               } else {
-                  // Handshake failed, fire an exceptionCaught event
-                  
future.channel().pipeline().fireExceptionCaught(future.cause());
-               }
+         handshake.addListener((ChannelFutureListener) future -> {
+            if (future.isSuccess()) {
+               // we need to insert an encoder that takes the underlying 
ChannelBuffer of a StompFrame.toActiveMQBuffer and
+               // wrap it in a web socket frame before letting the wsencoder 
send it on the wire
+               WebSocketFrameEncoder  encoder = new 
WebSocketFrameEncoder(maxFramePayloadLength, encoderType);
+               future.channel().pipeline().addAfter("wsencoder", 
"websocket-frame-encoder", encoder);
+            } else {
+               // Handshake failed, fire an exceptionCaught event
+               future.channel().pipeline().fireExceptionCaught(future.cause());
             }
          });
       }
diff --git 
a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoderTest.java
 
b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoderTest.java
index e4c2a46f58..18e2d91d84 100644
--- 
a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoderTest.java
+++ 
b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketFrameEncoderTest.java
@@ -16,19 +16,18 @@
  */
 package org.apache.activemq.artemis.core.server.protocol.websocket;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 
+import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
+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 org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -36,13 +35,14 @@ import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPromise;
-import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
-import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
-import io.netty.handler.codec.http.websocketx.WebSocketFrame;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 /**
  * WebSocketContinuationFrameEncoderTest
@@ -51,7 +51,8 @@ import io.netty.handler.codec.http.websocketx.WebSocketFrame;
 public class WebSocketFrameEncoderTest {
 
    private int maxFramePayloadLength = 100;
-   private WebSocketFrameEncoder spy;
+   private WebSocketFrameEncoder binarySpy;
+   private WebSocketFrameEncoder textSpy;
 
    @Mock
    private ChannelHandlerContext ctx;
@@ -60,11 +61,21 @@ public class WebSocketFrameEncoderTest {
 
    @Before
    public void setUp() throws Exception {
-      spy = spy(new WebSocketFrameEncoder(maxFramePayloadLength));
+      binarySpy = spy(new WebSocketFrameEncoder(maxFramePayloadLength, 
WebSocketFrameEncoderType.BINARY));
+      textSpy = spy(new WebSocketFrameEncoder(maxFramePayloadLength, 
WebSocketFrameEncoderType.TEXT));
    }
 
    @Test
-   public void testWriteNonByteBuf() throws Exception {
+   public void testWriteNonByteBufBinary() throws Exception {
+      testWriteNonByteBuf(binarySpy);
+   }
+
+   @Test
+   public void testWriteNonByteBufText() throws Exception {
+      testWriteNonByteBuf(textSpy);
+   }
+
+   private void testWriteNonByteBuf(WebSocketFrameEncoder spy) throws 
Exception {
       Object msg = "Not a ByteBuf";
 
       spy.write(ctx, msg, promise); //test
@@ -76,7 +87,16 @@ public class WebSocketFrameEncoderTest {
    }
 
    @Test
-   public void testWriteReleaseBuffer() throws Exception {
+   public void testWriteReleaseBufferBinary() throws Exception {
+      testWriteReleaseBuffer(binarySpy, BinaryWebSocketFrame.class);
+   }
+
+   @Test
+   public void testWriteReleaseBufferText() throws Exception {
+      testWriteReleaseBuffer(textSpy, TextWebSocketFrame.class);
+   }
+
+   private void testWriteReleaseBuffer(WebSocketFrameEncoder spy, Class 
webSocketFrameClass) throws Exception {
       String content = "Buffer should be released";
 
       int utf8Bytes = ByteBufUtil.utf8Bytes(content);
@@ -91,14 +111,22 @@ public class WebSocketFrameEncoderTest {
       assertEquals(0, msg.readableBytes());
       verify(ctx).writeAndFlush(frameCaptor.capture(), eq(promise));
       WebSocketFrame frame = frameCaptor.getValue();
-      assertTrue(frame instanceof BinaryWebSocketFrame);
+      assertTrue(webSocketFrameClass.isInstance(frame));
       assertTrue(frame.isFinalFragment());
       assertEquals(content, frame.content().toString(StandardCharsets.UTF_8));
    }
 
+   @Test
+   public void testWriteSingleFrameBinary() throws Exception {
+      testWriteSingleFrame(binarySpy, BinaryWebSocketFrame.class);
+   }
 
    @Test
-   public void testWriteSingleFrame() throws Exception {
+   public void testWriteSingleFrameText() throws Exception {
+      testWriteSingleFrame(textSpy, TextWebSocketFrame.class);
+   }
+
+   private void testWriteSingleFrame(WebSocketFrameEncoder spy, Class 
webSocketFrameClass) throws Exception {
       String content = "Content MSG length less than max frame payload length: 
" + maxFramePayloadLength;
       ByteBuf msg = Unpooled.copiedBuffer(content, StandardCharsets.UTF_8);
       ArgumentCaptor<WebSocketFrame> frameCaptor = 
ArgumentCaptor.forClass(WebSocketFrame.class);
@@ -108,13 +136,22 @@ public class WebSocketFrameEncoderTest {
       assertEquals(0, msg.readableBytes());
       verify(ctx).writeAndFlush(frameCaptor.capture(), eq(promise));
       WebSocketFrame frame = frameCaptor.getValue();
-      assertTrue(frame instanceof BinaryWebSocketFrame);
+      assertTrue(webSocketFrameClass.isInstance(frame));
       assertTrue(frame.isFinalFragment());
       assertEquals(content, frame.content().toString(StandardCharsets.UTF_8));
    }
 
    @Test
-   public void testWriteContinuationFrames() throws Exception {
+   public void testWriteContinuationFramesBinary() throws Exception {
+      testWriteContinuationFrames(binarySpy, BinaryWebSocketFrame.class);
+   }
+
+   @Test
+   public void testWriteContinuationFramesText() throws Exception {
+      testWriteContinuationFrames(textSpy, TextWebSocketFrame.class);
+   }
+
+   private void testWriteContinuationFrames(WebSocketFrameEncoder spy, Class 
webSocketFrameClass) throws Exception {
       String contentPart = "Content MSG Length @ ";
       StringBuilder contentBuilder = new StringBuilder(3 * 
maxFramePayloadLength);
 
@@ -140,7 +177,7 @@ public class WebSocketFrameEncoderTest {
 
       int offset = 0;
       WebSocketFrame first = frames.get(0);
-      assertTrue(first instanceof BinaryWebSocketFrame);
+      assertTrue(webSocketFrameClass.isInstance(first));
       assertFalse(first.isFinalFragment());
       assertEquals(content.substring(offset, offset + maxFramePayloadLength),
                    first.content().toString(StandardCharsets.UTF_8));
diff --git 
a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketServerHandlerTest.java
 
b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketServerHandlerTest.java
index a7578d7777..b37e145e8b 100644
--- 
a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketServerHandlerTest.java
+++ 
b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/protocol/websocket/WebSocketServerHandlerTest.java
@@ -50,7 +50,7 @@ public class WebSocketServerHandlerTest {
    public void setup() throws Exception {
       maxFramePayloadLength = 8192;
       supportedProtocols = Arrays.asList("STOMP");
-      spy = spy(new WebSocketServerHandler(supportedProtocols, 
maxFramePayloadLength));
+      spy = spy(new WebSocketServerHandler(supportedProtocols, 
maxFramePayloadLength, WebSocketFrameEncoderType.BINARY));
    }
 
    @Test
diff --git a/docs/user-manual/en/stomp.md b/docs/user-manual/en/stomp.md
index eeb123738c..be8b6603ef 100644
--- a/docs/user-manual/en/stomp.md
+++ b/docs/user-manual/en/stomp.md
@@ -366,6 +366,11 @@ frame this length can be adjusted by using the 
`webSocketMaxFramePayloadLength`
 parameter on the acceptor. In previous version this was configured via the
 similarly named `stompMaxFramePayloadLength` acceptor URL parameter.
 
+Web Socket frames can be encoded as either [binary or 
text](https://datatracker.ietf.org/doc/html/rfc6455#section-11.8).
+By default the broker encodes them as binary. However, this can be changed by
+using the `webSocketEncoderType` acceptor URL parameter. Valid values are
+`binary` and `text`.
+
 The `stomp-websockets` example shows how to configure an Apache ActiveMQ
 Artemis broker to have web browsers and Java applications exchanges messages.
 

Reply via email to