Skip to content

Commit 309ffc6

Browse files
committed
OverflowStrategy in ConcurrentWebSocketSessionDecorator
Issue: SPR-17140
1 parent 61c52d6 commit 309ffc6

File tree

2 files changed

+95
-9
lines changed

2 files changed

+95
-9
lines changed

spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat
5252

5353
private final int bufferSizeLimit;
5454

55+
private final OverflowStrategy overflowStrategy;
56+
5557
private final Queue<WebSocketMessage<?>> buffer = new LinkedBlockingQueue<>();
5658

5759
private final AtomicInteger bufferSize = new AtomicInteger();
@@ -68,15 +70,31 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat
6870

6971

7072
/**
71-
* Create a new {@code ConcurrentWebSocketSessionDecorator}.
73+
* Basic constructor.
7274
* @param delegate the {@code WebSocketSession} to delegate to
7375
* @param sendTimeLimit the send-time limit (milliseconds)
7476
* @param bufferSizeLimit the buffer-size limit (number of bytes)
7577
*/
7678
public ConcurrentWebSocketSessionDecorator(WebSocketSession delegate, int sendTimeLimit, int bufferSizeLimit) {
79+
this(delegate, sendTimeLimit, bufferSizeLimit, OverflowStrategy.TERMINATE);
80+
}
81+
82+
/**
83+
* Constructor that also specifies the overflow strategy to use.
84+
* @param delegate the {@code WebSocketSession} to delegate to
85+
* @param sendTimeLimit the send-time limit (milliseconds)
86+
* @param bufferSizeLimit the buffer-size limit (number of bytes)
87+
* @param overflowStrategy the overflow strategy to use; by default the
88+
* session is terminated.
89+
* @since 5.1
90+
*/
91+
public ConcurrentWebSocketSessionDecorator(
92+
WebSocketSession delegate, int sendTimeLimit, int bufferSizeLimit, OverflowStrategy overflowStrategy) {
93+
7794
super(delegate);
7895
this.sendTimeLimit = sendTimeLimit;
7996
this.bufferSizeLimit = bufferSizeLimit;
97+
this.overflowStrategy = overflowStrategy;
8098
}
8199

82100

@@ -148,7 +166,7 @@ private boolean tryFlushMessageBuffer() throws IOException {
148166
if (message == null || shouldNotSend()) {
149167
break;
150168
}
151-
this.bufferSize.addAndGet(message.getPayloadLength() * -1);
169+
this.bufferSize.addAndGet(-message.getPayloadLength());
152170
this.sendStartTime = System.currentTimeMillis();
153171
getDelegate().sendMessage(message);
154172
this.sendStartTime = 0;
@@ -167,14 +185,35 @@ private void checkSessionLimits() {
167185
if (!shouldNotSend() && this.closeLock.tryLock()) {
168186
try {
169187
if (getTimeSinceSendStarted() > getSendTimeLimit()) {
170-
String format = "Message send time %d (ms) for session '%s' exceeded the allowed limit %d";
188+
String format = "Send time %d (ms) for session '%s' exceeded the allowed limit %d";
171189
String reason = String.format(format, getTimeSinceSendStarted(), getId(), getSendTimeLimit());
172190
limitExceeded(reason);
173191
}
174192
else if (getBufferSize() > getBufferSizeLimit()) {
175-
String format = "The send buffer size %d bytes for session '%s' exceeded the allowed limit %d";
176-
String reason = String.format(format, getBufferSize(), getId(), getBufferSizeLimit());
177-
limitExceeded(reason);
193+
switch (this.overflowStrategy) {
194+
case TERMINATE:
195+
String format = "Buffer size %d bytes for session '%s' exceeds the allowed limit %d";
196+
String reason = String.format(format, getBufferSize(), getId(), getBufferSizeLimit());
197+
limitExceeded(reason);
198+
break;
199+
case DROP:
200+
int i = 0;
201+
while (getBufferSize() > getBufferSizeLimit()) {
202+
WebSocketMessage<?> message = this.buffer.poll();
203+
if (message == null) {
204+
break;
205+
}
206+
this.bufferSize.addAndGet(-message.getPayloadLength());
207+
i++;
208+
}
209+
if (logger.isDebugEnabled()) {
210+
logger.debug("Dropped " + i + " messages, buffer size: " + getBufferSize());
211+
}
212+
break;
213+
default:
214+
// Should never happen..
215+
throw new IllegalStateException("Unexpected OverflowStrategy: " + this.overflowStrategy);
216+
}
178217
}
179218
}
180219
finally {
@@ -223,4 +262,23 @@ public String toString() {
223262
return getDelegate().toString();
224263
}
225264

265+
266+
/**
267+
* Enum for options of what to do when the buffer fills up.
268+
* @since 5.1
269+
*/
270+
public enum OverflowStrategy {
271+
272+
/**
273+
* Throw {@link SessionLimitExceededException} that would will result
274+
* in the session being terminated.
275+
*/
276+
TERMINATE,
277+
278+
/**
279+
* Drop the oldest messages from the buffer.
280+
*/
281+
DROP
282+
}
283+
226284
}

spring-websocket/src/test/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecoratorTests.java

Lines changed: 31 additions & 3 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.
@@ -28,6 +28,7 @@
2828
import org.springframework.web.socket.TextMessage;
2929
import org.springframework.web.socket.WebSocketMessage;
3030
import org.springframework.web.socket.WebSocketSession;
31+
import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator.OverflowStrategy;
3132

3233
import static org.junit.Assert.*;
3334

@@ -104,7 +105,7 @@ public void sendTimeLimitExceeded() throws IOException, InterruptedException {
104105
}
105106
catch (SessionLimitExceededException ex) {
106107
String actual = ex.getMessage();
107-
String regex = "Message send time [\\d]+ \\(ms\\) for session '123' exceeded the allowed limit 100";
108+
String regex = "Send time [\\d]+ \\(ms\\) for session '123' exceeded the allowed limit 100";
108109
assertTrue("Unexpected message: " + actual, actual.matches(regex));
109110
assertEquals(CloseStatus.SESSION_NOT_RELIABLE, ex.getStatus());
110111
}
@@ -139,12 +140,39 @@ public void sendBufferSizeExceeded() throws IOException, InterruptedException {
139140
}
140141
catch (SessionLimitExceededException ex) {
141142
String actual = ex.getMessage();
142-
String regex = "The send buffer size [\\d]+ bytes for session '123' exceeded the allowed limit 1024";
143+
String regex = "Buffer size [\\d]+ bytes for session '123' exceeds the allowed limit 1024";
143144
assertTrue("Unexpected message: " + actual, actual.matches(regex));
144145
assertEquals(CloseStatus.SESSION_NOT_RELIABLE, ex.getStatus());
145146
}
146147
}
147148

149+
@Test // SPR-17140
150+
public void overflowStrategyDrop() throws IOException, InterruptedException {
151+
152+
BlockingSession session = new BlockingSession();
153+
session.setId("123");
154+
session.setOpen(true);
155+
156+
final ConcurrentWebSocketSessionDecorator decorator =
157+
new ConcurrentWebSocketSessionDecorator(session, 10*1000, 1024, OverflowStrategy.DROP);
158+
159+
sendBlockingMessage(decorator);
160+
161+
StringBuilder sb = new StringBuilder();
162+
for (int i = 0 ; i < 1023; i++) {
163+
sb.append("a");
164+
}
165+
166+
for (int i=0; i < 5; i++) {
167+
TextMessage message = new TextMessage(sb.toString());
168+
decorator.sendMessage(message);
169+
}
170+
171+
assertEquals(1023, decorator.getBufferSize());
172+
assertTrue(session.isOpen());
173+
174+
}
175+
148176
@Test
149177
public void closeStatusNormal() throws Exception {
150178

0 commit comments

Comments
 (0)