Repository: netty/netty
Issue: HttpServerKeepAliveHandler throws java.lang.IllegalStateException: void future
Labels: defect

### Expected behavior

No error when I do `ctx.writeAndFlush(msg, ctx.voidPromise());` where msg is with header "Connection:close".

### Actual behavior

HttpServerKeepAliveHandler throws IllegalStateException exception.

### Steps to reproduce

Use `ctx.writeAndFlush(msg, ctx.voidPromise());` where msg is `DefaultFullHttpResponse` with header "Connection:close".

```
java.lang.IllegalStateException: void future
	at io.netty.channel.VoidChannelPromise.fail(VoidChannelPromise.java:178) ~[netty-transport-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.channel.VoidChannelPromise.addListener(VoidChannelPromise.java:44) ~[netty-transport-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.channel.VoidChannelPromise.addListener(VoidChannelPromise.java:24) ~[netty-transport-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.handler.codec.http.HttpServerKeepAliveHandler.write(HttpServerKeepAliveHandler.java:85) ~[netty-codec-http-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:738) [netty-transport-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:730) [netty-transport-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:816) [netty-transport-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:723) [netty-transport-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.handler.stream.ChunkedWriteHandler.doFlush(ChunkedWriteHandler.java:304) [netty-handler-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.handler.stream.ChunkedWriteHandler.flush(ChunkedWriteHandler.java:137) [netty-handler-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:776) [netty-transport-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeWriteAndFlush(AbstractChannelHandlerContext.java:802) [netty-transport-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:814) [netty-transport-4.1.10.Final.jar:4.1.10.Final]
	at io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:794) [netty-transport-4.1.10.Final.jar:4.1.10.Final]
```

### Netty version

4.1.10

Issue comments:
Fixed by #6699

Pull request review comments:
maybe just merge these two lines ?

`promise.unvoid().addListener(....)`
use `HttpHeaderValues.CLOSE`
use `HttpHeaderValues.CLOSE`
@normanmaurer the next line is 

`super.write(ctx, msg, promise);`

so I think newly created promise should be passed there. Am I wrong?
ah yeah... then yes.
Or you mean this 

`promise = promise.unvoid().addListener(ChannelFutureListener.CLOSE);`

?

Patch that resolved the issue:
diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerKeepAliveHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerKeepAliveHandler.java
index c6305a5e2a2..28813332857 100644
--- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerKeepAliveHandler.java
+++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerKeepAliveHandler.java
@@ -82,7 +82,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
             }
         }
         if (msg instanceof LastHttpContent && !shouldKeepAlive()) {
-            promise.addListener(ChannelFutureListener.CLOSE);
+            promise = promise.unvoid().addListener(ChannelFutureListener.CLOSE);
         }
         super.write(ctx, msg, promise);
     }


Test patch:
diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerKeepAliveHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerKeepAliveHandlerTest.java
index bcd64bebad0..1332a2b8a89 100644
--- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerKeepAliveHandlerTest.java
+++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerKeepAliveHandlerTest.java
@@ -117,6 +117,34 @@ public void test_KeepAlive() throws Exception {
         assertFalse(channel.finishAndReleaseAll());
     }
 
+    @Test
+    public void testConnectionCloseHeaderHandledCorrectly() throws Exception {
+        HttpResponse response = new DefaultFullHttpResponse(httpVersion, responseStatus);
+        response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
+        setupMessageLength(response);
+
+        channel.writeAndFlush(response);
+        HttpResponse writtenResponse = channel.readOutbound();
+
+        assertFalse(channel.isOpen());
+        ReferenceCountUtil.release(writtenResponse);
+        assertFalse(channel.finishAndReleaseAll());
+    }
+
+    @Test
+    public void testConnectionCloseHeaderHandledCorrectlyForVoidPromise() throws Exception {
+        HttpResponse response = new DefaultFullHttpResponse(httpVersion, responseStatus);
+        response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
+        setupMessageLength(response);
+
+        channel.writeAndFlush(response, channel.voidPromise());
+        HttpResponse writtenResponse = channel.readOutbound();
+
+        assertFalse(channel.isOpen());
+        ReferenceCountUtil.release(writtenResponse);
+        assertFalse(channel.finishAndReleaseAll());
+    }
+
     @Test
     public void test_PipelineKeepAlive() {
         FullHttpRequest firstRequest = new DefaultFullHttpRequest(httpVersion, HttpMethod.GET, "/v1/foo/bar");