From b4fb6b3355d2d1fafd21905b6945fbb1e63ec79d Mon Sep 17 00:00:00 2001 From: Juhyung Park Date: Wed, 8 Oct 2025 00:51:35 +0900 Subject: [PATCH 1/2] SSLSocketChannel2: throttle processHandshake() if no handshake is done Signed-off-by: Juhyung Park --- src/main/java/org/java_websocket/SSLSocketChannel2.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/org/java_websocket/SSLSocketChannel2.java b/src/main/java/org/java_websocket/SSLSocketChannel2.java index 23c4f8af..487b6b74 100644 --- a/src/main/java/org/java_websocket/SSLSocketChannel2.java +++ b/src/main/java/org/java_websocket/SSLSocketChannel2.java @@ -190,6 +190,14 @@ private synchronized void processHandshake(boolean isReading) throws IOException if (writeEngineResult.getHandshakeStatus() == HandshakeStatus.FINISHED) { createBuffers(sslEngine.getSession()); return; + } else { + // processHandshake() can spin in a loop if a connection is made with no SSL handshake, + // throttle this by delaying 10ms to avoid consuming a CPU core. + try { + Thread.sleep(10); + } catch (InterruptedException e) { + // Ignore + } } } assert (sslEngine.getHandshakeStatus() From a806a913e8b6fc8f6d5279b409bea346065e56ff Mon Sep 17 00:00:00 2001 From: Juhyung Park Date: Wed, 8 Oct 2025 00:52:53 +0900 Subject: [PATCH 2/2] SSLSocketChannel2: close stale connection with no handshake in 60s Signed-off-by: Juhyung Park --- .../org/java_websocket/SSLSocketChannel2.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/java/org/java_websocket/SSLSocketChannel2.java b/src/main/java/org/java_websocket/SSLSocketChannel2.java index 487b6b74..f7b4872a 100644 --- a/src/main/java/org/java_websocket/SSLSocketChannel2.java +++ b/src/main/java/org/java_websocket/SSLSocketChannel2.java @@ -39,7 +39,11 @@ import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; @@ -105,6 +109,12 @@ public class SSLSocketChannel2 implements ByteChannel, WrappedByteChannel, ISSLC **/ protected int bufferallocations = 0; + /** + * Scheduler to close the socket when the handshake takes too long + */ + protected ScheduledExecutorService closeSocketScheduler; + protected ScheduledFuture closeSocketTask; + public SSLSocketChannel2(SocketChannel channel, SSLEngine sslEngine, ExecutorService exec, SelectionKey key) throws IOException { if (channel == null || sslEngine == null || exec == null) { @@ -127,6 +137,16 @@ public SSLSocketChannel2(SocketChannel channel, SSLEngine sslEngine, ExecutorSer // kick off handshake socketChannel.write(wrap(emptybuffer));// initializes res processHandshake(false); + + // Close stale connection with no handshake in 60s + this.closeSocketScheduler = Executors.newSingleThreadScheduledExecutor(); + this.closeSocketTask = closeSocketScheduler.schedule(() -> { + try { + close(); + } catch (IOException e) { + // Ignored + } + }, 60, TimeUnit.SECONDS); } private void consumeFutureUninterruptible(Future f) { @@ -188,6 +208,11 @@ private synchronized void processHandshake(boolean isReading) throws IOException || sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) { socketChannel.write(wrap(emptybuffer)); if (writeEngineResult.getHandshakeStatus() == HandshakeStatus.FINISHED) { + if (closeSocketTask != null) { + closeSocketTask.cancel(false); + closeSocketTask = null; + closeSocketScheduler = null; + } createBuffers(sslEngine.getSession()); return; } else {