Author: markt
Date: Tue Nov 28 14:22:17 2017
New Revision: 1816549

URL: http://svn.apache.org/viewvc?rev=1816549&view=rev
Log:
Refactor: Move compression code to new class to allow re-use with HTTP/2

Added:
    tomcat/trunk/java/org/apache/coyote/CompressionConfig.java   (with props)
Modified:
    tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
    tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java

Added: tomcat/trunk/java/org/apache/coyote/CompressionConfig.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/CompressionConfig.java?rev=1816549&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/CompressionConfig.java (added)
+++ tomcat/trunk/java/org/apache/coyote/CompressionConfig.java Tue Nov 28 
14:22:17 2017
@@ -0,0 +1,274 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.coyote;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+public class CompressionConfig {
+
+    private int compressionLevel = 0;
+    private Pattern noCompressionUserAgents = null;
+    private String compressibleMimeType = 
"text/html,text/xml,text/plain,text/css," +
+            
"text/javascript,application/javascript,application/json,application/xml";
+    private String[] compressibleMimeTypes = null;
+    private int compressionMinSize = 2048;
+
+
+    /**
+     * Set compression level.
+     *
+     * @param compression One of <code>on</code>, <code>force</code>,
+     *                    <code>off</code> or the minimum compression size in
+     *                    bytes which implies <code>on</code>
+     */
+    public void setCompression(String compression) {
+        if (compression.equals("on")) {
+            this.compressionLevel = 1;
+        } else if (compression.equals("force")) {
+            this.compressionLevel = 2;
+        } else if (compression.equals("off")) {
+            this.compressionLevel = 0;
+        } else {
+            try {
+                // Try to parse compression as an int, which would give the
+                // minimum compression size
+                setCompressionMinSize(Integer.parseInt(compression));
+                this.compressionLevel = 1;
+            } catch (Exception e) {
+                this.compressionLevel = 0;
+            }
+        }
+    }
+
+
+    /**
+     * Return compression level.
+     *
+     * @return The current compression level in string form (off/on/force)
+     */
+    public String getCompression() {
+        switch (compressionLevel) {
+        case 0:
+            return "off";
+        case 1:
+            return "on";
+        case 2:
+            return "force";
+        }
+        return "off";
+    }
+
+
+    public int getCompressionLevel() {
+        return compressionLevel;
+    }
+
+
+    /**
+     * Obtain the String form of the regular expression that defines the user
+     * agents to not use gzip with.
+     *
+     * @return The regular expression as a String
+     */
+    public String getNoCompressionUserAgents() {
+        if (noCompressionUserAgents == null) {
+            return null;
+        } else {
+            return noCompressionUserAgents.toString();
+        }
+    }
+
+
+    public Pattern getNoCompressionUserAgentsPattern() {
+        return noCompressionUserAgents;
+    }
+
+
+    /**
+     * Set no compression user agent pattern. Regular expression as supported
+     * by {@link Pattern}. e.g.: <code>gorilla|desesplorer|tigrus</code>.
+     *
+     * @param noCompressionUserAgents The regular expression for user agent
+     *                                strings for which compression should not
+     *                                be applied
+     */
+    public void setNoCompressionUserAgents(String noCompressionUserAgents) {
+        if (noCompressionUserAgents == null || 
noCompressionUserAgents.length() == 0) {
+            this.noCompressionUserAgents = null;
+        } else {
+            this.noCompressionUserAgents =
+                Pattern.compile(noCompressionUserAgents);
+        }
+    }
+
+
+    public String getCompressibleMimeType() {
+        return compressibleMimeType;
+    }
+
+
+    public void setCompressibleMimeType(String valueS) {
+        compressibleMimeType = valueS;
+        compressibleMimeTypes = null;
+    }
+
+
+    public String[] getCompressibleMimeTypes() {
+        String[] result = compressibleMimeTypes;
+        if (result != null) {
+            return result;
+        }
+        List<String> values = new ArrayList<>();
+        StringTokenizer tokens = new StringTokenizer(compressibleMimeType, 
",");
+        while (tokens.hasMoreTokens()) {
+            String token = tokens.nextToken().trim();
+            if (token.length() > 0) {
+                values.add(token);
+            }
+        }
+        result = values.toArray(new String[values.size()]);
+        compressibleMimeTypes = result;
+        return result;
+    }
+
+
+    public int getCompressionMinSize() {
+        return compressionMinSize;
+    }
+
+
+    /**
+     * Set Minimum size to trigger compression.
+     *
+     * @param compressionMinSize The minimum content length required for
+     *                           compression in bytes
+     */
+    public void setCompressionMinSize(int compressionMinSize) {
+        this.compressionMinSize = compressionMinSize;
+    }
+
+
+    /**
+     * Determines if compression should be enabled for the given response and 
if
+     * it is, sets any necessary headers to mark it as such.
+     *
+     * @param request  The request that triggered the response
+     * @param response The response to consider compressing
+     *
+     * @return {@code true} if compression was enabled for the given response,
+     *         otherwise {@code false}
+     */
+    public boolean useCompression(Request request, Response response) {
+        // Check if compression is enabled
+        if (compressionLevel == 0) {
+            return false;
+        }
+
+        MimeHeaders responseHeaders = response.getMimeHeaders();
+
+        // Check if content is not already gzipped
+        MessageBytes contentEncodingMB = 
responseHeaders.getValue("Content-Encoding");
+        if ((contentEncodingMB != null) && (contentEncodingMB.indexOf("gzip") 
!= -1)) {
+            return false;
+        }
+
+        // If force mode, the length and MIME type checks are skipped
+        if (compressionLevel != 2) {
+            // Check if the response is of sufficient length to trigger the 
compression
+            long contentLength = response.getContentLengthLong();
+            if (contentLength != -1 && contentLength < compressionMinSize) {
+                return false;
+            }
+
+            // Check for compatible MIME-TYPE
+            String[] compressibleMimeTypes = this.compressibleMimeTypes;
+            if (compressibleMimeTypes != null &&
+                    !startsWithStringArray(compressibleMimeTypes, 
response.getContentType())) {
+                return false;
+            }
+        }
+
+        // If processing reaches this far, the response might be compressed.
+        // Therefore, set the Vary header to keep proxies happy
+        MessageBytes vary = responseHeaders.getValue("Vary");
+        if (vary == null) {
+            // Add a new Vary header
+            responseHeaders.setValue("Vary").setString("Accept-Encoding");
+        } else if (vary.equals("*")) {
+            // No action required
+        } else {
+            // Merge into current header
+            responseHeaders.setValue("Vary").setString(vary.getString() + 
",Accept-Encoding");
+        }
+
+
+        // Check if browser support gzip encoding
+        MessageBytes acceptEncodingMB = 
request.getMimeHeaders().getValue("accept-encoding");
+        if ((acceptEncodingMB == null) || (acceptEncodingMB.indexOf("gzip") == 
-1)) {
+            return false;
+        }
+
+        // If force mode, the browser checks are skipped
+        if (compressionLevel != 2) {
+            // Check for incompatible Browser
+            Pattern noCompressionUserAgents = this.noCompressionUserAgents;
+            if (noCompressionUserAgents != null) {
+                MessageBytes userAgentValueMB = 
request.getMimeHeaders().getValue("user-agent");
+                if(userAgentValueMB != null) {
+                    String userAgentValue = userAgentValueMB.toString();
+                    if 
(noCompressionUserAgents.matcher(userAgentValue).matches()) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        // All checks have passed. Compression is enabled.
+
+        // Compressed content length is unknown so mark it as such.
+        response.setContentLength(-1);
+        // Configure the content encoding for compressed content
+        responseHeaders.setValue("Content-Encoding").setString("gzip");
+
+        return true;
+    }
+
+
+    /**
+     * Checks if any entry in the string array starts with the specified value
+     *
+     * @param sArray the StringArray
+     * @param value string
+     */
+    private static boolean startsWithStringArray(String sArray[], String 
value) {
+        if (value == null) {
+            return false;
+        }
+        for (int i = 0; i < sArray.length; i++) {
+            if (value.startsWith(sArray[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

Propchange: tomcat/trunk/java/org/apache/coyote/CompressionConfig.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java?rev=1816549&r1=1816548&r2=1816549&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java 
(original)
+++ tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java Tue 
Nov 28 14:22:17 2017
@@ -24,14 +24,16 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.StringTokenizer;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Pattern;
 
 import javax.servlet.http.HttpUpgradeHandler;
 
 import org.apache.coyote.AbstractProtocol;
+import org.apache.coyote.CompressionConfig;
 import org.apache.coyote.Processor;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
 import org.apache.coyote.UpgradeProtocol;
 import org.apache.coyote.UpgradeToken;
 import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
@@ -48,6 +50,8 @@ public abstract class AbstractHttp11Prot
     protected static final StringManager sm =
             StringManager.getManager(AbstractHttp11Protocol.class);
 
+    private final CompressionConfig compressionConfig = new 
CompressionConfig();
+
 
     public AbstractHttp11Protocol(AbstractEndpoint<S,?> endpoint) {
         super(endpoint);
@@ -207,127 +211,49 @@ public abstract class AbstractHttp11Prot
     }
 
 
-    private int compressionLevel = 0;
-    /**
-     * Set compression level.
-     *
-     * @param compression One of <code>on</code>, <code>force</code>,
-     *                    <code>off</code> or the minimum compression size in
-     *                    bytes which implies <code>on</code>
-     */
     public void setCompression(String compression) {
-        if (compression.equals("on")) {
-            this.compressionLevel = 1;
-        } else if (compression.equals("force")) {
-            this.compressionLevel = 2;
-        } else if (compression.equals("off")) {
-            this.compressionLevel = 0;
-        } else {
-            try {
-                // Try to parse compression as an int, which would give the
-                // minimum compression size
-                setCompressionMinSize(Integer.parseInt(compression));
-                this.compressionLevel = 1;
-            } catch (Exception e) {
-                this.compressionLevel = 0;
-            }
-        }
+        compressionConfig.setCompression(compression);
     }
-
-
-    /**
-     * Return compression level.
-     *
-     * @return The current compression level in string form (off/on/force)
-     */
     public String getCompression() {
-        switch (compressionLevel) {
-        case 0:
-            return "off";
-        case 1:
-            return "on";
-        case 2:
-            return "force";
-        }
-        return "off";
+        return compressionConfig.getCompression();
     }
     protected int getCompressionLevel() {
-        return compressionLevel;
+        return compressionConfig.getCompressionLevel();
     }
 
 
-    private Pattern noCompressionUserAgents = null;
-    /**
-     * Obtain the String form of the regular expression that defines the user
-     * agents to not use gzip with.
-     *
-     * @return The regular expression as a String
-     */
     public String getNoCompressionUserAgents() {
-        if (noCompressionUserAgents == null) {
-            return null;
-        } else {
-            return noCompressionUserAgents.toString();
-        }
+        return compressionConfig.getNoCompressionUserAgents();
     }
     protected Pattern getNoCompressionUserAgentsPattern() {
-        return noCompressionUserAgents;
+        return compressionConfig.getNoCompressionUserAgentsPattern();
     }
-    /**
-     * Set no compression user agent pattern. Regular expression as supported
-     * by {@link Pattern}. e.g.: <code>gorilla|desesplorer|tigrus</code>.
-     *
-     * @param noCompressionUserAgents The regular expression for user agent
-     *                                strings for which compression should not
-     *                                be applied
-     */
     public void setNoCompressionUserAgents(String noCompressionUserAgents) {
-        if (noCompressionUserAgents == null || 
noCompressionUserAgents.length() == 0) {
-            this.noCompressionUserAgents = null;
-        } else {
-            this.noCompressionUserAgents =
-                Pattern.compile(noCompressionUserAgents);
-        }
+        compressionConfig.setNoCompressionUserAgents(noCompressionUserAgents);
     }
 
 
-    private String compressibleMimeType = 
"text/html,text/xml,text/plain,text/css," +
-                    
"text/javascript,application/javascript,application/json,application/xml";
-    private String[] compressibleMimeTypes = null;
-    public String getCompressibleMimeType() { return compressibleMimeType; }
+    public String getCompressibleMimeType() {
+        return compressionConfig.getCompressibleMimeType();
+    }
     public void setCompressibleMimeType(String valueS) {
-        compressibleMimeType = valueS;
-        compressibleMimeTypes = null;
+        compressionConfig.setCompressibleMimeType(valueS);
     }
     public String[] getCompressibleMimeTypes() {
-        String[] result = compressibleMimeTypes;
-        if (result != null) {
-            return result;
-        }
-        List<String> values = new ArrayList<>();
-        StringTokenizer tokens = new StringTokenizer(compressibleMimeType, 
",");
-        while (tokens.hasMoreTokens()) {
-            String token = tokens.nextToken().trim();
-            if (token.length() > 0) {
-                values.add(token);
-            }
-        }
-        result = values.toArray(new String[values.size()]);
-        compressibleMimeTypes = result;
-        return result;
+        return compressionConfig.getCompressibleMimeTypes();
     }
 
 
-    private int compressionMinSize = 2048;
-    public int getCompressionMinSize() { return compressionMinSize; }
-    /**
-     * Set Minimum size to trigger compression.
-     *
-     * @param compressionMinSize The minimum content length required for
-     *                           compression in bytes
-     */
+    public int getCompressionMinSize() {
+        return compressionConfig.getCompressionMinSize();
+    }
     public void setCompressionMinSize(int compressionMinSize) {
-        this.compressionMinSize = compressionMinSize;
+        compressionConfig.setCompressionMinSize(compressionMinSize);
+    }
+
+
+    public boolean useCompression(Request request, Response response) {
+        return compressionConfig.useCompression(request, response);
     }
 
 

Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java?rev=1816549&r1=1816548&r2=1816549&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java Tue Nov 28 
14:22:17 2017
@@ -190,90 +190,6 @@ public class Http11Processor extends Abs
 
 
     /**
-     * Checks if any entry in the string array starts with the specified value
-     *
-     * @param sArray the StringArray
-     * @param value string
-     */
-    private static boolean startsWithStringArray(String sArray[], String 
value) {
-        if (value == null) {
-            return false;
-        }
-        for (int i = 0; i < sArray.length; i++) {
-            if (value.startsWith(sArray[i])) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-
-    /**
-     * Check if the resource could be compressed, if the client supports it.
-     */
-    private boolean isCompressible() {
-
-        // Check if content is not already gzipped
-        MessageBytes contentEncodingMB = 
response.getMimeHeaders().getValue("Content-Encoding");
-
-        if ((contentEncodingMB != null) && (contentEncodingMB.indexOf("gzip") 
!= -1)) {
-            return false;
-        }
-
-        // If force mode, always compress (test purposes only)
-        if (protocol.getCompressionLevel() == 2) {
-            return true;
-        }
-
-        // Check if sufficient length to trigger the compression
-        long contentLength = response.getContentLengthLong();
-        if ((contentLength == -1) || (contentLength > 
protocol.getCompressionMinSize())) {
-            // Check for compatible MIME-TYPE
-            String[] compressibleMimeTypes = 
protocol.getCompressibleMimeTypes();
-            if (compressibleMimeTypes != null) {
-                return startsWithStringArray(compressibleMimeTypes, 
response.getContentType());
-            }
-        }
-
-        return false;
-    }
-
-
-    /**
-     * Check if compression should be used for this resource. Already checked
-     * that the resource could be compressed if the client supports it.
-     */
-    private boolean useCompression() {
-
-        // Check if browser support gzip encoding
-        MessageBytes acceptEncodingMB = 
request.getMimeHeaders().getValue("accept-encoding");
-
-        if ((acceptEncodingMB == null) || (acceptEncodingMB.indexOf("gzip") == 
-1)) {
-            return false;
-        }
-
-        // If force mode, always compress (test purposes only)
-        if (protocol.getCompressionLevel() == 2) {
-            return true;
-        }
-
-        // Check for incompatible Browser
-        Pattern noCompressionUserAgents = 
protocol.getNoCompressionUserAgentsPattern();
-        if (noCompressionUserAgents != null) {
-            MessageBytes userAgentValueMB = 
request.getMimeHeaders().getValue("user-agent");
-            if(userAgentValueMB != null) {
-                String userAgentValue = userAgentValueMB.toString();
-                if (noCompressionUserAgents.matcher(userAgentValue).matches()) 
{
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-
-    /**
      * Specialized utility method: find a sequence of lower case bytes inside
      * a ByteChunk.
      */
@@ -919,17 +835,10 @@ public class Http11Processor extends Abs
         }
 
         // Check for compression
-        boolean isCompressible = false;
+
         boolean useCompression = false;
-        if (entityBody && (protocol.getCompressionLevel() > 0) && sendfileData 
== null) {
-            isCompressible = isCompressible();
-            if (isCompressible) {
-                useCompression = useCompression();
-            }
-            // Change content-length to -1 to force chunking
-            if (useCompression) {
-                response.setContentLength(-1);
-            }
+        if (entityBody && sendfileData == null) {
+            useCompression = protocol.useCompression(request, response);
         }
 
         MimeHeaders headers = response.getMimeHeaders();
@@ -972,22 +881,6 @@ public class Http11Processor extends Abs
 
         if (useCompression) {
             outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
-            headers.setValue("Content-Encoding").setString("gzip");
-        }
-        // If it might be compressed, set the Vary header
-        if (isCompressible) {
-            // Make Proxies happy via Vary (from mod_deflate)
-            MessageBytes vary = headers.getValue("Vary");
-            if (vary == null) {
-                // Add a new Vary header
-                headers.setValue("Vary").setString("Accept-Encoding");
-            } else if (vary.equals("*")) {
-                // No action required
-            } else {
-                // Merge into current header
-                headers.setValue("Vary").setString(
-                        vary.getString() + ",Accept-Encoding");
-            }
         }
 
         // Add date header unless application has already set one (e.g. in a



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

Reply via email to