diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java index 0f794aac574f..04f2f745ee07 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandler.java @@ -22,10 +22,12 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import javax.websocket.ClientEndpointConfig; import javax.websocket.CloseReason; @@ -60,6 +62,8 @@ public class JavaxWebSocketFrameHandler implements FrameHandler private final Logger logger; private final JavaxWebSocketContainer container; private final Object endpointInstance; + private final AtomicBoolean closeNotified = new AtomicBoolean(); + /** * List of configured named variables in the uri-template. *

@@ -278,9 +282,27 @@ public void onFrame(Frame frame, Callback callback) dataType = OpCode.UNDEFINED; } + public void onClose(Frame frame, Callback callback) + { + notifyOnClose(CloseStatus.getCloseStatus(frame), callback); + } + @Override public void onClosed(CloseStatus closeStatus, Callback callback) { + notifyOnClose(closeStatus, callback); + container.notifySessionListeners((listener) -> listener.onJavaxWebSocketSessionClosed(session)); + } + + private void notifyOnClose(CloseStatus closeStatus, Callback callback) + { + // Make sure onClose is only notified once. + if (!closeNotified.compareAndSet(false, true)) + { + callback.failed(new ClosedChannelException()); + return; + } + try { if (closeHandle != null) @@ -288,14 +310,13 @@ public void onClosed(CloseStatus closeStatus, Callback callback) CloseReason closeReason = new CloseReason(CloseReason.CloseCodes.getCloseCode(closeStatus.getCode()), closeStatus.getReason()); closeHandle.invoke(closeReason); } + callback.succeeded(); } catch (Throwable cause) { callback.failed(new WebSocketException(endpointInstance.getClass().getSimpleName() + " CLOSE method error: " + cause.getMessage(), cause)); } - - container.notifySessionListeners((listener) -> listener.onJavaxWebSocketSessionClosed(session)); } @Override @@ -572,11 +593,6 @@ private void acceptMessage(Frame frame, Callback callback) activeMessageSink = null; } - public void onClose(Frame frame, Callback callback) - { - callback.succeeded(); - } - public void onPing(Frame frame, Callback callback) { ByteBuffer payload = BufferUtil.copy(frame.getPayload()); diff --git a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java index e2688befbbda..556eabd835ea 100644 --- a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java +++ b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java @@ -20,7 +20,9 @@ import java.lang.invoke.MethodHandle; import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -57,6 +59,7 @@ private enum SuspendState private final WebSocketContainer container; private final Object endpointInstance; private final BatchMode batchMode; + private final AtomicBoolean closeNotified = new AtomicBoolean(); private MethodHandle openHandle; private MethodHandle closeHandle; private MethodHandle errorHandle; @@ -278,6 +281,11 @@ public void onError(Throwable cause, Callback callback) } } + private void onCloseFrame(Frame frame, Callback callback) + { + notifyOnClose(CloseStatus.getCloseStatus(frame), callback); + } + @Override public void onClosed(CloseStatus closeStatus, Callback callback) { @@ -287,6 +295,19 @@ public void onClosed(CloseStatus closeStatus, Callback callback) state = SuspendState.CLOSED; } + notifyOnClose(closeStatus, callback); + container.notifySessionListeners((listener) -> listener.onWebSocketSessionClosed(session)); + } + + private void notifyOnClose(CloseStatus closeStatus, Callback callback) + { + // Make sure onClose is only notified once. + if (!closeNotified.compareAndSet(false, true)) + { + callback.failed(new ClosedChannelException()); + return; + } + try { if (closeHandle != null) @@ -298,8 +319,6 @@ public void onClosed(CloseStatus closeStatus, Callback callback) { callback.failed(new WebSocketException(endpointInstance.getClass().getSimpleName() + " CLOSE method error: " + cause.getMessage(), cause)); } - - container.notifySessionListeners((listener) -> listener.onWebSocketSessionClosed(session)); } public String toString() @@ -330,11 +349,6 @@ private void onBinaryFrame(Frame frame, Callback callback) acceptMessage(frame, callback); } - private void onCloseFrame(Frame frame, Callback callback) - { - callback.succeeded(); - } - private void onContinuationFrame(Frame frame, Callback callback) { acceptMessage(frame, callback);