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.