Repository: netty/netty
Issue: SslHandler.flush throws IllegalArgumentException on upstream write exception

### Expected behavior

Did not expect flush to throw IllegalArgumentException

### Actual behavior
```
promise already done: DefaultChannelPromise@3d07c0a4(
io.netty.channel.AbstractChannelHandlerContext.isNotValidPromise - Line 910 (AbstractChannelHandlerContext.java)
io.netty.channel.AbstractChannelHandlerContext.write - Line 714 (AbstractChannelHandlerContext.java)
io.netty.handler.ssl.SslHandler.finishWrap - Line 850 (SslHandler.java)
io.netty.handler.ssl.SslHandler.wrap - Line 836 (SslHandler.java)
io.netty.handler.ssl.SslHandler.wrapAndFlush - Line 753 (SslHandler.java)
io.netty.handler.ssl.SslHandler.flush - Line 734 (SslHandler.java)
io.netty.channel.AbstractChannelHandlerContext.invokeFlush0 - Line 776 (AbstractChannelHandlerContext.java)
io.netty.channel.AbstractChannelHandlerContext.invokeWriteAndFlush - Line 802 (AbstractChannelHandlerContext.java)
io.netty.channel.AbstractChannelHandlerContext.write - Line 814 (AbstractChannelHandlerContext.java)
io.netty.channel.AbstractChannelHandlerContext.writeAndFlush - Line 794 (AbstractChannelHandlerContext.java)
```

### Steps to reproduce

Occurs when an upstream handler throws an exception in write()

### Minimal yet complete reproducer code (or URL to code)

### Netty version

4.1.22-current

### JVM version (e.g. `java -version`)
1.8
### OS version (e.g. `uname -a`)
linux

Issue comments:
https://github.com/netty/netty/pull/9972/commits/69b3089cd6fd173c0ea9348033ee3919af14ab15

Pull request review comments:
How about `out.clear()` here too?
this should not be needed. 

Patch that resolved the issue:
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 bb76f9a246b..b180b0424e6 100644
--- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
+++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
@@ -858,11 +858,25 @@ private void wrap(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLExcepti
                         case NOT_HANDSHAKING:
                             setHandshakeSuccessIfStillHandshaking();
                             // deliberate fall-through
-                        case NEED_WRAP:
-                            finishWrap(ctx, out, promise, inUnwrap, false);
+                        case NEED_WRAP: {
+                            ChannelPromise p = promise;
+
+                            // Null out the promise so it is not reused in the finally block in the cause of
+                            // finishWrap(...) throwing.
                             promise = null;
-                            out = null;
+                            final ByteBuf b;
+
+                            if (out.isReadable()) {
+                                // There is something in the out buffer. Ensure we null it out so it is not re-used.
+                                b = out;
+                                out = null;
+                            } else {
+                                // If out is not readable we can re-use it and so save an extra allocation
+                                b = null;
+                            }
+                            finishWrap(ctx, b, p, inUnwrap, false);
                             break;
+                        }
                         case NEED_UNWRAP:
                             needUnwrap = true;
                             return;


Test patch: