This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new 6d88a95952 Treat RST_STREAM during an incomplete HEADERS block as a 
connection error (#1124)
6d88a95952 is described below

commit 6d88a959523418577c13cdf59683aff126c93559
Author: Arturo Bernal <[email protected]>
AuthorDate: Fri Nov 6 17:12:54 2026 +0100

    Treat RST_STREAM during an incomplete HEADERS block as a connection error 
(#1124)
    
    RFC 9113 section 6.2 (and RFC 7540 section 6.2) require that a HEADERS
    frame without the END_HEADERS flag set MUST be followed by a CONTINUATION
    frame on the same stream. Any other frame type, including RST_STREAM, MUST
    be treated as a connection error of type PROTOCOL_ERROR.
    
    Http2Parser.validateFrame() previously had a NO-OP branch that silently
    accepted RST_STREAM frames while a header block was in progress. Remove
    that branch so RST_STREAM, like every other non-CONTINUATION frame type,
    triggers a ConnectionException with Http2Error.PROTOCOL_ERROR.
    
    Also correct the error code for other interleaved frame types from
    COMPRESSION_ERROR to PROTOCOL_ERROR as required by the RFC.
    
    Claude-Session: https://claude.ai/code/session_015BFDJFN9jasV9fx5g4KwAm
---
 java/org/apache/coyote/http2/Http2Parser.java      |  8 +--
 .../apache/coyote/http2/TestHttp2Section_4_3.java  |  2 +-
 .../apache/coyote/http2/TestHttp2Section_5_5.java  |  2 +-
 .../apache/coyote/http2/TestHttp2Section_6_2.java  | 64 ++++++++++++++++++++++
 .../apache/coyote/http2/TestHttp2Section_6_3.java  |  2 +-
 5 files changed, 70 insertions(+), 8 deletions(-)

diff --git a/java/org/apache/coyote/http2/Http2Parser.java 
b/java/org/apache/coyote/http2/Http2Parser.java
index dd6356f6b2..fca94b82d9 100644
--- a/java/org/apache/coyote/http2/Http2Parser.java
+++ b/java/org/apache/coyote/http2/Http2Parser.java
@@ -755,13 +755,11 @@ class Http2Parser {
                 throw new ConnectionException(
                         sm.getString("http2Parser.headers.wrongStream", 
connectionId,
                                 Integer.toString(headersCurrentStream), 
Integer.toString(streamId)),
-                        Http2Error.COMPRESSION_ERROR);
+                        Http2Error.PROTOCOL_ERROR);
             }
-            if (frameType == FrameType.RST) {
-                // NO-OP: RST is OK here
-            } else if (frameType != FrameType.CONTINUATION) {
+            if (frameType != FrameType.CONTINUATION) {
                 throw new 
ConnectionException(sm.getString("http2Parser.headers.wrongFrameType", 
connectionId,
-                        Integer.toString(headersCurrentStream), frameType), 
Http2Error.COMPRESSION_ERROR);
+                        Integer.toString(headersCurrentStream), frameType), 
Http2Error.PROTOCOL_ERROR);
             }
         }
 
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_4_3.java 
b/test/org/apache/coyote/http2/TestHttp2Section_4_3.java
index 48798b3cd1..aef53683f3 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_4_3.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_4_3.java
@@ -246,6 +246,6 @@ public class TestHttp2Section_4_3 extends Http2TestBase {
 
         sendPing();
 
-        handleGoAwayResponse(3, Http2Error.COMPRESSION_ERROR);
+        handleGoAwayResponse(3, Http2Error.PROTOCOL_ERROR);
     }
 }
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_5_5.java 
b/test/org/apache/coyote/http2/TestHttp2Section_5_5.java
index c202eecb0f..c389c919a9 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_5_5.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_5_5.java
@@ -88,6 +88,6 @@ public class TestHttp2Section_5_5 extends Http2TestBase {
         os.write(UNKNOWN_FRAME);
         os.flush();
 
-        handleGoAwayResponse(3, Http2Error.COMPRESSION_ERROR);
+        handleGoAwayResponse(3, Http2Error.PROTOCOL_ERROR);
     }
 }
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_2.java 
b/test/org/apache/coyote/http2/TestHttp2Section_6_2.java
index de9b90415f..85cc497074 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_6_2.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_2.java
@@ -135,4 +135,68 @@ public class TestHttp2Section_6_2 extends Http2TestBase {
         readSimpleGetResponse();
         Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace());
     }
+
+
+    @Test
+    public void testContinuationFrameOnWrongStream() throws Exception {
+        // RFC 9113, section 6.2: a HEADERS frame without the END_HEADERS flag 
set MUST be
+        // followed by a CONTINUATION frame on the same stream. A CONTINUATION 
frame on a
+        // different stream MUST be treated as a connection error of type 
PROTOCOL_ERROR.
+        http2Connect();
+
+        // Send HEADERS on stream 3 without END_HEADERS
+        byte[] frameHeader = new byte[9];
+        ByteBuffer headersPayload = ByteBuffer.allocate(128);
+        buildSimpleGetRequestPart1(frameHeader, headersPayload, 3);
+        writeFrame(frameHeader, headersPayload);
+
+        // Send CONTINUATION on stream 5 instead of stream 3
+        byte[] frameHeader2 = new byte[9];
+        ByteBuffer headersPayload2 = ByteBuffer.allocate(128);
+        buildSimpleGetRequestPart2(frameHeader2, headersPayload2, 5);
+        writeFrame(frameHeader2, headersPayload2);
+
+        handleGoAwayResponse(3, Http2Error.PROTOCOL_ERROR);
+    }
+
+
+    @Test
+    public void testRstStreamOnDifferentStreamDuringHeaderBlock() throws 
Exception {
+        // RFC 9113, section 6.2: a HEADERS frame without the END_HEADERS flag 
set MUST be
+        // followed by a CONTINUATION frame on the same stream. A RST_STREAM 
frame on a
+        // different stream MUST be treated as a connection error of type 
PROTOCOL_ERROR.
+        http2Connect();
+
+        // Send HEADERS on stream 3 without END_HEADERS
+        byte[] frameHeader = new byte[9];
+        ByteBuffer headersPayload = ByteBuffer.allocate(128);
+        buildSimpleGetRequestPart1(frameHeader, headersPayload, 3);
+        writeFrame(frameHeader, headersPayload);
+
+        // Send RST_STREAM for stream 5 instead of CONTINUATION for stream 3
+        sendRst(5, Http2Error.CANCEL.getCode());
+
+        handleGoAwayResponse(3, Http2Error.PROTOCOL_ERROR);
+    }
+
+
+    @Test
+    public void testRstStreamDuringHeaderBlock() throws Exception {
+        // RFC 9113, section 6.2: a HEADERS frame without the END_HEADERS flag
+        // set MUST be followed by a CONTINUATION frame for the same stream.
+        // The receipt of any other frame type, including RST_STREAM, MUST be
+        // treated as a connection error of type PROTOCOL_ERROR.
+        http2Connect();
+
+        // Send HEADERS on stream 3 without the END_HEADERS flag
+        byte[] frameHeader = new byte[9];
+        ByteBuffer headersPayload = ByteBuffer.allocate(128);
+        buildSimpleGetRequestPart1(frameHeader, headersPayload, 3);
+        writeFrame(frameHeader, headersPayload);
+
+        // Send RST_STREAM for stream 3 instead of the required CONTINUATION
+        sendRst(3, Http2Error.CANCEL.getCode());
+
+        handleGoAwayResponse(3, Http2Error.PROTOCOL_ERROR);
+    }
 }
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_3.java 
b/test/org/apache/coyote/http2/TestHttp2Section_6_3.java
index 5432daa27e..62077fbfcb 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_6_3.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_3.java
@@ -51,7 +51,7 @@ public class TestHttp2Section_6_3 extends Http2TestBase {
 
         sendPriority(5, 3, 15);
 
-        handleGoAwayResponse(3, Http2Error.COMPRESSION_ERROR);
+        handleGoAwayResponse(3, Http2Error.PROTOCOL_ERROR);
     }
 
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to