Repository: netty/netty (Java)

Issue:
Http2ConnectionHandler doesn't set the correct header state for streams generated from upgrades
### Expected behavior

When stream 1 is created from a h2c upgrade, the resultant stream should signal that it has sent the headers for the client, and that it has received the headers for the server.

### Actual behavior

The stream state doesn't acknowledge that the request headers have been sent/received.

### Minimal yet complete reproducer code (or URL to code)
Add the following test cases to the Http2ConnectionHandlerTest suite:

```java
    @Test
    public void clientUpgradeSetsStreamHeadersSent() throws Exception {
        when(local.createStream(STREAM_ID, true)).thenReturn(stream);
        handler = newHandler();
        handler.onHttpClientUpgrade();
        Http2Stream upgradeStream = handler.connection().stream(HTTP_UPGRADE_STREAM_ID);
        assertSame(stream, upgradeStream);
        verify(stream).headersSent(false);
    }

    @Test
    public void serverUpgradeSetsStreamHeadersReceived() throws Exception {
        when(connection.isServer()).thenReturn(true);
        when(remote.createStream(STREAM_ID, true)).thenReturn(stream);
        handler = newHandler();
        handler.onHttpServerUpgrade(new Http2Settings());
        Http2Stream upgradeStream = handler.connection().stream(HTTP_UPGRADE_STREAM_ID);
        assertSame(stream, upgradeStream);
        verify(stream).headersReceived(false);
    }
```

### Netty version

4.1 branch.

### JVM version (e.g. `java -version`)

openjdk version "1.8.0_162"

### OS version (e.g. `uname -a`)

OSX Darwin Kernel Version 17.5.0


Hints:

Patch:
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java
@@ -478,11 +478,19 @@ public Http2Stream open(boolean halfClosed) throws Http2Exception {
             if (!createdBy().canOpenStream()) {
                 throw connectionError(PROTOCOL_ERROR, "Maximum active streams violated for this endpoint.");
             }
+
             activate();
             return this;
         }
 
         void activate() {
+            // If the stream is opened in a half-closed state, the headers must have either
+            // been sent if this is a local stream, or received if it is a remote stream.
+            if (state == HALF_CLOSED_LOCAL) {
+                headersSent(/*isInformational*/ false);
+            } else if (state == HALF_CLOSED_REMOTE) {
+                headersReceived(/*isInformational*/ false);
+            }
             activeStreams.activate(this);
         }
 
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Stream.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Stream.java
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Stream.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Stream.java
@@ -76,9 +76,9 @@ public boolean remoteSideOpen() {
      * <ul>
      * <li>{@link State#OPEN} if {@link #state()} is {@link State#IDLE} and {@code halfClosed} is {@code false}.</li>
      * <li>{@link State#HALF_CLOSED_LOCAL} if {@link #state()} is {@link State#IDLE} and {@code halfClosed}
-     * is {@code true} and the stream is local.</li>
+     * is {@code true} and the stream is local. In this state, {@link #isHeadersSent()} is {@code true}</li>
      * <li>{@link State#HALF_CLOSED_REMOTE} if {@link #state()} is {@link State#IDLE} and {@code halfClosed}
-     * is {@code true} and the stream is remote.</li>
+     * is {@code true} and the stream is remote. In this state, {@link #isHeadersReceived()} is {@code true}</li>
      * <li>{@link State#RESERVED_LOCAL} if {@link #state()} is {@link State#HALF_CLOSED_REMOTE}.</li>
      * <li>{@link State#RESERVED_REMOTE} if {@link #state()} is {@link State#HALF_CLOSED_LOCAL}.</li>
      * </ul>


Test patch:
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java
@@ -294,12 +294,14 @@ public void clientCreateStreamShouldSucceed() throws Http2Exception {
         assertEquals(State.HALF_CLOSED_REMOTE, stream.state());
         assertEquals(2, client.numActiveStreams());
         assertEquals(4, client.remote().lastStreamCreated());
+        assertTrue(stream.isHeadersReceived());
 
         stream = client.local().createStream(3, true);
         assertEquals(3, stream.id());
         assertEquals(State.HALF_CLOSED_LOCAL, stream.state());
         assertEquals(3, client.numActiveStreams());
         assertEquals(3, client.local().lastStreamCreated());
+        assertTrue(stream.isHeadersSent());
 
         stream = client.local().createStream(5, false);
         assertEquals(5, stream.id());