diff --git a/.github/native-tests.json b/.github/native-tests.json
index f31618bc7b435..87547f9ca469d 100644
--- a/.github/native-tests.json
+++ b/.github/native-tests.json
@@ -92,8 +92,8 @@
},
{
"category": "HTTP",
- "timeout": 120,
- "test-modules": "elytron-resteasy, resteasy-jackson, elytron-resteasy-reactive, resteasy-mutiny, resteasy-reactive-kotlin/standard, vertx, vertx-http, vertx-web, vertx-http-compressors/all, vertx-http-compressors/some, vertx-web-jackson, vertx-graphql, virtual-http, rest-client, rest-client-reactive, rest-client-reactive-stork, rest-client-reactive-multipart, websockets, management-interface, management-interface-auth, mutiny-native-jctools",
+ "timeout": 130,
+ "test-modules": "elytron-resteasy, resteasy-jackson, elytron-resteasy-reactive, resteasy-mutiny, resteasy-reactive-kotlin/standard, vertx, vertx-http, vertx-web, vertx-http-compressors/all, vertx-http-compressors/some, vertx-web-jackson, vertx-graphql, virtual-http, rest-client, rest-client-reactive, rest-client-reactive-stork, rest-client-reactive-multipart, websockets, websockets-next, management-interface, management-interface-auth, mutiny-native-jctools",
"os-name": "ubuntu-latest"
},
{
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index 624ee3141e8db..356cb2336c5c3 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -234,6 +234,7 @@
vertx
vertx-kotlin
websockets
+ websockets-next
spring-di
spring-web
spring-data-jpa
diff --git a/integration-tests/websockets-next/pom.xml b/integration-tests/websockets-next/pom.xml
new file mode 100644
index 0000000000000..ba98a606d088d
--- /dev/null
+++ b/integration-tests/websockets-next/pom.xml
@@ -0,0 +1,60 @@
+
+
+ 4.0.0
+
+
+ quarkus-integration-tests-parent
+ io.quarkus
+ 999-SNAPSHOT
+
+
+ quarkus-integration-test-websockets-next
+ Quarkus - Integration Tests - WebSockets Next
+
+
+
+ io.quarkus
+ quarkus-websockets-next
+
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+
+
+ io.quarkus
+ quarkus-websockets-next-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+
+ build
+
+
+
+
+
+
+
+
diff --git a/integration-tests/websockets-next/src/main/java/io/quarkus/websockets/ChatServer.java b/integration-tests/websockets-next/src/main/java/io/quarkus/websockets/ChatServer.java
new file mode 100644
index 0000000000000..8f1422238c081
--- /dev/null
+++ b/integration-tests/websockets-next/src/main/java/io/quarkus/websockets/ChatServer.java
@@ -0,0 +1,40 @@
+package io.quarkus.websockets;
+
+import io.quarkus.runtime.annotations.RegisterForReflection;
+import io.quarkus.websockets.next.OnClose;
+import io.quarkus.websockets.next.OnOpen;
+import io.quarkus.websockets.next.OnTextMessage;
+import io.quarkus.websockets.next.PathParam;
+import io.quarkus.websockets.next.WebSocket;
+import io.quarkus.websockets.next.WebSocketConnection;
+
+@WebSocket(path = "/chat/{username}")
+public class ChatServer {
+
+ public enum MessageType {
+ USER_JOINED,
+ USER_LEFT,
+ CHAT_MESSAGE
+ }
+
+ @RegisterForReflection
+ public record ChatMessage(MessageType type, String from, String message) {
+ }
+
+ @OnOpen(broadcast = true)
+ public ChatMessage onOpen(@PathParam String username) {
+ return new ChatMessage(MessageType.USER_JOINED, username, "Hello!");
+ }
+
+ @OnClose
+ public void onClose(WebSocketConnection connection) {
+ connection.broadcast()
+ .sendTextAndAwait(new ChatMessage(MessageType.USER_LEFT, connection.pathParam("username"), "Bye!"));
+ }
+
+ @OnTextMessage(broadcast = true)
+ public ChatMessage onMessage(ChatMessage message) {
+ return message;
+ }
+
+}
diff --git a/integration-tests/websockets-next/src/test/java/io/quarkus/websockets/ChatClientTest.java b/integration-tests/websockets-next/src/test/java/io/quarkus/websockets/ChatClientTest.java
new file mode 100644
index 0000000000000..25ad7db42401e
--- /dev/null
+++ b/integration-tests/websockets-next/src/test/java/io/quarkus/websockets/ChatClientTest.java
@@ -0,0 +1,55 @@
+package io.quarkus.websockets;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.net.URI;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TimeUnit;
+
+import jakarta.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.common.http.TestHTTPResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.websockets.ChatServer.ChatMessage;
+import io.quarkus.websockets.ChatServer.MessageType;
+import io.quarkus.websockets.next.OnTextMessage;
+import io.quarkus.websockets.next.WebSocketClient;
+import io.quarkus.websockets.next.WebSocketClientConnection;
+import io.quarkus.websockets.next.WebSocketConnector;
+
+@QuarkusTest
+public class ChatClientTest {
+
+ private static final LinkedBlockingDeque MESSAGES = new LinkedBlockingDeque<>();
+
+ @TestHTTPResource("/")
+ URI uri;
+
+ @Inject
+ WebSocketConnector connector;
+
+ @Test
+ public void testWebsocketChat() throws Exception {
+ WebSocketClientConnection connection = connector
+ .baseUri(uri)
+ .pathParam("username", "Tom")
+ .connectAndAwait();
+ assertEquals(new ChatMessage(MessageType.USER_JOINED, "Tom", "Hello!"), MESSAGES.poll(10, TimeUnit.SECONDS));
+ connection.sendTextAndAwait(new ChatMessage(MessageType.CHAT_MESSAGE, "Tom", "Ping"));
+ assertEquals(new ChatMessage(MessageType.CHAT_MESSAGE, "Tom", "Ping"), MESSAGES.poll(10, TimeUnit.SECONDS));
+ connection.closeAndAwait();
+ }
+
+ @WebSocketClient(path = "/chat/{username}")
+ public static class ChatClient {
+
+ @OnTextMessage
+ void message(ChatMessage message) {
+ MESSAGES.add(message);
+ }
+
+ }
+
+}
diff --git a/integration-tests/websockets-next/src/test/java/io/quarkus/websockets/ChatIT.java b/integration-tests/websockets-next/src/test/java/io/quarkus/websockets/ChatIT.java
new file mode 100644
index 0000000000000..1f941aae6d66a
--- /dev/null
+++ b/integration-tests/websockets-next/src/test/java/io/quarkus/websockets/ChatIT.java
@@ -0,0 +1,7 @@
+package io.quarkus.websockets;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+public class ChatIT extends ChatTest {
+}
diff --git a/integration-tests/websockets-next/src/test/java/io/quarkus/websockets/ChatTest.java b/integration-tests/websockets-next/src/test/java/io/quarkus/websockets/ChatTest.java
new file mode 100644
index 0000000000000..6b54ad5e705a9
--- /dev/null
+++ b/integration-tests/websockets-next/src/test/java/io/quarkus/websockets/ChatTest.java
@@ -0,0 +1,59 @@
+package io.quarkus.websockets;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.common.http.TestHTTPResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.websockets.ChatServer.ChatMessage;
+import io.quarkus.websockets.ChatServer.MessageType;
+import io.vertx.core.Vertx;
+import io.vertx.core.http.WebSocketClient;
+import io.vertx.core.http.WebSocketConnectOptions;
+import io.vertx.core.json.Json;
+
+@QuarkusTest
+public class ChatTest {
+
+ @TestHTTPResource("/chat/Tom")
+ URI uri;
+
+ @Test
+ public void testWebsocketChat() throws Exception {
+ CountDownLatch messageLatch = new CountDownLatch(2);
+ List messages = new CopyOnWriteArrayList<>();
+ Vertx vertx = Vertx.vertx();
+ WebSocketClient client = vertx.createWebSocketClient();
+ try {
+ client.connect(new WebSocketConnectOptions()
+ .setHost(uri.getHost())
+ .setPort(uri.getPort())
+ .setURI(uri.getPath()))
+ .onSuccess(
+ ws -> {
+ ws.textMessageHandler(m -> {
+ messages.add(Json.decodeValue(m, ChatMessage.class));
+ messageLatch.countDown();
+ });
+ ws.writeTextMessage(Json.encode(new ChatMessage(MessageType.CHAT_MESSAGE, "Tom", "Ping")));
+ });
+ assertTrue(messageLatch.await(10, TimeUnit.SECONDS), messageLatch.toString());
+ assertEquals(new ChatMessage(MessageType.USER_JOINED, "Tom", "Hello!"),
+ messages.get(0));
+ assertEquals(new ChatMessage(MessageType.CHAT_MESSAGE, "Tom", "Ping"),
+ messages.get(1));
+ } finally {
+ client.close().toCompletionStage().toCompletableFuture().get(5, TimeUnit.SECONDS);
+ vertx.close();
+ }
+ }
+
+}