WebSockets Next extension reference guide

experimental

この技術は、experimentalと考えられています。

experimental モードでは、アイデアを成熟させるために早期のフィードバックが求められます。ソリューションが成熟するまでの間、プラットフォームの安定性や長期的な存在を保証するものではありません。フィードバックは メーリングリスト や GitHubの課題管理 で受け付けています。

とりうるステータスの完全なリストについては、 FAQの項目 を参照してください。

quarkus-websockets-next エクステンションは、WebSocket サーバーおよびクライアントエンドポイントを定義するための最新の宣言型 API を提供します。

1. WebSocket プロトコル

RFC6455 に記載されている WebSocket プロトコルは、単一の TCP 接続を介してクライアントとサーバー間の双方向通信チャネルを作成するための標準化された方法を確立します。 HTTP とは異なり、WebSocket は別個の TCP プロトコルとして動作しますが、HTTP とシームレスに連携して機能するように設計されています。 たとえば、同じポートを再利用し、同じセキュリティーメカニズムと互換性があります。

WebSocket を使用したやり取りは、WebSocket プロトコルに移行するための 'Upgrade' ヘッダーを使用する HTTP リクエストで開始されます。 サーバーは、 200 OK レスポンスの代わりに 101 Switching Protocols レスポンスを返し、HTTP 接続を WebSocket 接続へとアップグレードします。 このハンドシェイクが成功すると、最初の HTTP アップグレードリクエストで使用された TCP ソケットは開いたままとなり、クライアントとサーバーの双方が継続的に双方向のメッセージをやり取りできるようになります。

2. HTTP および WebSocket アーキテクチャースタイル

WebSocket は HTTP と互換性があり、HTTP リクエストを通じて開始されますが、2 つのプロトコルは異なるアーキテクチャーとプログラミングモデルを導くため、その違いを認識することが重要です。

HTTP/REST では、アプリケーションはリソース/エンドポイントを中心に構成され、さまざまな HTTP メソッドやパスを処理します。 クライアントとのやり取りは、適切なメソッドとパスを指定した HTTP リクエストを送信することで行われ、リクエスト/レスポンスのパターンに従います サーバーは、パス、メソッド、ヘッダーに基づいて、受信したリクエストを対応するハンドラーにルーティングし、明確に定義されたレスポンスで返します。

逆に、WebSocket では通常、最初の HTTP 接続に単一のエンドポイントが使用され、その後、すべてのメッセージが同じ TCP 接続を利用します。 これにより、非同期かつメッセージ駆動型のまったく異なるインタラクションモデルが導入されます。

WebSocket は、HTTP とは対照的に、低レベルのトランスポートプロトコルです。 メッセージの形式、ルーティング、または処理には、メッセージのセマンティクスに関するクライアントとサーバー間の事前の合意が必要です。

WebSocket クライアントとサーバーの場合、HTTP ハンドシェイクリクエストの Sec-WebSocket-Protocol ヘッダーにより、より高レベルのメッセージングプロトコルのネゴシエーションが可能になります。このヘッダーがない場合、サーバーとクライアントは独自の規則を確立する必要があります。

3. Quarkus WebSockets と Quarkus WebSockets Next

このガイドでは、従来の quarkus-websockets エクステンションに比べ、効率性と使いやすさが向上した WebSocket API の実装である quarkus-websockets-next エクステンションを利用します。 オリジナルの quarkus-websockets エクステンションは引き続きアクセス可能で、継続的なサポートが提供されますが、機能開発が行われる可能性は低いです。

quarkus-websockets とは異なり、 quarkus-websockets-next エクステンションは Jakarta WebSocket 仕様を 実装していません。 代わりに、使いやすさを重視した最新の API を導入しています。 さらに、Quarkus のリアクティブアーキテクチャーおよびネットワーク層とシームレスに統合するように調整されています。

Quarkus WebSockets Next エクステンションで使用されるアノテーションは、同じ名前を共有する場合もありますが、JSR 356 のアノテーションとは異なります。 JSR アノテーションには、Quarkus WebSockets Next エクステンションが従わないセマンティクスが含まれています。

4. プロジェクトのセットアップ

websockets-next エクステンションを使用するには、プロジェクトに io.quarkus:quarkus-websockets-next 依存関係を追加する必要があります。

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-websockets-next</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-websockets-next")

5. Endpoints

Both the server and client APIs allow you to define endpoints that are used to consume and send messages. The endpoints are implemented as CDI beans and support injection. Endpoints declare callback methods annotated with @OnTextMessage, @OnBinaryMessage, @OnPong, @OnOpen, @OnClose and @OnError. These methods are used to handle various WebSocket events. Typically, a method annotated with @OnTextMessage is called when the connected client sends a message to the server and vice versa.

The client API also includes connectors that are used to configure and create new WebSocket connections.

5.1. サーバーエンドポイント

サーバーエンドポイントは、 @io.quarkus.websockets.next.WebSocket アノテーションが付けられたクラスです。 WebSocket#path() の値は、エンドポイントのパスを定義するために使用されます。

package org.acme.websockets;

import io.quarkus.websockets.next.WebSocket;
import jakarta.inject.Inject;

@WebSocket(path = "/chat/{username}") (1)
public class ChatWebSocket {

}

したがって、クライアントは ws://localhost:8080/chat/your-name を使用して、この Web ソケットエンドポイントに接続できます。 TLS が使用されている場合、URL は wss://localhost:8443/chat/your-name です。

The endpoint path is relative to the root context configured by the quarkus.http.root-path (which is / by default). For example, if you add quarkus.http.root-path=/api to your application.properties then a client can connect to this endpoint using http://localhost:8080/api/chat/the-name.

5.2. クライアントエンドポイント

クライアントエンドポイントは、 @io.quarkus.websockets.next.WebSocketClient アノテーションが付けられたクラスです。 WebSocketClient#path() の値は、このクライアントが接続されるエンドポイントのパスを定義するために使用されます。

package org.acme.websockets;

import io.quarkus.websockets.next.WebSocketClient;
import jakarta.inject.Inject;

@WebSocketClient(path = "/chat/{username}") (1)
public class ChatWebSocket {

}
Client endpoints are used to consume and send messages. You’ll need the connectors API to configure and open new WebSocket connections.

5.3. パスパラメーター

WebSocket エンドポイントのパスには、パスパラメーターを含めることができます。 構文は JAX-RS リソースの場合と同じです: {parameterName}

パスパラメーター値には、それぞれ io.quarkus.websockets.next.WebSocketConnection#pathParam(String) メソッドまたは io.quarkus.websockets.next.WebSocketClientConnection#pathParam(String) を使用してアクセスできます。 あるいは、 @io.quarkus.websockets.next.PathParam アノテーションが付与されたエンドポイントコールバックメソッドパラメーターが自動的に注入されます。

WebSocketConnection#pathParam(String) example
@Inject io.quarkus.websockets.next.WebSocketConnection connection;
// ...
String value = connection.pathParam("parameterName");

パスパラメーターの値は常に文字列です。 パス内にパスパラメーターが存在しない場合、 WebSocketConnection#pathParam(String)/WebSocketClientConnection#pathParam(String) メソッドは null を返します。 @PathParam アノテーションが付けられたエンドポイントコールバックメソッドパラメーターがあり、パラメーター名がエンドポイントパスで定義されていない場合、ビルドは失敗します。

Query parameters are not supported. However, you can access the query using WebSocketConnection#handshakeRequest().query()

5.4. CDI スコープ

エンドポイントは CDI Bean として管理されます。 デフォルトでは、 @Singleton スコープが使用されます。 ただし、開発者は特定の要件に合わせて代替スコープを指定できます。

@Singleton および @ApplicationScoped エンドポイントは、すべての WebSocket 接続間で共有されます。 したがって、実装はステートレスまたはスレッドセーフのいずれかである必要があります。

import jakarta.enterprise.context.SessionScoped;

@WebSocket(path = "/ws")
@SessionScoped (1)
public class MyWebSocket {

}
1 This server endpoint is not shared and is scoped to the session.

Each WebSocket connection is associated with its own session context. When the @OnOpen method is invoked, a session context corresponding to the WebSocket connection is created. Subsequent calls to @On[Text|Binary]Message or @OnClose methods utilize this same session context. The session context remains active until the @OnClose method completes execution, at which point it is terminated.

In cases where a WebSocket endpoint does not declare an @OnOpen method, the session context is still created. It remains active until the connection terminates, regardless of the presence of an @OnClose method.

Methods annotated with @OnTextMessage, @OnBinaryMessage, @OnOpen, and @OnClose also have the request scope activated for the duration of the method execution (until it produced its result).

5.5. コールバックメソッド

WebSocket エンドポイントは以下を宣言できます。

  • 最大 1 つの @OnTextMessage メソッド: 接続されたクライアント/サーバーからのテキストメッセージを処理します。

  • 最大 1 つの @OnBinaryMessage メソッド: 接続されたクライアント/サーバーからのバイナリーメッセージを処理します。

  • 最大 1 つの @OnPongMessage メソッド: 接続されたクライアント/サーバーからの pong メッセージを処理します。

  • 最大 1 つの @OnOpen メソッド: 接続が開かれたときに呼び出されます。

  • 最大 1 つの @OnClose メソッド: 接続が閉じられたときに実行されます。

  • 任意の数の @OnError メソッド: エラーが発生したときに呼び出されます。つまり、エンドポイントコールバックがランタイムエラーをスローしたとき、変換エラーが発生したとき、または返された io.smallrye.mutiny.Uni/io.smallrye.mutiny.Multi が失敗を受け取ったときです。

一部のエンドポイントのみにすべてのメソッドを含める必要があります。 ただし、少なくとも @On[Text|Binary]Message または @OnOpen が含まれている必要があります。

いずれかのエンドポイントがこれらのルールに違反すると、ビルド時にエラーがスローされます。 sub-websocket を表す静的なネストされたクラスは同じガイドラインに従います。

Any methods annotated with @OnTextMessage, @OnBinaryMessage, @OnOpen, and @OnClose outside a WebSocket endpoint are considered erroneous and will result in the build failing with an appropriate error message.

5.6. メッセージの処理

クライアントからメッセージを受信するメソッドには、 @OnTextMessage または @OnBinaryMessage アノテーションが付けられます。

OnTextMessage は、クライアントから受信されるすべての テキスト メッセージに対して呼び出されます。 OnBinaryMessage は、クライアントが受信するすべての バイナリー メッセージに対して呼び出されます。

5.6.1. 呼び出しルール

When invoking these annotated methods, the session scope linked to the WebSocket connection remains active. In addition, the request scope is active until the completion of the method (or until it produces its result for async and reactive methods).

Quarkus WebSocket Next supports blocking and non-blocking logic, akin to Quarkus REST, determined by the method signature and additional annotations such as @Blocking and @NonBlocking.

実行に関するルールは次のとおりです。

  • Non-blocking methods must execute on the connection’s event loop.

  • Methods annotated with @RunOnVirtualThread are considered blocking and should execute on a virtual thread.

  • Blocking methods must execute on a worker thread if not annotated with @RunOnVirtualThread.

  • When @RunOnVirtualThread is employed, each invocation spawns a new virtual thread.

  • Methods returning CompletionStage, Uni and Multi are considered non-blocking.

  • Methods returning void or plain objects are considered blocking.

  • Kotlin suspend functions are considered non-blocking.

5.6.2. メソッドパラメーター

メソッドは、メッセージパラメーターを 1 つのみ受け入れる必要があります。

  • メッセージオブジェクト (任意のタイプ)。

  • メッセージタイプが X の Multi<X>。

ただし、次のパラメーターも受け入れる場合があります。

  • WebSocketConnection/WebSocketClientConnection

  • HandshakeRequest

  • @PathParam アノテーションが付けられた String パラメーター

メッセージオブジェクトは送信されたデータを表し、raw のコンテンツ (String、 JsonObject、 JsonArray、 Buffer、または byte[]) またはデシリアライズされた高レベルオブジェクトとしてアクセスできます。後者のアプローチが推奨されます。

When receiving a Multi, the method is invoked once per connection, and the provided Multi receives the items transmitted by this connection. The method must subscribe to the Multi to receive these items (or return a Multi).

5.6.3. サポートされている戻り値のタイプ

@OnTextMessage または @OnBinaryMessage アノテーションが付与されたメソッドは、WebSocket 通信を効率的に処理するためにさまざまなタイプを返すことができます。

  • void: 明示的なレスポンスがクライアントに返されないブロッキングメソッドを示します。

  • Uni<Void>: 返された Uni の完了が処理の終了を意味するノンブロッキングメソッドを示します。明示的なレスポンスはクライアントに返されません。

  • X タイプのオブジェクトは、返されたオブジェクトがシリアライズされ、レスポンスとしてクライアントに送り返されるブロッキングメソッドを表します。

  • Uni<X>: null 以外の Uni によって発行された項目がレスポンスとしてクライアントに送信されるノンブロッキングメソッドを指定します。

  • Multi<X>: null 以外の Multi によって発行された項目が完了またはキャンセルされるまでクライアントに順番に送信されるノンブロッキングメソッドを示します。

  • Unit を返す Kotlin の suspend 関数: 明示的なレスポンスがクライアントに返されないノンブロッキングメソッドを示します。

  • X を返す Kotlin の suspend 関数: 返された項目がレスポンスとしてクライアントに送信されるノンブロッキングメソッドを指定します。

これらの方法の例をいくつか示します。

@OnTextMessage
void consume(Message m) {
// Process the incoming message. The method is called on an executor thread for each incoming message.
}

@OnTextMessage
Uni<Void> consumeAsync(Message m) {
// Process the incoming message. The method is called on an event loop thread for each incoming message.
// The method completes when the returned Uni emits its item.
}

@OnTextMessage
ResponseMessage process(Message m) {
// Process the incoming message and send a response to the client.
// The method is called for each incoming message.
// Note that if the method returns `null`, no response will be sent to the client.
}

@OnTextMessage
Uni<ResponseMessage> processAsync(Message m) {
// Process the incoming message and send a response to the client.
// The method is called for each incoming message.
// Note that if the method returns `null`, no response will be sent to the client. The method completes when the returned Uni emits its item.
}

@OnTextMessage
Multi<ResponseMessage> stream(Message m) {
// Process the incoming message and send multiple responses to the client.
// The method is called for each incoming message.
// The method completes when the returned Multi emits its completion signal.
// The method cannot return `null` (but an empty multi if no response must be sent)
}

When returning a Multi, Quarkus subscribes to the returned Multi automatically and writes the emitted items until completion, failure, or cancellation. Failure or cancellation terminates the connection.

5.6.4. ストリーム

WebSocket エンドポイントは、個々のメッセージに加えて、メッセージのストリームも処理できます。 この場合、メソッドは Multi<X> をパラメーターとして受け取ります。 X の各インスタンスは、上記と同じルールを使用してデシリアライズされます。

Multi を受け取るメソッドは、別の Multi または void を返すことができます。 メソッドが Multi を返す場合、受信する multi をサブスクライブする必要はありません。

@OnTextMessage
public Multi<ChatMessage> stream(Multi<ChatMessage> incoming) {
    return incoming.log();
}

このアプローチにより、双方向のストリーミングが可能になります。

When the method returns void, it must subscribe to the incoming Multi:

@OnTextMessage
public void stream(Multi<ChatMessage> incoming) {
    incoming.subscribe().with(item -> log(item));
}

5.6.5. 返信をスキップする

メソッドがクライアントに書き込まれるメッセージを生成することを意図している場合、 null を発行できます。 null を発行すると、クライアントにレスポンスが送信されないことを意味し、必要なときにレスポンスをスキップできるようになります。

5.6.6. JsonObject および JsonArray

Vert.x の JsonObject および JsonArray インスタンスは、シリアライゼーションおよびデシリアライゼーションのメカニズムをバイパスします。 メッセージはテキストメッセージとして送信されます。

5.6.7. OnOpen および OnClose メソッド

クライアントが接続または切断したときに、WebSocket エンドポイントに通知することもできます。

これは、メソッドに @OnOpen または @OnClose アノテーションを付けることで行われます。

@OnOpen(broadcast = true)
public ChatMessage onOpen() {
    return new ChatMessage(MessageType.USER_JOINED, connection.pathParam("username"), null);
}

@Inject WebSocketConnection connection;

@OnClose
public void onClose() {
    ChatMessage departure = new ChatMessage(MessageType.USER_LEFT, connection.pathParam("username"), null);
    connection.broadcast().sendTextAndAwait(departure);
}

@OnOpen はクライアント接続時にトリガーされ、 @OnClose は切断時に呼び出されます。

これらのメソッドは、セッションスコープ の WebSocketConnection Bean にアクセスできます。

5.6.8. パラメーター

@OnOpen および @OnClose アノテーションが付けられたメソッドは、次のパラメーターを受け入れることができます。

  • WebSocketConnection/WebSocketClientConnection

  • HandshakeRequest

  • @PathParam アノテーションが付けられた String パラメーター

@OnClose アノテーションが付与されたエンドポイントメソッドは、接続を閉じる理由を示す io.quarkus.websockets.next.CloseReason パラメーターも受け入れる場合があります。

5.6.9. サポートされている戻り値のタイプ

@OnOpen メソッドと @OnClose メソッドは、異なる戻り値のタイプをサポートします。

@OnOpen メソッドには、 @On[Text|Binary]Message と同じルールが適用されます。 したがって、 @OnOpen アノテーションが付与されたメソッドは、接続後すぐにクライアントにメッセージを送信できます。 @OnOpen メソッドでサポートされている戻り値のタイプは次のとおりです。

  • void: 接続されたクライアントに明示的なメッセージが返されないブロッキングメソッドを示します。

  • Uni<Void>: 返された Uni の完了が処理の終了を意味するノンブロッキングメソッドを示します。クライアントにメッセージは返されません。

  • X タイプのオブジェクト: 返されたオブジェクトがシリアライズされてクライアントに送り返されるブロッキングメソッドを表します。

  • Uni<X>: null 以外の Uni によって発行された項目がクライアントに送信されるノンブロッキングメソッドを指定します。

  • Multi<X>: null 以外の Multi によって発行された項目が完了またはキャンセルされるまでクライアントに順番に送信されるノンブロッキングメソッドを示します。

  • Unit を返す Kotlin の suspend 関数: 明示的なメッセージがクライアントに返されないノンブロッキングメソッドを示します。

  • X を返す Kotlin の suspend 関数: 返された項目がクライアントに送信されるノンブロッキングメソッドを指定します。

クライアントに送信される項目は、 String、 io.vertx.core.json.JsonObject、 io.vertx.core.json.JsonArray、 io.vertx.core.buffer.Buffer、および byte[] タイプを除いて、シリアライズ されます。 Multi の場合、Quarkus は返された Multi をサブスクライブし、項目が発行されると WebSocket に書き込みます。 String、 JsonObject、 JsonArray はテキストメッセージとして送信されます。 Buffers とバイト配列はバイナリーメッセージとして送信されます。

@OnClose メソッドの場合、サポートされる戻り値のタイプは次のとおりです。

  • void: メソッドはブロッキングであるとみなされます。

  • Uni<Void>: メソッドはノンブロッキングであるとみなされます。

  • Unit を返す Kotlin の suspend 関数: このメソッドはノンブロッキングとみなされます。

@OnClose methods declared on a server endpoint cannot send items to the connected client by returning objects. They can only send messages to the other clients by using the WebSocketConnection object.

5.7. エラー処理

エラーが発生したときに WebSocket エンドポイントに通知することもできます。 @io.quarkus.websockets.next.OnError アノテーションが付与された WebSocket エンドポイントメソッドは、エンドポイントコールバックがランタイムエラーをスローしたとき、または変換エラーが発生したとき、 あるいは返された io.smallrye.mutiny.Uni/io.smallrye.mutiny.Multi が失敗した場合に呼び出されます。

メソッドは、error パラメーター、つまり java.lang.Throwable から割り当て可能なパラメーターを 1 つだけ受け入れる必要があります。 このメソッドは次のパラメーターも受け入れます。

  • WebSocketConnection/WebSocketClientConnection

  • HandshakeRequest

  • @PathParam アノテーションが付けられた String パラメーター

エンドポイントは、 @io.quarkus.websockets.next.OnError アノテーションが付与された複数のメソッドを宣言できます。 ただし、各メソッドは異なるエラーパラメーターを宣言する必要があります。 実際の例外の最も具体的なスーパータイプを宣言するメソッドが選択されます。

The @io.quarkus.websockets.next.OnError annotation can be also used to declare a global error handler, i.e. a method that is not declared on a WebSocket endpoint. Such a method may not accept @PathParam parameters. Error handlers declared on an endpoint take precedence over the global error handlers.

When an error occurs but no error handler can handle the failure, Quarkus uses the strategy specified by quarkus.websockets-next.server.unhandled-failure-strategy. By default, the connection is closed. Alternatively, an error message can be logged or no operation performed.

5.8. シリアライズとデシリアライズ

WebSocket Next エクステンションは、メッセージの自動シリアライゼーションとデシリアライゼーションをサポートします。

String、 JsonObject、 JsonArray、 Buffer、および byte[] タイプのオブジェクトはそのまま送信され、シリアライゼーションとデシリアライゼーションをバイパスします。 コーデックが指定されていない場合、シリアライゼーションとデシリアライゼーションによってメッセージは JSON から、または JSON に自動的に変換されます。

シリアライゼーションとデシリアライゼーションをカスタマイズする必要がある場合は、カスタムコーデックを提供できます。

5.8.1. カスタムコーデック

カスタムコーデックを実装するには、以下を実装する CDI Bean を提供する必要があります。

  • バイナリーメッセージ用の io.quarkus.websockets.next.BinaryMessageCodec

  • テキストメッセージの io.quarkus.websockets.next.TextMessageCodec

次の例は、 Item クラスのカスタムコーデックを実装する方法を示しています。

@Singleton
public class ItemBinaryMessageCodec implements BinaryMessageCodec<Item> {

    @Override
    public boolean supports(Type type) {
        // Allows selecting the right codec for the right type
        return type.equals(Item.class);
    }

    @Override
    public Buffer encode(Item value) {
        // Serialization
        return Buffer.buffer(value.toString());
    }

    @Override
    public Item decode(Type type, Buffer value) {
        // Deserialization
        return new Item(value.toString());
    }
}

OnTextMessage メソッドと OnBinaryMessage メソッドでは、どのコーデックを使用するかを明示的に指定することもできます。

@OnTextMessage(codec = MyInputCodec.class) (1)
Item find(Item item) {
        //....
}
  1. メッセージのデシリアライゼーションとシリアライゼーションの両方に使用するコーデックを指定します。

シリアライゼーションとデシリアライゼーションで異なるコーデックを使用する必要がある場合は、シリアライゼーションとデシリアライゼーションに使用するコーデックを個別に指定できます。

@OnTextMessage(
        codec = MyInputCodec.class, (1)
        outputCodec = MyOutputCodec.class (2)
Item find(Item item) {
        //....
}
  1. 受信メッセージのデシリアライズに使用するコーデックを指定します

  2. 送信メッセージのシリアライゼーションに使用するコーデックを指定します。

5.9. Ping/pong messages

ping メッセージ は、キープアライブとして、またはリモートエンドポイントを確認するために使用できます。 pong メッセージ は、ping メッセージへのレスポンスとして送信され、同一のペイロードを持つ必要があります。

Server/client endpoints automatically respond to a ping message sent from the client/server. In other words, there is no need for @OnPingMessage callback declared on an endpoint.

The server can send ping messages to a connected client. WebSocketConnection/WebSocketClientConnection declare methods to send ping messages; there is a non-blocking variant: sendPing(Buffer) and a blocking variant: sendPingAndAwait(Buffer). By default, the ping messages are not sent automatically. However, the configuration properties quarkus.websockets-next.server.auto-ping-interval and quarkus.websockets-next.client.auto-ping-interval can be used to set the interval after which, the server/client sends a ping message to a connected client/server automatically.

quarkus.websockets-next.server.auto-ping-interval=2 (1)
1 Sends a ping message from the server to a connected client every 2 seconds.

The @OnPongMessage annotation is used to define a callback that consumes pong messages sent from the client/server. An endpoint must declare at most one method annotated with @OnPongMessage. The callback method must return either void or Uni<Void> (or be a Kotlin suspend function returning Unit), and it must accept a single parameter of type Buffer.

@OnPongMessage
void pong(Buffer data) {
    // ....
}
The server/client can also send unsolicited pong messages that may serve as a unidirectional heartbeat. There is a non-blocking variant: WebSocketConnection#sendPong(Buffer) and also a blocking variant: WebSocketConnection#sendPongAndAwait(Buffer).

5.10. 受信処理モード

WebSocket エンドポイントは、それぞれ @WebSocket#inboundProcessingMode() と @WebSocketClient.inboundProcessingMode() を使用して、特定の接続の受信イベントを処理するために使用されるモードを定義できます。 受信イベントは、メッセージ (テキスト、バイナリー、pong)、接続の開始、接続の終了を表すことができます。 デフォルトでは、イベントは順番に処理され、順序が保証されます。 つまり、エンドポイントがイベント A と B を (この特定の順序で) 受信した場合、イベント A のコールバックが完了した後にイベント B のコールバックが呼び出されます。 ただし、状況によっては、順序の保証はなく、同時実行の制限もない状態でイベントを同時に処理することが望ましい場合があります。 このような場合には、 InboundProcessingMode#CONCURRENT を使用する必要があります。

6. サーバー API

6.1. HTTP サーバーの設定

このエクステンションは、メイン の HTTP サーバーを再利用します。

したがって、WebSocket サーバーの設定は quarkus.http. 設定セクションで行われます。

アプリケーション内で設定された WebSocket パスは、 quarkus.http.root (デフォルトは /) で定義されたルートパスと連結されます。 この連結により、WebSocket エンドポイントがアプリケーションの URL 構造内に適切に配置されるようになります。

詳細は、HTTP ガイド を参照してください。

6.2. sub-websocket エンドポイント

@WebSocket エンドポイントは、静的なネストされたクラスをカプセル化できます。これらのクラスも @WebSocket アノテーションが付与され、sub-websocket を表します。 これらの sub-websocket のパスは、外側のクラスとネストされたクラスからのパスを連結したものになります。 この結果得られるパスは、HTTP URL の規則に従って正規化されます。

sub-websocket は、外側のクラスとネストされたクラスの両方の @WebSocket アノテーションで宣言されたパスパラメーターへのアクセスを継承します。 次の例では、外側のクラス内の consumePrimary メソッドは version パラメーターにアクセスできます。 一方、ネストされたクラス内の consumeNested メソッドは、 version パラメーターと id パラメーターの両方にアクセスできます。

@WebSocket(path = "/ws/v{version}")
public class MyPrimaryWebSocket {

    @OnTextMessage
    void consumePrimary(String s)    { ... }

    @WebSocket(path = "/products/{id}")
    public static class MyNestedWebSocket {

      @OnTextMessage
      void consumeNested(String s)    { ... }

    }
}

6.3. WebSocket 接続

io.quarkus.websockets.next.WebSocketConnection オブジェクトは WebSocket 接続を表します。 Quarkus は、このインターフェイスを実装し、 WebSocket エンドポイントに注入して、接続されたクライアントと対話するために使用できる @SessionScoped CDI Bean を提供します。

@OnOpen、 @OnTextMessage、 @OnBinaryMessage、および @OnClose アノテーションが付与されたメソッドは、注入された WebSocketConnection オブジェクトにアクセスできます。

@Inject WebSocketConnection connection;
Note that outside of these methods, the WebSocketConnection object is not available. However, it is possible to list all open connections.

接続を使用して、クライアントにメッセージを送信したり、パスパラメーターにアクセスしたり、接続されているすべてのクライアントにメッセージをブロードキャストしたりできます。

// Send a message:
connection.sendTextAndAwait("Hello!");

// Broadcast messages:
connection.broadcast().sendTextAndAwait(departure);

// Access path parameters:
String param = connection.pathParam("foo");

WebSocketConnection は、メッセージを送信するためのブロッキングメソッドとノンブロッキングメソッドの両方のバリアントを提供します。

  • sendTextAndAwait(String message): テキストメッセージをクライアントに送信し、メッセージが送信されるのを待ちます。これはブロッキングであり、エグゼキュータースレッドからのみ呼び出す必要があります。

  • sendText(String message): Sends a text message to the client. It returns a Uni. It’s non-blocking, but you must subscribe to it.

6.3.1. 開いている接続をリスト表示する

開いているすべての接続をリスト表示することもできます。 Quarkus は、接続にアクセスするための便利なメソッドを宣言する io.quarkus.websockets.next.OpenConnections タイプの CDI Bean を提供します。

import io.quarkus.logging.Log;
import io.quarkus.websockets.next.OpenConnections;

class MyBean {

  @Inject
  OpenConnections connections;

  void logAllOpenConnections() {
     Log.infof("Open connections: %s", connections.listAll()); (1)
  }
}
1 OpenConnections#listAll() は、指定された時点で開いているすべての接続のイミュータブルなスナップショットを返します。

他にも便利な方法があります。 たとえば、 OpenConnections#findByEndpointId(String) を使用すると、特定のエンドポイントの接続を簡単に見つけることができます。

6.3.2. CDI イベント

Quarkus は、新しい接続が開かれると、修飾子 @io.quarkus.websockets.next.Open を持つ io.quarkus.websockets.next.WebSocketConnection タイプの CDI イベントを非同期的に起動します。 さらに、接続が閉じられると、修飾子 @io.quarkus.websockets.next.Closed を持つ WebSocketConnection タイプの CDI イベントが非同期的に起動します。

import jakarta.enterprise.event.ObservesAsync;
import io.quarkus.websockets.next.Open;
import io.quarkus.websockets.next.WebSocketConnection;

class MyBean {

  void connectionOpened(@ObservesAsync @Open WebSocketConnection connection) { (1)
     // This observer method is called when a connection is opened...
  }
}
1 非同期オブザーバーメソッドは、デフォルトのブロッキングエグゼキューターサービスを使用して実行されます。

6.4. セキュリティ

WebSocket エンドポイントのコールバックメソッドは、 io.quarkus.security.Authenticated や jakarta.annotation.security.RolesAllowed などのセキュリティーアノテーション、および サポート対象セキュリティーアノテーション ドキュメントに記載されているその他のアノテーションを使用して保護できます。

例えば、以下のようになります:

package io.quarkus.websockets.next.test.security;

import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;

import io.quarkus.security.ForbiddenException;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.websockets.next.OnError;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;

@WebSocket(path = "/end")
public class Endpoint {

    @Inject
    SecurityIdentity currentIdentity;

    @OnOpen
    String open() {
        return "ready";
    }

    @RolesAllowed("admin")
    @OnTextMessage
    String echo(String message) { (1)
        return message;
    }

    @OnError
    String error(ForbiddenException t) { (2)
        return "forbidden:" + currentIdentity.getPrincipal().getName();
    }
}
1 エコーコールバックメソッドは、現在のセキュリティーアイデンティティーに admin ロールがある場合にのみ呼び出すことができます。
2 認可に失敗した場合はエラーハンドラーが呼び出されます。

SecurityIdentity is initially created during a secure HTTP upgrade and associated with the websocket connection.

When OpenID Connect extension is used and token expires, Quarkus automatically closes connection.

6.5. セキュア HTTP アップグレード

An HTTP upgrade is secured when standard security annotation is placed on an endpoint class or an HTTP Security policy is defined. The advantage of securing HTTP upgrade is less processing, the authorization is performed early and only once. You should always prefer HTTP upgrade security unless, like in th example above, you need to perform action on error.

Use standard security annotation to secure an HTTP upgrade
package io.quarkus.websockets.next.test.security;

import io.quarkus.security.Authenticated;
import jakarta.inject.Inject;

import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;

@Authenticated (1)
@WebSocket(path = "/end")
public class Endpoint {

    @Inject
    SecurityIdentity currentIdentity;

    @OnOpen
    String open() {
        return "ready";
    }

    @OnTextMessage
    String echo(String message) {
        return message;
    }
}
1 匿名ユーザーの場合、最初の HTTP ハンドシェイクは 401 ステータスで終了します。 quarkus.websockets-next.server.security.auth-failure-redirect-url 設定プロパティーを使用して、認可失敗時にハンドシェイクリクエストをリダイレクトすることもできます。
HTTP upgrade is only secured when a security annotation is declared on an endpoint class next to the @WebSocket annotation. Placing a security annotation on an endpoint bean will not secure bean methods, only the HTTP upgrade. You must always verify that your endpoint is secured as intended.
Use HTTP Security policy to secure an HTTP upgrade
quarkus.http.auth.permission.http-upgrade.paths=/end
quarkus.http.auth.permission.http-upgrade.policy=authenticated

6.6. HTTP アップグレードを検査および/または拒否する

HTTP アップグレードを検査するには、 io.quarkus.websockets.next.HttpUpgradeCheck インターフェイスを実装する CDI Bean を提供する必要があります。 Quarkus は、WebSocket 接続にアップグレードする必要があるすべての HTTP リクエストに対して HttpUpgradeCheck#perform メソッドを呼び出します。 このメソッド内では、任意のビジネスロジックを実行したり、HTTP アップグレードを拒否したりできます。

Example HttpUpgradeCheck
package io.quarkus.websockets.next.test;

import io.quarkus.websockets.next.HttpUpgradeCheck;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped (1)
public class ExampleHttpUpgradeCheck implements HttpUpgradeCheck {

    @Override
    public Uni<CheckResult> perform(HttpUpgradeContext ctx) {
        if (rejectUpgrade(ctx)) {
            return CheckResult.rejectUpgrade(400); (2)
        }
        return CheckResult.permitUpgrade();
    }

    private boolean rejectUpgrade(HttpUpgradeContext ctx) {
        var headers = ctx.httpRequest().headers();
        // implement your business logic in here
    }
}
1 HttpUpgradeCheck インターフェイスを実装する CDI Bean は、 @ApplicationScoped、 @Singleton、または @Dependent Bean のいずれかになりますが、 @RequestScoped Bean になることはできません。
2 HTTP アップグレードを拒否します。最初の HTTP ハンドシェイクは、400 Bad Request レスポンスステータスコードで終了します。
You can choose WebSocket endpoints to which the HttpUpgradeCheck is applied with the HttpUpgradeCheck#appliesTo method.

6.7. TLS

このエクステンションは、メイン の HTTP サーバーを再利用するという事実の直接的な結果として、関連するすべてのサーバー設定が適用されます。詳細は、HTTP ガイド を参照してください。

7. クライアント API

7.1. クライアントコネクター

The io.quarkus.websockets.next.WebSocketConnector<CLIENT> is used to configure and create new connections for client endpoints. A CDI bean that implements this interface is provided and can be injected in other beans. The actual type argument is used to determine the client endpoint. The type is validated during build - if it does not represent a client endpoint the build fails.

次のクライアントエンドポイントを考えてみましょう。

Client endpoint
@WebSocketClient(path = "/endpoint/{name}")
public class ClientEndpoint {

    @OnTextMessage
    void onMessage(@PathParam String name, String message, WebSocketClientConnection connection) {
        // ...
    }
}

このクライアントエンドポイントのコネクターは次のように使用されます。

Connector
@Singleton
public class MyBean {

    @ConfigProperty(name = "endpoint.uri")
    URI myUri;

    @Inject
    WebSocketConnector<ClientEndpoint> connector; (1)

    void openAndSendMessage() {
        WebSocketClientConnection connection = connector
            .baseUri(uri) (2)
            .pathParam("name", "Roxanne") (3)
            .connectAndAwait();
        connection.sendTextAndAwait("Hi!"); (4)
    }
}
1 ClientEndpoint のコネクターを注入します。
2 ベース URI が指定されていない場合は、設定から値を取得しようとします。キーは、クライアント ID と .base-uri 接尾辞で構成されます。
3 パスパラメーター値を設定します。クライアントエンドポイントパスに指定された名前のパラメーターが含まれていない場合は、 IllegalArgumentException がスローされます。
4 必要に応じて、接続を使用してメッセージを送信します。
If an application attempts to inject a connector for a missing endpoint, an error is thrown.

7.1.1. 基本コネクター

アプリケーション開発者がクライアントエンドポイントとコネクターの組み合わせを必要としない場合は、基本コネクター を使用できます。 基本コネクターは、クライアントエンドポイントを定義せずに接続を作成し、メッセージを消費/送信する簡単な方法です。

Basic connector
@Singleton
public class MyBean {

    @Inject
    BasicWebSocketConnector connector; (1)

    void openAndConsume() {
        WebSocketClientConnection connection = connector
            .baseUri(uri) (2)
            .path("/ws") (3)
            .executionModel(ExecutionModel.NON_BLOCKING) (4)
            .onTextMessage((c, m) -> { (5)
               // ...
            })
            .connectAndAwait();
    }
}
1 コネクターを注入します。
2 ベース URI は常に設定する必要があります。
3 ベース URI に追加する必要がある追加パス。
4 コールバックハンドラーの実行モデルを設定します。デフォルトでは、コールバックは現在のスレッドをブロックする可能性があります。ただし、この場合、コールバックはイベントループで実行され、現在のスレッドをブロックしない可能性があります。
5 lambda は、サーバーから送信されるテキストメッセージごとに呼び出されます。

基本コネクターは低レベル API に近いため、上級ユーザー向けに予約されています。 ただし、他の低レベルの WebSocket クライアントとは異なり、これは引き続き CDI Bean であり、他の Bean に注入できます。 また、コールバックの実行モデルを設定する方法も提供し、Quarkus の他の部分との最適なインテグレーションを確保します。

7.2. WebSocket クライアント接続

io.quarkus.websockets.next.WebSocketClientConnection オブジェクトは WebSocket 接続を表します。 Quarkus は、このインターフェイスを実装し、 WebSocketClient エンドポイントに注入して、接続されたサーバーと対話するために使用できる @SessionScoped CDI Bean を提供します。

@OnOpen、 @OnTextMessage、 @OnBinaryMessage、および @OnClose アノテーションが付与されたメソッドは、注入された WebSocketClientConnection オブジェクトにアクセスできます。

@Inject WebSocketClientConnection connection;
Note that outside of these methods, the WebSocketClientConnection object is not available. However, it is possible to list all open client connections.

この接続を使用して、クライアントにメッセージを送信したり、パスパラメーターにアクセスしたりできます。

// Send a message:
connection.sendTextAndAwait("Hello!");

// Broadcast messages:
connection.broadcast().sendTextAndAwait(departure);

// Access path parameters:
String param = connection.pathParam("foo");

WebSocketClientConnection は、メッセージを送信するためのブロッキングメソッドとノンブロッキングメソッドの両方のバリアントを提供します。

  • sendTextAndAwait(String message): テキストメッセージをクライアントに送信し、メッセージが送信されるのを待ちます。これはブロッキングであり、エグゼキュータースレッドからのみ呼び出す必要があります。

  • sendText(String message): Sends a text message to the client. It returns a Uni. It’s non-blocking, but you must subscribe to it.

7.2.1. 開いているクライアント接続をリスト表示する

開いているすべての接続をリスト表示することもできます。 Quarkus は、接続にアクセスするための便利なメソッドを宣言する io.quarkus.websockets.next.OpenClientConnections タイプの CDI Bean を提供します。

import io.quarkus.logging.Log;
import io.quarkus.websockets.next.OpenClientConnections;

class MyBean {

  @Inject
  OpenClientConnections connections;

  void logAllOpenClinetConnections() {
     Log.infof("Open client connections: %s", connections.listAll()); (1)
  }
}
1 OpenClientConnections#listAll() は、指定された時点で開いているすべての接続のイミュータブルなスナップショットを返します。

他にも便利な方法があります。 たとえば、 OpenClientConnections#findByClientId(String) を使用すると、特定のエンドポイントの接続を簡単に見つけることができます。

7.2.2. CDI イベント

Quarkus は、新しい接続が開かれると、修飾子 @io.quarkus.websockets.next.Open を持つ io.quarkus.websockets.next.WebSocketConnection タイプの CDI イベントを非同期的に起動します。 さらに、接続が閉じられると、修飾子 @io.quarkus.websockets.next.Closed を持つ WebSocketClientConnection タイプの CDI イベントが非同期的に起動します。

import jakarta.enterprise.event.ObservesAsync;
import io.quarkus.websockets.next.Open;
import io.quarkus.websockets.next.WebSocketClientConnection;

class MyBean {

  void connectionOpened(@ObservesAsync @Open WebSocketClientConnection connection) { (1)
     // This observer method is called when a connection is opened...
  }
}
1 非同期オブザーバーメソッドは、デフォルトのブロッキングエグゼキューターサービスを使用して実行されます。

7.3. SSL/TLS の設定

TLS 接続を確立するには、TLS レジストリー を使用して、名前付き の設定を設定する必要があります。

quarkus.tls.my-ws-client.trust-store.p12.path=server-truststore.p12
quarkus.tls.my-ws-client.trust-store.p12.password=secret

quarkus.websockets-next.client.tls-configuration-name=my-ws-client # Reference the named configuration
When using the WebSocket client, using a named configuration is required to avoid conflicts with other TLS configurations. The client will not use the default TLS configuration.

名前付き TLS 設定を行うと、TLS はデフォルトで有効になります。

8. トラフィックロギング

Quarkus は、デバッグの目的で送受信されたメッセージをログに記録できます。 サーバーのトラフィックロギングを有効にするには、 quarkus.websockets-next.server.traffic-logging.enabled 設定プロパティーを true に設定します。 クライアントのトラフィックロギングを有効にするには、 quarkus.websockets-next.client.traffic-logging.enabled 設定プロパティーを true に設定します。 テキストメッセージのペイロードも記録されます。 ただし、記録される文字数には制限があります。 デフォルトの制限は 100 ですが、 quarkus.websockets-next.server.traffic-logging.text-payload-limit および quarkus.websockets-next.client.traffic-logging.text-payload-limit 設定プロパティーをそれぞれ使用してこの制限を変更できます。

The messages are only logged if the DEBUG level is enabled for the logger io.quarkus.websockets.next.traffic.
Example server configuration
quarkus.websockets-next.server.traffic-logging.enabled=true (1)
quarkus.websockets-next.server.traffic-logging.text-payload-limit=50 (2)

quarkus.log.category."io.quarkus.websockets.next.traffic".level=DEBUG (3)
1 トラフィックロギングを有効にします。
2 ログに記録されるテキストメッセージペイロードの文字数を設定します。
3 ロガー io.quarkus.websockets.next.traffic に対して DEBUG レベルを有効にします。

9. 設定リファレンス

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

タイプ

デフォルト

Compression Extensions for WebSocket are supported by default.

See also RFC 7692

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_OFFER_PER_MESSAGE_COMPRESSION

Show more

boolean

false

The compression level must be a value between 0 and 9. The default value is io.vertx.core.http.HttpClientOptions#DEFAULT_WEBSOCKET_COMPRESSION_LEVEL.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_COMPRESSION_LEVEL

Show more

int

The maximum size of a message in bytes. The default values is io.vertx.core.http.HttpClientOptions#DEFAULT_MAX_WEBSOCKET_MESSAGE_SIZE.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_MAX_MESSAGE_SIZE

Show more

int

The interval after which, when set, the client sends a ping message to a connected server automatically.

Ping messages are not sent automatically by default.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_AUTO_PING_INTERVAL

Show more

Duration

The strategy used when an error occurs but no error handler can handle the failure.

By default, the connection is closed when an unhandled failure occurs.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_UNHANDLED_FAILURE_STRATEGY

Show more

closeClose the connection., logLog an error message., noopNo operation.

closeClose the connection.

The name of the TLS configuration to use.

If a name is configured, it uses the configuration from quarkus.tls.<name>.* If a name is configured, but no TLS configuration is found with that name then an error will be thrown.

The default TLS configuration is not used by default.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_TLS_CONFIGURATION_NAME

Show more

string

If set to true then binary/text messages received/sent are logged if the DEBUG level is enabled for the logger io.quarkus.websockets.next.traffic.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_TRAFFIC_LOGGING_ENABLED

Show more

boolean

false

The number of characters of a text message which will be logged if traffic logging is enabled. The payload of a binary message is never logged.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_CLIENT_TRAFFIC_LOGGING_TEXT_PAYLOAD_LIMIT

Show more

int

100

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_SUPPORTED_SUBPROTOCOLS

Show more

文字列のリスト

Compression Extensions for WebSocket are supported by default.

See also RFC 7692

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_PER_MESSAGE_COMPRESSION_SUPPORTED

Show more

boolean

true

The compression level must be a value between 0 and 9. The default value is io.vertx.core.http.HttpServerOptions#DEFAULT_WEBSOCKET_COMPRESSION_LEVEL.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_COMPRESSION_LEVEL

Show more

int

The maximum size of a message in bytes. The default values is io.vertx.core.http.HttpServerOptions#DEFAULT_MAX_WEBSOCKET_MESSAGE_SIZE.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_MAX_MESSAGE_SIZE

Show more

int

The interval after which, when set, the server sends a ping message to a connected client automatically.

Ping messages are not sent automatically by default.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_AUTO_PING_INTERVAL

Show more

Duration

The strategy used when an error occurs but no error handler can handle the failure.

By default, the connection is closed when an unhandled failure occurs.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_UNHANDLED_FAILURE_STRATEGY

Show more

closeClose the connection., logLog an error message., noopNo operation.

closeClose the connection.

Quarkus redirects HTTP handshake request to this URL if an HTTP upgrade is rejected due to the authorization failure. This configuration property takes effect when you secure endpoint with a standard security annotation. For example, the HTTP upgrade is secured if an endpoint class is annotated with the @RolesAllowed annotation.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_SECURITY_AUTH_FAILURE_REDIRECT_URL

Show more

string

The limit of messages kept for a Dev UI connection. If less than zero then no messages are stored and sent to the Dev UI view.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_DEV_MODE_CONNECTION_MESSAGES_LIMIT

Show more

long

1000

If set to true then binary/text messages received/sent are logged if the DEBUG level is enabled for the logger io.quarkus.websockets.next.traffic.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_TRAFFIC_LOGGING_ENABLED

Show more

boolean

false

The number of characters of a text message which will be logged if traffic logging is enabled. The payload of a binary message is never logged.

Environment variable: QUARKUS_WEBSOCKETS_NEXT_SERVER_TRAFFIC_LOGGING_TEXT_PAYLOAD_LIMIT

Show more

int

100

期間フォーマットについて

期間の値を書くには、標準の java.time.Duration フォーマットを使います。 詳細は Duration#parse() Java API documentation を参照してください。

数字で始まる簡略化した書式を使うこともできます:

  • 数値のみの場合は、秒単位の時間を表します。

  • 数値の後に ms が続く場合は、ミリ秒単位の時間を表します。

その他の場合は、簡略化されたフォーマットが解析のために java.time.Duration フォーマットに変換されます:

  • 数値の後に h 、 m 、 s が続く場合は、その前に PT が付けられます。

  • 数値の後に d が続く場合は、その前に P が付けられます。

関連コンテンツ