/*
 *  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.List;
import java.util.StringTokenizer;
import java.util.regex.Pattern;

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;

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.
Params:
  • compression – One of on, force, off or the minimum compression size in bytes which implies on
/** * 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.
Returns:The current compression level in string form (off/on/force)
/** * 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.
Returns:The regular expression as a String
/** * 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 Pattern. e.g.: gorilla|desesplorer|tigrus.
Params:
  • noCompressionUserAgents – The regular expression for user agent strings for which compression should not be applied
/** * 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.
Params:
  • compressionMinSize – The minimum content length required for compression in bytes
/** * 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.
Params:
  • request – The request that triggered the response
  • response – The response to consider compressing
Returns:true if compression was enabled for the given response, otherwise false
/** * 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 && (contentEncodingMB.indexOf("gzip") != -1 || contentEncodingMB.indexOf("br") != -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 = getCompressibleMimeTypes(); 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 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
Params:
  • sArray – the StringArray
  • value – string
/** * 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; } }