You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2026/03/27 11:55:57 UTC
(camel) branch CAMEL-23259 created (now 12b7d4330f6d)
This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a change to branch CAMEL-23259
in repository https://gitbox.apache.org/repos/asf/camel.git
at 12b7d4330f6d CAMEL-23259: Close channel on SSL/TLS handshake failure in Netty components
This branch includes the following new commits:
new 12b7d4330f6d CAMEL-23259: Close channel on SSL/TLS handshake failure in Netty components
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(camel) 01/01: CAMEL-23259: Close channel on SSL/TLS handshake failure in Netty components
Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a commit to branch CAMEL-23259
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 12b7d4330f6d5961c4f6efb2656f5f5a1fe5093b
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Fri Mar 27 12:53:57 2026 +0100
CAMEL-23259: Close channel on SSL/TLS handshake failure in Netty components
Add SslHandshakeFailureHandler to close channels when SSL handshake
fails, replacing the removed Netty 3.x SslHandler.setCloseOnSSLException(true)
API. The handler listens for SslHandshakeCompletionEvent and closes the
channel on failure, preventing failed SSL connections from remaining open.
Applied to all four pipeline initializer factories:
- DefaultServerInitializerFactory (camel-netty)
- DefaultClientInitializerFactory (camel-netty)
- HttpServerInitializerFactory (camel-netty-http)
- HttpClientInitializerFactory (camel-netty-http)
Signed-off-by: Andrea Cosentino <an...@gmail.com>
---
.../netty/http/HttpClientInitializerFactory.java | 4 +-
.../netty/http/HttpServerInitializerFactory.java | 5 +-
.../netty/DefaultClientInitializerFactory.java | 4 +-
.../netty/DefaultServerInitializerFactory.java | 4 +-
.../netty/handlers/SslHandshakeFailureHandler.java | 51 +++++++++++++++++++
.../netty/SslHandshakeFailureHandlerTest.java | 59 ++++++++++++++++++++++
6 files changed, 118 insertions(+), 9 deletions(-)
diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientInitializerFactory.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientInitializerFactory.java
index 9aa91f14807b..1460decfcd4d 100644
--- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientInitializerFactory.java
+++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientInitializerFactory.java
@@ -38,6 +38,7 @@ import org.apache.camel.component.netty.ChannelHandlerFactory;
import org.apache.camel.component.netty.ClientInitializerFactory;
import org.apache.camel.component.netty.NettyConfiguration;
import org.apache.camel.component.netty.NettyProducer;
+import org.apache.camel.component.netty.handlers.SslHandshakeFailureHandler;
import org.apache.camel.component.netty.http.handlers.HttpClientChannelHandler;
import org.apache.camel.component.netty.http.handlers.HttpInboundStreamHandler;
import org.apache.camel.component.netty.http.handlers.HttpOutboundStreamHandler;
@@ -86,10 +87,9 @@ public class HttpClientInitializerFactory extends ClientInitializerFactory {
SslHandler sslHandler = configureClientSSLOnDemand();
if (sslHandler != null) {
- //TODO must close on SSL exception
- //sslHandler.setCloseOnSSLException(true);
LOG.debug("Client SSL handler configured and added as an interceptor against the ChannelPipeline: {}", sslHandler);
pipeline.addLast("ssl", sslHandler);
+ pipeline.addLast("sslHandshakeFailure", SslHandshakeFailureHandler.INSTANCE);
}
pipeline.addLast("http", new HttpClientCodec());
diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerInitializerFactory.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerInitializerFactory.java
index 43623e9f79aa..4d31ff93e468 100644
--- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerInitializerFactory.java
+++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerInitializerFactory.java
@@ -37,6 +37,7 @@ import org.apache.camel.component.netty.ChannelHandlerFactory;
import org.apache.camel.component.netty.NettyConsumer;
import org.apache.camel.component.netty.NettyServerBootstrapConfiguration;
import org.apache.camel.component.netty.ServerInitializerFactory;
+import org.apache.camel.component.netty.handlers.SslHandshakeFailureHandler;
import org.apache.camel.component.netty.http.handlers.HttpInboundStreamHandler;
import org.apache.camel.component.netty.http.handlers.HttpOutboundStreamHandler;
import org.apache.camel.component.netty.ssl.SSLEngineFactory;
@@ -83,9 +84,6 @@ public class HttpServerInitializerFactory extends ServerInitializerFactory {
ChannelHandler sslHandler = configureServerSSLOnDemand();
if (sslHandler != null) {
- //TODO must close on SSL exception
- // sslHandler.setCloseOnSSLException(true);
-
if (sslHandler instanceof ChannelHandlerFactory) {
// use the factory to create a new instance of the channel as it may not be shareable
sslHandler = ((ChannelHandlerFactory) sslHandler).newChannelHandler();
@@ -93,6 +91,7 @@ public class HttpServerInitializerFactory extends ServerInitializerFactory {
LOG.debug("Server SSL handler configured and added as an interceptor against the ChannelPipeline: {}", sslHandler);
pipeline.addLast("ssl", sslHandler);
+ pipeline.addLast("sslHandshakeFailure", SslHandshakeFailureHandler.INSTANCE);
}
pipeline.addLast("decoder", new HttpRequestDecoder(
diff --git a/components/camel-netty/src/main/java/org/apache/camel/component/netty/DefaultClientInitializerFactory.java b/components/camel-netty/src/main/java/org/apache/camel/component/netty/DefaultClientInitializerFactory.java
index 0c897865bf9b..1b18f3f61461 100644
--- a/components/camel-netty/src/main/java/org/apache/camel/component/netty/DefaultClientInitializerFactory.java
+++ b/components/camel-netty/src/main/java/org/apache/camel/component/netty/DefaultClientInitializerFactory.java
@@ -30,6 +30,7 @@ import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.component.netty.handlers.ClientChannelHandler;
+import org.apache.camel.component.netty.handlers.SslHandshakeFailureHandler;
import org.apache.camel.component.netty.ssl.SSLEngineFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,10 +57,9 @@ public class DefaultClientInitializerFactory extends ClientInitializerFactory {
SslHandler sslHandler = configureClientSSLOnDemand();
if (sslHandler != null) {
- //TODO must close on SSL exception
- //sslHandler.setCloseOnSSLException(true);
LOG.debug("Client SSL handler configured and added to the ChannelPipeline: {}", sslHandler);
addToPipeline("ssl", channelPipeline, sslHandler);
+ addToPipeline("sslHandshakeFailure", channelPipeline, SslHandshakeFailureHandler.INSTANCE);
}
List<ChannelHandler> decoders = producer.getConfiguration().getDecodersAsList();
diff --git a/components/camel-netty/src/main/java/org/apache/camel/component/netty/DefaultServerInitializerFactory.java b/components/camel-netty/src/main/java/org/apache/camel/component/netty/DefaultServerInitializerFactory.java
index 638f37ad7fcd..a3d73b991c4f 100644
--- a/components/camel-netty/src/main/java/org/apache/camel/component/netty/DefaultServerInitializerFactory.java
+++ b/components/camel-netty/src/main/java/org/apache/camel/component/netty/DefaultServerInitializerFactory.java
@@ -30,6 +30,7 @@ import io.netty.util.concurrent.EventExecutorGroup;
import org.apache.camel.CamelContext;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.component.netty.handlers.ServerChannelHandler;
+import org.apache.camel.component.netty.handlers.SslHandshakeFailureHandler;
import org.apache.camel.component.netty.ssl.SSLEngineFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -60,10 +61,9 @@ public class DefaultServerInitializerFactory extends ServerInitializerFactory {
SslHandler sslHandler = configureServerSSLOnDemand();
if (sslHandler != null) {
- //TODO must close on SSL exception
- //sslHandler.setCloseOnSSLException(true);
LOG.debug("Server SSL handler configured and added as an interceptor against the ChannelPipeline: {}", sslHandler);
addToPipeline("ssl", channelPipeline, sslHandler);
+ addToPipeline("sslHandshakeFailure", channelPipeline, SslHandshakeFailureHandler.INSTANCE);
}
List<ChannelHandler> encoders = consumer.getConfiguration().getEncodersAsList();
diff --git a/components/camel-netty/src/main/java/org/apache/camel/component/netty/handlers/SslHandshakeFailureHandler.java b/components/camel-netty/src/main/java/org/apache/camel/component/netty/handlers/SslHandshakeFailureHandler.java
new file mode 100644
index 000000000000..139cb4834f45
--- /dev/null
+++ b/components/camel-netty/src/main/java/org/apache/camel/component/netty/handlers/SslHandshakeFailureHandler.java
@@ -0,0 +1,51 @@
+/*
+ * 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.camel.component.netty.handlers;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.handler.ssl.SslHandshakeCompletionEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Closes the channel when an SSL/TLS handshake failure is detected.
+ * <p>
+ * This handler replaces the removed Netty 3.x {@code SslHandler.setCloseOnSSLException(true)} functionality. It listens
+ * for {@link SslHandshakeCompletionEvent} and closes the channel on failure, preventing failed SSL connections from
+ * remaining open.
+ */
+@ChannelHandler.Sharable
+public class SslHandshakeFailureHandler extends ChannelInboundHandlerAdapter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SslHandshakeFailureHandler.class);
+
+ public static final SslHandshakeFailureHandler INSTANCE = new SslHandshakeFailureHandler();
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+ if (evt instanceof SslHandshakeCompletionEvent handshakeEvent) {
+ if (!handshakeEvent.isSuccess()) {
+ LOG.debug("SSL/TLS handshake failed on channel {}, closing: {}",
+ ctx.channel(), handshakeEvent.cause().getMessage());
+ ctx.close();
+ }
+ }
+ super.userEventTriggered(ctx, evt);
+ }
+}
diff --git a/components/camel-netty/src/test/java/org/apache/camel/component/netty/SslHandshakeFailureHandlerTest.java b/components/camel-netty/src/test/java/org/apache/camel/component/netty/SslHandshakeFailureHandlerTest.java
new file mode 100644
index 000000000000..a868c81c0b2a
--- /dev/null
+++ b/components/camel-netty/src/test/java/org/apache/camel/component/netty/SslHandshakeFailureHandlerTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.camel.component.netty;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.ssl.SslHandshakeCompletionEvent;
+import org.apache.camel.component.netty.handlers.SslHandshakeFailureHandler;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class SslHandshakeFailureHandlerTest {
+
+ @Test
+ public void testChannelClosedOnHandshakeFailure() {
+ EmbeddedChannel channel = new EmbeddedChannel(SslHandshakeFailureHandler.INSTANCE);
+ assertTrue(channel.isOpen());
+
+ SslHandshakeCompletionEvent failureEvent
+ = new SslHandshakeCompletionEvent(new SSLHandshakeException("test handshake failure"));
+ channel.pipeline().fireUserEventTriggered(failureEvent);
+
+ assertFalse(channel.isOpen(), "Channel should be closed after SSL handshake failure");
+ }
+
+ @Test
+ public void testChannelOpenOnHandshakeSuccess() {
+ EmbeddedChannel channel = new EmbeddedChannel(SslHandshakeFailureHandler.INSTANCE);
+ assertTrue(channel.isOpen());
+
+ channel.pipeline().fireUserEventTriggered(SslHandshakeCompletionEvent.SUCCESS);
+
+ assertTrue(channel.isOpen(), "Channel should remain open after successful SSL handshake");
+ channel.close();
+ }
+
+ @Test
+ public void testHandlerIsSharable() {
+ assertTrue(SslHandshakeFailureHandler.INSTANCE.isSharable(),
+ "SslHandshakeFailureHandler should be sharable");
+ }
+}