--- tomcat/java/org/apache/coyote/http2/Http2Protocol.java	2019/02/01 10:28:18	1852701
+++ tomcat/java/org/apache/coyote/http2/Http2Protocol.java	2019/02/01 10:28:22	1852702
@@ -54,6 +54,8 @@
     // This default is defined by the HTTP/2 specification
     static final int DEFAULT_INITIAL_WINDOW_SIZE = (1 << 16) - 1;
 
+    static final int DEFAULT_OVERHEAD_COUNT_FACTOR = 1;
+
     private static final String HTTP_UPGRADE_NAME = "h2c";
     private static final String ALPN_NAME = "h2";
     private static final byte[] ALPN_IDENTIFIER = ALPN_NAME.getBytes(StandardCharsets.UTF_8);
@@ -79,6 +81,8 @@
     private int maxHeaderSize = Constants.DEFAULT_MAX_HEADER_SIZE;
     private int maxTrailerCount = Constants.DEFAULT_MAX_TRAILER_COUNT;
     private int maxTrailerSize = Constants.DEFAULT_MAX_TRAILER_SIZE;
+    private int overheadCountFactor = DEFAULT_OVERHEAD_COUNT_FACTOR;
+
     private boolean initiatePingDisabled = false;
     private boolean useSendfile = true;
     // Compression
@@ -306,6 +310,16 @@
     }
 
 
+    public int getOverheadCountFactor() {
+        return overheadCountFactor;
+    }
+
+
+    public void setOverheadCountFactor(int overheadCountFactor) {
+        this.overheadCountFactor = overheadCountFactor;
+    }
+
+
     public void setInitiatePingDisabled(boolean initiatePingDisabled) {
         this.initiatePingDisabled = initiatePingDisabled;
     }
--- tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java	2019/02/01 10:28:18	1852701
+++ tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java	2019/02/01 10:28:22	1852702
@@ -30,6 +30,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.servlet.http.WebConnection;
@@ -139,6 +140,9 @@
     private AtomicInteger streamConcurrency = null;
     private Queue<StreamRunnable> queuedRunnable = null;
 
+    // Track 'overhead' frames vs 'request/response' frames
+    private final AtomicLong overheadCount = new AtomicLong(-10);
+
 
     Http2UpgradeHandler(Http2Protocol protocol, Adapter adapter, Request coyoteRequest) {
         super (STREAM_ID_ZERO);
@@ -330,6 +334,10 @@
                         }
                     }
 
+                    if (overheadCount.get() > 0) {
+                        throw new ConnectionException("Too much overhead", Http2Error.ENHANCE_YOUR_CALM);
+                    }
+
                     if (activeRemoteStreamCount.get() == 0) {
                         // No streams currently active. Use the keep-alive
                         // timeout for the connection.
@@ -638,6 +646,9 @@
             log.debug(sm.getString("upgradeHandler.writeBody", connectionId, stream.getIdentifier(),
                     Integer.toString(len)));
         }
+
+        reduceOverheadCount();
+
         // Need to check this now since sending end of stream will change this.
         boolean writeable = stream.canWrite();
         byte[] header = new byte[9];
@@ -1193,6 +1204,16 @@
     }
 
 
+    private void reduceOverheadCount() {
+        overheadCount.decrementAndGet();
+    }
+
+
+    private void increaseOverheadCount() {
+        overheadCount.addAndGet(getProtocol().getOverheadCountFactor());
+    }
+
+
     // ----------------------------------------------- Http2Parser.Input methods
 
     @Override
@@ -1247,6 +1268,7 @@
 
     @Override
     public ByteBuffer startRequestBodyFrame(int streamId, int payloadSize) throws Http2Exception {
+        reduceOverheadCount();
         Stream stream = getStream(streamId, true);
         stream.checkState(FrameType.DATA);
         stream.receivedData(payloadSize);
@@ -1291,6 +1313,8 @@
         // determines if a new stream is created or if this stream is ignored.
         checkPauseState();
 
+        reduceOverheadCount();
+
         if (connectionState.get().isNewStreamAllowed()) {
             Stream stream = getStream(streamId, false);
             if (stream == null) {
@@ -1340,6 +1364,9 @@
             throw new ConnectionException(sm.getString("upgradeHandler.dependency.invalid",
                     getConnectionId(), Integer.valueOf(streamId)), Http2Error.PROTOCOL_ERROR);
         }
+
+        increaseOverheadCount();
+
         Stream stream = getStream(streamId, false);
         if (stream == null) {
             stream = createRemoteStream(streamId);
@@ -1384,6 +1411,9 @@
 
     @Override
     public void setting(Setting setting, long value) throws ConnectionException {
+
+        increaseOverheadCount();
+
         // Special handling required
         if (setting == Setting.INITIAL_WINDOW_SIZE) {
             long oldValue = remoteSettings.getInitialWindowSize();
@@ -1425,6 +1455,9 @@
 
     @Override
     public void pingReceive(byte[] payload, boolean ack) throws IOException {
+        if (!ack) {
+            increaseOverheadCount();
+        }
         pingManager.receivePing(payload, ack);
     }
 

