@Override public void flush(final ChannelHandlerContext ctx, ChannelFuture future) throws Exception { final ByteBuf in = ctx.outboundByteBuffer(); final ByteBuf out = ctx.nextOutboundByteBuffer(); out.unsafe().discardSomeReadBytes(); // Do not encrypt the first write request if this handler is // created with startTLS flag turned on. if (startTls && !sentFirstMessage) { sentFirstMessage = true; out.writeBytes(in); ctx.flush(future); return; } if (ctx.executor() == ctx.channel().eventLoop()) { flushFutureNotifier.addFlushFuture(future, in.readableBytes()); } else { synchronized (flushFutureNotifier) { flushFutureNotifier.addFlushFuture(future, in.readableBytes()); } } boolean unwrapLater = false; int bytesConsumed = 0; try { for (; ; ) { SSLEngineResult result = wrap(engine, in, out); bytesConsumed += result.bytesConsumed(); if (result.getStatus() == Status.CLOSED) { // SSLEngine has been closed already. // Any further write attempts should be denied. if (in.readable()) { in.clear(); SSLException e = new SSLException("SSLEngine already closed"); future.setFailure(e); ctx.fireExceptionCaught(e); flush0(ctx, bytesConsumed, e); bytesConsumed = 0; } break; } else { switch (result.getHandshakeStatus()) { case NEED_WRAP: ctx.flush(); continue; case NEED_UNWRAP: if (ctx.inboundByteBuffer().readable()) { unwrapLater = true; } break; case NEED_TASK: runDelegatedTasks(); continue; case FINISHED: setHandshakeSuccess(); continue; case NOT_HANDSHAKING: break; default: throw new IllegalStateException( "Unknown handshake status: " + result.getHandshakeStatus()); } if (result.bytesConsumed() == 0 && result.bytesProduced() == 0) { break; } } } if (unwrapLater) { inboundBufferUpdated(ctx); } } catch (SSLException e) { setHandshakeFailure(e); throw e; } finally { in.unsafe().discardSomeReadBytes(); flush0(ctx, bytesConsumed); } }