Repository: netty/netty
Issue: HTTP/2 RST_STREAM frame for an IDLE stream should result in connection error
Labels: defect

http://http2.github.io/http2-spec/index.html#RST_STREAM

> RST_STREAM frames MUST NOT be sent for a stream in the "idle" state. If a RST_STREAM frame identifying an idle stream is received, the recipient MUST treat this as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.


Issue comments:
PR pending for this....


Pull request review comments:

Patch that resolved the issue:
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java
index f839e6d0390..714710a5722 100644
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java
@@ -349,9 +349,13 @@ public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDe
         @Override
         public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
             Http2Stream stream = connection.requireStream(streamId);
-            if (stream.state() == CLOSED) {
-                // RstStream frames must be ignored for closed streams.
-                return;
+            switch(stream.state()) {
+            case IDLE:
+                throw connectionError(PROTOCOL_ERROR, "RST_STREAM received for IDLE stream %d", streamId);
+            case CLOSED:
+                return; // RST_STREAM frames must be ignored for closed streams.
+            default:
+                break;
             }
 
             listener.onRstStreamRead(ctx, streamId, errorCode);


Test patch:
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java
index 82319597145..c119dc94cd6 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java
@@ -20,6 +20,7 @@
 import static io.netty.handler.codec.http2.Http2CodecUtil.emptyPingBuf;
 import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
 import static io.netty.handler.codec.http2.Http2Exception.connectionError;
+import static io.netty.handler.codec.http2.Http2Stream.State.IDLE;
 import static io.netty.handler.codec.http2.Http2Stream.State.OPEN;
 import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_REMOTE;
 import static io.netty.util.CharsetUtil.UTF_8;
@@ -455,6 +456,14 @@ public void rstStreamReadShouldCloseStream() throws Exception {
         verify(listener).onRstStreamRead(eq(ctx), eq(STREAM_ID), eq(PROTOCOL_ERROR.code()));
     }
 
+    @Test(expected = Http2Exception.class)
+    public void rstStreamOnIdleStreamShouldThrow() throws Exception {
+        when(stream.state()).thenReturn(IDLE);
+        decode().onRstStreamRead(ctx, STREAM_ID, PROTOCOL_ERROR.code());
+        verify(lifecycleManager).closeStream(eq(stream), eq(future));
+        verify(listener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong());
+    }
+
     @Test
     public void pingReadWithAckShouldNotifylistener() throws Exception {
         decode().onPingAckRead(ctx, emptyPingBuf());