|
|
|
@ -71,7 +71,37 @@ Then map it to a URL and add a `WebSocketHandlerAdapter`: |
|
|
|
[[webflux-websockethandler]] |
|
|
|
[[webflux-websockethandler]] |
|
|
|
=== 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] |
|
|
|
[source,java,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
@ -94,17 +124,17 @@ class ExampleHandler implements WebSocketHandler { |
|
|
|
<1> Access stream of inbound messages. |
|
|
|
<1> Access stream of inbound messages. |
|
|
|
<2> Do something with each message. |
|
|
|
<2> Do something with each message. |
|
|
|
<3> Perform nested async operation using message content. |
|
|
|
<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 |
|
|
|
For nested, asynchronous operations, you may need to call `message.retain()` on underlying |
|
|
|
`message.retain()` if the underlying server uses pooled data buffers (e.g. Netty), or |
|
|
|
servers that use pooled data buffers (e.g. Netty), or otherwise the data buffer may be |
|
|
|
otherwise the data buffer may be released before you've had a chance to read the data. |
|
|
|
released before you've had a chance to read the data. For more background see |
|
|
|
For more on this see <<core.adoc#databuffers,Data Buffers and Codecs>>. |
|
|
|
<<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] |
|
|
|
[source,java,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
@ -114,28 +144,25 @@ class ExampleHandler implements WebSocketHandler { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Mono<Void> handle(WebSocketSession session) { |
|
|
|
public Mono<Void> handle(WebSocketSession session) { |
|
|
|
|
|
|
|
|
|
|
|
Mono<Void> input = session.receive() <1> |
|
|
|
Flux<WebSocketMessage> output = session.receive() <1> |
|
|
|
.doOnNext(message -> { |
|
|
|
.doOnNext(message -> { |
|
|
|
// ... |
|
|
|
// ... |
|
|
|
}) |
|
|
|
}) |
|
|
|
.concatMap(message -> { |
|
|
|
.concatMap(message -> { |
|
|
|
// ... |
|
|
|
// ... |
|
|
|
}) |
|
|
|
}) |
|
|
|
.then(); |
|
|
|
.map(value -> session.textMessage("Echo " + value)); <2> |
|
|
|
|
|
|
|
|
|
|
|
Flux<String> source = ... ; |
|
|
|
|
|
|
|
Mono<Void> output = session.send(source.map(session::textMessage)); <2> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Mono.zip(input, output).then(); <3> |
|
|
|
return session.send(output); <3> |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
---- |
|
|
|
---- |
|
|
|
<1> Handle inbound message stream. |
|
|
|
<1> Handle inbound message stream. |
|
|
|
<2> Send outgoing messages. |
|
|
|
<2> Create outbound message, producing a combined flow. |
|
|
|
<3> Join the streams and return `Mono<Void>` that completes when _either_ stream ends. |
|
|
|
<3> Return `Mono<Void>` that doesn't complete while we continue to receive. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Inbound and outbound streams can be independent, and joined only for completion: |
|
|
|
|
|
|
|
|
|
|
|
A handler can compose a connected flow of inbound and outbound messages: |
|
|
|
|
|
|
|
4 |
|
|
|
|
|
|
|
[source,java,indent=0] |
|
|
|
[source,java,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
@ -144,22 +171,25 @@ class ExampleHandler implements WebSocketHandler { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Mono<Void> handle(WebSocketSession session) { |
|
|
|
public Mono<Void> handle(WebSocketSession session) { |
|
|
|
|
|
|
|
|
|
|
|
Flux<WebSocketMessage> output = session.receive() <1> |
|
|
|
Mono<Void> input = session.receive() <1> |
|
|
|
.doOnNext(message -> { |
|
|
|
.doOnNext(message -> { |
|
|
|
// ... |
|
|
|
// ... |
|
|
|
}) |
|
|
|
}) |
|
|
|
.concatMap(message -> { |
|
|
|
.concatMap(message -> { |
|
|
|
// ... |
|
|
|
// ... |
|
|
|
}) |
|
|
|
}) |
|
|
|
.map(value -> session.textMessage("Echo " + value)); <2> |
|
|
|
.then(); |
|
|
|
|
|
|
|
|
|
|
|
return session.send(output); <3> |
|
|
|
Flux<String> source = ... ; |
|
|
|
|
|
|
|
Mono<Void> output = session.send(source.map(session::textMessage)); <2> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Mono.zip(input, output).then(); <3> |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
---- |
|
|
|
---- |
|
|
|
<1> Handle inbound message stream. |
|
|
|
<1> Handle inbound message stream. |
|
|
|
<2> Create outbound message, producing a combined flow. |
|
|
|
<2> Send outgoing messages. |
|
|
|
<3> Return `Mono<Void>` that doesn't complete while we continue to receive. |
|
|
|
<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 |
|
|
|
then uses `RequestUpgradeStrategy` for the server in use. Currently there is built-in |
|
|
|
then uses `RequestUpgradeStrategy` for the server in use. Currently there is built-in |
|
|
|
support for Reactor Netty, Tomcat, Jetty, and Undertow. |
|
|
|
support for Reactor Netty, Tomcat, Jetty, and Undertow. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The above are just 3 examples to serve as a starting point. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[webflux-websocket-server-config]] |
|
|
|
[[webflux-websocket-server-config]] |
|
|
|
|