init
This commit is contained in:
351
java/org/apache/coyote/CompressionConfig.java
Normal file
351
java/org/apache/coyote/CompressionConfig.java
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* 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.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," +
|
||||
"text/javascript,application/javascript,application/json,application/xml";
|
||||
private String[] compressibleMimeTypes = null;
|
||||
private int compressionMinSize = 2048;
|
||||
private boolean noCompressionStrongETag = true;
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if compression is disabled if the resource has a strong ETag.
|
||||
*
|
||||
* @return {@code true} if compression is disabled, otherwise {@code false}
|
||||
*
|
||||
* @deprecated Will be removed in Tomcat 10 where it will be hard-coded to
|
||||
* {@code true}
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean getNoCompressionStrongETag() {
|
||||
return noCompressionStrongETag;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set whether compression is disabled for resources with a strong ETag.
|
||||
*
|
||||
* @param noCompressionStrongETag {@code true} if compression is disabled,
|
||||
* otherwise {@code false}
|
||||
*
|
||||
* @deprecated Will be removed in Tomcat 10 where it will be hard-coded to
|
||||
* {@code true}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setNoCompressionStrongETag(boolean noCompressionStrongETag) {
|
||||
this.noCompressionStrongETag = noCompressionStrongETag;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 compressed
|
||||
MessageBytes contentEncodingMB = responseHeaders.getValue("Content-Encoding");
|
||||
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
|
||||
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 = getCompressibleMimeTypes();
|
||||
if (compressibleMimeTypes != null &&
|
||||
!startsWithStringArray(compressibleMimeTypes, response.getContentType())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the resource has a strong ETag
|
||||
if (noCompressionStrongETag) {
|
||||
String eTag = responseHeaders.getHeader("ETag");
|
||||
if (eTag != null && !eTag.trim().startsWith("W/")) {
|
||||
// Has an ETag that doesn't start with "W/..." so it must be a
|
||||
// strong ETag
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If processing reaches this far, the response might be compressed.
|
||||
// Therefore, set the Vary header to keep proxies happy
|
||||
ResponseUtil.addVaryFieldName(responseHeaders, "accept-encoding");
|
||||
|
||||
// Check if user-agent supports gzip encoding
|
||||
// Only interested in whether gzip encoding is supported. Other
|
||||
// encodings and weights can be ignored.
|
||||
Enumeration<String> headerValues = request.getMimeHeaders().values("accept-encoding");
|
||||
boolean foundGzip = false;
|
||||
while (!foundGzip && headerValues.hasMoreElements()) {
|
||||
List<AcceptEncoding> acceptEncodings = null;
|
||||
try {
|
||||
acceptEncodings = AcceptEncoding.parse(new StringReader(headerValues.nextElement()));
|
||||
} catch (IOException ioe) {
|
||||
// If there is a problem reading the header, disable compression
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AcceptEncoding acceptEncoding : acceptEncodings) {
|
||||
if ("gzip".equalsIgnoreCase(acceptEncoding.getEncoding())) {
|
||||
foundGzip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundGzip) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user