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 76046c53b4..c83827d868 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -725,8 +725,13 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH } } - // This method will not call setHandshakeFailure(...) ! - private void wrapNonAppData(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException { + /** + * This method will not call + * {@link #setHandshakeFailure(io.netty.channel.ChannelHandlerContext, Throwable, boolean)} or + * {@link #setHandshakeFailure(io.netty.channel.ChannelHandlerContext, Throwable)}. + * @return {@code true} if this method ends on {@link SSLEngineResult.HandshakeStatus#NOT_HANDSHAKING}. + */ + private boolean wrapNonAppData(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException { ByteBuf out = null; ByteBufAllocator alloc = ctx.alloc(); try { @@ -752,7 +757,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH switch (result.getHandshakeStatus()) { case FINISHED: setHandshakeSuccess(); - break; + return false; case NEED_TASK: runDelegatedTasks(); break; @@ -770,7 +775,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH if (!inUnwrap) { unwrapNonAppData(ctx); } - break; + return true; default: throw new IllegalStateException("Unknown handshake status: " + result.getHandshakeStatus()); } @@ -790,6 +795,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH out.release(); } } + return false; } private SSLEngineResult wrap(ByteBufAllocator alloc, SSLEngine engine, ByteBuf in, ByteBuf out) @@ -1113,7 +1119,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH try { // Only continue to loop if the handler was not removed in the meantime. // See https://github.com/netty/netty/issues/5860 - while (!ctx.isRemoved()) { + unwrapLoop: while (!ctx.isRemoved()) { final SSLEngineResult result = engineType.unwrap(this, packet, offset, length, decodeOut); final Status status = result.getStatus(); final HandshakeStatus handshakeStatus = result.getHandshakeStatus(); @@ -1163,7 +1169,12 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH case NEED_UNWRAP: break; case NEED_WRAP: - wrapNonAppData(ctx, true); + // If the wrap operation transitions the status to NOT_HANDSHAKING and there is no more data to + // unwrap then the next call to unwrap will not produce any data. We can avoid the potentially + // costly unwrap operation and break out of the loop. + if (wrapNonAppData(ctx, true) && length == 0) { + break unwrapLoop; + } break; case NEED_TASK: runDelegatedTasks(); @@ -1196,7 +1207,12 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH flushedBeforeHandshake = false; wrapLater = true; } - + // If we are not handshaking and there is no more data to unwrap then the next call to unwrap + // will not produce any data. We can avoid the potentially costly unwrap operation and break + // out of the loop. + if (length == 0) { + break unwrapLoop; + } break; default: throw new IllegalStateException("unknown handshake status: " + handshakeStatus);