Skip to content

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

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

Closed
spring-projects-issues opened this issue Oct 20, 2017 · 8 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues 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:

Referenced from: commits 268ccb6, 5809f5b, a4537b1

Backported to: 4.3.13

@spring-projects-issues
Copy link
Collaborator Author

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-projects-issues
Copy link
Collaborator Author

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-projects-issues
Copy link
Collaborator Author

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-projects-issues
Copy link
Collaborator Author

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-projects-issues
Copy link
Collaborator Author

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-projects-issues
Copy link
Collaborator Author

ChenYang commented

Thank you!

@spring-projects-issues spring-projects-issues added status: backported An issue that has been backported to maintenance branches type: enhancement A general enhancement in: web Issues in web modules (web, webmvc, webflux, websocket) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 5.0.1 milestone Jan 11, 2019
@gashutos02
Copy link

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.

@vraddy
Copy link

vraddy commented Sep 28, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants