Skip to content

Commit

Permalink
Fix Websocket ping after close. Fix Websocket unhandled exception.
Browse files Browse the repository at this point in the history
Fix for quarkusio#99 where a Websocket ping was responded after session was marked closed and other messages were dropped.

Fix for unhandled exceptions in Websocket channel pipeline. FrameHandler catches them
and passes to onError instead of ignoring and running in default logger / stack trace printing by DefaultChannelPipeline.TailContext.
  • Loading branch information
Hans-Christian Halfbrodt committed Feb 15, 2022
1 parent 154f07a commit a03257b
Showing 1 changed file with 28 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.util.ReferenceCountUtil;
import io.undertow.websockets.util.ClassUtils;

/**
Expand Down Expand Up @@ -130,15 +131,27 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) throw
processFrame(msg);
}

/**
* Catch unhandled channel pipeline exceptions and avoid default handler with stack trace print.
* Pass to OnError to enable implementation to handle that exception.
* This might occur for example on unexpected client connection close.
*/
@Override
public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {
try {
invokeOnError(cause);
} finally {
ReferenceCountUtil.release(cause);
}
}

private void processFrame(WebSocketFrame msg) throws IOException {
if (msg instanceof CloseWebSocketFrame) {
onCloseFrame((CloseWebSocketFrame) msg);
} else if (msg instanceof PongWebSocketFrame) {
onPongMessage((PongWebSocketFrame) msg);
} else if (msg instanceof PingWebSocketFrame) {
byte[] data = new byte[msg.content().readableBytes()];
msg.content().readBytes(data);
session.getAsyncRemote().sendPong(ByteBuffer.wrap(data));
onPingFrame(msg);
} else if (msg instanceof TextWebSocketFrame) {
onText(msg, ((TextWebSocketFrame) msg).text());
} else if (msg instanceof BinaryWebSocketFrame) {
Expand All @@ -152,6 +165,18 @@ private void processFrame(WebSocketFrame msg) throws IOException {
}
}

void onPingFrame(final WebSocketFrame message) throws IOException {
if (session.isSessionClosed()) {
//to bad, the channel has already been closed
//we just ignore messages that are received after we have closed, as the endpoint is no longer in a valid state to deal with them
//this this should only happen if a message was on the wire when we called close()
return;
}
byte[] data = new byte[message.content().readableBytes()];
message.content().readBytes(data);
session.getAsyncRemote().sendPong(ByteBuffer.wrap(data));
}

void onCloseFrame(final CloseWebSocketFrame message) {
if (session.isSessionClosed()) {
//we have already handled this when we sent the close frame
Expand Down

0 comments on commit a03257b

Please sign in to comment.