HenryOrz opened SPR-17140 and commented

In a spring-websocket application, Once org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator bufferSizeLimit was exceeded, the method sendMessage() does't work anymore.

There is a limitExceeded flag in ConcurrentWebSocketSessionDecorator, when the bufferSizeLimit was exceeded, the flag will set to true, but it's never reset to false. The method tryFlushMessageBuffer() which called by sendMessage() will check the limitExceeded flag, so the sendMessage() does't work after bufferSizeLimit  was exceeded

Thanks Artem Bilan for answer my question on stackoverflow


Affects: 4.3.18, 5.0.8

Reference URL: https://stackoverflow.com/questions/51723413/how-to-flush-buffer-when-concurrentwebsocketsessiondecorator-buffersizelimit-exc

Issue Links: - #20638 Prevent WebSocket buffer overflow through application-level flow control

Referenced from: commits https://github.com/spring-projects/spring-framework/commit/309ffc6d0d2e62c85c09ad0ed0eca86eedad8df8

Comment From: spring-projects-issues

Rossen Stoyanchev commented

ConcurrentWebSocketSessionDecorator puts an upper limit on the send time and the send buffer size. Once those limits are exceeded, a SessionLimitExceededException is raised and the session is terminated. That's the point of those limits, to set a threshold. So this is by design, it's not a bug.

Comment From: spring-projects-issues

HenryOrz commented

@Rossen Stoyanchev Thank you! You are right, I have read the comment of the class, It's not a bug, I misunderstand it.

But I'm puzzled by this feature, if buffer limit was exceeded, and I have also caught the SessionLimitExceededException, but I still want to send message, how can I restore it?

I have tried to new ConcurrentWebSocketSessionDecorator to replace the exceeded one, but there is no public access to get the buffer, which means I lost the message in the buffer of old one. 

Thanks to @Juergen Hoeller for linking issue #20638, It's reminds me to do some basic check by method getBufferSize(). But if I just check it simply(just like the code below). It may bring some problem like race condition, the code will be complex if I want to make it correct

if((decorator.getBufferSize() + messageSize) < decorator.getBufferSizeLimit()){
   decorator.sendMessage();
}

Is there any way to send message more gracefully after limits exceeded?

Might we have a method to flush buffer and reset the limitExceed flag when limits exceeded?Just like

try {
   decorator.sendMessage();
} catch (SessionLimitExceededException e) {
   decorator.flushAndReset();
}

Or could we remove the limitExceed condition in method tryFlushMessageBuffer() , and reset the limitExceed flag after all the message in buffer was flushed?

Comment From: spring-projects-issues

Rossen Stoyanchev commented

There are only so many ways to deal with overflow. Currently it simply drops the connection once the limit is reached. We could add an option to enable an alternative overflow strategy such as to drop messages. Is that what you're looking for?

Comment From: spring-projects-issues

HenryOrz commented

Thank you! That is what I'm looking for!

More precisely, I want a strategy to flush buffer when buffer overflow.

I have commit a pull request  on github. 

Comment From: spring-projects-issues

Rossen Stoyanchev commented

I've implemented this a little differently. Basically it's controlled through an OverflowStrategy enum flag on the constructor and automatically applied when the limit is reached.

Comment From: spring-projects-issues

HenryOrz commented

Thank you!

Comment From: KeithWoods

We've hit a similar/same issue, with the ConcurrentWebSocketSessionDecorator in respect of the SessionLimitExceededException

I don't believe any code in ConcurrentWebSocketSessionDecorator actually closes the session when SessionLimitExceededException is thrown and when the strategy is OverflowStrategy.TERMINATE.

If you're using stomp on top of websockets that does handle this differently. For example in the SubProtocolWebSocketHandlerit catches and closes the session That logic seems to be missing from ConcurrentWebSocketSessionDecorator, it get left in an odd state, the app think it's ok but it won't send due to limitExceeded. The workaround is to catch the above exception when you're calling sendMessage() and close the session yourself.

I noticed in the above stackover post Thanks Artem Bilan mentioned it looks like a bug

Comment From: rstoyanchev

The decorator is intended to raise an exception only and leave it to the surrounding code to take further action as necessary. There is also an ExceptionWebSocketHandlerDecorator that will close the session for any Exception that bubbles up.

As for the referenced SO discussion, that seems about doing the opposite, which is to reset limitExceeded flag and presumably continue using the session.