[tomcat] 03/03: Workaround https://bz.apache.org/bugzilla/show_bug.cgi?id=63690

2019-09-05 Thread markt
This is an automated email from the ASF dual-hosted git repository.

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

commit fbbbfc03c8b9b26dff68c19fe9d8f5f95303928a
Author: Mark Thomas 
AuthorDate: Thu Sep 5 12:27:24 2019 +0100

Workaround https://bz.apache.org/bugzilla/show_bug.cgi?id=63690

Use the average of the current and previous sizes when calculating
overhead for HTTP/2 DATA and WINDOW_UPDATE frames to avoid false
positives as a result of client side buffering behaviour that causes a
small percentage of non-final DATA frames to be smaller than expected.
---
 .../apache/coyote/http2/Http2UpgradeHandler.java   | 45 --
 webapps/docs/changelog.xml |  7 
 webapps/docs/config/http2.xml  | 29 +++---
 3 files changed, 55 insertions(+), 26 deletions(-)

diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java 
b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index adeafa9..0b7c05a 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -156,6 +156,8 @@ public class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpU
 
 // Track 'overhead' frames vs 'request/response' frames
 private final AtomicLong overheadCount = new AtomicLong(-10);
+private volatile int lastNonFinalDataPayload;
+private volatile int lastWindowUpdate;
 
 
 /**
@@ -176,6 +178,9 @@ public class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpU
 this.adapter = adapter;
 this.connectionId = 
Integer.toString(connectionIdGenerator.getAndIncrement());
 
+lastNonFinalDataPayload = protocol.getOverheadDataThreshold() * 2;
+lastWindowUpdate = protocol.getOverheadWindowUpdateThreshold() * 2;
+
 remoteSettings = new ConnectionSettingsRemote(connectionId);
 localSettings = new ConnectionSettingsLocal(connectionId);
 
@@ -1492,15 +1497,21 @@ public class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpU
 // .. but lots of small payloads are inefficient so that will increase
 // the overhead count unless it is the final DATA frame where small
 // payloads are expected.
+
+// See also https://bz.apache.org/bugzilla/show_bug.cgi?id=63690
+// The buffering behaviour of some clients means that small data frames
+// are much more frequent (roughly 1 in 20) than expected. Use an
+// average over two frames to avoid false positives.
 if (!endOfStream) {
 int overheadThreshold = protocol.getOverheadDataThreshold();
-if (payloadSize < overheadThreshold) {
-if (payloadSize == 0) {
-// Avoid division by zero
-overheadCount.addAndGet(overheadThreshold);
-} else {
-overheadCount.addAndGet(overheadThreshold / payloadSize);
-}
+int average = (lastNonFinalDataPayload >> 1) + (payloadSize >> 1);
+lastNonFinalDataPayload = payloadSize;
+// Avoid division by zero
+if (average == 0) {
+average = 1;
+}
+if (average < overheadThreshold) {
+overheadCount.addAndGet(overheadThreshold / average);
 }
 }
 
@@ -1735,13 +1746,25 @@ public class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpU
 
 @Override
 public void incrementWindowSize(int streamId, int increment) throws 
Http2Exception {
+// See also https://bz.apache.org/bugzilla/show_bug.cgi?id=63690
+// The buffering behaviour of some clients means that small data frames
+// are much more frequent (roughly 1 in 20) than expected. Some clients
+// issue a Window update for every DATA frame so a similar pattern may
+// be observed. Use an average over two frames to avoid false 
positives.
+
+int average = (lastWindowUpdate >> 1) + (increment >> 1);
 int overheadThreshold = protocol.getOverheadWindowUpdateThreshold();
+lastWindowUpdate = increment;
+// Avoid division by zero
+if (average == 0) {
+average = 1;
+}
 
 if (streamId == 0) {
 // Check for small increments which are inefficient
-if (increment < overheadThreshold) {
+if (average < overheadThreshold) {
 // The smaller the increment, the larger the overhead
-overheadCount.addAndGet(overheadThreshold / increment);
+overheadCount.addAndGet(overheadThreshold / average);
 }
 
 incrementWindowSize(increment);
@@ -1749,13 +1772,13 @@ public class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpU
 Stream stream = getStream(streamId, tr

[tomcat] 03/03: Workaround https://bz.apache.org/bugzilla/show_bug.cgi?id=63690

2019-09-05 Thread markt
This is an automated email from the ASF dual-hosted git repository.

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

commit 3a5556feff7e2be96e53c630f3c3e73a31adc975
Author: Mark Thomas 
AuthorDate: Thu Sep 5 12:27:24 2019 +0100

Workaround https://bz.apache.org/bugzilla/show_bug.cgi?id=63690

Use the average of the current and previous sizes when calculating
overhead for HTTP/2 DATA and WINDOW_UPDATE frames to avoid false
positives as a result of client side buffering behaviour that causes a
small percentage of non-final DATA frames to be smaller than expected.
---
 .../apache/coyote/http2/Http2UpgradeHandler.java   | 45 --
 webapps/docs/changelog.xml |  7 
 webapps/docs/config/http2.xml  | 29 +++---
 3 files changed, 55 insertions(+), 26 deletions(-)

diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java 
b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index 9753ebc..10a1f65 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -142,6 +142,8 @@ class Http2UpgradeHandler extends AbstractStream implements 
InternalHttpUpgradeH
 
 // Track 'overhead' frames vs 'request/response' frames
 private final AtomicLong overheadCount = new AtomicLong(-10);
+private volatile int lastNonFinalDataPayload;
+private volatile int lastWindowUpdate;
 
 
 Http2UpgradeHandler(Http2Protocol protocol, Adapter adapter, Request 
coyoteRequest) {
@@ -150,6 +152,9 @@ class Http2UpgradeHandler extends AbstractStream implements 
InternalHttpUpgradeH
 this.adapter = adapter;
 this.connectionId = 
Integer.toString(connectionIdGenerator.getAndIncrement());
 
+lastNonFinalDataPayload = protocol.getOverheadDataThreshold() * 2;
+lastWindowUpdate = protocol.getOverheadWindowUpdateThreshold() * 2;
+
 remoteSettings = new ConnectionSettingsRemote(connectionId);
 localSettings = new ConnectionSettingsLocal(connectionId);
 
@@ -1372,15 +1377,21 @@ class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpUpgradeH
 // .. but lots of small payloads are inefficient so that will increase
 // the overhead count unless it is the final DATA frame where small
 // payloads are expected.
+
+// See also https://bz.apache.org/bugzilla/show_bug.cgi?id=63690
+// The buffering behaviour of some clients means that small data frames
+// are much more frequent (roughly 1 in 20) than expected. Use an
+// average over two frames to avoid false positives.
 if (!endOfStream) {
 int overheadThreshold = protocol.getOverheadDataThreshold();
-if (payloadSize < overheadThreshold) {
-if (payloadSize == 0) {
-// Avoid division by zero
-overheadCount.addAndGet(overheadThreshold);
-} else {
-overheadCount.addAndGet(overheadThreshold / payloadSize);
-}
+int average = (lastNonFinalDataPayload >> 1) + (payloadSize >> 1);
+lastNonFinalDataPayload = payloadSize;
+// Avoid division by zero
+if (average == 0) {
+average = 1;
+}
+if (average < overheadThreshold) {
+overheadCount.addAndGet(overheadThreshold / average);
 }
 }
 
@@ -1615,13 +1626,25 @@ class Http2UpgradeHandler extends AbstractStream 
implements InternalHttpUpgradeH
 
 @Override
 public void incrementWindowSize(int streamId, int increment) throws 
Http2Exception {
+// See also https://bz.apache.org/bugzilla/show_bug.cgi?id=63690
+// The buffering behaviour of some clients means that small data frames
+// are much more frequent (roughly 1 in 20) than expected. Some clients
+// issue a Window update for every DATA frame so a similar pattern may
+// be observed. Use an average over two frames to avoid false 
positives.
+
+int average = (lastWindowUpdate >> 1) + (increment >> 1);
 int overheadThreshold = protocol.getOverheadWindowUpdateThreshold();
+lastWindowUpdate = increment;
+// Avoid division by zero
+if (average == 0) {
+average = 1;
+}
 
 if (streamId == 0) {
 // Check for small increments which are inefficient
-if (increment < overheadThreshold) {
+if (average < overheadThreshold) {
 // The smaller the increment, the larger the overhead
-overheadCount.addAndGet(overheadThreshold / increment);
+overheadCount.addAndGet(overheadThreshold / average);
 }
 
 incrementWindowSize(increment);
@@ -1629,13 +1652,13 @@ class Http2UpgradeHandler extends AbstractStream