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


The following commit(s) were added to refs/heads/8.5.x by this push:
     new 9054e10  Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63829
9054e10 is described below

commit 9054e10d53170afcd7dd85bd22335238625958dc
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Oct 23 08:50:11 2019 +0200

    Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63829
    
    Improve the check of the Content-Encoding header when looking to see if
    Tomcat is serving pre-compressed content. Ensure that only a full token
    is matched and that the match is case insensitive.
---
 java/org/apache/coyote/CompressionConfig.java      | 28 ++++++++++++++++++----
 java/org/apache/coyote/LocalStrings.properties     |  2 ++
 java/org/apache/coyote/http11/Http11Processor.java | 18 ++------------
 .../apache/tomcat/util/http/parser/TokenList.java  | 24 ++++++++++++++++++-
 webapps/docs/changelog.xml                         |  6 +++++
 5 files changed, 57 insertions(+), 21 deletions(-)

diff --git a/java/org/apache/coyote/CompressionConfig.java 
b/java/org/apache/coyote/CompressionConfig.java
index fd0652e..520ed2e 100644
--- a/java/org/apache/coyote/CompressionConfig.java
+++ b/java/org/apache/coyote/CompressionConfig.java
@@ -20,17 +20,26 @@ import java.io.IOException;
 import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.regex.Pattern;
 
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.buf.MessageBytes;
 import org.apache.tomcat.util.http.MimeHeaders;
 import org.apache.tomcat.util.http.ResponseUtil;
 import org.apache.tomcat.util.http.parser.AcceptEncoding;
+import org.apache.tomcat.util.http.parser.TokenList;
+import org.apache.tomcat.util.res.StringManager;
 
 public class CompressionConfig {
 
+    private static final Log log = LogFactory.getLog(CompressionConfig.class);
+    private static final StringManager sm = 
StringManager.getManager(CompressionConfig.class);
+
     private int compressionLevel = 0;
     private Pattern noCompressionUserAgents = null;
     private String compressibleMimeType = 
"text/html,text/xml,text/plain,text/css," +
@@ -193,10 +202,21 @@ public class CompressionConfig {
 
         // Check if content is not already compressed
         MessageBytes contentEncodingMB = 
responseHeaders.getValue("Content-Encoding");
-        if (contentEncodingMB != null &&
-                (contentEncodingMB.indexOf("gzip") != -1 ||
-                        contentEncodingMB.indexOf("br") != -1)) {
-            return false;
+        if (contentEncodingMB != null) {
+            // Content-Encoding values are ordered but order is not important
+            // for this check so use a Set rather than a List
+            Set<String> tokens = new HashSet<>();
+            try {
+                
TokenList.parseTokenList(responseHeaders.values("Content-Encoding"), tokens);
+            } catch (IOException e) {
+                // Because we are using StringReader, any exception here is a
+                // Tomcat bug.
+                
log.warn(sm.getString("compressionConfig.ContentEncodingParseFail"), e);
+                return false;
+            }
+            if (tokens.contains("gzip") || tokens.contains("br")) {
+                return false;
+            }
         }
 
         // If force mode, the length and MIME type checks are skipped
diff --git a/java/org/apache/coyote/LocalStrings.properties 
b/java/org/apache/coyote/LocalStrings.properties
index 03dec37..3f0fbdf 100644
--- a/java/org/apache/coyote/LocalStrings.properties
+++ b/java/org/apache/coyote/LocalStrings.properties
@@ -48,6 +48,8 @@ abstractProtocolHandler.stopError=Failed to stop end point 
associated with Proto
 
 asyncStateMachine.invalidAsyncState=Calling [{0}] is not valid for a request 
with Async state [{1}]
 
+compressionConfig.ContentEncodingParseFail=Failed to parse Content-Encoding 
header when chekcing to see if compression was already in use
+
 request.notAsync=It is only valid to switch to non-blocking IO within async 
processing or HTTP upgrade processing
 request.nullReadListener=The listener passed to setReadListener() may not be 
null
 request.readListenerSet=The non-blocking read listener has already been set
diff --git a/java/org/apache/coyote/http11/Http11Processor.java 
b/java/org/apache/coyote/http11/Http11Processor.java
index 05e595a..f6de5f1 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -18,10 +18,7 @@ package org.apache.coyote.http11;
 
 import java.io.IOException;
 import java.io.InterruptedIOException;
-import java.io.StringReader;
 import java.nio.ByteBuffer;
-import java.util.Collection;
-import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
@@ -956,7 +953,7 @@ public class Http11Processor extends AbstractProcessor {
         MessageBytes connectionValueMB = 
headers.getValue(Constants.CONNECTION);
         if (connectionValueMB != null && !connectionValueMB.isNull()) {
             Set<String> tokens = new HashSet<>();
-            parseConnectionTokens(headers, tokens);
+            TokenList.parseTokenList(headers.values(Constants.CONNECTION), 
tokens);
             if (tokens.contains(Constants.CLOSE)) {
                 keepAlive = false;
             } else if (tokens.contains(Constants.KEEPALIVE)) {
@@ -1369,22 +1366,11 @@ public class Http11Processor extends AbstractProcessor {
         }
 
         Set<String> tokens = new HashSet<>();
-        parseConnectionTokens(headers, tokens);
+        TokenList.parseTokenList(headers.values(Constants.CONNECTION), tokens);
         return tokens.contains(token);
     }
 
 
-    private static void parseConnectionTokens(MimeHeaders headers, 
Collection<String> tokens) throws IOException {
-        Enumeration<String> values = headers.values(Constants.CONNECTION);
-        while (values.hasMoreElements()) {
-            String nextHeaderValue = values.nextElement();
-            if (nextHeaderValue != null) {
-                TokenList.parseTokenList(new StringReader(nextHeaderValue), 
tokens);
-            }
-        }
-    }
-
-
     private void prepareSendfile(OutputFilter[] outputFilters) {
         String fileName = (String) request.getAttribute(
                 org.apache.coyote.Constants.SENDFILE_FILENAME_ATTR);
diff --git a/java/org/apache/tomcat/util/http/parser/TokenList.java 
b/java/org/apache/tomcat/util/http/parser/TokenList.java
index ca5e153..db40877 100644
--- a/java/org/apache/tomcat/util/http/parser/TokenList.java
+++ b/java/org/apache/tomcat/util/http/parser/TokenList.java
@@ -18,7 +18,9 @@ package org.apache.tomcat.util.http.parser;
 
 import java.io.IOException;
 import java.io.Reader;
+import java.io.StringReader;
 import java.util.Collection;
+import java.util.Enumeration;
 import java.util.Locale;
 
 public class TokenList {
@@ -29,12 +31,32 @@ public class TokenList {
 
 
     /**
+     * Parses an enumeration of header values of the form 1#token, forcing all
+     * parsed values to lower case.
+     *
+     * @param inputs The headers to parse
+     * @param result The Collection (usually a list of a set) to which the
+     *                   parsed tokens should be added
+     *
+     * @throws IOException If an I/O error occurs reading the header
+     */
+    public static void parseTokenList(Enumeration<String> inputs, 
Collection<String> result) throws IOException {
+        while (inputs.hasMoreElements()) {
+            String nextHeaderValue = inputs.nextElement();
+            if (nextHeaderValue != null) {
+                TokenList.parseTokenList(new StringReader(nextHeaderValue), 
result);
+            }
+        }
+    }
+
+
+    /**
      * Parses a header of the form 1#token, forcing all parsed values to lower
      * case. This is typically used when header values are case-insensitive.
      *
      * @param input  The header to parse
      * @param result The Collection (usually a list of a set) to which the
-     *                   parsed token should be added
+     *                   parsed tokens should be added
      *
      * @throws IOException If an I/O error occurs reading the header
      */
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 0c42ec0..a96f393 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -73,6 +73,12 @@
         <code>Connection</code> HTTP headers looking for a specific token, be
         stricter in ensuring that the exact token is present. (markt)
       </fix>
+      <fix>
+        <bug>63829</bug>: Improve the check of the 
<code>Content-Encoding</code>
+        header when looking to see if Tomcat is serving pre-compressed content.
+        Ensure that only a full token is matched and that the match is case
+        insensitive. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Other">


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

Reply via email to