This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 8.5.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push: new 2099e4d Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63737 2099e4d is described below commit 2099e4d5a64e018c5dcba48db775e302b55c3aad Author: Mark Thomas <ma...@apache.org> AuthorDate: Tue Sep 10 20:33:14 2019 +0100 Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63737 Correct various issues with parsing the accept-encoding header when looking to see if gzip is supported. - only the first header was examined - xxgzip was treated as indicating support for gzip --- java/org/apache/coyote/CompressionConfig.java | 30 ++++++++- .../tomcat/util/http/parser/AcceptEncoding.java | 75 ++++++++++++++++++++++ test/org/apache/coyote/TestCompressionConfig.java | 67 +++++++++++++++++++ webapps/docs/changelog.xml | 5 ++ 4 files changed, 174 insertions(+), 3 deletions(-) diff --git a/java/org/apache/coyote/CompressionConfig.java b/java/org/apache/coyote/CompressionConfig.java index e921917..fd0652e 100644 --- a/java/org/apache/coyote/CompressionConfig.java +++ b/java/org/apache/coyote/CompressionConfig.java @@ -16,7 +16,10 @@ */ 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; @@ -24,6 +27,7 @@ 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 { @@ -215,9 +219,29 @@ public class CompressionConfig { // Therefore, set the Vary header to keep proxies happy ResponseUtil.addVaryFieldName(responseHeaders, "accept-encoding"); - // Check if browser support gzip encoding - MessageBytes acceptEncodingMB = request.getMimeHeaders().getValue("accept-encoding"); - if ((acceptEncodingMB == null) || (acceptEncodingMB.indexOf("gzip") == -1)) { + // 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; } diff --git a/java/org/apache/tomcat/util/http/parser/AcceptEncoding.java b/java/org/apache/tomcat/util/http/parser/AcceptEncoding.java new file mode 100644 index 0000000..ee145c6 --- /dev/null +++ b/java/org/apache/tomcat/util/http/parser/AcceptEncoding.java @@ -0,0 +1,75 @@ +/* + * 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.tomcat.util.http.parser; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +public class AcceptEncoding { + + private final String encoding; + private final double quality; + + protected AcceptEncoding(String encoding, double quality) { + this.encoding = encoding; + this.quality = quality; + } + + public String getEncoding() { + return encoding; + } + + public double getQuality() { + return quality; + } + + + public static List<AcceptEncoding> parse(StringReader input) throws IOException { + + List<AcceptEncoding> result = new ArrayList<>(); + + do { + String encoding = HttpParser.readToken(input); + if (encoding == null) { + // Invalid encoding, skip to the next one + HttpParser.skipUntil(input, 0, ','); + continue; + } + + if (encoding.length() == 0) { + // No more data to read + break; + } + + // See if a quality has been provided + double quality = 1; + SkipResult lookForSemiColon = HttpParser.skipConstant(input, ";"); + if (lookForSemiColon == SkipResult.FOUND) { + quality = HttpParser.readWeight(input, ','); + } + + if (quality > 0) { + result.add(new AcceptEncoding(encoding, quality)); + } + } while (true); + + return result; + } +} diff --git a/test/org/apache/coyote/TestCompressionConfig.java b/test/org/apache/coyote/TestCompressionConfig.java new file mode 100644 index 0000000..23d9d12 --- /dev/null +++ b/test/org/apache/coyote/TestCompressionConfig.java @@ -0,0 +1,67 @@ +/* + * 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.Collection; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; + +@RunWith(Parameterized.class) +public class TestCompressionConfig { + + @Parameterized.Parameters(name = "{index}: headers[{0}], compress[{1}]") + public static Collection<Object[]> parameters() { + List<Object[]> parameterSets = new ArrayList<>(); + + parameterSets.add(new Object[] { new String[] { }, Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { "gzip" }, Boolean.TRUE }); + parameterSets.add(new Object[] { new String[] { "xgzip" }, Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { "<>gzip" }, Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { "foo", "gzip" }, Boolean.TRUE }); + parameterSets.add(new Object[] { new String[] { "<>", "gzip" }, Boolean.TRUE }); + + return parameterSets; + } + + @Parameter(0) + public String[] headers; + @Parameter(1) + public Boolean compress; + + @Test + public void testUseCompression() throws Exception { + + CompressionConfig compressionConfig = new CompressionConfig(); + // Skip length and MIME type checks + compressionConfig.setCompression("force"); + + Request request = new Request(); + Response response = new Response(); + + for (String header : headers) { + request.getMimeHeaders().addValue("accept-encoding").setString(header); + } + + Assert.assertEquals(compress, Boolean.valueOf(compressionConfig.useCompression(request, response))); + } +} diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index b7f2a10..6cec550 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -77,6 +77,11 @@ connection so that the initial size of the flow control window for the connection is consistent with the increased value. (markt) </fix> + <fix> + <bug>63737</bug>: Correct various issues when parsing the + <code>accept-encoding</code> header to determine if gzip encoding is + supported including only parsing the first header found. (markt) + </fix> </changelog> </subsection> <subsection name="Web applications"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org