diff --git a/webclient/websocket/src/main/java/io/helidon/webclient/websocket/ClientWsConnection.java b/webclient/websocket/src/main/java/io/helidon/webclient/websocket/ClientWsConnection.java
index 35f324ee8a8..6b7c57827a8 100644
--- a/webclient/websocket/src/main/java/io/helidon/webclient/websocket/ClientWsConnection.java
+++ b/webclient/websocket/src/main/java/io/helidon/webclient/websocket/ClientWsConnection.java
@@ -175,12 +175,18 @@ private ClientWsConnection send(ClientWsFrame frame) {
opCodeFull |= opCode.code();
sendBuffer.write(opCodeFull);
- long payloadLength = frame.payloadLength();
- if (frame.payloadLength() < 126) {
- // this is a masked frame (all client frames MUST be masked)
- payloadLength = payloadLength | 0b10000000;
- sendBuffer.write((int) payloadLength);
- // TODO finish other options (payload longer than 126 bytes)
+ long length = frame.payloadLength();
+ if (length < 126) {
+ sendBuffer.write((int) length | 0b10000000);
+ } else if (length < 1 << 16) {
+ sendBuffer.write(126 | 0b10000000);
+ sendBuffer.write((int) (length >>> 8));
+ sendBuffer.write((int) (length & 0xFF));
+ } else {
+ sendBuffer.write(127 | 0b10000000);
+ for (int i = 56; i >= 0; i -= 8){
+ sendBuffer.write((int) (length >>> i) & 0xFF);
+ }
}
// write masking key
diff --git a/webserver/tests/websocket/pom.xml b/webserver/tests/websocket/pom.xml
index 05e183a8539..450dbd62e13 100644
--- a/webserver/tests/websocket/pom.xml
+++ b/webserver/tests/websocket/pom.xml
@@ -38,6 +38,11 @@
helidon-webserver-testing-junit5
test
+
+ io.helidon.webserver.testing.junit5
+ helidon-webserver-testing-junit5-websocket
+ test
+
org.junit.jupiter
junit-jupiter-api
diff --git a/webserver/tests/websocket/src/test/java/io/helidon/webserver/tests/websocket/WebSocketClientTest.java b/webserver/tests/websocket/src/test/java/io/helidon/webserver/tests/websocket/WebSocketClientTest.java
new file mode 100644
index 00000000000..2c387d25f36
--- /dev/null
+++ b/webserver/tests/websocket/src/test/java/io/helidon/webserver/tests/websocket/WebSocketClientTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates.
+ *
+ * Licensed 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.helidon.webserver.tests.websocket;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import io.helidon.webclient.websocket.WsClient;
+import io.helidon.webserver.Router;
+import io.helidon.webserver.WebServerConfig;
+import io.helidon.webserver.testing.junit5.ServerTest;
+import io.helidon.webserver.testing.junit5.SetUpRoute;
+import io.helidon.webserver.testing.junit5.SetUpServer;
+import io.helidon.webserver.websocket.WsConfig;
+import io.helidon.webserver.websocket.WsRouting;
+import io.helidon.websocket.WsCloseCodes;
+import io.helidon.websocket.WsListener;
+import io.helidon.websocket.WsSession;
+import org.junit.jupiter.api.Test;
+
+import static io.helidon.webserver.tests.websocket.WebSocketTest.randomString;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+@ServerTest
+public class WebSocketClientTest {
+
+ private final WsClient wsClient;
+
+ private static final String[] text = {
+ randomString(100),
+ randomString(1000),
+ randomString(10000),
+ randomString(100000)
+ };
+
+ WebSocketClientTest(WsClient wsClient) {
+ this.wsClient = wsClient;
+ }
+
+ @SetUpServer
+ public static void setup(WebServerConfig.Builder builder) {
+ builder.addProtocol(
+ WsConfig.builder()
+ .maxFrameLength(100000) // overrides application.yaml
+ .build());
+ }
+
+ @SetUpRoute
+ static void router(Router.RouterBuilder> router) {
+ router.addRouting(WsRouting.builder().endpoint("/echo", new EchoService()));
+ }
+
+ /**
+ * Tests sending long text messages using Helidon's WS client.
+ */
+ @Test
+ void testLongTextMessages() throws InterruptedException {
+ Set messages = new HashSet<>();
+ CountDownLatch messageLatch = new CountDownLatch(text.length);
+
+ wsClient.connect("/echo", new WsListener() {
+ @Override
+ public void onMessage(WsSession session, String text, boolean last) {
+ messages.add(text);
+ messageLatch.countDown();
+ if (messageLatch.getCount() == 0) {
+ session.close(WsCloseCodes.NORMAL_CLOSE, "Bye!");
+ }
+ }
+
+ @Override
+ public void onOpen(WsSession session) {
+ for (String s : text) {
+ session.send(s, false);
+ }
+ }
+ });
+
+ boolean await = messageLatch.await(10, TimeUnit.SECONDS);
+ assertThat(await, is(true));
+ assertThat(messages, hasItems(text));
+ }
+}
diff --git a/webserver/tests/websocket/src/test/java/io/helidon/webserver/tests/websocket/WebSocketTest.java b/webserver/tests/websocket/src/test/java/io/helidon/webserver/tests/websocket/WebSocketTest.java
index dc3aa21b0ee..f4715c869c0 100644
--- a/webserver/tests/websocket/src/test/java/io/helidon/webserver/tests/websocket/WebSocketTest.java
+++ b/webserver/tests/websocket/src/test/java/io/helidon/webserver/tests/websocket/WebSocketTest.java
@@ -215,7 +215,7 @@ Results results() throws ExecutionException, InterruptedException, TimeoutExcept
}
}
- private static String randomString(int length) {
+ static String randomString(int length) {
int leftLimit = 97; // letter 'a'
int rightLimit = 122; // letter 'z'
return new Random().ints(leftLimit, rightLimit + 1)