ChenYang opened SPR-16089 and commented

Class ConcurrentWebSocketSessionDecorator in Spring Websocket contains

private final Queue<WebSocketMessage<?>> buffer = new LinkedBlockingQueue<>(); that represent bunch of messages that for a queue to be sent towards clients. If the field

private final int bufferSizeLimit; exceeded, an exception is thrown and client is disconnected. I would like to be able to check this buffer size from my application in order to execute flow control and be able to prevent buffer overflow.

How I could possibly peek into this buffer?


Affects: 4.3.12

Reference URL: https://stackoverflow.com/questions/39709409/how-to-peek-into-concurrentwebsocketsessiondecorator-buffer-in-spring-websocket

Issue Links: - #21677 limitExceeded is never reset in ConcurrentWebSocketSessionDecorator

Referenced from: commits https://github.com/spring-projects/spring-framework/commit/268ccb6b6b90413b177bb15a020088471682550c, https://github.com/spring-projects/spring-framework/commit/5809f5b8ebd66f1dd4589f7f326a24c10762e24f, https://github.com/spring-projects/spring-framework/commit/a4537b1b6dfef03fb2c004ca1fab5d2019ab52fb

Backported to: 4.3.13

Comment From: spring-projects-issues

Juergen Hoeller commented

There is already a public getBufferSize() accessor on ConcurrentWebSocketSessionDecorator. Wouldn't this allow for some basic check upfront? What specifically do you need beyond it for your use case?

I've noticed that there is no accessor for the configured bufferSizeLimit on a given ConcurrentWebSocketSessionDecorator instance. We should add that in any case.

Comment From: spring-projects-issues

ChenYang commented

I see that There is already a public getBufferSize() accessor on ConcurrentWebSocketSessionDecorator, but i can't find a way to access ConcurrentWebSocketSessionDecorator object of special session.These are store in private field "sessions" in SubProtocolWebSocketHandler

Comment From: spring-projects-issues

ChenYang commented

My use case is:I use web socket to transport lots of images, network environment issue always leads to buffer overflow, then web socket is closed.But that isn't what i want, throwing away some images by some policy is accepted instead of web socket is closed. My solution for now is rewriting afterConnectionEstablished(WebSocketSession session) of SubProtocolWebSocketHandler by reflection, in this way i could use my custom ConcurrentWebSocketSessionDecorator, which i add some policy of throwing away unnecessary package in. But i don't think that is a elegant solution, so i submit this improvement to ask for a official solution.

Comment From: spring-projects-issues

ChenYang commented

public class CustomSubProtocolWebSocketHandler extends SubProtocolWebSocketHandler {
public CustomSubProtocolWebSocketHandler(MessageChannel clientInboundChannel,
SubscribableChannel clientOutboundChannel)
{ super(clientInboundChannel, clientOutboundChannel); }
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info("New webSocket connection was established,{}", session.getId());
// do superclass's work by reflection, because some field is private
if (!session.isOpen())
{ return; }
Field sessionsField = CustomSubProtocolWebSocketHandler.class.getSuperclass().getDeclaredField("sessions");
Field statsField = CustomSubProtocolWebSocketHandler.class.getSuperclass().getDeclaredField("stats");
Field clientInboundChannelField = CustomSubProtocolWebSocketHandler.class.getSuperclass().getDeclaredField("clientInboundChannel");
sessionsField.setAccessible(true);
statsField.setAccessible(true);
clientInboundChannelField.setAccessible(true);
Class[] innerClazz = CustomSubProtocolWebSocketHandler.class.getSuperclass().getDeclaredClasses();
Method incrementSessionCountMethod = null;
Constructor holderConstructor = null;
for(Class in :innerClazz) {
if (in.getSimpleName().equals("Stats"))
{ incrementSessionCountMethod = in.getDeclaredMethod("incrementSessionCount", WebSocketSession.class); incrementSessionCountMethod.setAccessible(true); }
else if (in.getSimpleName().equals("WebSocketSessionHolder"))
{ holderConstructor = in.getDeclaredConstructor(WebSocketSession.class); holderConstructor.setAccessible(true); }
}
incrementSessionCountMethod.invoke(statsField.get(this), session);
//use my custom WebSocketSessionDecorator
session = new AvoidBufferOverflowWebSocketSessionDecorator(session, getSendTimeLimit(), getSendBufferSizeLimit());
((Map)sessionsField.get(this)).put(session.getId(), holderConstructor.newInstance(session));
findProtocolHandler(session).afterSessionStarted(session, (MessageChannel)clientInboundChannelField.get(this));
}
}

Comment From: spring-projects-issues

Juergen Hoeller commented

I've introduced a dedicated protected decorateSession(WebSocketSession) method which allows you to apply a custom ConcurrentWebSocketSessionDecorator subclass if desired, not having to interfere with afterConnectionEstablished's standard steps.

Comment From: spring-projects-issues

ChenYang commented

Thank you!

Comment From: gashutos02

Hi @Juergen Hoeller,

How can we override can we override SubProtocolWebSocketHandler. As soon as we override we get error stating "Failed to start bean 'customSubProtocolWebSocketHandler'; nested exception is java.lang.IllegalArgumentException: No handlers"

how can we achive that as we want to use overflow drop stratagy.

Comment From: vraddy

Hi @jhoeller, How can I access ConcurrentWebSocketSessionDecorator instance from controller class, so that I can check bufferSize and decide whether to send data to client or wait. I don't want connection to be terminated, instead want to pause sending data to client, wait till the existing buffer flushed to client and resume. Thanks.