@ -71,7 +71,37 @@ Then map it to a URL and add a `WebSocketHandlerAdapter`:
@@ -71,7 +71,37 @@ Then map it to a URL and add a `WebSocketHandlerAdapter`:
[[webflux-websockethandler]]
=== WebSocketHandler
The most basic implementation of a handler is one that handles inbound messages:
The `handle` method of `WebSocketHandler` takes `WebSocketSession` and returns `Mono<Void>`
to indicate when application handling of the session is complete. The session is handled
through two streams, one for inbound and one for outbound messages:
[options="header"]
|===
| WebSocketSession method | Description
| `Flux<WebSocketMessage> receive()`
| Provides access to the inbound message stream, and completes when the connection is closed.
| `Mono<Void> send(Publisher<WebSocketMessage>)`
| Takes a source for outgoing messages, writes the messages, and returns a `Mono<Void>` that
completes when the source completes and writing is done.
|===
A `WebSocketHandler` must compose the inbound and outbound streams into a unified flow, and
return a `Mono<Void>` that reflects the completion of that flow. Depending on application
requirements, the unified flow completes when:
* Either inbound or outbound message streams complete.
* Inbound stream completes (i.e. connection closed), while outbound is infinite.
* At a chosen point through the `close` method of `WebSocketSession`.
When inbound and outbound message streams are composed together, there is no need to
check if the connection is open, since Reactive Streams signals will terminate activity.
The inbound stream receives a completion/error signal, and the outbound stream receives
receives a cancellation signal.
The most basic implementation of a handler is one that handles the inbound stream:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -94,17 +124,17 @@ class ExampleHandler implements WebSocketHandler {
@@ -94,17 +124,17 @@ class ExampleHandler implements WebSocketHandler {
<1> Access stream of inbound messages.
<2> Do something with each message.
<3> Perform nested async operation using message content.
<4> Return `Mono<Void>` that doesn't complete while we continue to receive.
<4> Return `Mono<Void>` that completes when receiving completes.
[NOTE]
[TIP]
====
If performing a nested, asynchronous operation, you'll need to call
`message.retain()` if the underlying server uses pooled data buffers (e.g. Netty), or
otherwise the data buffer may be released before you've had a chance to read the data.
For more on this see <<core.adoc#databuffers,Data Buffers and Codecs>>.
For nested, asynchronous operations, you may need to call `message.retain()` on underlying
servers that use pooled data buffers (e.g. Netty), or otherwise the data buffer may be
released before you've had a chance to read the data. For more background see
<<core.adoc#databuffers,Data Buffers and Codecs>>.
====
A handler can work with inbound and outbound messages as independent streams:
The below implementation combines the inbound with the outbound streams:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -114,28 +144,25 @@ class ExampleHandler implements WebSocketHandler {
@@ -114,28 +144,25 @@ class ExampleHandler implements WebSocketHandler {
@Override
public Mono<Void> handle(WebSocketSession session) {
<2> Create outbound message, producing a combined flow.
<3> Return `Mono<Void>` that doesn't complete while we continue to receive.
<2> Send outgoing messages.
<3> Join the streams and return `Mono<Void>` that completes when _either_ stream ends.
@ -172,6 +202,8 @@ of `HandshakeWebSocketService`, which performs basic checks on the WebSocket req
@@ -172,6 +202,8 @@ of `HandshakeWebSocketService`, which performs basic checks on the WebSocket req
then uses `RequestUpgradeStrategy` for the server in use. Currently there is built-in
support for Reactor Netty, Tomcat, Jetty, and Undertow.
The above are just 3 examples to serve as a starting point.