diff --git a/handler/src/main/java/io/netty/handler/ssl/HandshakeCompletionEvent.java b/handler/src/main/java/io/netty/handler/ssl/HandshakeCompletionEvent.java new file mode 100644 index 0000000000..1e0c37fba1 --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/HandshakeCompletionEvent.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project 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 io.netty.handler.ssl; + + +/** + * Event that is fired once the SSL handshake is complete, which may be because it was successful or there + * was an error. + */ +public final class HandshakeCompletionEvent { + private final Throwable cause; + + HandshakeCompletionEvent(Throwable cause) { + this.cause = cause; + } + + /** + * Return {@code true} if the handshake was successful + */ + public boolean isSuccess() { + return cause == null; + } + + /** + * Return the {@link Throwable} if {@link #isSuccess()} returns {@code false} + * and so the handshake failed. + */ + public Throwable cause() { + return cause; + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index 2406f98449..0594756bed 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -28,6 +28,7 @@ import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelOutboundByteHandler; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; +import io.netty.channel.ChannelStateHandler; import io.netty.channel.DefaultChannelPromise; import io.netty.channel.FileRegion; import io.netty.util.concurrent.ImmediateExecutor; @@ -66,6 +67,11 @@ import java.util.regex.Pattern; * renegotiating. You will be notified by the {@link ChannelFuture} which is * returned by the {@link #handshake()} method when the handshake * process succeeds or fails. + *

+ * Beside using the handshake {@link ChannelFuture} to get notified about the completation of the handshake it's + * also possible to detect it by implement the + * {@link ChannelStateHandler#userEventTriggered(ChannelHandlerContext, Object)} + * method and check for a {@link HandshakeCompletionEvent}. * *

Handshake

*

@@ -173,6 +179,8 @@ public class SslHandler private volatile long handshakeTimeoutMillis = 10000; private volatile long closeNotifyTimeoutMillis = 3000; + private static final HandshakeCompletionEvent HANDSHAKE_SUCCESS_EVENT = new HandshakeCompletionEvent(null); + /** * Creates a new instance. * @@ -958,12 +966,16 @@ public class SslHandler * Notify all the handshake futures about the successfully handshake */ private void setHandshakeSuccess() { - for (;;) { - ChannelPromise p = handshakePromises.poll(); - if (p == null) { - break; + try { + for (;;) { + ChannelPromise p = handshakePromises.poll(); + if (p == null) { + break; + } + p.setSuccess(); } - p.setSuccess(); + } finally { + ctx.fireUserEventTriggered(HANDSHAKE_SUCCESS_EVENT); } } @@ -971,39 +983,41 @@ public class SslHandler * Notify all the handshake futures about the failure during the handshake. */ private void setHandshakeFailure(Throwable cause) { - - // Release all resources such as internal buffers that SSLEngine - // is managing. - engine.closeOutbound(); - - final boolean disconnected = cause == null || cause instanceof ClosedChannelException; try { - engine.closeInbound(); - } catch (SSLException e) { - if (!disconnected) { - logger.warn("SSLEngine.closeInbound() raised an exception after a handshake failure.", e); - } else if (!closeNotifyWriteListener.done) { - logger.warn("SSLEngine.closeInbound() raised an exception due to closed connection.", e); - } else { - // cause == null && sentCloseNotify - // closeInbound() will raise an exception with bogus truncation attack warning. - } - } + // Release all resources such as internal buffers that SSLEngine + // is managing. + engine.closeOutbound(); - if (!handshakePromises.isEmpty()) { - if (cause == null) { - cause = new ClosedChannelException(); - } - - for (;;) { - ChannelPromise p = handshakePromises.poll(); - if (p == null) { - break; + final boolean disconnected = cause == null || cause instanceof ClosedChannelException; + try { + engine.closeInbound(); + } catch (SSLException e) { + if (!disconnected) { + logger.warn("SSLEngine.closeInbound() raised an exception after a handshake failure.", e); + } else if (!closeNotifyWriteListener.done) { + logger.warn("SSLEngine.closeInbound() raised an exception due to closed connection.", e); + } else { + // cause == null && sentCloseNotify + // closeInbound() will raise an exception with bogus truncation attack warning. } - p.setFailure(cause); } - } + if (!handshakePromises.isEmpty()) { + if (cause == null) { + cause = new ClosedChannelException(); + } + + for (;;) { + ChannelPromise p = handshakePromises.poll(); + if (p == null) { + break; + } + p.setFailure(cause); + } + } + } finally { + ctx.fireUserEventTriggered(new HandshakeCompletionEvent(cause)); + } flush0(ctx, 0, cause); }