Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent WebSocket buffer overflow through application-level flow control [SPR-16089] #20638

Closed
spring-issuemaster opened this issue Oct 20, 2017 · 6 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

commented Oct 20, 2017

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 268ccb6, 5809f5b, a4537b1

Backported to: 4.3.13

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Oct 20, 2017

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.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Oct 21, 2017

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

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Oct 21, 2017

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.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Oct 21, 2017

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));
}
}
@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Oct 21, 2017

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.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Oct 23, 2017

ChenYang commented

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.