Skip to content

Commit 73ee86c

Browse files
committed
Split messages only if configured to do so
See gh-31970
1 parent 76d00d7 commit 73ee86c

File tree

2 files changed

+71
-28
lines changed

2 files changed

+71
-28
lines changed

spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketStompClient.java

+50-24
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.net.URI;
2121
import java.nio.ByteBuffer;
2222
import java.time.Duration;
23+
import java.util.ArrayList;
2324
import java.util.Collections;
2425
import java.util.List;
2526
import java.util.concurrent.CompletableFuture;
@@ -75,16 +76,13 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif
7576

7677
private static final Log logger = LogFactory.getLog(WebSocketStompClient.class);
7778

78-
/**
79-
* The default max size for in&outbound STOMP message.
80-
*/
81-
private static final int DEFAULT_MESSAGE_MAX_SIZE = 64 * 1024;
8279

8380
private final WebSocketClient webSocketClient;
8481

85-
private int inboundMessageSizeLimit = DEFAULT_MESSAGE_MAX_SIZE;
82+
private int inboundMessageSizeLimit = 64 * 1024;
8683

87-
private int outboundMessageSizeLimit = DEFAULT_MESSAGE_MAX_SIZE;
84+
@Nullable
85+
private Integer outboundMessageSizeLimit;
8886

8987
private boolean autoStartup = true;
9088

@@ -131,7 +129,7 @@ public void setTaskScheduler(@Nullable TaskScheduler taskScheduler) {
131129
* Since a STOMP message can be received in multiple WebSocket messages,
132130
* buffering may be required and this property determines the maximum buffer
133131
* size per message.
134-
* <p>By default this is set to 64 * 1024 (64K), see {@link WebSocketStompClient#DEFAULT_MESSAGE_MAX_SIZE}.
132+
* <p>By default this is set to 64 * 1024 (64K).
135133
*/
136134
public void setInboundMessageSizeLimit(int inboundMessageSizeLimit) {
137135
this.inboundMessageSizeLimit = inboundMessageSizeLimit;
@@ -148,18 +146,19 @@ public int getInboundMessageSizeLimit() {
148146
* Configure the maximum size allowed for outbound STOMP message.
149147
* If STOMP message's size exceeds {@link WebSocketStompClient#outboundMessageSizeLimit},
150148
* STOMP message is split into multiple frames.
151-
* <p>By default this is set to 64 * 1024 (64K), see {@link WebSocketStompClient#DEFAULT_MESSAGE_MAX_SIZE}.
149+
* <p>By default this is not set in which case each STOMP message are not split.
152150
* @since 6.2
153151
*/
154-
public void setOutboundMessageSizeLimit(int outboundMessageSizeLimit) {
152+
public void setOutboundMessageSizeLimit(Integer outboundMessageSizeLimit) {
155153
this.outboundMessageSizeLimit = outboundMessageSizeLimit;
156154
}
157155

158156
/**
159157
* Get the configured outbound message buffer size in bytes.
160158
* @since 6.2
161159
*/
162-
public int getOutboundMessageSizeLimit() {
160+
@Nullable
161+
public Integer getOutboundMessageSizeLimit() {
163162
return this.outboundMessageSizeLimit;
164163
}
165164

@@ -479,8 +478,13 @@ public CompletableFuture<Void> sendAsync(Message<byte[]> message) {
479478
try {
480479
WebSocketSession session = this.session;
481480
Assert.state(session != null, "No WebSocketSession available");
482-
for (WebSocketMessage<?> webSocketMessage : this.codec.encode(message, session.getClass())) {
483-
session.sendMessage(webSocketMessage);
481+
if (this.codec.hasSplittingEncoder()) {
482+
for (WebSocketMessage<?> outMessage : this.codec.encodeAndSplit(message, session.getClass())) {
483+
session.sendMessage(outMessage);
484+
}
485+
}
486+
else {
487+
session.sendMessage(this.codec.encode(message, session.getClass()));
484488
}
485489
future.complete(null);
486490
}
@@ -592,11 +596,13 @@ private static class StompWebSocketMessageCodec {
592596

593597
private final BufferingStompDecoder bufferingDecoder;
594598

599+
@Nullable
595600
private final SplittingStompEncoder splittingEncoder;
596601

597-
public StompWebSocketMessageCodec(int inboundMessageSizeLimit, int outboundMessageSizeLimit) {
602+
public StompWebSocketMessageCodec(int inboundMessageSizeLimit, @Nullable Integer outboundMessageSizeLimit) {
598603
this.bufferingDecoder = new BufferingStompDecoder(DECODER, inboundMessageSizeLimit);
599-
this.splittingEncoder = new SplittingStompEncoder(ENCODER, outboundMessageSizeLimit);
604+
this.splittingEncoder = (outboundMessageSizeLimit != null ?
605+
new SplittingStompEncoder(ENCODER, outboundMessageSizeLimit) : null);
600606
}
601607

602608
public List<Message<byte[]>> decode(WebSocketMessage<?> webSocketMessage) {
@@ -622,21 +628,41 @@ else if (webSocketMessage instanceof BinaryMessage binaryMessage) {
622628
return result;
623629
}
624630

625-
public List<WebSocketMessage<?>> encode(Message<byte[]> message, Class<? extends WebSocketSession> sessionType) {
631+
public boolean hasSplittingEncoder() {
632+
return (this.splittingEncoder != null);
633+
}
634+
635+
public WebSocketMessage<?> encode(Message<byte[]> message, Class<? extends WebSocketSession> sessionType) {
636+
StompHeaderAccessor accessor = getStompHeaderAccessor(message);
637+
byte[] payload = message.getPayload();
638+
byte[] frame = ENCODER.encode(accessor.getMessageHeaders(), payload);
639+
return (useBinary(accessor, payload, sessionType) ? new BinaryMessage(frame) : new TextMessage(frame));
640+
}
641+
642+
public List<WebSocketMessage<?>> encodeAndSplit(Message<byte[]> message, Class<? extends WebSocketSession> sessionType) {
643+
Assert.state(this.splittingEncoder != null, "No SplittingEncoder");
644+
StompHeaderAccessor accessor = getStompHeaderAccessor(message);
645+
byte[] payload = message.getPayload();
646+
List<byte[]> frames = this.splittingEncoder.encode(accessor.getMessageHeaders(), payload);
647+
boolean useBinary = useBinary(accessor, payload, sessionType);
648+
649+
List<WebSocketMessage<?>> messages = new ArrayList<>(frames.size());
650+
frames.forEach(frame -> messages.add(useBinary ? new BinaryMessage(frame) : new TextMessage(frame)));
651+
return messages;
652+
}
653+
654+
private static StompHeaderAccessor getStompHeaderAccessor(Message<byte[]> message) {
626655
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
627656
Assert.notNull(accessor, "No StompHeaderAccessor available");
628-
byte[] payload = message.getPayload();
629-
List<byte[]> frames = splittingEncoder.encode(accessor.getMessageHeaders(), payload);
657+
return accessor;
658+
}
659+
660+
private static boolean useBinary(
661+
StompHeaderAccessor accessor, byte[] payload, Class<? extends WebSocketSession> sessionType) {
630662

631-
boolean useBinary = (payload.length > 0 &&
663+
return (payload.length > 0 &&
632664
!(SockJsSession.class.isAssignableFrom(sessionType)) &&
633665
MimeTypeUtils.APPLICATION_OCTET_STREAM.isCompatibleWith(accessor.getContentType()));
634-
635-
List<WebSocketMessage<?>> messages = new ArrayList<>();
636-
for (byte[] frame : frames) {
637-
messages.add(useBinary ? new BinaryMessage(frame) : new TextMessage(frame));
638-
}
639-
return messages;
640666
}
641667
}
642668

spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientIntegrationTests.java

+21-4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class WebSocketStompClientIntegrationTests {
6666

6767
private AnnotationConfigWebApplicationContext wac;
6868

69+
private String url;
70+
6971

7072
@BeforeEach
7173
void setUp(TestInfo testInfo) throws Exception {
@@ -83,6 +85,8 @@ void setUp(TestInfo testInfo) throws Exception {
8385
WebSocketClient webSocketClient = new StandardWebSocketClient();
8486
this.stompClient = new WebSocketStompClient(webSocketClient);
8587
this.stompClient.setMessageConverter(new StringMessageConverter());
88+
89+
this.url = "ws://127.0.0.1:" + this.server.getPort() + "/stomp";
8690
}
8791

8892
@AfterEach
@@ -109,17 +113,30 @@ void tearDown() {
109113

110114

111115
@Test
112-
@SuppressWarnings("deprecation")
113116
void publishSubscribe() throws Exception {
114-
String url = "ws://127.0.0.1:" + this.server.getPort() + "/stomp";
115-
116117
TestHandler testHandler = new TestHandler("/topic/foo", "payload");
117-
this.stompClient.connect(url, testHandler);
118+
this.stompClient.connectAsync(this.url, testHandler);
118119

119120
assertThat(testHandler.awaitForMessageCount(1, 5000)).isTrue();
120121
assertThat(testHandler.getReceived()).containsExactly("payload");
121122
}
122123

124+
@Test
125+
void publishSubscribeWithSlitMessage() throws Exception {
126+
StringBuilder sb = new StringBuilder();
127+
while (sb.length() < 1024) {
128+
sb.append("A message with a long body... ");
129+
}
130+
String payload = sb.toString();
131+
132+
TestHandler testHandler = new TestHandler("/topic/foo", payload);
133+
this.stompClient.setOutboundMessageSizeLimit(512);
134+
this.stompClient.connectAsync(this.url, testHandler);
135+
136+
assertThat(testHandler.awaitForMessageCount(1, 5000)).isTrue();
137+
assertThat(testHandler.getReceived()).containsExactly(payload);
138+
}
139+
123140

124141
@Configuration(proxyBeanMethods = false)
125142
static class TestConfig extends WebSocketMessageBrokerConfigurationSupport {

0 commit comments

Comments
 (0)