This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/10.1.x by this push:
new c828eeced8 Add an HTTP configuration setting, noCompressionEncodings
c828eeced8 is described below
commit c828eeced84b0e3db7f0f945d7dc9672105cafb2
Author: Mark Thomas <[email protected]>
AuthorDate: Fri Feb 6 18:55:30 2026 +0000
Add an HTTP configuration setting, noCompressionEncodings
This can be used to control which content encodings will not be
compressed when compression is enabled.
Based on pull request #914 by Long9725.
---
java/org/apache/coyote/CompressionConfig.java | 34 ++++++++++++--
.../coyote/http11/AbstractHttp11Protocol.java | 9 ++++
test/org/apache/coyote/TestCompressionConfig.java | 54 ++++++++++++++--------
.../apache/coyote/http11/TestHttp11Processor.java | 30 +++++++++---
webapps/docs/changelog.xml | 10 ++++
webapps/docs/config/http.xml | 12 +++++
6 files changed, 120 insertions(+), 29 deletions(-)
diff --git a/java/org/apache/coyote/CompressionConfig.java
b/java/org/apache/coyote/CompressionConfig.java
index abe3a0dce6..32827d8243 100644
--- a/java/org/apache/coyote/CompressionConfig.java
+++ b/java/org/apache/coyote/CompressionConfig.java
@@ -19,6 +19,7 @@ package org.apache.coyote;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
@@ -47,6 +48,35 @@ public class CompressionConfig {
"text/javascript,application/javascript,application/json,application/xml";
private String[] compressibleMimeTypes = null;
private int compressionMinSize = 2048;
+ private Set<String> noCompressionEncodings =
+ new HashSet<>(Arrays.asList("br", "compress", "dcb", "dcz",
"deflate", "gzip", "pack200-gzip", "zstd"));
+
+
+ public String getNoCompressionEncodings() {
+ return String.join(",", noCompressionEncodings);
+ }
+
+
+ /**
+ * Set the list of content encodings that indicate already-compressed
content.
+ * When content is already encoded with one of these encodings,
compression will not be applied
+ * to prevent double compression.
+ *
+ * @param encodings Comma-separated list of encoding names (e.g.,
"gzip,br.dflate")
+ */
+ public void setNoCompressionEncodings(String encodings) {
+ Set<String> newEncodings = new HashSet<>();
+ if (encodings != null && !encodings.isEmpty()) {
+ StringTokenizer tokens = new StringTokenizer(encodings, ",");
+ while (tokens.hasMoreTokens()) {
+ String token = tokens.nextToken().trim();
+ if(!token.isEmpty()) {
+ newEncodings.add(token);
+ }
+ }
+ }
+ this.noCompressionEncodings = newEncodings;
+ }
/**
@@ -215,9 +245,7 @@ public class CompressionConfig {
if (tokens.contains("identity")) {
// If identity, do not do content modifications
useContentEncoding = false;
- } else if (tokens.contains("br") || tokens.contains("compress") ||
tokens.contains("dcb") ||
- tokens.contains("dcz") || tokens.contains("deflate") ||
tokens.contains("gzip") ||
- tokens.contains("pack200-gzip") ||
tokens.contains("zstd")) {
+ } else if
(noCompressionEncodings.stream().anyMatch(tokens::contains)) {
// Content should not be compressed twice
return false;
}
diff --git a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
index 02ac4e589e..ba599cd5b4 100644
--- a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
+++ b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
@@ -402,6 +402,15 @@ public abstract class AbstractHttp11Protocol<S> extends
AbstractProtocol<S> {
}
+ public String getNoCompressionEncodings() {
+ return compressionConfig.getNoCompressionEncodings();
+ }
+
+ public void setNoCompressionEncodings(String encodings) {
+ compressionConfig.setNoCompressionEncodings(encodings);
+ }
+
+
public boolean useCompression(Request request, Response response) {
return compressionConfig.useCompression(request, response);
}
diff --git a/test/org/apache/coyote/TestCompressionConfig.java
b/test/org/apache/coyote/TestCompressionConfig.java
index ae16c02f8f..be336ec7e6 100644
--- a/test/org/apache/coyote/TestCompressionConfig.java
+++ b/test/org/apache/coyote/TestCompressionConfig.java
@@ -17,6 +17,7 @@
package org.apache.coyote;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -33,29 +34,30 @@ public class TestCompressionConfig {
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
- parameterSets.add(new Object[] { new String[] { }, null,
Boolean.FALSE, Boolean.FALSE });
- parameterSets.add(new Object[] { new String[] { "gzip" }, null,
Boolean.TRUE, Boolean.FALSE });
- parameterSets.add(new Object[] { new String[] { "xgzip" }, null,
Boolean.FALSE, Boolean.FALSE });
- parameterSets.add(new Object[] { new String[] { "<>gzip" }, null,
Boolean.FALSE, Boolean.FALSE });
- parameterSets.add(new Object[] { new String[] { "foo", "gzip" }, null,
Boolean.TRUE, Boolean.FALSE });
- parameterSets.add(new Object[] { new String[] { "<>", "gzip" }, null,
Boolean.TRUE, Boolean.FALSE });
+ parameterSets.add(new Object[] { new String[] {}, null, Boolean.FALSE,
Boolean.FALSE });
+ parameterSets.add(new Object[] { new String[] { "gzip" }, null,
Boolean.TRUE, Boolean.FALSE });
+ parameterSets.add(new Object[] { new String[] { "xgzip" }, null,
Boolean.FALSE, Boolean.FALSE });
+ parameterSets.add(new Object[] { new String[] { "<>gzip" }, null,
Boolean.FALSE, Boolean.FALSE });
+ parameterSets.add(new Object[] { new String[] { "foo", "gzip" }, null,
Boolean.TRUE, Boolean.FALSE });
+ parameterSets.add(new Object[] { new String[] { "<>", "gzip" }, null,
Boolean.TRUE, Boolean.FALSE });
- parameterSets.add(new Object[] { new String[] { "gzip" }, null,
Boolean.TRUE, Boolean.FALSE });
- parameterSets.add(new Object[] { new String[] { "gzip" }, "W/",
Boolean.TRUE, Boolean.FALSE });
- parameterSets.add(new Object[] { new String[] { "gzip" }, "XX",
Boolean.FALSE, Boolean.FALSE });
+ parameterSets.add(new Object[] { new String[] { "gzip" }, null,
Boolean.TRUE, Boolean.FALSE });
+ parameterSets.add(new Object[] { new String[] { "gzip" }, "W/",
Boolean.TRUE, Boolean.FALSE });
+ parameterSets.add(new Object[] { new String[] { "gzip" }, "XX",
Boolean.FALSE, Boolean.FALSE });
- parameterSets.add(new Object[] { new String[] { }, null,
Boolean.FALSE, Boolean.TRUE });
- parameterSets.add(new Object[] { new String[] { "gzip" }, null,
Boolean.TRUE, Boolean.TRUE });
- parameterSets.add(new Object[] { new String[] { "xgzip" }, null,
Boolean.FALSE, Boolean.TRUE });
- parameterSets.add(new Object[] { new String[] { "<>gzip" }, null,
Boolean.FALSE, Boolean.TRUE });
- parameterSets.add(new Object[] { new String[] { "foo", "gzip" }, null,
Boolean.TRUE, Boolean.TRUE });
- parameterSets.add(new Object[] { new String[] { "<>", "gzip" }, null,
Boolean.TRUE, Boolean.TRUE });
+ parameterSets.add(new Object[] { new String[] {}, null, Boolean.FALSE,
Boolean.TRUE });
+ parameterSets.add(new Object[] { new String[] { "gzip" }, null,
Boolean.TRUE, Boolean.TRUE });
+ parameterSets.add(new Object[] { new String[] { "xgzip" }, null,
Boolean.FALSE, Boolean.TRUE });
+ parameterSets.add(new Object[] { new String[] { "<>gzip" }, null,
Boolean.FALSE, Boolean.TRUE });
+ parameterSets.add(new Object[] { new String[] { "foo", "gzip" }, null,
Boolean.TRUE, Boolean.TRUE });
+ parameterSets.add(new Object[] { new String[] { "<>", "gzip" }, null,
Boolean.TRUE, Boolean.TRUE });
- parameterSets.add(new Object[] { new String[] { "gzip" }, null,
Boolean.TRUE, Boolean.TRUE });
- parameterSets.add(new Object[] { new String[] { "gzip" }, "W/",
Boolean.TRUE, Boolean.TRUE });
- parameterSets.add(new Object[] { new String[] { "gzip" }, "XX",
Boolean.TRUE, Boolean.TRUE });
+ parameterSets.add(new Object[] { new String[] { "gzip" }, null,
Boolean.TRUE, Boolean.TRUE });
+ parameterSets.add(new Object[] { new String[] { "gzip" }, "W/",
Boolean.TRUE, Boolean.TRUE });
+ parameterSets.add(new Object[] { new String[] { "gzip" }, "XX",
Boolean.TRUE, Boolean.TRUE });
- parameterSets.add(new Object[] { new String[] { "foobar;foo=bar,
gzip;bla=\"quoted\"" }, "XX", Boolean.TRUE, Boolean.TRUE });
+ parameterSets.add(new Object[] { new String[] { "foobar;foo=bar,
gzip;bla=\"quoted\"" }, "XX", Boolean.TRUE,
+ Boolean.TRUE });
return parameterSets;
}
@@ -110,4 +112,18 @@ public class TestCompressionConfig {
}
}
}
+
+
+ @Test
+ public void testNoCompressionEncodings() {
+ CompressionConfig config = new CompressionConfig();
+ String encodings = config.getNoCompressionEncodings();
+ Assert.assertTrue(Arrays.asList("br", "compress", "dcb", "dcz",
"deflate", "gzip", "pack200-gzip", "zstd")
+ .stream().anyMatch(encodings::contains));
+
+ config.setNoCompressionEncodings("br");
+ String newEncodings = config.getNoCompressionEncodings();
+ Assert.assertTrue(newEncodings.contains("br"));
+ Assert.assertFalse(newEncodings.contains("gzip"));
+ }
}
diff --git a/test/org/apache/coyote/http11/TestHttp11Processor.java
b/test/org/apache/coyote/http11/TestHttp11Processor.java
index a8faab3f5b..31a3b3f69f 100644
--- a/test/org/apache/coyote/http11/TestHttp11Processor.java
+++ b/test/org/apache/coyote/http11/TestHttp11Processor.java
@@ -32,6 +32,7 @@ import java.net.SocketAddress;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -421,7 +422,7 @@ public class TestHttp11Processor extends TomcatBaseTest {
tomcat.start();
ByteChunk responseBody = new ByteChunk();
- Map<String, List<String>> responseHeaders = new HashMap<>();
+ Map<String,List<String>> responseHeaders = new HashMap<>();
int rc = getUrl("http://localhost:" + getPort() + "/test",
responseBody, responseHeaders);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
@@ -445,7 +446,7 @@ public class TestHttp11Processor extends TomcatBaseTest {
tomcat.start();
ByteChunk responseBody = new ByteChunk();
- Map<String, List<String>> responseHeaders = new HashMap<>();
+ Map<String,List<String>> responseHeaders = new HashMap<>();
int rc = getUrl("http://localhost:" + getPort() + "/test",
responseBody, responseHeaders);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
@@ -854,11 +855,11 @@ public class TestHttp11Processor extends TomcatBaseTest {
tomcat.start();
ByteChunk getBody = new ByteChunk();
- Map<String, List<String>> getHeaders = new HashMap<>();
+ Map<String,List<String>> getHeaders = new HashMap<>();
int getStatus = getUrl("http://localhost:" + getPort() + "/test",
getBody, getHeaders);
ByteChunk headBody = new ByteChunk();
- Map<String, List<String>> headHeaders = new HashMap<>();
+ Map<String,List<String>> headHeaders = new HashMap<>();
int headStatus = getUrl("http://localhost:" + getPort() + "/test",
headBody, headHeaders);
Assert.assertEquals(HttpServletResponse.SC_OK, getStatus);
@@ -997,7 +998,7 @@ public class TestHttp11Processor extends TomcatBaseTest {
tomcat.start();
ByteChunk responseBody = new ByteChunk();
- Map<String, List<String>> responseHeaders = new HashMap<>();
+ Map<String,List<String>> responseHeaders = new HashMap<>();
int rc = getUrl("http://localhost:" + getPort() + "/test",
responseBody, responseHeaders);
Assert.assertEquals(HttpServletResponse.SC_RESET_CONTENT, rc);
@@ -2149,7 +2150,6 @@ public class TestHttp11Processor extends TomcatBaseTest {
}
-
private static class EarlyHintsServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@@ -2165,6 +2165,7 @@ public class TestHttp11Processor extends TomcatBaseTest {
this.useSendError = useSendError;
this.errorString = errorString;
}
+
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.addHeader("Link", "</style.css>; rel=preload; as=style");
@@ -2185,4 +2186,19 @@ public class TestHttp11Processor extends TomcatBaseTest {
resp.getWriter().write("OK");
}
}
-}
+
+
+ @Test
+ public void testNoCompressionEncodings() {
+ Http11NioProtocol protocol = new Http11NioProtocol();
+ String encodings = protocol.getNoCompressionEncodings();
+ Assert.assertTrue(Arrays.asList("br", "compress", "dcb", "dcz",
"deflate", "gzip", "pack200-gzip", "zstd")
+ .stream().anyMatch(encodings::contains));
+
+ protocol.setNoCompressionEncodings("br");
+
+ String newEncodings = protocol.getNoCompressionEncodings();
+ Assert.assertTrue(newEncodings.contains("br"));
+ Assert.assertFalse(newEncodings.contains("gzip"));
+ }
+}
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 57dd59ed8d..4ad4eacf28 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -105,6 +105,16 @@
issues do not "pop up" wrt. others).
-->
<section name="Tomcat 10.1.53 (schultz)" rtext="in development">
+ <subsection name="Coyote">
+ <changelog>
+ <add>
+ Add an HTTP configuration setting, <code>noCompressionEncodings</code>,
+ that can be used to control which content encodings will not be
+ compressed when compression is enabled. Based on pull request
+ <pr>914</pr> by Long9725. (markt)
+ </add>
+ </changelog>
+ </subsection>
<subsection name="Jasper">
<changelog>
<fix>
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index dcbc076034..b43915c4dc 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -674,6 +674,18 @@
used.</p>
</attribute>
+ <attribute name="noCompressionEncodings" requried="false">
+ <p>A comma-separated list of content encodings that indicate
+ already-compressed content. When the response already has a
+ <code>Content-Encoding</code> header with one of these values,
compression
+ will not be applied to prevent double compression. This attribute is only
+ used if <strong>compression</strong> is set to <code>on</code> or
+ <code>force</code>.</p>
+ <p>If not specified, the default values is
+ <code>br,compress,dcb,dcz,deflate,gzip,pack2000-gzip,zstd</code>, which
+ includes all commonly used compression algorithms.</p>
+ </attribute>
+
<attribute name="noCompressionUserAgents" required="false">
<p>The value is a regular expression (using <code>java.util.regex</code>)
matching the <code>user-agent</code> header of HTTP clients for which
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]