Skip to content

Commit bd013e0

Browse files
GH-8681: Expose send-buffer-overflow-strategy
Fixes #8681 * Expose `send-buffer-overflow-strategy` attribute for WebSocket XML configuration * GH-8681: Add send-buffer-overflow-strategy to documentation
1 parent f4212d8 commit bd013e0

File tree

7 files changed

+111
-47
lines changed

7 files changed

+111
-47
lines changed

spring-integration-websocket/src/main/java/org/springframework/integration/websocket/config/ClientWebSocketContainerParser.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2019 the original author or authors.
2+
* Copyright 2014-2023 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.
@@ -33,6 +33,7 @@
3333
* the {@code <websocket:client-container/>} element.
3434
*
3535
* @author Artem Bilan
36+
* @author Julian Koch
3637
* @since 4.1
3738
*/
3839
public class ClientWebSocketContainerParser extends AbstractSingleBeanDefinitionParser {
@@ -55,6 +56,7 @@ protected void doParse(Element element, ParserContext parserContext, BeanDefinit
5556

5657
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-buffer-size-limit");
5758
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-time-limit");
59+
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-buffer-overflow-strategy");
5860
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "origin");
5961
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.AUTO_STARTUP);
6062
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.PHASE);

spring-integration-websocket/src/main/java/org/springframework/integration/websocket/config/ServerWebSocketContainerParser.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2019 the original author or authors.
2+
* Copyright 2014-2023 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.
@@ -36,6 +36,7 @@
3636
* the {@code <websocket:server-container/>} element.
3737
*
3838
* @author Artem Bilan
39+
* @author Julian Koch
3940
* @since 4.1
4041
*/
4142
public class ServerWebSocketContainerParser extends AbstractSingleBeanDefinitionParser {
@@ -104,6 +105,7 @@ protected void doParse(Element element, ParserContext parserContext, BeanDefinit
104105
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "handshake-handler");
105106
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-buffer-size-limit");
106107
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-time-limit");
108+
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-buffer-overflow-strategy");
107109
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "allowed-origins");
108110
}
109111

spring-integration-websocket/src/main/resources/org/springframework/integration/websocket/config/spring-integration-websocket.xsd

+35
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,20 @@
9494
<xsd:union memberTypes="xsd:int xsd:string"/>
9595
</xsd:simpleType>
9696
</xsd:attribute>
97+
<xsd:attribute name="send-buffer-overflow-strategy">
98+
<xsd:annotation>
99+
<xsd:documentation>
100+
The WebSocket session's outbound message buffer overflow strategy.
101+
102+
Concurrently generated outbound messages are buffered if sending is slow.
103+
This strategy determines the behavior when the buffer has reached the limit
104+
configured with &lt;send-buffer-size-limit&gt;.
105+
</xsd:documentation>
106+
</xsd:annotation>
107+
<xsd:simpleType>
108+
<xsd:union memberTypes="overflowStrategyEnumeration xsd:string"/>
109+
</xsd:simpleType>
110+
</xsd:attribute>
97111
<xsd:attributeGroup ref="integration:smartLifeCycleAttributeGroup"/>
98112
</xsd:complexType>
99113
</xsd:element>
@@ -320,6 +334,20 @@
320334
<xsd:union memberTypes="xsd:int xsd:string"/>
321335
</xsd:simpleType>
322336
</xsd:attribute>
337+
<xsd:attribute name="send-buffer-overflow-strategy">
338+
<xsd:annotation>
339+
<xsd:documentation>
340+
The WebSocket session's outbound message buffer overflow strategy.
341+
342+
Concurrently generated outbound messages are buffered if sending is slow.
343+
This strategy determines the behavior when the buffer has reached the limit
344+
configured with &lt;send-buffer-size-limit&gt;.
345+
</xsd:documentation>
346+
</xsd:annotation>
347+
<xsd:simpleType>
348+
<xsd:union memberTypes="overflowStrategyEnumeration xsd:string"/>
349+
</xsd:simpleType>
350+
</xsd:attribute>
323351
<xsd:attribute name="allowed-origins" type="xsd:string">
324352
<xsd:annotation>
325353
<xsd:documentation>
@@ -505,4 +533,11 @@
505533
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
506534
</xsd:complexType>
507535

536+
<xsd:simpleType name="overflowStrategyEnumeration">
537+
<xsd:restriction base="xsd:token">
538+
<xsd:enumeration value="TERMINATE"/>
539+
<xsd:enumeration value="DROP"/>
540+
</xsd:restriction>
541+
</xsd:simpleType>
542+
508543
</xsd:schema>

spring-integration-websocket/src/test/java/org/springframework/integration/websocket/config/WebSocketParserTests-context.xml

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
path="/ws"
1818
send-buffer-size-limit="100000"
1919
send-time-limit="100"
20+
send-buffer-overflow-strategy="DROP"
2021
handshake-handler="handshakeHandler"
2122
handshake-interceptors="handshakeInterceptor"
2223
decorator-factories="decoratorFactory"
@@ -66,6 +67,7 @@
6667
uri-variables="ws,user"
6768
send-buffer-size-limit="1000"
6869
send-time-limit="100"
70+
send-buffer-overflow-strategy="DROP"
6971
origin="FOO"
7072
phase="100">
7173
<int-websocket:http-headers>

spring-integration-websocket/src/test/java/org/springframework/integration/websocket/config/WebSocketParserTests.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2022 the original author or authors.
2+
* Copyright 2014-2023 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.
@@ -47,6 +47,7 @@
4747
import org.springframework.web.socket.WebSocketHandler;
4848
import org.springframework.web.socket.WebSocketHttpHeaders;
4949
import org.springframework.web.socket.client.WebSocketClient;
50+
import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator;
5051
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
5152
import org.springframework.web.socket.messaging.StompSubProtocolHandler;
5253
import org.springframework.web.socket.server.HandshakeHandler;
@@ -60,6 +61,7 @@
6061

6162
/**
6263
* @author Artem Bilan
64+
* @author Julian Koch
6365
*
6466
* @since 4.1
6567
*/
@@ -155,6 +157,8 @@ public void testDefaultInboundChannelAdapterAndServerContainer() {
155157
assertThat(interceptors[0]).isSameAs(this.handshakeInterceptor);
156158
assertThat(TestUtils.getPropertyValue(this.serverWebSocketContainer, "sendTimeLimit")).isEqualTo(100);
157159
assertThat(TestUtils.getPropertyValue(this.serverWebSocketContainer, "sendBufferSizeLimit")).isEqualTo(100000);
160+
assertThat(TestUtils.getPropertyValue(this.serverWebSocketContainer, "sendBufferOverflowStrategy"))
161+
.isEqualTo(ConcurrentWebSocketSessionDecorator.OverflowStrategy.DROP);
158162
assertThat(TestUtils.getPropertyValue(this.serverWebSocketContainer, "origins", String[].class))
159163
.isEqualTo(new String[] {"https://foo.com"});
160164

@@ -244,6 +248,8 @@ public void testCustomInboundChannelAdapterAndClientContainer() throws URISyntax
244248
.isSameAs(this.customInboundAdapter);
245249
assertThat(TestUtils.getPropertyValue(this.clientWebSocketContainer, "sendTimeLimit")).isEqualTo(100);
246250
assertThat(TestUtils.getPropertyValue(this.clientWebSocketContainer, "sendBufferSizeLimit")).isEqualTo(1000);
251+
assertThat(TestUtils.getPropertyValue(this.clientWebSocketContainer, "sendBufferOverflowStrategy"))
252+
.isEqualTo(ConcurrentWebSocketSessionDecorator.OverflowStrategy.DROP);
247253
assertThat(TestUtils.getPropertyValue(this.clientWebSocketContainer, "connectionManager.uri", URI.class))
248254
.isEqualTo(new URI("ws://foo.bar/ws?service=user"));
249255
assertThat(TestUtils.getPropertyValue(this.clientWebSocketContainer, "connectionManager.client"))
@@ -258,6 +264,8 @@ public void testCustomInboundChannelAdapterAndClientContainer() throws URISyntax
258264
.isEqualTo(10 * 1000);
259265
assertThat(TestUtils.getPropertyValue(this.simpleClientWebSocketContainer, "sendBufferSizeLimit"))
260266
.isEqualTo(512 * 1024);
267+
assertThat(TestUtils.getPropertyValue(this.simpleClientWebSocketContainer, "sendBufferOverflowStrategy"))
268+
.isNull();
261269
assertThat(TestUtils.getPropertyValue(this.simpleClientWebSocketContainer, "connectionManager.uri", URI.class))
262270
.isEqualTo(new URI("ws://foo.bar"));
263271
assertThat(TestUtils.getPropertyValue(this.simpleClientWebSocketContainer, "connectionManager.client"))

src/reference/asciidoc/web-sockets.adoc

+52-44
Original file line numberDiff line numberDiff line change
@@ -171,18 +171,19 @@ The following listing shows the attributes available for the `<int-websocket:cli
171171
[source,xml]
172172
----
173173
<int-websocket:client-container
174-
id="" <1>
175-
client="" <2>
176-
uri="" <3>
177-
uri-variables="" <4>
178-
origin="" <5>
179-
send-time-limit="" <6>
180-
send-buffer-size-limit="" <7>
181-
auto-startup="" <8>
182-
phase=""> <9>
174+
id="" <1>
175+
client="" <2>
176+
uri="" <3>
177+
uri-variables="" <4>
178+
origin="" <5>
179+
send-time-limit="" <6>
180+
send-buffer-size-limit="" <7>
181+
send-buffer-overflow-strategy="" <8>
182+
auto-startup="" <9>
183+
phase=""> <10>
183184
<int-websocket:http-headers>
184185
<entry key="" value=""/>
185-
</int-websocket:http-headers> <10>
186+
</int-websocket:http-headers> <11>
186187
</int-websocket:client-container>
187188
----
188189
@@ -198,14 +199,17 @@ See https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/w
198199
Defaults to `10000`.
199200
<7> The WebSocket session 'send' message size limit.
200201
Defaults to `524288`.
201-
<8> Boolean value indicating whether this endpoint should start automatically.
202+
<8> The WebSocket session send buffer overflow strategy
203+
determines the behavior when a session's outbound message buffer has reached the `send-buffer-size-limit`.
204+
See `ConcurrentWebSocketSessionDecorator.OverflowStrategy` for possible values and more details.
205+
<9> Boolean value indicating whether this endpoint should start automatically.
202206
Defaults to `false`, assuming that this container is started from the <<web-socket-inbound-adapter, WebSocket inbound adapter>>.
203-
<9> The lifecycle phase within which this endpoint should start and stop.
207+
<10> The lifecycle phase within which this endpoint should start and stop.
204208
The lower the value, the earlier this endpoint starts and the later it stops.
205209
The default is `Integer.MAX_VALUE`.
206210
Values can be negative.
207211
See https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/SmartLifecycle.html[`SmartLifeCycle`].
208-
<10> A `Map` of `HttpHeaders` to be used with the Handshake request.
212+
<11> A `Map` of `HttpHeaders` to be used with the Handshake request.
209213
====
210214

211215
==== `<int-websocket:server-container>` Attributes
@@ -216,26 +220,27 @@ The following listing shows the attributes available for the `<int-websocket:ser
216220
[source,xml]
217221
----
218222
<int-websocket:server-container
219-
id="" <1>
220-
path="" <2>
221-
handshake-handler="" <3>
222-
handshake-interceptors="" <4>
223-
decorator-factories="" <5>
224-
send-time-limit="" <6>
225-
send-buffer-size-limit="" <7>
226-
allowed-origins=""> <8>
223+
id="" <1>
224+
path="" <2>
225+
handshake-handler="" <3>
226+
handshake-interceptors="" <4>
227+
decorator-factories="" <5>
228+
send-time-limit="" <6>
229+
send-buffer-size-limit="" <7>
230+
send-buffer-overflow-strategy="" <8>
231+
allowed-origins=""> <9>
227232
<int-websocket:sockjs
228-
client-library-url="" <9>
229-
stream-bytes-limit="" <10>
230-
session-cookie-needed="" <11>
231-
heartbeat-time="" <12>
232-
disconnect-delay="" <13>
233-
message-cache-size="" <14>
234-
websocket-enabled="" <15>
235-
scheduler="" <16>
236-
message-codec="" <17>
237-
transport-handlers="" <18>
238-
suppress-cors="true"="" /> <19>
233+
client-library-url="" <10>
234+
stream-bytes-limit="" <11>
235+
session-cookie-needed="" <12>
236+
heartbeat-time="" <13>
237+
disconnect-delay="" <14>
238+
message-cache-size="" <15>
239+
websocket-enabled="" <16>
240+
scheduler="" <17>
241+
message-codec="" <18>
242+
transport-handlers="" <19>
243+
suppress-cors="true" /> <20>
239244
</int-websocket:server-container>
240245
----
241246
@@ -251,41 +256,44 @@ the WebSocket session when the corresponding HTTP session expires).
251256
See the https://docs.spring.io/spring-session/docs/current/reference/html5/#websocket[Spring Session Project] for more information.
252257
<6> See the same option on the <<websocket-client-container-attributes,`<int-websocket:client-container>`>>.
253258
<7> See the same option on the <<websocket-client-container-attributes,`<int-websocket:client-container>`>>.
254-
<8> The allowed origin header values.
259+
<8> The WebSocket session send buffer overflow strategy
260+
determines the behavior when a session's outbound message buffer has reached the `send-buffer-size-limit`.
261+
See `ConcurrentWebSocketSessionDecorator.OverflowStrategy` for possible values and more details.
262+
<9> The allowed origin header values.
255263
You can specify multiple origins as a comma-separated list.
256264
This check is mostly designed for browser clients.
257265
There is nothing preventing other types of client from modifying the origin header value.
258266
When SockJS is enabled and allowed origins are restricted, transport types that do not use origin headers for cross-origin requests (`jsonp-polling`, `iframe-xhr-polling`, `iframe-eventsource`, and `iframe-htmlfile`) are disabled.
259267
As a consequence, IE6 and IE7 are not supported, and IE8 and IE9 are supported only without cookies.
260268
By default, all origins are allowed.
261-
<9> Transports with no native cross-domain communication (such as `eventsource` and `htmlfile`) must get a simple page from the "`foreign`" domain in an invisible iframe so that code in the iframe can run from a domain local to the SockJS server.
269+
<10> Transports with no native cross-domain communication (such as `eventsource` and `htmlfile`) must get a simple page from the "`foreign`" domain in an invisible iframe so that code in the iframe can run from a domain local to the SockJS server.
262270
Since the iframe needs to load the SockJS javascript client library, this property lets you specify the location from which to load it.
263271
By default, it points to `https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js`.
264272
However, you can also set it to point to a URL served by the application.
265273
Note that it is possible to specify a relative URL, in which case the URL must be relative to the iframe URL.
266274
For example, assuming a SockJS endpoint mapped to `/sockjs` and the resulting iframe URL is `/sockjs/iframe.html`, the relative URL must start with "../../" to traverse up to the location above the SockJS mapping.
267275
For prefix-based servlet mapping, you may need one more traversal.
268-
<10> Minimum number of bytes that can be sent over a single HTTP streaming request before it is closed.
276+
<11> Minimum number of bytes that can be sent over a single HTTP streaming request before it is closed.
269277
Defaults to `128K` (that is, 128*1024 or 131072 bytes).
270-
<11> The `cookie_needed` value in the response from the SockJs `/info` endpoint.
278+
<12> The `cookie_needed` value in the response from the SockJs `/info` endpoint.
271279
This property indicates whether a `JSESSIONID` cookie is required for the application to function correctly (for example, for load balancing or in Java Servlet containers for the use of an HTTP session).
272-
<12> The amount of time (in milliseconds) when the server has not sent any messages and after which the server should
280+
<13> The amount of time (in milliseconds) when the server has not sent any messages and after which the server should
273281
send a heartbeat frame to the client in order to keep the connection from breaking.
274282
The default value is `25,000` (25 seconds).
275-
<13> The amount of time (in milliseconds) before a client is considered disconnected after not having a receiving connection (that is, an active connection over which the server can send data to the client).
283+
<14> The amount of time (in milliseconds) before a client is considered disconnected after not having a receiving connection (that is, an active connection over which the server can send data to the client).
276284
The default value is `5000`.
277-
<14> The number of server-to-client messages that a session can cache while waiting for the next HTTP polling request from the client.
285+
<15> The number of server-to-client messages that a session can cache while waiting for the next HTTP polling request from the client.
278286
The default size is `100`.
279-
<15> Some load balancers do not support WebSockets.
287+
<16> Some load balancers do not support WebSockets.
280288
Set this option to `false` to disable the WebSocket transport on the server side.
281289
The default value is `true`.
282-
<16> The `TaskScheduler` bean reference.
290+
<17> The `TaskScheduler` bean reference.
283291
A new `ThreadPoolTaskScheduler` instance is created if no value is provided.
284292
This scheduler instance is used for scheduling heart-beat messages.
285-
<17> The `SockJsMessageCodec` bean reference to use for encoding and decoding SockJS messages.
293+
<18> The `SockJsMessageCodec` bean reference to use for encoding and decoding SockJS messages.
286294
By default, `Jackson2SockJsMessageCodec` is used, which requires the Jackson library to be present on the classpath.
287-
<18> List of `TransportHandler` bean references.
288-
<19> Whether to disable automatic addition of CORS headers for SockJS requests.
295+
<19> List of `TransportHandler` bean references.
296+
<20> Whether to disable automatic addition of CORS headers for SockJS requests.
289297
The default value is `false`.
290298
====
291299

src/reference/asciidoc/whats-new.adoc

+7
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,10 @@ See <<./endpoint.adoc#endpoint-pollingconsumer, Polling Consumer>> for more info
3131

3232
- Java, Groovy and Kotlin DSLs have now context-specific methods in the `IntegationFlowDefinition` with a single `Consumer` argument to configure an endpoint and its handler with one builder and readable options.
3333
See, for example, `transformWith()`, `splitWith()` in <<./dsl.adoc#java-dsl, Java DSL Chapter>>.
34+
35+
[[x6.2-websockets]]
36+
=== WebSockets Changes
37+
38+
- For the server and client WebSocket containers, the send buffer overflow strategy is now configurable in `IntegrationWebSocketContainer` and in XML via `send-buffer-overflow-strategy`.
39+
This strategy determines the behavior when a session's outbound message buffer has reached the configured limit.
40+
See <<./web-sockets.adoc#websocket-client-container-attributes, WebSockets Support>> for more information.

0 commit comments

Comments
 (0)