Skip to content

Commit c7adf28

Browse files
committed
Expand WebFlux docs with WebSocketHandler examples
Issue: SPR-16820
1 parent c555fef commit c7adf28

File tree

2 files changed

+166
-16
lines changed

2 files changed

+166
-16
lines changed

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

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 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.
@@ -19,11 +19,69 @@
1919
import java.util.Collections;
2020
import java.util.List;
2121

22+
import org.reactivestreams.Publisher;
2223
import reactor.core.publisher.Mono;
2324

2425
/**
2526
* Handler for a WebSocket session.
2627
*
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.
31+
*
32+
* <p>You can handle inbound and outbound messages as independent streams, and
33+
* then join them:
34+
*
35+
* <pre class="code">
36+
* class ExampleHandler implements WebSocketHandler {
37+
38+
* &#064;Override
39+
* public Mono&lt;Void&gt; handle(WebSocketSession session) {
40+
*
41+
* Mono&lt;Void&gt; input = session.receive()
42+
* .doOnNext(message -> {
43+
* // ...
44+
* })
45+
* .concatMap(message -> {
46+
* // ...
47+
* })
48+
* .then();
49+
*
50+
* Flux&lt;String&gt; source = ... ;
51+
* Mono&lt;Void&gt; output = session.send(source.map(session::textMessage));
52+
*
53+
* return Mono.zip(input, output).then();
54+
* }
55+
* }
56+
* </pre>
57+
*
58+
* <p>You can also create a single flow including inbound and outbound messages:
59+
* <pre class="code">
60+
* class ExampleHandler implements WebSocketHandler {
61+
62+
* &#064;Override
63+
* public Mono&lt;Void&gt; handle(WebSocketSession session) {
64+
*
65+
* Flux&lt;WebSocketMessage&gt; input = session.receive()
66+
* .doOnNext(message -> {
67+
* // ...
68+
* })
69+
* .concatMap(message -> {
70+
* // ...
71+
* })
72+
* .map(value -> session.textMessage("Echo " + value));
73+
*
74+
* return session.send(output);
75+
* }
76+
* }
77+
* </pre>
78+
*
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.
84+
*
2785
* @author Rossen Stoyanchev
2886
* @since 5.0
2987
*/
@@ -39,6 +97,9 @@ default List<String> getSubProtocols() {
3997

4098
/**
4199
* Handle the WebSocket session.
100+
*
101+
*
102+
*
42103
* @param session the session to handle
43104
* @return completion {@code Mono<Void>} to indicate the outcome of the
44105
* WebSocket session handling.

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

Lines changed: 104 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ server side applications that handle WebSocket messages.
2020

2121

2222
[[webflux-websocket-server-handler]]
23-
=== WebSocketHandler
23+
=== Server
2424
[.small]#<<web.adoc#websocket-server-handler,Same in Servlet stack>>#
2525

26-
Creating a WebSocket server is as simple as implementing `WebSocketHandler`:
26+
To create a WebSocket server, first create a `WebSocketHandler`:
2727

2828
[source,java,indent=0]
2929
[subs="verbatim,quotes"]
@@ -40,10 +40,7 @@ Creating a WebSocket server is as simple as implementing `WebSocketHandler`:
4040
}
4141
----
4242

43-
Spring WebFlux provides a `WebSocketHandlerAdapter` that can adapt WebSocket
44-
requests and use the above handler to handle the resulting WebSocket session. After the
45-
adapter is registered as a bean, you can map requests to your handler, for example using
46-
`SimpleUrlHandlerMapping`. This is shown below:
43+
Then map it to a URL and add a `WebSocketHandlerAdapter`:
4744

4845
[source,java,indent=0]
4946
[subs="verbatim,quotes"]
@@ -71,17 +68,109 @@ adapter is registered as a bean, you can map requests to your handler, for examp
7168

7269

7370

71+
[[webflux-websockethandler]]
72+
=== WebSocketHandler
73+
74+
The most basic implementation of a handler is one that handles inbound messages:
75+
76+
[source,java,indent=0]
77+
[subs="verbatim,quotes"]
78+
----
79+
class ExampleHandler implements WebSocketHandler {
80+
81+
@Override
82+
public Mono<Void> handle(WebSocketSession session) {
83+
return session.receive() <1>
84+
.doOnNext(message -> {
85+
// ... <2>
86+
})
87+
.concatMap(message -> {
88+
// ... <3>
89+
})
90+
.then(); <4>
91+
}
92+
}
93+
----
94+
<1> Access stream of inbound messages.
95+
<2> Do something with each message.
96+
<3> Perform nested async operation using message content.
97+
<4> Return `Mono<Void>` that doesn't complete while we continue to receive.
98+
99+
[NOTE]
100+
====
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>>.
105+
====
106+
107+
A handler can work with inbound and outbound messages as independent streams:
108+
109+
[source,java,indent=0]
110+
[subs="verbatim,quotes"]
111+
----
112+
class ExampleHandler implements WebSocketHandler {
113+
114+
@Override
115+
public Mono<Void> handle(WebSocketSession session) {
116+
117+
Mono<Void> input = session.receive() <1>
118+
.doOnNext(message -> {
119+
// ...
120+
})
121+
.concatMap(message -> {
122+
// ...
123+
})
124+
.then();
125+
126+
Flux<String> source = ... ;
127+
Mono<Void> output = session.send(source.map(session::textMessage)); <2>
128+
129+
return Mono.zip(input, output).then(); <3>
130+
}
131+
}
132+
----
133+
<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.
136+
137+
A handler can compose a connected flow of inbound and outbound messages:
138+
4
139+
[source,java,indent=0]
140+
[subs="verbatim,quotes"]
141+
----
142+
class ExampleHandler implements WebSocketHandler {
143+
144+
@Override
145+
public Mono<Void> handle(WebSocketSession session) {
146+
147+
Flux<WebSocketMessage> output = session.receive() <1>
148+
.doOnNext(message -> {
149+
// ...
150+
})
151+
.concatMap(message -> {
152+
// ...
153+
})
154+
.map(value -> session.textMessage("Echo " + value)); <2>
155+
156+
return session.send(output); <3>
157+
}
158+
}
159+
----
160+
<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.
163+
164+
165+
74166
[[webflux-websocket-server-handshake]]
75-
=== WebSocket Handshake
167+
=== Handshake
76168
[.small]#<<web.adoc#websocket-server-handshake,Same in Servlet stack>>#
77169

78-
`WebSocketHandlerAdapter` does not perform WebSocket handshakes itself. Instead it
79-
delegates to an instance of `WebSocketService`. The default `WebSocketService`
80-
implementation is `HandshakeWebSocketService`.
81-
82-
The `HandshakeWebSocketService` performs basic checks on the WebSocket request and
83-
delegates to a server-specific `RequestUpgradeStrategy`. At present upgrade strategies
84-
exist for Reactor Netty, Tomcat, Jetty, and Undertow.
170+
`WebSocketHandlerAdapter` delegates to a `WebSocketService`. By default that's an instance
171+
of `HandshakeWebSocketService`, which performs basic checks on the WebSocket request and
172+
then uses `RequestUpgradeStrategy` for the server in use. Currently there is built-in
173+
support for Reactor Netty, Tomcat, Jetty, and Undertow.
85174

86175

87176

@@ -132,7 +221,7 @@ specify CORS settings by URL pattern. If both are specified they're combined via
132221

133222

134223
[[webflux-websocket-client]]
135-
== WebSocketClient
224+
=== Client
136225

137226
Spring WebFlux provides a `WebSocketClient` abstraction with implementations for
138227
Reactor Netty, Tomcat, Jetty, Undertow, and standard Java (i.e. JSR-356).

0 commit comments

Comments
 (0)