-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Issue #11307 - Explicit demand control in WebSocket endpoints with only onWebSocketFrame #12342
Issue #11307 - Explicit demand control in WebSocket endpoints with only onWebSocketFrame #12342
Conversation
…ly onWebSocketFrame Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
Solution 1. Keep current behavior.The problem is that by only having Solution 2. Deprecate/Remove
|
I definitely don't like solution 3 as it comes with too many surprising behaviors. Now, between solutions 1 and 2, I'm not sure which ones is best. It feels like we would like a new mechanism incompatible with the existing one, but still keep the existing one for backward compatibility. Do we want to maintain both? In that case, would something similar to the client's |
@lorban the "single mechanism" would be The idea with the "high" level WebSocket APIs is that applications need only to override what they are interested in (for example, only text messages), and only write application logic. |
I agree that solution 3 is too tricky for someone to write their application correctly. Didn't we add some rule that to remove a method we need to deprecate it for 6months + x dot releases. So if we follow that rule we can never remove it for 12.1.x anyway. So we could just deprecate in 12.1.0 and remove in the next major release? Another alternative would be to not deprecate it, but enforce that if they use the |
About failing the deployment, I think we already forbid the case where an application overrides both The case of Solution 4. Restrict deployment I prefer Solution 2 because Solution 4 would work at deployment, but then likely break the application. |
Well I'm assuming solution 2 is really to deprecate it because of our new removal policy, so very similar to solution 1. |
I'm currently "none of the above". Let me get my head into this a bit more.... stand by.... |
To decide the best course to take on this one, I think we need to be very explicit about the responsibilities of a Demanding listener (auto-demand == false). With auto-demand, the listener never needs to call The simple flip side of this would be that without auto-demand, then listener always needs to call
It is case 2) above that is the problematic one, as if the listener implements So I think the first step of the solution is to modify the cases in which we will call demand on a non-auto listener to be:
This can be paraphrased as we will call demand only when the event of the frames arrival is not notified to the listener (either waiting for a FIN or because there was no handler). I think this is a good change and should be our starting point to allowing implementations to be in public class MyWebSocketListener implements Session.Listener
{
private Session _session;
@Override
public void onWebSocketOpen(Session session)
{
_session = session;
}
@Override
public void onWebSocketFrame(Frame frame, Callback callback)
{
System.err.println(frame);
if (frame.getOpCode() != Frame.Type.TEXT.getOpCode())
_session.demand();
}
@Override
public void onWebSocketText(String message)
{
System.err.println(message);
_session.demand();
}
} The problem here, is that this doesn't work due to non-fin continuation frames for a text, which will be demanded both by the public class MyWebSocketListener implements Session.Listener
{
private Session _session;
@Override
public void onWebSocketOpen(Session session)
{
_session = session;
}
@Override
public void onWebSocketFrame(Frame frame, Callback callback)
{
System.err.println(frame);
if (frame.getEffectiveOpCode() != Frame.Type.TEXT.getOpCode())
_session.demand();
}
@Override
public void onWebSocketText(String message)
{
System.err.println(message);
_session.demand();
}
} Finally, there is the issue of existing applications that are overriding |
...etty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java
Outdated
Show resolved
Hide resolved
Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good... but some questions/niggles.
But most importantly, it would be good to see an example and/or test actually using getEffectiveOpCode()
...socket/jetty-websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/Callback.java
Outdated
Show resolved
Hide resolved
...socket/jetty-websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/Callback.java
Outdated
Show resolved
Hide resolved
...websocket/jetty-websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/Frame.java
Outdated
Show resolved
Hide resolved
} | ||
} | ||
|
||
static Frame copy(Frame frame) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
javadoc needed as this looks to be a combination of a deep and shallow copy???
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary? Only used in tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the tests was saving the Frame past the onWebSocketFrame
method, but the frames payload is released after this. So if that was testing something we want to support, then we should allow them to copy the frame like we do in websocket-core. Otherwise I will have to change the test to not save the Frame.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm for giving retain semantic to a Frame
, which is a concept that we have elsewhere.
Adding copy()
seems to introduce another, different, mechanism for applications, and if we later decide to go with retain, we will have to maintain two mechanisms.
I think the tests should be changed, if it wants to retain the payload.
If this behavior documented, that the frame must be consumed in the context of a WebSocket event method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No point having a retain as the buffer is already retained until the callback is completed.
I changed the test and removed the copy/wrapper.
...websocket/jetty-websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/Frame.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to avoid forcing applications that implement onWebSocketFrame()
to have to send PONGs.
I prefer the solution where either you have onWebSocketFrame()
and nothing else, or no onWebSocketFrame()
with other methods.
We already enforce similar rules at deployment for text and partial text, or binary and partial binary, exactly to avoid processing the same frame twice, and I think this is no exception.
...websocket/jetty-websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/Frame.java
Outdated
Show resolved
Hide resolved
} | ||
} | ||
|
||
static Frame copy(Frame frame) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary? Only used in tests.
...etty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java
Outdated
Show resolved
Hide resolved
...websocket/jetty-websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/Frame.java
Outdated
Show resolved
Hide resolved
…e handlers Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than a thought bubble, this looks good
...mon/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerMetadata.java
Show resolved
Hide resolved
} | ||
} | ||
|
||
static Frame copy(Frame frame) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm for giving retain semantic to a Frame
, which is a concept that we have elsewhere.
Adding copy()
seems to introduce another, different, mechanism for applications, and if we later decide to go with retain, we will have to maintain two mechanisms.
I think the tests should be changed, if it wants to retain the payload.
If this behavior documented, that the frame must be consumed in the context of a WebSocket event method?
...ocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrame.java
Outdated
Show resolved
Hide resolved
...etty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java
Outdated
Show resolved
Hide resolved
...etty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java
Outdated
Show resolved
Hide resolved
...mon/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerMetadata.java
Show resolved
Hide resolved
Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
...ocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrame.java
Outdated
Show resolved
Hide resolved
Signed-off-by: Lachlan Roberts <lachlan.p.roberts@gmail.com>
Issue #11307
Support endpoints in the Jetty WebSocket API to be use only a
onWebSocketFrame
method. They should demand in theonWebSocketFrame
if they don't have another handler registered for this frame type.There are some surprising changes of behavior needed to implement this which will likely surprise users if they are using
onWebSocketFrame
.onWebSocketFrame
and noonWebSocketPing
method defined, then we now expect the user to handlePING
frames in theironWebSocketFrame
, so we will no longer send an automatic frame in this case.onWebSocketText
and noonWebSocketBinary
and also aonWebSocketFrame
, then they would now be expected to demand forBINARY
/CONTINUATION
opCodes in theonWebSocketFrame
method.