diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketHandler.java
index 75ed51c4569..32ae15599fe 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketHandler.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketHandler.java
@@ -25,12 +25,41 @@ import reactor.core.publisher.Mono;
/**
* Handler for a WebSocket session.
*
- *
Use {@link WebSocketSession#receive()} to compose on the stream of
- * inbound messages and {@link WebSocketSession#send(Publisher)} to write the
- * stream of outbound messages.
+ *
A server {@code WebSocketHandler} is mapped to requests with
+ * {@link org.springframework.web.reactive.handler.SimpleUrlHandlerMapping
+ * SimpleUrlHandlerMapping} and
+ * {@link org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter
+ * WebSocketHandlerAdapter}. A client {@code WebSocketHandler} is passed to the
+ * {@link org.springframework.web.reactive.socket.client.WebSocketClient
+ * WebSocketClient} execute method.
*
- *
You can handle inbound and outbound messages as independent streams, and
- * then join them:
+ *
Use {@link WebSocketSession#receive() session.receive()} to compose on
+ * the inbound message stream, and {@link WebSocketSession#send(Publisher)
+ * session.send(publisher)} for the outbound message stream. Below is an
+ * example, combined flow to process inbound and to send outbound messages:
+ *
+ *
+ * class ExampleHandler implements WebSocketHandler {
+
+ * @Override
+ * public Mono<Void> handle(WebSocketSession session) {
+ *
+ * Flux<WebSocketMessage> input = session.receive()
+ * .doOnNext(message -> {
+ * // ...
+ * })
+ * .concatMap(message -> {
+ * // ...
+ * })
+ * .map(value -> session.textMessage("Echo " + value));
+ *
+ * return session.send(output);
+ * }
+ * }
+ *
+ *
+ * If processing inbound and sending outbound messages are independent
+ * streams, they can be joined together with the "zip" operator:
*
*
* class ExampleHandler implements WebSocketHandler {
@@ -55,32 +84,12 @@ import reactor.core.publisher.Mono;
* }
*
*
- * You can also create a single flow including inbound and outbound messages:
- *
- * class ExampleHandler implements WebSocketHandler {
-
- * @Override
- * public Mono<Void> handle(WebSocketSession session) {
- *
- * Flux<WebSocketMessage> input = session.receive()
- * .doOnNext(message -> {
- * // ...
- * })
- * .concatMap(message -> {
- * // ...
- * })
- * .map(value -> session.textMessage("Echo " + value));
- *
- * return session.send(output);
- * }
- * }
- *
- *
- * When the connection is closed, the inbound stream will receive a
- * completion/error signal, while the outbound stream will get a cancellation
- * signal. The above flows are composed in such a way that the
- * {@code Mono} returned from the {@code WebSocketHandler} won't complete
- * until the connection is closed.
+ * A {@code WebSocketHandler} must compose the inbound and outbound streams
+ * into a unified flow and return a {@code Mono} that reflects the
+ * completion of that flow. That means 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.
*
* @author Rossen Stoyanchev
* @since 5.0
@@ -96,13 +105,17 @@ public interface WebSocketHandler {
}
/**
- * Handle the WebSocket session.
- *
+ * Invoked when a new WebSocket connection is established, and allows
+ * handling of the session.
*
+ * See the class-level doc and the reference for more details and
+ * examples of how to handle the session.
*
* @param session the session to handle
- * @return completion {@code Mono} to indicate the outcome of the
- * WebSocket session handling.
+ * @return indicates when appilcation handling of the session is complete,
+ * which should reflect the completion of the inbound message stream
+ * (i.e. connection closing) and possibly the completion of the outbound
+ * message stream and the writing of messages.
*/
Mono handle(WebSocketSession session);
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketSession.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketSession.java
index bca6047a2fc..10fa28176d9 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketSession.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,15 +25,11 @@ import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
/**
- * Represents a WebSocket session with Reactive Streams input and output.
+ * Represents a WebSocket session.
*
- * On the server side a WebSocket session can be handled by mapping
- * requests to a {@link WebSocketHandler} and ensuring there is a
- * {@link org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter
- * WebSocketHandlerAdapter} strategy registered in Spring configuration.
- * On the client side a {@link WebSocketHandler} can be provided to a
- * {@link org.springframework.web.reactive.socket.client.WebSocketClient
- * WebSocketClient}.
+ *
Use {@link WebSocketSession#receive() session.receive()} to compose on
+ * the inbound message stream, and {@link WebSocketSession#send(Publisher)
+ * session.send(publisher)} to provide the outbound message stream.
*
* @author Rossen Stoyanchev
* @since 5.0
@@ -57,13 +53,24 @@ public interface WebSocketSession {
DataBufferFactory bufferFactory();
/**
- * Get access to the stream of incoming messages.
+ * Provides access to the stream of inbound messages.
+ *
This stream receives a completion or error signal when the connection
+ * is closed. In a typical {@link WebSocketHandler} implementation this
+ * stream is composed into the overall processing flow, so that when the
+ * connection is closed, handling will end.
+ *
+ *
See the class-level doc of {@link WebSocketHandler} and the reference
+ * for more details and examples of how to handle the session.
*/
Flux receive();
/**
- * Write the given messages to the WebSocket connection.
- * @param messages the messages to write
+ * Give a source of outgoing messages, write the messages and return a
+ * {@code Mono} that completes when the source completes and writing
+ * is done.
+ *
+ * See the class-level doc of {@link WebSocketHandler} and the reference
+ * for more details and examples of how to handle the session.
*/
Mono send(Publisher messages);
diff --git a/src/docs/asciidoc/web/webflux-websocket.adoc b/src/docs/asciidoc/web/webflux-websocket.adoc
index cdd9e432b54..17a36d9b487 100644
--- a/src/docs/asciidoc/web/webflux-websocket.adoc
+++ b/src/docs/asciidoc/web/webflux-websocket.adoc
@@ -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`
+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 receive()`
+| Provides access to the inbound message stream, and completes when the connection is closed.
+
+| `Mono send(Publisher)`
+| Takes a source for outgoing messages, writes the messages, and returns a `Mono` 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` 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,44 @@ 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` that doesn't complete while we continue to receive.
+<4> Return `Mono` 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 <>.
+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
+<>.
====
-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"]
+----
+class ExampleHandler implements WebSocketHandler {
+
+ @Override
+ public Mono handle(WebSocketSession session) {
+
+ Flux output = session.receive() <1>
+ .doOnNext(message -> {
+ // ...
+ })
+ .concatMap(message -> {
+ // ...
+ })
+ .map(value -> session.textMessage("Echo " + value)); <2>
+
+ return session.send(output); <3>
+ }
+}
+----
+<1> Handle inbound message stream.
+<2> Create outbound message, producing a combined flow.
+<3> Return `Mono` that doesn't complete while we continue to receive.
+
+Inbound and outbound streams can be independent, and joined only for completion:
[source,java,indent=0]
[subs="verbatim,quotes"]
@@ -134,33 +191,6 @@ class ExampleHandler implements WebSocketHandler {
<2> Send outgoing messages.
<3> Join the streams and return `Mono` that completes when _either_ stream ends.
-A handler can compose a connected flow of inbound and outbound messages:
-4
-[source,java,indent=0]
-[subs="verbatim,quotes"]
-----
-class ExampleHandler implements WebSocketHandler {
-
- @Override
- public Mono handle(WebSocketSession session) {
-
- Flux output = session.receive() <1>
- .doOnNext(message -> {
- // ...
- })
- .concatMap(message -> {
- // ...
- })
- .map(value -> session.textMessage("Echo " + value)); <2>
-
- return session.send(output); <3>
- }
-}
-----
-<1> Handle inbound message stream.
-<2> Create outbound message, producing a combined flow.
-<3> Return `Mono` that doesn't complete while we continue to receive.
-
[[webflux-websocket-server-handshake]]
@@ -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.
+
[[webflux-websocket-server-config]]