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 fc9ec9590f8459dd862fe38e8eb1f6ba1ee8554c
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon Sep 30 12:37:45 2024 +0100

    Improve HTTP/2 handling of trailer headers
    
    Prior to this change, trailers for a stream had to be received before
    any headers of a subsequent stream. This commit removes that limitation.
    
    The idle stream handling is removed since Tomcat never creates streams
    in the idle state without immediately transitioning them to open.
---
 .../apache/coyote/http2/Http2UpgradeHandler.java   | 34 +++++++------------
 .../apache/coyote/http2/TestHttp2Section_8_1.java  | 38 +++++++++++++++++++---
 2 files changed, 45 insertions(+), 27 deletions(-)

diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java 
b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index 53a2fbddf3..925f20be78 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -118,8 +118,7 @@ class Http2UpgradeHandler extends AbstractStream implements 
InternalHttpUpgradeH
 
     private final ConcurrentNavigableMap<Integer,AbstractNonZeroStream> 
streams = new ConcurrentSkipListMap<>();
     protected final AtomicInteger activeRemoteStreamCount = new 
AtomicInteger(0);
-    // Start at -1 so the 'add 2' logic in closeIdleStreams() works
-    private volatile int maxActiveRemoteStreamId = -1;
+    private volatile int maxActiveRemoteStreamId = 0;
     private volatile int maxProcessedStreamId;
     private final PingManager pingManager = getPingManager();
     private volatile int newStreamsSinceLastPrune = 0;
@@ -177,7 +176,7 @@ class Http2UpgradeHandler extends AbstractStream implements 
InternalHttpUpgradeH
             Integer key = Integer.valueOf(1);
             Stream stream = new Stream(key, this, coyoteRequest);
             streams.put(key, stream);
-            maxActiveRemoteStreamId = 1;
+            maxActiveRemoteStreamId = 0;
             activeRemoteStreamCount.set(1);
             maxProcessedStreamId = 1;
         }
@@ -1536,16 +1535,19 @@ class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpUpgradeH
         if (connectionState.get().isNewStreamAllowed()) {
             Stream stream = getStream(streamId, false);
             if (stream == null) {
-                stream = createRemoteStream(streamId);
-                activeRemoteStreamCount.incrementAndGet();
-            }
-            if (streamId < maxActiveRemoteStreamId) {
-                throw new 
ConnectionException(sm.getString("upgradeHandler.stream.old", 
Integer.valueOf(streamId),
-                        Integer.valueOf(maxActiveRemoteStreamId)), 
Http2Error.PROTOCOL_ERROR);
+                // New stream
+                if (streamId > maxActiveRemoteStreamId) {
+                    stream = createRemoteStream(streamId);
+                    activeRemoteStreamCount.incrementAndGet();
+                    maxActiveRemoteStreamId = streamId;
+                } else {
+                    // ID for new stream must always be greater than any 
previous stream
+                    throw new 
ConnectionException(sm.getString("upgradeHandler.stream.old", 
Integer.valueOf(streamId),
+                            Integer.valueOf(maxActiveRemoteStreamId)), 
Http2Error.PROTOCOL_ERROR);
+                }
             }
             stream.checkState(FrameType.HEADERS);
             stream.receivedStartOfHeaders(headersEndStream);
-            closeIdleStreams(streamId);
             return stream;
         } else {
             if (log.isTraceEnabled()) {
@@ -1558,18 +1560,6 @@ class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpUpgradeH
     }
 
 
-    private void closeIdleStreams(int newMaxActiveRemoteStreamId) {
-        final ConcurrentNavigableMap<Integer,AbstractNonZeroStream> subMap = 
streams.subMap(
-                Integer.valueOf(maxActiveRemoteStreamId), false, 
Integer.valueOf(newMaxActiveRemoteStreamId), false);
-        for (AbstractNonZeroStream stream : subMap.values()) {
-            if (stream instanceof Stream) {
-                ((Stream) stream).closeIfIdle();
-            }
-        }
-        maxActiveRemoteStreamId = newMaxActiveRemoteStreamId;
-    }
-
-
     @Override
     public void headersContinue(int payloadSize, boolean endOfHeaders) {
         // Generally, continuation frames don't impact the overhead count but 
if
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_8_1.java 
b/test/org/apache/coyote/http2/TestHttp2Section_8_1.java
index 278f266474..45a6864f09 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_8_1.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_8_1.java
@@ -64,19 +64,24 @@ public class TestHttp2Section_8_1 extends Http2TestBase {
         ByteBuffer trailerPayload = ByteBuffer.allocate(256);
 
         buildPostRequest(headersFrameHeader, headersPayload, false, 
dataFrameHeader, dataPayload, null, true, 3);
-        buildTrailerHeaders(trailerFrameHeader, trailerPayload, 3);
 
         // Write the headers
         writeFrame(headersFrameHeader, headersPayload);
         // Body
         writeFrame(dataFrameHeader, dataPayload);
+
+        sendSimpleGetRequest(5);
+
         // Trailers
+        buildTrailerHeaders(trailerFrameHeader, trailerPayload, 3);
         writeFrame(trailerFrameHeader, trailerPayload);
 
         parser.readFrame();
         parser.readFrame();
         parser.readFrame();
         parser.readFrame();
+        parser.readFrame();
+        parser.readFrame();
 
         String len;
         if (allowTrailerHeader) {
@@ -85,10 +90,33 @@ public class TestHttp2Section_8_1 extends Http2TestBase {
             len = "256";
         }
 
-        Assert.assertEquals("0-WindowSize-[256]\n" + "3-WindowSize-[256]\n" + 
"3-HeadersStart\n" +
-                "3-Header-[:status]-[200]\n" + "3-Header-[content-length]-[" + 
len + "]\n" + "3-Header-[date]-[" +
-                DEFAULT_DATE + "]\n" + "3-HeadersEnd\n" + "3-Body-" + len + 
"\n" + "3-EndOfStream\n",
-                output.getTrace());
+        /*
+         * There is no guaranteed order between stream 3 and stream 5. It all 
depends on server side timing. Also need
+         * to ensure no unexpected frames are received.
+         */
+        String stream0WindowSize = "0-WindowSize-[256]\n";
+        String stream3WindowSize = "3-WindowSize-[256]\n";
+        String stream3Headers = "3-HeadersStart\n" + 
"3-Header-[:status]-[200]\n" +
+                "3-Header-[content-length]-[" + len + "]\n" + 
"3-Header-[date]-[" + DEFAULT_DATE + "]\n" +
+                "3-HeadersEnd\n";
+        String stream3Body = "3-Body-" + len + "\n" + "3-EndOfStream\n";
+        String stream5Headers = "5-HeadersStart\n" + 
"5-Header-[:status]-[200]\n" +
+                "5-Header-[content-type]-[application/octet-stream]\n" + 
"5-Header-[content-length]-[8192]\n" +
+                "5-Header-[date]-[" + DEFAULT_DATE + "]\n" + "5-HeadersEnd\n";
+        String stream5Body = "5-Body-8192\n" + "5-EndOfStream\n";
+
+        String trace = output.getTrace();
+        System.out.println(trace);
+
+        Assert.assertTrue(trace, trace.contains(stream0WindowSize));
+        Assert.assertTrue(trace, trace.contains(stream3WindowSize));
+        Assert.assertTrue(trace, trace.contains(stream3Headers));
+        Assert.assertTrue(trace, trace.contains(stream3Body));
+        Assert.assertTrue(trace, trace.contains(stream5Headers));
+        Assert.assertTrue(trace, trace.contains(stream5Body));
+        Assert.assertEquals("Unexpected data in trace - " + trace, 
stream0WindowSize.length() +
+                stream3WindowSize.length() + stream3Headers.length() + 
stream3Body.length() + stream5Headers.length() +
+                stream5Body.length(), trace.length());
     }
 
 


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

Reply via email to