This is an automated email from the ASF dual-hosted git repository.

zrlw pushed a commit to branch 3.3
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.3 by this push:
     new c587ff67c0 Close channel on TLS exception to prevent half-open 
connections (#16202)
c587ff67c0 is described below

commit c587ff67c0aaccb7c459851e65ac19cbc8a1d3f4
Author: uuuyuqi <[email protected]>
AuthorDate: Thu Jul 9 10:45:44 2026 +0800

    Close channel on TLS exception to prevent half-open connections (#16202)
    
    * fix: close channel on TLS exception to prevent half-open connections
    
    SslServerTlsHandler.exceptionCaught() only logged the error without
    closing the channel or propagating the exception. This caused channels
    to remain TCP-active but application-dead when exceptions like
    NoClassDefFoundError occurred during Netty read loops.
    
    Similarly, SslClientTlsHandler did not close the channel on TLS
    handshake failure, only firing exceptionCaught to downstream handlers.
    
    This fix ensures both handlers close the channel on failure, allowing
    Dubbo's existing health-check mechanism (isAvailable/addInvalidateInvoker)
    to properly detect and remove broken invokers.
    
    Change-Id: Ia466bc4db9e59b1f5c1f01a3062c619aabbbb4e1
    Co-developed-by: Cursor <[email protected]>
    
    * style: apply spotless formatting to test file
    
    Change-Id: Ie3ac6e2e6d1ce5e8f6bd2ad4fe2a5c9286d66fbf
    Co-developed-by: Cursor <[email protected]>
    
    * Address Copilot review: preserve exception propagation, fix client log 
message, clean up test
    
    - Restore ctx.fireExceptionCaught() before ctx.close() in 
SslClientTlsHandler
      so downstream handlers can observe the TLS handshake failure cause
    - Fix misleading "accept new connection" log message to "connecting to 
remote server"
    - Remove unused ChannelPipeline mock and when() stub in test
    
    🤖 Generated with [Qoder][https://qoder.com]
    
    Change-Id: Ic8d46b8ab340d5d9b40a5e48caf1df7aaccad532
    Co-developed-by: Qoder CLI <[email protected]>
    
    ---------
    
    Co-authored-by: Ken Liu <[email protected]>
---
 .../transport/netty4/ssl/SslClientTlsHandler.java  |  3 +-
 .../netty4/ssl/SslHandlerExceptionTest.java        | 60 ++++++++++++++++++++++
 2 files changed, 62 insertions(+), 1 deletion(-)

diff --git 
a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/ssl/SslClientTlsHandler.java
 
b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/ssl/SslClientTlsHandler.java
index fd3f245884..e1c08d3f01 100644
--- 
a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/ssl/SslClientTlsHandler.java
+++ 
b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/ssl/SslClientTlsHandler.java
@@ -68,9 +68,10 @@ public class SslClientTlsHandler extends 
ChannelInboundHandlerAdapter {
                         INTERNAL_ERROR,
                         "unknown error in remoting module",
                         "",
-                        "TLS negotiation failed when trying to accept new 
connection.",
+                        "TLS negotiation failed when connecting to remote 
server.",
                         handshakeEvent.cause());
                 ctx.fireExceptionCaught(handshakeEvent.cause());
+                ctx.close();
             }
         }
     }
diff --git 
a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ssl/SslHandlerExceptionTest.java
 
b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ssl/SslHandlerExceptionTest.java
new file mode 100644
index 0000000000..8a239dc30a
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ssl/SslHandlerExceptionTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport.netty4.ssl;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslHandshakeCompletionEvent;
+import org.junit.jupiter.api.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Verify that TLS handlers properly close channels on exceptions,
+ * preventing half-open connections that are TCP-alive but application-dead.
+ */
+class SslHandlerExceptionTest {
+
+    @Test
+    void serverTlsHandler_exceptionCaught_shouldCloseChannel() throws 
Exception {
+        ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+
+        SslServerTlsHandler handler = new SslServerTlsHandler(null, true);
+        NoClassDefFoundError error =
+                new NoClassDefFoundError("Could not initialize class 
io.netty.buffer.PooledUnsafeDirectByteBuf");
+
+        handler.exceptionCaught(ctx, error);
+
+        verify(ctx).close();
+    }
+
+    @Test
+    void clientTlsHandler_handshakeFailure_shouldCloseChannel() throws 
Exception {
+        ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+
+        SslClientTlsHandler handler = new 
SslClientTlsHandler(mock(SslContext.class));
+        SslHandshakeCompletionEvent failureEvent =
+                new SslHandshakeCompletionEvent(new SSLHandshakeException("TLS 
handshake timeout"));
+
+        handler.userEventTriggered(ctx, failureEvent);
+
+        verify(ctx).close();
+    }
+}

Reply via email to