This is an automated email from the ASF dual-hosted git repository. markt 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 9fec9a8288 Make counting of active streams more robust 9fec9a8288 is described below commit 9fec9a82887853402833a80b584e3762c7423f5f Author: Mark Thomas <ma...@apache.org> AuthorDate: Wed May 8 08:36:43 2024 +0100 Make counting of active streams more robust --- .../coyote/http2/Http2AsyncUpgradeHandler.java | 2 +- .../org/apache/coyote/http2/Http2UpgradeHandler.java | 20 ++++++++++---------- java/org/apache/coyote/http2/Stream.java | 16 ++++++++++++++++ webapps/docs/changelog.xml | 4 ++++ 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java b/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java index 758b63e666..bedc8788d9 100644 --- a/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java +++ b/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java @@ -156,7 +156,7 @@ public class Http2AsyncUpgradeHandler extends Http2UpgradeHandler { boolean active = state.isActive(); state.sendReset(); if (active) { - decrementActiveRemoteStreamCount(); + decrementActiveRemoteStreamCount(getStream(se.getStreamId())); } } diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java index a50f62287d..2ac1b969b3 100644 --- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java +++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java @@ -288,8 +288,8 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH } - protected void decrementActiveRemoteStreamCount() { - setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet()); + protected void decrementActiveRemoteStreamCount(Stream stream) { + setConnectionTimeoutForStreamCount(stream.decrementAndGetActiveRemoteStreamCount()); } @@ -596,7 +596,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH boolean active = state.isActive(); state.sendReset(); if (active) { - decrementActiveRemoteStreamCount(); + decrementActiveRemoteStreamCount(getStream(se.getStreamId())); } } socketWrapper.write(true, rstFrame, 0, rstFrame.length); @@ -839,7 +839,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH protected void sentEndOfStream(Stream stream) { stream.sentEndOfStream(); if (!stream.isActive()) { - decrementActiveRemoteStreamCount(); + decrementActiveRemoteStreamCount(stream); } } @@ -1221,7 +1221,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH } - private Stream getStream(int streamId) { + Stream getStream(int streamId) { Integer key = Integer.valueOf(streamId); AbstractStream result = streams.get(key); if (result instanceof Stream) { @@ -1590,6 +1590,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH 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), @@ -1668,9 +1669,8 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH Stream stream = (Stream) abstractNonZeroStream; if (stream.isActive()) { if (stream.receivedEndOfHeaders()) { - - if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) { - decrementActiveRemoteStreamCount(); + if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.get()) { + decrementActiveRemoteStreamCount(stream); // Ignoring maxConcurrentStreams increases the overhead count increaseOverheadCount(FrameType.HEADERS); throw new StreamException( @@ -1714,7 +1714,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH private void receivedEndOfStream(Stream stream) throws ConnectionException { stream.receivedEndOfStream(); if (!stream.isActive()) { - decrementActiveRemoteStreamCount(); + decrementActiveRemoteStreamCount(stream); } } @@ -1740,7 +1740,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH boolean active = stream.isActive(); stream.receiveReset(errorCode); if (active) { - decrementActiveRemoteStreamCount(); + decrementActiveRemoteStreamCount(stream); } } } diff --git a/java/org/apache/coyote/http2/Stream.java b/java/org/apache/coyote/http2/Stream.java index 1a74f3b84c..95b596a004 100644 --- a/java/org/apache/coyote/http2/Stream.java +++ b/java/org/apache/coyote/http2/Stream.java @@ -28,6 +28,7 @@ import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; @@ -92,6 +93,7 @@ class Stream extends AbstractNonZeroStream implements HeaderEmitter { private final StreamInputBuffer inputBuffer; private final StreamOutputBuffer streamOutputBuffer = new StreamOutputBuffer(); private final Http2OutputBuffer http2OutputBuffer = new Http2OutputBuffer(coyoteResponse, streamOutputBuffer); + private final AtomicBoolean removedFromActiveCount = new AtomicBoolean(false); // State machine would be too much overhead private int headerState = HEADER_STATE_START; @@ -883,6 +885,20 @@ class Stream extends AbstractNonZeroStream implements HeaderEmitter { } + int decrementAndGetActiveRemoteStreamCount() { + /* + * Protect against mis-counting of active streams. This method should only be called once per stream but since + * the count of active streams is used to enforce the maximum concurrent streams limit, make sure each stream is + * only removed from the active count exactly once. + */ + if (removedFromActiveCount.compareAndSet(false, true)) { + return handler.activeRemoteStreamCount.decrementAndGet(); + } else { + return handler.activeRemoteStreamCount.get(); + } + } + + private static void push(final Http2UpgradeHandler handler, final Request request, final Stream stream) throws IOException { if (org.apache.coyote.Constants.IS_SECURITY_ENABLED) { diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index dce08e2919..76b0fb554b 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -165,6 +165,10 @@ <code>Connector</code> element, similar to the <code>Executor</code> element, for consistency. (remm) </update> + <fix> + Make counting of active HTTP/2 streams per connection more robust. + (markt) + </fix> </changelog> </subsection> <subsection name="Jasper"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org