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: [email protected]
For additional commands, e-mail: [email protected]