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.