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