[#1260] Fire a HandshakeCompletationEvent once the SSL handshake completes

This commit is contained in:
Norman Maurer 2013-04-11 06:47:43 +02:00
parent 725156f082
commit b3429af1cc
2 changed files with 91 additions and 33 deletions

View File

@ -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;
}
}

View File

@ -28,6 +28,7 @@ import io.netty.channel.ChannelInboundByteHandler;
import io.netty.channel.ChannelOutboundByteHandler; import io.netty.channel.ChannelOutboundByteHandler;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.channel.ChannelStateHandler;
import io.netty.channel.DefaultChannelPromise; import io.netty.channel.DefaultChannelPromise;
import io.netty.channel.FileRegion; import io.netty.channel.FileRegion;
import io.netty.util.concurrent.ImmediateExecutor; 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 * renegotiating. You will be notified by the {@link ChannelFuture} which is
* returned by the {@link #handshake()} method when the handshake * returned by the {@link #handshake()} method when the handshake
* process succeeds or fails. * process succeeds or fails.
* <p>
* 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}.
* *
* <h3>Handshake</h3> * <h3>Handshake</h3>
* <p> * <p>
@ -173,6 +179,8 @@ public class SslHandler
private volatile long handshakeTimeoutMillis = 10000; private volatile long handshakeTimeoutMillis = 10000;
private volatile long closeNotifyTimeoutMillis = 3000; private volatile long closeNotifyTimeoutMillis = 3000;
private static final HandshakeCompletionEvent HANDSHAKE_SUCCESS_EVENT = new HandshakeCompletionEvent(null);
/** /**
* Creates a new instance. * Creates a new instance.
* *
@ -958,12 +966,16 @@ public class SslHandler
* Notify all the handshake futures about the successfully handshake * Notify all the handshake futures about the successfully handshake
*/ */
private void setHandshakeSuccess() { private void setHandshakeSuccess() {
for (;;) { try {
ChannelPromise p = handshakePromises.poll(); for (;;) {
if (p == null) { ChannelPromise p = handshakePromises.poll();
break; 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. * Notify all the handshake futures about the failure during the handshake.
*/ */
private void setHandshakeFailure(Throwable cause) { 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 { try {
engine.closeInbound(); // Release all resources such as internal buffers that SSLEngine
} catch (SSLException e) { // is managing.
if (!disconnected) { engine.closeOutbound();
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.
}
}
if (!handshakePromises.isEmpty()) { final boolean disconnected = cause == null || cause instanceof ClosedChannelException;
if (cause == null) { try {
cause = new ClosedChannelException(); engine.closeInbound();
} } catch (SSLException e) {
if (!disconnected) {
for (;;) { logger.warn("SSLEngine.closeInbound() raised an exception after a handshake failure.", e);
ChannelPromise p = handshakePromises.poll(); } else if (!closeNotifyWriteListener.done) {
if (p == null) { logger.warn("SSLEngine.closeInbound() raised an exception due to closed connection.", e);
break; } 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); flush0(ctx, 0, cause);
} }