You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2021/09/13 16:05:08 UTC

[tomcat] branch main updated (878caf6 -> dbd137f)

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

markt pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git.


    from 878caf6  Use DataSource in DataSourceUserDatabase constructor
     new 3910ab7  Avoid StackOverflowException
     new 74681f5  Step 1 - merge BackLogTracker into AbstractStream
     new dbd137f  Remove clear whole backlog shortcut

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 java/org/apache/coyote/http2/AbstractStream.java   |  65 ++++++++++
 .../coyote/http2/Http2AsyncUpgradeHandler.java     | 127 ++++++++++--------
 .../apache/coyote/http2/Http2UpgradeHandler.java   | 144 ++++-----------------
 webapps/docs/changelog.xml                         |   4 +
 4 files changed, 165 insertions(+), 175 deletions(-)

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


[tomcat] 01/03: Avoid StackOverflowException

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 3910ab722be9b791533f8662e45cc6b5701c3177
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Sep 10 08:21:36 2021 +0100

    Avoid StackOverflowException
    
    On a fast network (e.g. localhost) the writes all complete in-line. That
    leads to the CompletionHandler triggering another write, which triggers
    the CompletionHandler which trigegrs another write and so on. With large
    response bodies and recursive in-line writes, it is easy to trigger a
    StackOverflowException.
---
 .../coyote/http2/Http2AsyncUpgradeHandler.java     | 127 ++++++++++++---------
 webapps/docs/changelog.xml                         |   4 +
 2 files changed, 75 insertions(+), 56 deletions(-)

diff --git a/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java b/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java
index 4dc268a..19c88a1 100644
--- a/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java
@@ -36,6 +36,7 @@ import org.apache.tomcat.util.http.MimeHeaders;
 import org.apache.tomcat.util.net.SendfileState;
 import org.apache.tomcat.util.net.SocketWrapperBase;
 import org.apache.tomcat.util.net.SocketWrapperBase.BlockingMode;
+import org.apache.tomcat.util.net.SocketWrapperBase.CompletionState;
 
 public class Http2AsyncUpgradeHandler extends Http2UpgradeHandler {
 
@@ -363,70 +364,84 @@ public class Http2AsyncUpgradeHandler extends Http2UpgradeHandler {
     protected class SendfileCompletionHandler implements CompletionHandler<Long, SendfileData> {
         @Override
         public void completed(Long nBytes, SendfileData sendfile) {
+            CompletionState completionState = null;
             long bytesWritten = nBytes.longValue() - 9;
-            sendfile.left -= bytesWritten;
-            if (sendfile.left == 0) {
-                try {
-                    sendfile.stream.getOutputBuffer().end();
-                } catch (IOException e) {
-                    failed(e, sendfile);
+
+            /*
+             * Loop for in-line writes only. Avoids a possible stack-overflow of
+             * chained completion handlers with a long series of in-line writes.
+             */
+            do {
+                sendfile.left -= bytesWritten;
+                if (sendfile.left == 0) {
+                    try {
+                        sendfile.stream.getOutputBuffer().end();
+                    } catch (IOException e) {
+                        failed(e, sendfile);
+                    }
+                    return;
                 }
-                return;
-            }
-            sendfile.streamReservation -= bytesWritten;
-            sendfile.connectionReservation -= bytesWritten;
-            sendfile.pos += bytesWritten;
-            try {
-                if (sendfile.connectionReservation == 0) {
-                    if (sendfile.streamReservation == 0) {
-                        int reservation = (sendfile.end - sendfile.pos > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) (sendfile.end - sendfile.pos);
-                        sendfile.streamReservation = sendfile.stream.reserveWindowSize(reservation, true);
+                sendfile.streamReservation -= bytesWritten;
+                sendfile.connectionReservation -= bytesWritten;
+                sendfile.pos += bytesWritten;
+                try {
+                    if (sendfile.connectionReservation == 0) {
+                        if (sendfile.streamReservation == 0) {
+                            int reservation = (sendfile.end - sendfile.pos > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) (sendfile.end - sendfile.pos);
+                            sendfile.streamReservation = sendfile.stream.reserveWindowSize(reservation, true);
+                        }
+                        sendfile.connectionReservation = reserveWindowSize(sendfile.stream, sendfile.streamReservation, true);
                     }
-                    sendfile.connectionReservation = reserveWindowSize(sendfile.stream, sendfile.streamReservation, true);
+                } catch (IOException e) {
+                    failed (e, sendfile);
+                    return;
                 }
-            } catch (IOException e) {
-                failed (e, sendfile);
-                return;
-            }
 
-            if (log.isDebugEnabled()) {
-                log.debug(sm.getString("upgradeHandler.sendfile.reservation", connectionId, sendfile.stream.getIdAsString(),
-                        Integer.valueOf(sendfile.connectionReservation), Integer.valueOf(sendfile.streamReservation)));
-            }
-
-            // connectionReservation will always be smaller than or the same as
-            // streamReservation
-            int frameSize = Integer.min(getMaxFrameSize(), sendfile.connectionReservation);
-            boolean finished = (frameSize == sendfile.left) && sendfile.stream.getCoyoteResponse().getTrailerFields() == null;
-
-            // Need to check this now since sending end of stream will change this.
-            boolean writeable = sendfile.stream.canWrite();
-            byte[] header = new byte[9];
-            ByteUtil.setThreeBytes(header, 0, frameSize);
-            header[3] = FrameType.DATA.getIdByte();
-            if (finished) {
-                header[4] = FLAG_END_OF_STREAM;
-                sendfile.stream.sentEndOfStream();
-                if (!sendfile.stream.isActive()) {
-                    setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
-                }
-            }
-            if (writeable) {
                 if (log.isDebugEnabled()) {
-                    log.debug(sm.getString("upgradeHandler.writeBody", connectionId, sendfile.stream.getIdAsString(),
-                            Integer.toString(frameSize), Boolean.valueOf(finished)));
+                    log.debug(sm.getString("upgradeHandler.sendfile.reservation", connectionId, sendfile.stream.getIdAsString(),
+                            Integer.valueOf(sendfile.connectionReservation), Integer.valueOf(sendfile.streamReservation)));
                 }
-                ByteUtil.set31Bits(header, 5, sendfile.stream.getIdAsInt());
-                sendfile.mappedBuffer.limit(sendfile.mappedBuffer.position() + frameSize);
-                socketWrapper.write(BlockingMode.SEMI_BLOCK, protocol.getWriteTimeout(),
-                        TimeUnit.MILLISECONDS, sendfile, SocketWrapperBase.COMPLETE_WRITE_WITH_COMPLETION,
-                        this, ByteBuffer.wrap(header), sendfile.mappedBuffer);
-                try {
-                    handleAsyncException();
-                } catch (IOException e) {
-                    failed(e, sendfile);
+
+                // connectionReservation will always be smaller than or the same as
+                // streamReservation
+                int frameSize = Integer.min(getMaxFrameSize(), sendfile.connectionReservation);
+                boolean finished = (frameSize == sendfile.left) && sendfile.stream.getCoyoteResponse().getTrailerFields() == null;
+
+                // Need to check this now since sending end of stream will change this.
+                boolean writeable = sendfile.stream.canWrite();
+                byte[] header = new byte[9];
+                ByteUtil.setThreeBytes(header, 0, frameSize);
+                header[3] = FrameType.DATA.getIdByte();
+                if (finished) {
+                    header[4] = FLAG_END_OF_STREAM;
+                    sendfile.stream.sentEndOfStream();
+                    if (!sendfile.stream.isActive()) {
+                        setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
+                    }
                 }
-            }
+                if (writeable) {
+                    if (log.isDebugEnabled()) {
+                        log.debug(sm.getString("upgradeHandler.writeBody", connectionId, sendfile.stream.getIdAsString(),
+                                Integer.toString(frameSize), Boolean.valueOf(finished)));
+                    }
+                    ByteUtil.set31Bits(header, 5, sendfile.stream.getIdAsInt());
+                    sendfile.mappedBuffer.limit(sendfile.mappedBuffer.position() + frameSize);
+                    // Note: Completion handler not called in the write
+                    //       completes in-line. The wrote will continue via the
+                    //       surrounding loop.
+                    completionState = socketWrapper.write(BlockingMode.SEMI_BLOCK, protocol.getWriteTimeout(),
+                            TimeUnit.MILLISECONDS, sendfile, SocketWrapperBase.COMPLETE_WRITE,
+                            this, ByteBuffer.wrap(header), sendfile.mappedBuffer);
+                    try {
+                        handleAsyncException();
+                    } catch (IOException e) {
+                        failed(e, sendfile);
+                        return;
+                    }
+                }
+                // Update bytesWritten for start of next loop iteration
+                bytesWritten = frameSize;
+            } while (completionState == CompletionState.INLINE);
         }
 
         @Override
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 224fafd..8132f24 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -121,6 +121,10 @@
         after <code>bytes</code>. Fix based on pull request <pr>449</pr> by
         Thierry Guérin. (markt)
       </fix>
+      <fix>
+        Correct a potential <code>StackOverflowException</code> with HTTP/2 and
+        sendfile. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Jasper">

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


[tomcat] 02/03: Step 1 - merge BackLogTracker into AbstractStream

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 74681f56475541ff6069aadb1a88bd4d687e7446
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon Sep 13 15:10:58 2021 +0100

    Step 1 - merge BackLogTracker into AbstractStream
---
 java/org/apache/coyote/http2/AbstractStream.java   |  65 +++++++++++
 .../apache/coyote/http2/Http2UpgradeHandler.java   | 128 ++++-----------------
 2 files changed, 86 insertions(+), 107 deletions(-)

diff --git a/java/org/apache/coyote/http2/AbstractStream.java b/java/org/apache/coyote/http2/AbstractStream.java
index c7374b6..d3195eb 100644
--- a/java/org/apache/coyote/http2/AbstractStream.java
+++ b/java/org/apache/coyote/http2/AbstractStream.java
@@ -40,6 +40,9 @@ abstract class AbstractStream {
     private final Set<AbstractNonZeroStream> childStreams = Collections.newSetFromMap(new ConcurrentHashMap<>());
     private long windowSize = ConnectionSettingsBase.DEFAULT_INITIAL_WINDOW_SIZE;
 
+    volatile int remainingReservation;
+    volatile int unusedAllocation;
+    volatile boolean notifyInProgress;
 
     AbstractStream(Integer identifier) {
         this.identifier = identifier;
@@ -157,4 +160,66 @@ abstract class AbstractStream {
     abstract String getConnectionId();
 
     abstract int getWeight();
+
+
+    /**
+     * @return The number of bytes requiring an allocation from the
+     *         Connection flow control window
+     */
+    public int getRemainingReservation() {
+        return remainingReservation;
+    }
+
+    /**
+     *
+     * @return The number of bytes allocated from the Connection flow
+     *         control window but not yet written
+     */
+    public int getUnusedAllocation() {
+        return unusedAllocation;
+    }
+
+    /**
+     * The purpose of this is to avoid the incorrect triggering of a timeout
+     * for the following sequence of events:
+     * <ol>
+     * <li>window update 1</li>
+     * <li>allocation 1</li>
+     * <li>notify 1</li>
+     * <li>window update 2</li>
+     * <li>allocation 2</li>
+     * <li>act on notify 1 (using allocation 1 and 2)</li>
+     * <li>notify 2</li>
+     * <li>act on notify 2 (timeout due to no allocation)</li>
+     * </ol>
+     *
+     * @return {@code true} if a notify has been issued but the associated
+     *         allocation has not been used, otherwise {@code false}
+     */
+    public boolean isNotifyInProgress() {
+        return notifyInProgress;
+    }
+
+    public void useAllocation() {
+        unusedAllocation = 0;
+        notifyInProgress = false;
+    }
+
+    public void startNotify() {
+        notifyInProgress = true;
+    }
+
+    protected int allocate(int allocation) {
+        if (remainingReservation >= allocation) {
+            remainingReservation -= allocation;
+            unusedAllocation += allocation;
+            return 0;
+        }
+
+        int left = allocation - remainingReservation;
+        unusedAllocation += remainingReservation;
+        remainingReservation = 0;
+
+        return left;
+    }
 }
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index f28ae6d..b45dad0 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -20,10 +20,9 @@ import java.io.EOFException;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Queue;
 import java.util.Set;
 import java.util.TreeSet;
@@ -132,7 +131,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
     private final AtomicInteger nextLocalStreamId = new AtomicInteger(2);
     private final PingManager pingManager = getPingManager();
     private volatile int newStreamsSinceLastPrune = 0;
-    private final Map<AbstractStream, BacklogTracker> backLogStreams = new ConcurrentHashMap<>();
+    private final Set<AbstractStream> backLogStreams = Collections.newSetFromMap(new ConcurrentHashMap<>());
     private long backLogSize = 0;
     // The time at which the connection will timeout unless data arrives before
     // then. -1 means no timeout.
@@ -882,21 +881,20 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
                     long windowSize = getWindowSize();
                     if (windowSize < 1 || backLogSize > 0) {
                         // Has this stream been granted an allocation
-                        BacklogTracker tracker = backLogStreams.get(stream);
-                        if (tracker == null) {
-                            tracker = new BacklogTracker(reservation);
-                            backLogStreams.put(stream, tracker);
+                        if (stream.remainingReservation == 0) {
+                            backLogStreams.add(stream);
+                            stream.remainingReservation = reservation;
                             backLogSize += reservation;
                             // Add the parents as well
                             AbstractStream parent = stream.getParentStream();
-                            while (parent != null && backLogStreams.putIfAbsent(parent, new BacklogTracker()) == null) {
+                            while (parent != null && backLogStreams.add(parent)) {
                                 parent = parent.getParentStream();
                             }
                         } else {
-                            if (tracker.getUnusedAllocation() > 0) {
-                                allocation = tracker.getUnusedAllocation();
+                            if (stream.getUnusedAllocation() > 0) {
+                                allocation = stream.getUnusedAllocation();
                                 decrementWindowSize(allocation);
-                                if (tracker.getRemainingReservation() == 0) {
+                                if (stream.getRemainingReservation() == 0) {
                                     // The reservation has been fully allocated
                                     // so this stream can be removed from the
                                     // backlog.
@@ -905,7 +903,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
                                     // This allocation has been used. Leave the
                                     // stream on the backlog as it still has
                                     // more bytes to write.
-                                    tracker.useAllocation();
+                                    stream.useAllocation();
                                 }
                             }
                         }
@@ -928,12 +926,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
                             // Has this stream been granted an allocation
                             // Note: If the stream in not in this Map then the
                             //       requested write has been fully allocated
-                            BacklogTracker tracker;
-                            // Ensure allocations made in other threads are visible
-                            synchronized (this) {
-                                tracker = backLogStreams.get(stream);
-                            }
-                            if (tracker != null && tracker.getUnusedAllocation() == 0) {
+                            if (stream.getUnusedAllocation() == 0) {
                                 String msg;
                                 Http2Error error;
                                 if (stream.isActive()) {
@@ -1033,7 +1026,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
         Set<AbstractStream> result = new HashSet<>();
         if (backLogSize < increment) {
             // Can clear the whole backlog
-            result.addAll(backLogStreams.keySet());
+            result.addAll(backLogStreams);
             backLogStreams.clear();
             backLogSize = 0;
         } else {
@@ -1041,13 +1034,13 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
             while (leftToAllocate > 0) {
                 leftToAllocate = allocate(this, leftToAllocate);
             }
-            for (Entry<AbstractStream,BacklogTracker> entry : backLogStreams.entrySet()) {
-                int allocation = entry.getValue().getUnusedAllocation();
+            for (AbstractStream stream : backLogStreams) {
+                int allocation = stream.getUnusedAllocation();
                 if (allocation > 0) {
                     backLogSize -= allocation;
-                    if (!entry.getValue().isNotifyInProgress()) {
-                        result.add(entry.getKey());
-                        entry.getValue().startNotify();
+                    if (!stream.isNotifyInProgress()) {
+                        result.add(stream);
+                        stream.startNotify();
                     }
                 }
             }
@@ -1061,10 +1054,8 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
             log.debug(sm.getString("upgradeHandler.allocate.debug", getConnectionId(),
                     stream.getIdAsString(), Integer.toString(allocation)));
         }
-        // Allocate to the specified stream
-        BacklogTracker tracker = backLogStreams.get(stream);
 
-        int leftToAllocate = tracker.allocate(allocation);
+        int leftToAllocate = stream.allocate(allocation);
 
         if (leftToAllocate == 0) {
             return 0;
@@ -1078,12 +1069,12 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
         // Recipients are children of the current stream that are in the
         // backlog.
         Set<AbstractStream> recipients = new HashSet<>(stream.getChildStreams());
-        recipients.retainAll(backLogStreams.keySet());
+        recipients.retainAll(backLogStreams);
 
         // Loop until we run out of allocation or recipients
         while (leftToAllocate > 0) {
             if (recipients.size() == 0) {
-                if (tracker.getUnusedAllocation() == 0) {
+                if (stream.getUnusedAllocation() == 0) {
                     backLogStreams.remove(stream);
                 }
                 return leftToAllocate;
@@ -1832,8 +1823,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
             if (average < overheadThreshold) {
                 // For Streams, client might only release the minimum so check
                 // against current demand
-                BacklogTracker tracker = backLogStreams.get(stream);
-                if (tracker == null || increment < tracker.getRemainingReservation()) {
+                if (increment < stream.getRemainingReservation()) {
                     // The smaller the increment, the larger the overhead
                     increaseOverheadCount(FrameType.WINDOW_UPDATE, overheadThreshold / average);
                 }
@@ -2044,80 +2034,4 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
             payload = ByteBuffer.allocate(payload.capacity() * 2);
         }
     }
-
-
-    private static class BacklogTracker {
-
-        private int remainingReservation;
-        private int unusedAllocation;
-        private boolean notifyInProgress;
-
-        public BacklogTracker() {
-        }
-
-        public BacklogTracker(int reservation) {
-            remainingReservation = reservation;
-        }
-
-        /**
-         * @return The number of bytes requiring an allocation from the
-         *         Connection flow control window
-         */
-        public int getRemainingReservation() {
-            return remainingReservation;
-        }
-
-        /**
-         *
-         * @return The number of bytes allocated from the Connection flow
-         *         control window but not yet written
-         */
-        public int getUnusedAllocation() {
-            return unusedAllocation;
-        }
-
-        /**
-         * The purpose of this is to avoid the incorrect triggering of a timeout
-         * for the following sequence of events:
-         * <ol>
-         * <li>window update 1</li>
-         * <li>allocation 1</li>
-         * <li>notify 1</li>
-         * <li>window update 2</li>
-         * <li>allocation 2</li>
-         * <li>act on notify 1 (using allocation 1 and 2)</li>
-         * <li>notify 2</li>
-         * <li>act on notify 2 (timeout due to no allocation)</li>
-         * </ol>
-         *
-         * @return {@code true} if a notify has been issued but the associated
-         *         allocation has not been used, otherwise {@code false}
-         */
-        public boolean isNotifyInProgress() {
-            return notifyInProgress;
-        }
-
-        public void useAllocation() {
-            unusedAllocation = 0;
-            notifyInProgress = false;
-        }
-
-        public void startNotify() {
-            notifyInProgress = true;
-        }
-
-        private int allocate(int allocation) {
-            if (remainingReservation >= allocation) {
-                remainingReservation -= allocation;
-                unusedAllocation += allocation;
-                return 0;
-            }
-
-            int left = allocation - remainingReservation;
-            unusedAllocation += remainingReservation;
-            remainingReservation = 0;
-
-            return left;
-        }
-    }
 }

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


Re: [tomcat] branch main updated (878caf6 -> dbd137f)

Posted by Mark Thomas <ma...@apache.org>.
On 13/09/2021 17:05, markt@apache.org wrote:
> This is an automated email from the ASF dual-hosted git repository.
> 
> markt pushed a change to branch main
> in repository https://gitbox.apache.org/repos/asf/tomcat.git.
> 
> 
>      from 878caf6  Use DataSource in DataSourceUserDatabase constructor
>       new 3910ab7  Avoid StackOverflowException
>       new 74681f5  Step 1 - merge BackLogTracker into AbstractStream
>       new dbd137f  Remove clear whole backlog shortcut

Sorry, I pushed this before I was ready. I'm still working on the full fix.

Mark

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


[tomcat] 03/03: Remove clear whole backlog shortcut

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit dbd137f142385fe2db21f72a04e8a9a789f92ae3
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon Sep 13 17:04:52 2021 +0100

    Remove clear whole backlog shortcut
---
 .../apache/coyote/http2/Http2UpgradeHandler.java   | 28 ++++++++--------------
 1 file changed, 10 insertions(+), 18 deletions(-)

diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index b45dad0..ebd79b6 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -1024,27 +1024,19 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
 
     private synchronized Set<AbstractStream> releaseBackLog(int increment) {
         Set<AbstractStream> result = new HashSet<>();
-        if (backLogSize < increment) {
-            // Can clear the whole backlog
-            result.addAll(backLogStreams);
-            backLogStreams.clear();
-            backLogSize = 0;
-        } else {
-            int leftToAllocate = increment;
-            while (leftToAllocate > 0) {
-                leftToAllocate = allocate(this, leftToAllocate);
-            }
-            for (AbstractStream stream : backLogStreams) {
-                int allocation = stream.getUnusedAllocation();
-                if (allocation > 0) {
-                    backLogSize -= allocation;
-                    if (!stream.isNotifyInProgress()) {
-                        result.add(stream);
-                        stream.startNotify();
-                    }
+
+        int leftToAllocate = allocate(this, increment);
+        for (AbstractStream stream : backLogStreams) {
+            int allocation = stream.getUnusedAllocation();
+            if (allocation > 0) {
+                backLogSize -= allocation;
+                if (!stream.isNotifyInProgress()) {
+                    result.add(stream);
+                    stream.startNotify();
                 }
             }
         }
+
         return result;
     }
 

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