Skip to content

Commit b385ff1

Browse files
committed
Polish WebFlux WebSocket docs
Issue: SPR-16820
1 parent 3c88029 commit b385ff1

File tree

3 files changed

+111
-59
lines changed

3 files changed

+111
-59
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketHandler.java

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,62 +25,71 @@
2525
/**
2626
* Handler for a WebSocket session.
2727
*
28-
* <p>Use {@link WebSocketSession#receive()} to compose on the stream of
29-
* inbound messages and {@link WebSocketSession#send(Publisher)} to write the
30-
* stream of outbound messages.
28+
* <p>A server {@code WebSocketHandler} is mapped to requests with
29+
* {@link org.springframework.web.reactive.handler.SimpleUrlHandlerMapping
30+
* SimpleUrlHandlerMapping} and
31+
* {@link org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter
32+
* WebSocketHandlerAdapter}. A client {@code WebSocketHandler} is passed to the
33+
* {@link org.springframework.web.reactive.socket.client.WebSocketClient
34+
* WebSocketClient} execute method.
3135
*
32-
* <p>You can handle inbound and outbound messages as independent streams, and
33-
* then join them:
36+
* <p>Use {@link WebSocketSession#receive() session.receive()} to compose on
37+
* the inbound message stream, and {@link WebSocketSession#send(Publisher)
38+
* session.send(publisher)} for the outbound message stream. Below is an
39+
* example, combined flow to process inbound and to send outbound messages:
3440
*
3541
* <pre class="code">
3642
* class ExampleHandler implements WebSocketHandler {
3743
3844
* &#064;Override
3945
* public Mono&lt;Void&gt; handle(WebSocketSession session) {
4046
*
41-
* Mono&lt;Void&gt; input = session.receive()
47+
* Flux&lt;WebSocketMessage&gt; input = session.receive()
4248
* .doOnNext(message -> {
4349
* // ...
4450
* })
4551
* .concatMap(message -> {
4652
* // ...
4753
* })
48-
* .then();
49-
*
50-
* Flux&lt;String&gt; source = ... ;
51-
* Mono&lt;Void&gt; output = session.send(source.map(session::textMessage));
54+
* .map(value -> session.textMessage("Echo " + value));
5255
*
53-
* return Mono.zip(input, output).then();
56+
* return session.send(output);
5457
* }
5558
* }
5659
* </pre>
5760
*
58-
* <p>You can also create a single flow including inbound and outbound messages:
61+
* <p>If processing inbound and sending outbound messages are independent
62+
* streams, they can be joined together with the "zip" operator:
63+
*
5964
* <pre class="code">
6065
* class ExampleHandler implements WebSocketHandler {
6166
6267
* &#064;Override
6368
* public Mono&lt;Void&gt; handle(WebSocketSession session) {
6469
*
65-
* Flux&lt;WebSocketMessage&gt; input = session.receive()
70+
* Mono&lt;Void&gt; input = session.receive()
6671
* .doOnNext(message -> {
6772
* // ...
6873
* })
6974
* .concatMap(message -> {
7075
* // ...
7176
* })
72-
* .map(value -> session.textMessage("Echo " + value));
77+
* .then();
7378
*
74-
* return session.send(output);
79+
* Flux&lt;String&gt; source = ... ;
80+
* Mono&lt;Void&gt; output = session.send(source.map(session::textMessage));
81+
*
82+
* return Mono.zip(input, output).then();
7583
* }
7684
* }
7785
* </pre>
7886
*
79-
* <p>When the connection is closed, the inbound stream will receive a
80-
* completion/error signal, while the outbound stream will get a cancellation
81-
* signal. The above flows are composed in such a way that the
82-
* {@code Mono<Void>} returned from the {@code WebSocketHandler} won't complete
83-
* until the connection is closed.
87+
* <p>A {@code WebSocketHandler} must compose the inbound and outbound streams
88+
* into a unified flow and return a {@code Mono<Void>} that reflects the
89+
* completion of that flow. That means there is no need to check if the
90+
* connection is open, since Reactive Streams signals will terminate activity.
91+
* The inbound stream receives a completion/error signal, and the outbound
92+
* stream receives receives a cancellation signal.
8493
*
8594
* @author Rossen Stoyanchev
8695
* @since 5.0
@@ -96,13 +105,17 @@ default List<String> getSubProtocols() {
96105
}
97106

98107
/**
99-
* Handle the WebSocket session.
100-
*
108+
* Invoked when a new WebSocket connection is established, and allows
109+
* handling of the session.
101110
*
111+
* <p>See the class-level doc and the reference for more details and
112+
* examples of how to handle the session.
102113
*
103114
* @param session the session to handle
104-
* @return completion {@code Mono<Void>} to indicate the outcome of the
105-
* WebSocket session handling.
115+
* @return indicates when appilcation handling of the session is complete,
116+
* which should reflect the completion of the inbound message stream
117+
* (i.e. connection closing) and possibly the completion of the outbound
118+
* message stream and the writing of messages.
106119
*/
107120
Mono<Void> handle(WebSocketSession session);
108121

spring-webflux/src/main/java/org/springframework/web/reactive/socket/WebSocketSession.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,15 +25,11 @@
2525
import org.springframework.core.io.buffer.DataBufferFactory;
2626

2727
/**
28-
* Represents a WebSocket session with Reactive Streams input and output.
28+
* Represents a WebSocket session.
2929
*
30-
* <p>On the server side a WebSocket session can be handled by mapping
31-
* requests to a {@link WebSocketHandler} and ensuring there is a
32-
* {@link org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter
33-
* WebSocketHandlerAdapter} strategy registered in Spring configuration.
34-
* On the client side a {@link WebSocketHandler} can be provided to a
35-
* {@link org.springframework.web.reactive.socket.client.WebSocketClient
36-
* WebSocketClient}.
30+
* <p>Use {@link WebSocketSession#receive() session.receive()} to compose on
31+
* the inbound message stream, and {@link WebSocketSession#send(Publisher)
32+
* session.send(publisher)} to provide the outbound message stream.
3733
*
3834
* @author Rossen Stoyanchev
3935
* @since 5.0
@@ -57,13 +53,24 @@ public interface WebSocketSession {
5753
DataBufferFactory bufferFactory();
5854

5955
/**
60-
* Get access to the stream of incoming messages.
56+
* Provides access to the stream of inbound messages.
57+
* <p>This stream receives a completion or error signal when the connection
58+
* is closed. In a typical {@link WebSocketHandler} implementation this
59+
* stream is composed into the overall processing flow, so that when the
60+
* connection is closed, handling will end.
61+
*
62+
* <p>See the class-level doc of {@link WebSocketHandler} and the reference
63+
* for more details and examples of how to handle the session.
6164
*/
6265
Flux<WebSocketMessage> receive();
6366

6467
/**
65-
* Write the given messages to the WebSocket connection.
66-
* @param messages the messages to write
68+
* Give a source of outgoing messages, write the messages and return a
69+
* {@code Mono<Void>} that completes when the source completes and writing
70+
* is done.
71+
*
72+
* <p>See the class-level doc of {@link WebSocketHandler} and the reference
73+
* for more details and examples of how to handle the session.
6774
*/
6875
Mono<Void> send(Publisher<WebSocketMessage> messages);
6976

src/docs/asciidoc/web/webflux-websocket.adoc

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,37 @@ Then map it to a URL and add a `WebSocketHandlerAdapter`:
7171
[[webflux-websockethandler]]
7272
=== WebSocketHandler
7373

74-
The most basic implementation of a handler is one that handles inbound messages:
74+
The `handle` method of `WebSocketHandler` takes `WebSocketSession` and returns `Mono<Void>`
75+
to indicate when application handling of the session is complete. The session is handled
76+
through two streams, one for inbound and one for outbound messages:
77+
78+
[options="header"]
79+
|===
80+
| WebSocketSession method | Description
81+
82+
| `Flux<WebSocketMessage> receive()`
83+
| Provides access to the inbound message stream, and completes when the connection is closed.
84+
85+
| `Mono<Void> send(Publisher<WebSocketMessage>)`
86+
| Takes a source for outgoing messages, writes the messages, and returns a `Mono<Void>` that
87+
completes when the source completes and writing is done.
88+
89+
|===
90+
91+
A `WebSocketHandler` must compose the inbound and outbound streams into a unified flow, and
92+
return a `Mono<Void>` that reflects the completion of that flow. Depending on application
93+
requirements, the unified flow completes when:
94+
95+
* Either inbound or outbound message streams complete.
96+
* Inbound stream completes (i.e. connection closed), while outbound is infinite.
97+
* At a chosen point through the `close` method of `WebSocketSession`.
98+
99+
When inbound and outbound message streams are composed together, there is no need to
100+
check if the connection is open, since Reactive Streams signals will terminate activity.
101+
The inbound stream receives a completion/error signal, and the outbound stream receives
102+
receives a cancellation signal.
103+
104+
The most basic implementation of a handler is one that handles the inbound stream:
75105

76106
[source,java,indent=0]
77107
[subs="verbatim,quotes"]
@@ -94,17 +124,17 @@ class ExampleHandler implements WebSocketHandler {
94124
<1> Access stream of inbound messages.
95125
<2> Do something with each message.
96126
<3> Perform nested async operation using message content.
97-
<4> Return `Mono<Void>` that doesn't complete while we continue to receive.
127+
<4> Return `Mono<Void>` that completes when receiving completes.
98128

99-
[NOTE]
129+
[TIP]
100130
====
101-
If performing a nested, asynchronous operation, you'll need to call
102-
`message.retain()` if the underlying server uses pooled data buffers (e.g. Netty), or
103-
otherwise the data buffer may be released before you've had a chance to read the data.
104-
For more on this see <<core.adoc#databuffers,Data Buffers and Codecs>>.
131+
For nested, asynchronous operations, you may need to call `message.retain()` on underlying
132+
servers that use pooled data buffers (e.g. Netty), or otherwise the data buffer may be
133+
released before you've had a chance to read the data. For more background see
134+
<<core.adoc#databuffers,Data Buffers and Codecs>>.
105135
====
106136

107-
A handler can work with inbound and outbound messages as independent streams:
137+
The below implementation combines the inbound with the outbound streams:
108138

109139
[source,java,indent=0]
110140
[subs="verbatim,quotes"]
@@ -114,28 +144,25 @@ class ExampleHandler implements WebSocketHandler {
114144
@Override
115145
public Mono<Void> handle(WebSocketSession session) {
116146
117-
Mono<Void> input = session.receive() <1>
147+
Flux<WebSocketMessage> output = session.receive() <1>
118148
.doOnNext(message -> {
119149
// ...
120150
})
121151
.concatMap(message -> {
122152
// ...
123153
})
124-
.then();
125-
126-
Flux<String> source = ... ;
127-
Mono<Void> output = session.send(source.map(session::textMessage)); <2>
154+
.map(value -> session.textMessage("Echo " + value)); <2>
128155
129-
return Mono.zip(input, output).then(); <3>
156+
return session.send(output); <3>
130157
}
131158
}
132159
----
133160
<1> Handle inbound message stream.
134-
<2> Send outgoing messages.
135-
<3> Join the streams and return `Mono<Void>` that completes when _either_ stream ends.
161+
<2> Create outbound message, producing a combined flow.
162+
<3> Return `Mono<Void>` that doesn't complete while we continue to receive.
163+
164+
Inbound and outbound streams can be independent, and joined only for completion:
136165

137-
A handler can compose a connected flow of inbound and outbound messages:
138-
4
139166
[source,java,indent=0]
140167
[subs="verbatim,quotes"]
141168
----
@@ -144,22 +171,25 @@ class ExampleHandler implements WebSocketHandler {
144171
@Override
145172
public Mono<Void> handle(WebSocketSession session) {
146173
147-
Flux<WebSocketMessage> output = session.receive() <1>
174+
Mono<Void> input = session.receive() <1>
148175
.doOnNext(message -> {
149176
// ...
150177
})
151178
.concatMap(message -> {
152179
// ...
153180
})
154-
.map(value -> session.textMessage("Echo " + value)); <2>
181+
.then();
155182
156-
return session.send(output); <3>
183+
Flux<String> source = ... ;
184+
Mono<Void> output = session.send(source.map(session::textMessage)); <2>
185+
186+
return Mono.zip(input, output).then(); <3>
157187
}
158188
}
159189
----
160190
<1> Handle inbound message stream.
161-
<2> Create outbound message, producing a combined flow.
162-
<3> Return `Mono<Void>` that doesn't complete while we continue to receive.
191+
<2> Send outgoing messages.
192+
<3> Join the streams and return `Mono<Void>` that completes when _either_ stream ends.
163193

164194

165195

@@ -172,6 +202,8 @@ of `HandshakeWebSocketService`, which performs basic checks on the WebSocket req
172202
then uses `RequestUpgradeStrategy` for the server in use. Currently there is built-in
173203
support for Reactor Netty, Tomcat, Jetty, and Undertow.
174204

205+
The above are just 3 examples to serve as a starting point.
206+
175207

176208

177209
[[webflux-websocket-server-config]]

0 commit comments

Comments
 (0)