extensions/websockets-next/deployment/src/main/resources/META-INF/quarkus-skill.md
@WebSocket(path = "/my-endpoint/{param}") — it becomes a CDI bean (default @Singleton).@OnOpen, @OnTextMessage, @OnBinaryMessage, @OnClose, @OnError.@io.quarkus.websockets.next.PathParam to receive path parameters, or accept WebSocketConnection or HandshakeRequest directly. See the method parameters reference.WebSocketConnection to access pathParam("param"), id(), broadcast(), getOpenConnections().broadcast = true on @OnTextMessage or @OnOpen to broadcast the return value to all connected clients instead.@WebSocket(endpointId = "my-id") to assign a stable endpoint ID for use with OpenConnections.@OnTextMessage(broadcast = true) — return value is sent to ALL open connections.connection.broadcast().sendTextAndAwait(msg) — programmatic broadcast from any callback.connection.broadcast().filter(c -> !c.id().equals(excludeId)).sendTextAndAwait(msg) — broadcast with filter.OpenConnections and iterate:
@Inject OpenConnections connections;
for (var conn : connections.findByEndpointId("notifications")) {
conn.sendTextAndAwait(message);
}
@OnClose methods must return void or Uni<Void> — they cannot return a message.@OnClose fires — you cannot send to it.@OnClose WORKS — it sends to all OTHER open connections.String, JsonObject, JsonArray, Buffer, byte[] are sent as-is.TextMessageCodec<T> as a CDI bean:
@Singleton
public class MyCodec implements TextMessageCodec<MyType> {
@Override
public boolean supports(Type type) { return type.equals(MyType.class); }
@Override
public String encode(MyType value) { return JsonObject.mapFrom(value).encode(); }
@Override
public MyType decode(Type type, String value) { return new JsonObject(value).mapTo(MyType.class); }
}
supports() and decode() take java.lang.reflect.Type, not Class<?>.Multi<X> as a parameter in @OnTextMessage for streaming input.Multi<X> from @OnOpen to establish a persistent server-push stream for the connection's lifetime.Multi<X> from @OnTextMessage for bidirectional streaming — each incoming message produces a stream of responses.void with a Multi parameter, you MUST subscribe to the Multi manually.HttpUpgradeCheck as a CDI bean to validate upgrade requests:
@Singleton
public class TokenCheck implements HttpUpgradeCheck {
@Override
public Uni<CheckResult> perform(HttpUpgradeContext context) {
String token = context.httpRequest().getParam("token");
if (token == null) {
return CheckResult.rejectUpgrade(403);
}
return CheckResult.permitUpgrade();
}
}
perform() returns Uni<CheckResult> — use CheckResult.permitUpgrade() or CheckResult.rejectUpgrade(statusCode).context.httpRequest().getParam("name"), headers via context.httpRequest().headers().appliesTo(String endpointId) to scope the check to specific endpoints.@ApplicationScoped, @Singleton, or @Dependent — NOT @RequestScoped.BasicWebSocketConnector or WebSocketConnector for test clients — they are the standard API and handle context propagation:
@Inject BasicWebSocketConnector connector;
@Test
void testEndpoint() throws Exception {
CountDownLatch messageLatch = new CountDownLatch(1);
List<String> received = new CopyOnWriteArrayList<>();
var connection = connector
.baseUri(uri)
.path("/my-endpoint/myParam")
.onTextMessage((c, msg) -> {
received.add(msg);
messageLatch.countDown();
})
.connectAndAwait();
connection.sendTextAndAwait("hello");
assertTrue(messageLatch.await(5, TimeUnit.SECONDS));
connection.closeAndAwait();
}
CountDownLatch for synchronization — WebSocket messages are async.String on the client side — parse JSON manually with new JsonObject(msg).@OnClose cannot return a message — use connection.broadcast().sendTextAndAwait() instead.WebSocketConnection is session-scoped; do NOT inject it in @ApplicationScoped beans outside endpoint classes — it won't resolve to the correct connection.broadcast() includes the sender connection — use .filter() to exclude it if needed.void) run on a worker thread by default; use @NonBlocking for event loop execution.