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

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

commit dd447ac1ab2edcbe017cd9413895a89effcdfc1a
Author: opencode <[email protected]>
AuthorDate: Tue Oct 13 16:59:23 2026 +0200

    Fix CLOSED_RX unexpected frames handling
    
    RFC 9113 5.1 closed:
    An endpoint that sends a frame with the END_STREAM flag set or an
    RST_STREAM frame might receive a WINDOW_UPDATE or RST_STREAM frame from
    its peer in the time before the peer receives and processes the frame
    that closes the stream. An endpoint MUST NOT consider receipt of such a
    frame to be an error.
    
    The state reached when the final frame of the stream is received
    (CLOSED_RX) permitted only PRIORITY frames, while the symmetric state
    reached when the final frame is sent (CLOSED_TX) already permitted the
    in flight WINDOW_UPDATE and RST_STREAM grace frames. A stream closed by
    sending then receiving the end of stream therefore saw these legitimate
    in flight frames trigger a connection error of type STREAM_CLOSED, a
    GOAWAY and the loss of every multiplexed stream on an otherwise healthy
    connection.
    
    Permit the grace frames in CLOSED_RX, matching CLOSED_TX. Streams that
    have been replaced by the low memory representation already ignore these
    frames once the state check passes, and for a live stream the handling
    is identical to the currently tolerated CLOSED_TX case.
---
 .../apache/coyote/http2/StreamStateMachine.java    |  4 +-
 .../coyote/http2/TestStreamStateMachine.java       | 68 ++++++++++++++++++++++
 webapps/docs/changelog.xml                         |  8 +++
 3 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/java/org/apache/coyote/http2/StreamStateMachine.java 
b/java/org/apache/coyote/http2/StreamStateMachine.java
index 92a49810c3..f3f0f02d51 100644
--- a/java/org/apache/coyote/http2/StreamStateMachine.java
+++ b/java/org/apache/coyote/http2/StreamStateMachine.java
@@ -195,7 +195,9 @@ class StreamStateMachine {
                                                        FrameType.RST,
                                                        
FrameType.WINDOW_UPDATE),
         CLOSED_RX          (false, false, false, true,
-                            Http2Error.STREAM_CLOSED,  FrameType.PRIORITY),
+                            Http2Error.STREAM_CLOSED,  FrameType.PRIORITY,
+                                                       FrameType.RST,
+                                                       
FrameType.WINDOW_UPDATE),
         CLOSED_TX          (false, false, false, true,
                             Http2Error.STREAM_CLOSED,  FrameType.PRIORITY,
                                                        FrameType.RST,
diff --git a/test/org/apache/coyote/http2/TestStreamStateMachine.java 
b/test/org/apache/coyote/http2/TestStreamStateMachine.java
new file mode 100644
index 0000000000..392b46b275
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestStreamStateMachine.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.coyote.http2;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestStreamStateMachine {
+
+    private static final String CONNECTION_ID = "-1";
+    private static final String STREAM_ID = "3";
+
+    /*
+     * RFC 9113 section 5.1: an endpoint that sends the frame that closed the 
stream might receive a WINDOW_UPDATE or
+     * RST_STREAM frame from its peer in the time before the peer receives and 
processes that closing frame. Receipt of
+     * such frames must not be considered an error. These are the same frames 
permitted for the closed state reached
+     * when the final frame is sent rather than received.
+     */
+    @Test
+    public void closedRxAcceptsGraceFrames() throws Exception {
+        StreamStateMachine state = new StreamStateMachine(CONNECTION_ID, 
STREAM_ID);
+
+        // Close the stream by sending then receiving the end of stream.
+        state.receivedStartOfHeaders();
+        state.sentEndOfStream();
+        state.receivedEndOfStream();
+
+        state.checkFrameType(FrameType.PRIORITY);
+        state.checkFrameType(FrameType.RST);
+        state.checkFrameType(FrameType.WINDOW_UPDATE);
+    }
+
+
+    /*
+     * RFC 9113 section 5.1: receipt of any other frame type on a closed 
stream may be treated as a connection error of
+     * type STREAM_CLOSED.
+     */
+    @Test
+    public void closedRxRejectsOtherFrames() throws Exception {
+        StreamStateMachine state = new StreamStateMachine(CONNECTION_ID, 
STREAM_ID);
+
+        // Close the stream by sending then receiving the end of stream.
+        state.receivedStartOfHeaders();
+        state.sentEndOfStream();
+        state.receivedEndOfStream();
+
+        try {
+            state.checkFrameType(FrameType.DATA);
+            Assert.fail("Expected a connection error for a DATA frame received 
in the closed state");
+        } catch (ConnectionException ce) {
+            Assert.assertEquals(Http2Error.STREAM_CLOSED, ce.getError());
+        }
+    }
+}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index c11a903ad9..4115eb3ba9 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -303,6 +303,14 @@
         allocation remain in the back log and are considered again when the
         next window increment arrives. (remm)
       </fix>
+      <fix>
+        Permit the <code>WINDOW_UPDATE</code> and <code>RST_STREAM</code>
+        frames that may legitimately be in flight when an HTTP/2 stream is
+        closed by the receipt of an end of stream frame, rather than treating
+        them as a connection error of type <code>STREAM_CLOSED</code> that
+        closes the entire connection. These frames are already permitted when
+        the stream is closed in the other direction. (remm)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Web applications">


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

Reply via email to