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

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

commit c821b8b44278ed348b6ea6a8ad4c7277bddbe3fe
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon Sep 30 16:01:47 2024 +0100

    Improve HTTP/2 handling of trailer headers when connector is paused
    
    Prior to this change trailer headers for an existing stream would have
    been swallowed if received when the connector was paused.
---
 .../apache/coyote/http2/Http2UpgradeHandler.java   | 38 +++++------
 .../apache/coyote/http2/TestHttp2Section_6_8.java  | 75 ++++++++++++++++++++++
 2 files changed, 95 insertions(+), 18 deletions(-)

diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java 
b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index 925f20be78..f01678c455 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -1528,14 +1528,15 @@ class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpUpgradeH
     @Override
     public HeaderEmitter headersStart(int streamId, boolean headersEndStream) 
throws Http2Exception, IOException {
 
-        // Check the pause state before processing headers since the pause 
state
-        // determines if a new stream is created or if this stream is ignored.
-        checkPauseState();
-
-        if (connectionState.get().isNewStreamAllowed()) {
-            Stream stream = getStream(streamId, false);
-            if (stream == null) {
-                // New stream
+        Stream stream = getStream(streamId, false);
+        if (stream == null) {
+            // New stream
+
+            // Check the pause state before processing headers since the pause 
state
+            // determines if a new stream is created or if this stream is 
ignored.
+            checkPauseState();
+
+            if (connectionState.get().isNewStreamAllowed()) {
                 if (streamId > maxActiveRemoteStreamId) {
                     stream = createRemoteStream(streamId);
                     activeRemoteStreamCount.incrementAndGet();
@@ -1545,18 +1546,19 @@ class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpUpgradeH
                     throw new 
ConnectionException(sm.getString("upgradeHandler.stream.old", 
Integer.valueOf(streamId),
                             Integer.valueOf(maxActiveRemoteStreamId)), 
Http2Error.PROTOCOL_ERROR);
                 }
+            } else {
+                if (log.isTraceEnabled()) {
+                    log.trace(sm.getString("upgradeHandler.noNewStreams", 
connectionId, Integer.toString(streamId)));
+                }
+                reduceOverheadCount(FrameType.HEADERS);
+                // Stateless so a static can be used to save on GC
+                return HEADER_SINK;
             }
-            stream.checkState(FrameType.HEADERS);
-            stream.receivedStartOfHeaders(headersEndStream);
-            return stream;
-        } else {
-            if (log.isTraceEnabled()) {
-                log.trace(sm.getString("upgradeHandler.noNewStreams", 
connectionId, Integer.toString(streamId)));
-            }
-            reduceOverheadCount(FrameType.HEADERS);
-            // Stateless so a static can be used to save on GC
-            return HEADER_SINK;
         }
+
+        stream.checkState(FrameType.HEADERS);
+        stream.receivedStartOfHeaders(headersEndStream);
+        return stream;
     }
 
 
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_6_8.java 
b/test/org/apache/coyote/http2/TestHttp2Section_6_8.java
index ae245ca418..8b7ec1a6e2 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_6_8.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_6_8.java
@@ -16,9 +16,13 @@
  */
 package org.apache.coyote.http2;
 
+import java.nio.ByteBuffer;
+
 import org.junit.Assert;
 import org.junit.Test;
 
+import org.apache.coyote.http11.AbstractHttp11Protocol;
+
 /**
  * Unit tests for Section 6.8 of <a 
href="https://tools.ietf.org/html/rfc7540";>RFC 7540</a>. <br>
  * The order of tests in this class is aligned with the order of the 
requirements in the RFC.
@@ -68,6 +72,77 @@ public class TestHttp2Section_6_8 extends Http2TestBase {
     }
 
 
+    @Test
+    public void testGoawayIgnoreNewStreamsAllowTrailers() throws Exception {
+        setPingAckDelayMillis(PING_ACK_DELAY_MS);
+
+        http2Connect();
+
+        http2Protocol.setMaxConcurrentStreams(200);
+        // Ensure we see all the Window updates
+        http2Protocol.setOverheadWindowUpdateThreshold(0);
+        // Enable trailer headers
+        ((AbstractHttp11Protocol<?>) 
http2Protocol.getHttp11Protocol()).setAllowedTrailerHeaders(TRAILER_HEADER_NAME);
+
+        Thread.sleep(PING_ACK_DELAY_MS + TIMING_MARGIN_MS);
+
+        getTomcatInstance().getConnector().pause();
+
+        // Go away
+        parser.readFrame();
+        Assert.assertEquals("0-Goaway-[2147483647]-[0]-[null]", 
output.getTrace());
+        output.clearTrace();
+
+        // Should be processed
+        byte[] headersFrameHeader = new byte[9];
+        ByteBuffer headersPayload = ByteBuffer.allocate(128);
+        byte[] dataFrameHeader = new byte[9];
+        ByteBuffer dataPayload = ByteBuffer.allocate(256);
+        byte[] trailerFrameHeader = new byte[9];
+        ByteBuffer trailerPayload = ByteBuffer.allocate(256);
+
+        buildPostRequest(headersFrameHeader, headersPayload, false, 
dataFrameHeader, dataPayload, null, true, 3);
+
+        // Write the headers
+        writeFrame(headersFrameHeader, headersPayload);
+        // Body
+        writeFrame(dataFrameHeader, dataPayload);
+
+        Thread.sleep(PING_ACK_DELAY_MS + TIMING_MARGIN_MS);
+
+        // Should be ignored
+        sendSimpleGetRequest(5);
+
+        // Trailers
+        buildTrailerHeaders(trailerFrameHeader, trailerPayload, 3);
+        writeFrame(trailerFrameHeader, trailerPayload);
+
+        // Window updates after reading Stream 3 body
+        parser.readFrame();
+        parser.readFrame();
+        Assert.assertEquals("0-WindowSize-[256]\n3-WindowSize-[256]\n", 
output.getTrace());
+        output.clearTrace();
+
+        // Go away frame after trying to send a GET on stream 5
+        parser.readFrame();
+        Assert.assertEquals("0-Goaway-[3]-[0]-[null]", output.getTrace());
+        output.clearTrace();
+
+        // Stream 3 headers and body once trailer has been read (it is used to 
create body)
+        parser.readFrame();
+        parser.readFrame();
+
+        String len = Integer.toString(256 + TRAILER_HEADER_VALUE.length());
+        String expected = "3-HeadersStart\n" + "3-Header-[:status]-[200]\n" +
+                "3-Header-[content-length]-[" + len + "]\n" + 
"3-Header-[date]-[" + DEFAULT_DATE + "]\n" +
+                "3-HeadersEnd\n3-Body-" + len + "\n" + "3-EndOfStream\n";
+
+
+        Assert.assertEquals(expected, output.getTrace());
+        output.clearTrace();
+    }
+
+
     @Test
     public void testGoawayFrameNonZeroStream() throws Exception {
         // HTTP2 upgrade


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to