From 8c6ee05ed8d82b3dc8e8dcb9706d0d7cd0b1a466 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 24 Aug 2020 17:11:54 +0200 Subject: [PATCH] Issue #5171 Simplify GzipHandler user-agent handling + Remove User-Agent handling from GzipHandler + Allow Vary header to be set + Create rewrite MsieRule to remove Accept-Encoding from IE<=6 Signed-off-by: Greg Wilkins --- .../org/eclipse/jetty/http/HttpFields.java | 32 +++ .../eclipse/jetty/http/HttpFieldsTest.java | 23 +- .../src/main/config/modules/msie.mod | 13 + .../config/modules/rewrite/rewrite-msie.xml | 10 + .../config/modules/rewrite/rewrite-rules.xml | 16 +- .../jetty/rewrite/handler/MsieRule.java | 111 +++++++ .../jetty/rewrite/handler/MsieSslRule.java | 2 + .../jetty/rewrite/handler/MsieRuleTest.java | 270 ++++++++++++++++++ .../src/main/config/etc/jetty-gzip.xml | 7 - jetty-server/src/main/config/modules/gzip.mod | 9 +- .../server/handler/gzip/GzipHandler.java | 125 ++------ .../gzip/GzipHttpOutputInterceptor.java | 3 +- .../servlet/GzipHandlerBreakEvenSizeTest.java | 1 - .../jetty/servlet/GzipHandlerTest.java | 3 +- 14 files changed, 498 insertions(+), 127 deletions(-) create mode 100644 jetty-rewrite/src/main/config/modules/msie.mod create mode 100644 jetty-rewrite/src/main/config/modules/rewrite/rewrite-msie.xml create mode 100644 jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieRule.java create mode 100644 jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/MsieRuleTest.java diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index 673c8d750204..e1f77255c257 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -738,6 +738,38 @@ public Mutable clear() return this; } + /** Ensure that a multivalue CSV field contains the value + * @param header The header to check + * @param value The value that it must contain. + */ + public void ensure(HttpHeader header, String value) + { + computeField(header, (h,l) -> + { + if (l == null || l.isEmpty()) + return new HttpField(h, value); + + if (l.size() == 1) + { + HttpField f = l.get(0); + return f.contains(value) ? f : new HttpField(h, f.getValue() + ", " + value); + } + StringBuilder v = new StringBuilder(); + boolean hasIt = false; + for (HttpField f : l) + { + if (v.length() > 0) + v.append(", "); + v.append(f.getValue()); + if (f.contains(value)) + hasIt = true; + } + if (!hasIt) + v.append(", ").append(value); + return new HttpField(h, v.toString()); + }); + } + @Override public boolean equals(Object o) { diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java index e70a9322a7ba..9316ddf59598 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java @@ -922,4 +922,25 @@ public void testComputeField() fields.computeField("TEST", (n, f) -> null); assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "After: value")); } -} + + @Test + public void testEnsureValue() + { + HttpFields.Mutable fields = HttpFields.build(); + assertThat(fields.size(), is(0)); + + fields.ensure(HttpHeader.ETAG, "one"); + assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("ETag: one")); + + fields.ensure(HttpHeader.ETAG, "one"); + assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("ETag: one")); + + fields.add(new HttpField(HttpHeader.ETAG, "two")); + fields.ensure(HttpHeader.ETAG, "one"); + assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("ETag: one, two")); + + fields.add(new HttpField(HttpHeader.ETAG, "three")); + fields.ensure(HttpHeader.ETAG, "four"); + assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("ETag: one, two, three, four")); + } +} \ No newline at end of file diff --git a/jetty-rewrite/src/main/config/modules/msie.mod b/jetty-rewrite/src/main/config/modules/msie.mod new file mode 100644 index 000000000000..e5944100ff78 --- /dev/null +++ b/jetty-rewrite/src/main/config/modules/msie.mod @@ -0,0 +1,13 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables the MSIE rewrite rule for MSIE 5 and 6 known bugs. + +[depend] +rewrite + +[files] +basehome:modules/rewrite/rewrite-msie.xml|etc/rewrite-msie.xml + +[xml] +etc/rewrite-msie.xml diff --git a/jetty-rewrite/src/main/config/modules/rewrite/rewrite-msie.xml b/jetty-rewrite/src/main/config/modules/rewrite/rewrite-msie.xml new file mode 100644 index 000000000000..d4b5b3edccb7 --- /dev/null +++ b/jetty-rewrite/src/main/config/modules/rewrite/rewrite-msie.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/jetty-rewrite/src/main/config/modules/rewrite/rewrite-rules.xml b/jetty-rewrite/src/main/config/modules/rewrite/rewrite-rules.xml index 41be9bb71801..b12af8f4da34 100644 --- a/jetty-rewrite/src/main/config/modules/rewrite/rewrite-rules.xml +++ b/jetty-rewrite/src/main/config/modules/rewrite/rewrite-rules.xml @@ -2,14 +2,6 @@ - - + + diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieRule.java new file mode 100644 index 000000000000..09d79cbdc0f1 --- /dev/null +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieRule.java @@ -0,0 +1,111 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.rewrite.handler; + +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpHeaderValue; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.util.ArrayTernaryTrie; +import org.eclipse.jetty.util.Trie; + +/** + * Special handling for MSIE (Microsoft Internet Explorer). + * + */ +public class MsieRule extends MsieSslRule +{ + private static final int IEv5 = '5'; + private static final int IEv6 = '6'; + private static Trie __IE6_BadOS = new ArrayTernaryTrie<>(); + private static HttpField CONNECTION_CLOSE = new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE); + + { + __IE6_BadOS.put("NT 5.01", Boolean.TRUE); + __IE6_BadOS.put("NT 5.0", Boolean.TRUE); + __IE6_BadOS.put("NT 4.0", Boolean.TRUE); + __IE6_BadOS.put("98", Boolean.TRUE); + __IE6_BadOS.put("98; Win 9x 4.90", Boolean.TRUE); + __IE6_BadOS.put("95", Boolean.TRUE); + __IE6_BadOS.put("CE", Boolean.TRUE); + } + + public MsieRule() + { + _handling = false; + _terminating = false; + } + + @Override + public String matchAndApply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException + { + Request baseRequest = Request.getBaseRequest(request); + if (baseRequest == null) + return null; + String userAgent = baseRequest.getHttpFields().get(HttpHeader.USER_AGENT); + + int msie = userAgent.indexOf("MSIE"); + if (msie >= 0) + { + int version = (userAgent.length() - msie > 5) ? userAgent.charAt(msie + 5) : IEv5; + + if (version <= IEv6) + { + HttpFields.Mutable fields = HttpFields.build(baseRequest.getHttpFields()); + + // Don't gzip responses for IE<=6 + fields.remove(HttpHeader.ACCEPT_ENCODING); + + // IE<=6 can't do persistent SSL + if (request.isSecure()) + { + boolean badOs = false; + if (version == IEv6) + { + int windows = userAgent.indexOf("Windows", msie + 5); + if (windows > 0) + { + int end = userAgent.indexOf(')', windows + 8); + badOs = (end < 0 || __IE6_BadOS.get(userAgent, windows + 8, end - windows - 8) != null); + } + } + + if (version <= IEv5 || badOs) + { + fields.remove(HttpHeader.KEEP_ALIVE); + fields.ensure(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); + response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString()); + } + } + baseRequest.setHttpFields(fields); + return target; + } + } + return null; + } + +} diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java index f88e5640b461..fda3c1d3238d 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java @@ -30,7 +30,9 @@ /** * MSIE (Microsoft Internet Explorer) SSL Rule. * Disable keep alive for SSL from IE5 or IE6 on Windows 2000. + * @deprecated use MsieRule */ +@Deprecated public class MsieSslRule extends Rule { private static final int IEv5 = '5'; diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/MsieRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/MsieRuleTest.java new file mode 100644 index 000000000000..2ecbb48a0c84 --- /dev/null +++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/MsieRuleTest.java @@ -0,0 +1,270 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.rewrite.handler; + +import java.util.stream.Collectors; + +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpHeaderValue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class MsieRuleTest extends AbstractRuleTestCase +{ + private MsieRule _rule; + + @BeforeEach + public void init() throws Exception + { + // enable SSL + start(true); + _rule = new MsieRule(); + } + + @Test + public void testWin2kSP1WithIE5() throws Exception + { + HttpFields.Mutable fields = HttpFields.build(_request.getHttpFields()); + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.01)"); + _request.setHttpFields(fields); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.01)"); + _request.setHttpFields(fields); + result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.01)"); + _request.setHttpFields(fields); + result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + } + + @Test + public void testWin2kSP1WithIE6() throws Exception + { + _request.setHttpFields(HttpFields.build(_request.getHttpFields()) + .add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.01)")); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + } + + @Test + public void testWin2kSP1WithIE7() throws Exception + { + _request.setHttpFields(HttpFields.build(_request.getHttpFields()) + .add("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.01)")); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertNull(result); + assertNull(_response.getHeader(HttpHeader.CONNECTION.asString())); + } + + @Test + public void testWin2kWithIE5() throws Exception + { + HttpFields.Mutable fields = HttpFields.build(_request.getHttpFields()); + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0)"); + _request.setHttpFields(fields); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)"); + _request.setHttpFields(fields); + result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)"); + _request.setHttpFields(fields); + result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + } + + @Test + public void testWin2kWithIE6() throws Exception + { + _request.setHttpFields(HttpFields.build(_request.getHttpFields()) + .add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)") + .asImmutable()); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + } + + @Test + public void testWin2kWithIE7() throws Exception + { + _request.setHttpFields(HttpFields.build(_request.getHttpFields()) + .add("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.0)")); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertNull(result); + assertNull(_response.getHeader(HttpHeader.CONNECTION.asString())); + } + + @Test + public void testWinVistaWithIE5() throws Exception + { + HttpFields.Mutable fields = HttpFields.build(_request.getHttpFields()); + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 6.0)"); + _request.setHttpFields(fields); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 6.0)"); + _request.setHttpFields(fields); + result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 6.0)"); + _request.setHttpFields(fields); + result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + } + + @Test + public void testWinVistaWithIE6() throws Exception + { + _request.setHttpFields(HttpFields.build(_request.getHttpFields()) + .add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 6.0)")); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertEquals(_request.getRequestURI(), result); + assertThat(_request.getHttpFields().stream().map(HttpField::toString).collect(Collectors.toList()), + contains("Cookie: set=already", "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 6.0)")); + } + + @Test + public void testWinVistaWithIE7() throws Exception + { + _request.setHttpFields(HttpFields.build(_request.getHttpFields()) + .add("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)")); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertNull(result); + assertNull(_response.getHeader(HttpHeader.CONNECTION.asString())); + } + + @Test + public void testWinXpWithIE5() throws Exception + { + HttpFields.Mutable fields = HttpFields.build(_request.getHttpFields()); + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.1)"); + _request.setHttpFields(fields); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.1)"); + _request.setHttpFields(fields); + result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + + fields.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.1)"); + _request.setHttpFields(fields); + result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + assertEquals(_request.getRequestURI(), result); + assertEquals(HttpHeaderValue.CLOSE.asString(), _response.getHeader(HttpHeader.CONNECTION.asString())); + } + + @Test + public void testWinXpWithIE6() throws Exception + { + _request.setHttpFields(HttpFields.build(_request.getHttpFields()) + .add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)") + .add(HttpHeader.ACCEPT_ENCODING, "gzip")); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertEquals(_request.getRequestURI(), result); + assertThat(_request.getHttpFields().stream().map(HttpField::toString).collect(Collectors.toList()), + contains("Cookie: set=already", "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)")); + } + + @Test + public void testWinXpWithIE7() throws Exception + { + _request.setHttpFields(HttpFields.build(_request.getHttpFields()) + .add("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)") + .add(HttpHeader.ACCEPT_ENCODING, "gzip")); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertNull(result); + assertThat(_request.getHttpFields().stream().map(HttpField::toString).collect(Collectors.toList()), + contains( + "Cookie: set=already", + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)", + "Accept-Encoding: gzip")); + } + + @Test + public void testWithoutSsl() throws Exception + { + // disable SSL + super.stop(); + super.start(false); + + _request.setHttpFields(HttpFields.build(_request.getHttpFields()) + .add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0)") + .add(HttpHeader.ACCEPT_ENCODING, "deflate") + ); + + String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response); + + assertEquals(_request.getRequestURI(), result); + assertThat(_request.getHttpFields().stream().map(HttpField::toString).collect(Collectors.toList()), + contains("Cookie: set=already", "User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0)")); + } +} diff --git a/jetty-server/src/main/config/etc/jetty-gzip.xml b/jetty-server/src/main/config/etc/jetty-gzip.xml index 2ebd183623b3..24ccbbb4b8c8 100644 --- a/jetty-server/src/main/config/etc/jetty-gzip.xml +++ b/jetty-server/src/main/config/etc/jetty-gzip.xml @@ -18,13 +18,6 @@ - - - - - - - diff --git a/jetty-server/src/main/config/modules/gzip.mod b/jetty-server/src/main/config/modules/gzip.mod index 261615eb0c79..50fa8302865d 100644 --- a/jetty-server/src/main/config/modules/gzip.mod +++ b/jetty-server/src/main/config/modules/gzip.mod @@ -1,8 +1,8 @@ # DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html [description] -Enable GzipHandler for dynamic gzip compression -for the entire server. +Enable GzipHandler for dynamic gzip compression for the entire server. +If MSIE prior to version 7 are to be handled, also enable the msie module. [tags] handler @@ -23,9 +23,6 @@ etc/jetty-gzip.xml ## Gzip compression level (-1 for default) # jetty.gzip.compressionLevel=-1 -## User agents for which gzip is disabled -# jetty.gzip.excludedUserAgent=.*MSIE.6\.0.* - ## Inflate request buffer size, or 0 for no request inflation # jetty.gzip.inflateBufferSize=0 @@ -33,7 +30,7 @@ etc/jetty-gzip.xml # jetty.gzip.deflaterPoolCapacity=-1 ## Comma separated list of included methods -# jetty.gzip.includedMethodList=GET +# jetty.gzip.includedMethodList=GET,POST ## Comma separated list of excluded methods # jetty.gzip.excludedMethodList= diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java index 87130148d222..a423300fd1c6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java @@ -43,7 +43,6 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.util.IncludeExclude; -import org.eclipse.jetty.util.RegexSet; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.compression.DeflaterPool; import org.slf4j.Logger; @@ -167,11 +166,10 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory private int _inflateBufferSize = -1; private EnumSet _dispatchers = EnumSet.of(DispatcherType.REQUEST); // non-static, as other GzipHandler instances may have different configurations - private final IncludeExclude _agentPatterns = new IncludeExclude<>(RegexSet.class); private final IncludeExclude _methods = new IncludeExclude<>(); private final IncludeExclude _paths = new IncludeExclude<>(PathSpecSet.class); private final IncludeExclude _mimeTypes = new IncludeExclude<>(); - private HttpField _vary; + private HttpField _vary = GzipHttpOutputInterceptor.VARY_ACCEPT_ENCODING; /** * Instantiates a new GzipHandler. @@ -179,6 +177,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory public GzipHandler() { _methods.include(HttpMethod.GET.asString()); + _methods.include(HttpMethod.POST.asString()); for (String type : MimeTypes.getKnownMimeTypes()) { if ("image/svg+xml".equals(type)) @@ -198,19 +197,29 @@ else if (type.startsWith("image/") || if (LOG.isDebugEnabled()) LOG.debug("{} mime types {}", this, _mimeTypes); + } - _agentPatterns.exclude(".*MSIE 6.0.*"); + /** + * @return The VARY field to use . + */ + public HttpField getVary() + { + return _vary; } /** - * Add excluded to the User-Agent filtering. - * - * @param patterns Regular expressions matching user agents to exclude - * @see #addIncludedAgentPatterns(String...) + * @param vary The VARY field to use. It if is not an instance of {@link PreEncodedHttpField}, + * then it will be converted to one. */ - public void addExcludedAgentPatterns(String... patterns) + public void setVary(HttpField vary) { - _agentPatterns.exclude(patterns); + if (isRunning()) + throw new IllegalStateException(getState()); + + if (vary == null || (vary instanceof PreEncodedHttpField)) + _vary = vary; + else + _vary = new PreEncodedHttpField(vary.getHeader(), vary.getName(), vary.getValue()); } /** @@ -312,17 +321,6 @@ public void addExcludedPaths(String... pathspecs) } } - /** - * Adds included User-Agents for filtering. - * - * @param patterns Regular expressions matching user agents to include - * @see #addExcludedAgentPatterns(String...) - */ - public void addIncludedAgentPatterns(String... patterns) - { - _agentPatterns.include(patterns); - } - /** * Adds included HTTP Methods (eg: POST, PATCH, DELETE) for filtering. * @@ -413,20 +411,12 @@ public void addIncludedPaths(String... pathspecs) protected void doStart() throws Exception { _deflaterPool = newDeflaterPool(poolCapacity); - _vary = (_agentPatterns.size() > 0) ? GzipHttpOutputInterceptor.VARY_ACCEPT_ENCODING_USER_AGENT : GzipHttpOutputInterceptor.VARY_ACCEPT_ENCODING; super.doStart(); } @Override public Deflater getDeflater(Request request, long contentLength) { - String ua = request.getHttpFields().get(HttpHeader.USER_AGENT); - if (ua != null && !isAgentGzipable(ua)) - { - LOG.debug("{} excluded user agent {}", this, request); - return null; - } - if (contentLength >= 0 && contentLength < _minGzipSize) { LOG.debug("{} excluded minGzipSize {}", this, request); @@ -443,18 +433,6 @@ public Deflater getDeflater(Request request, long contentLength) return _deflaterPool.acquire(); } - /** - * Get the current filter list of excluded User-Agent patterns - * - * @return the filter list of excluded User-Agent patterns - * @see #getIncludedAgentPatterns() - */ - public String[] getExcludedAgentPatterns() - { - Set excluded = _agentPatterns.getExcluded(); - return excluded.toArray(new String[excluded.size()]); - } - /** * Get the current filter list of excluded HTTP methods * @@ -464,7 +442,7 @@ public String[] getExcludedAgentPatterns() public String[] getExcludedMethods() { Set excluded = _methods.getExcluded(); - return excluded.toArray(new String[excluded.size()]); + return excluded.toArray(new String[0]); } /** @@ -476,7 +454,7 @@ public String[] getExcludedMethods() public String[] getExcludedMimeTypes() { Set excluded = _mimeTypes.getExcluded(); - return excluded.toArray(new String[excluded.size()]); + return excluded.toArray(new String[0]); } /** @@ -488,19 +466,7 @@ public String[] getExcludedMimeTypes() public String[] getExcludedPaths() { Set excluded = _paths.getExcluded(); - return excluded.toArray(new String[excluded.size()]); - } - - /** - * Get the current filter list of included User-Agent patterns - * - * @return the filter list of included User-Agent patterns - * @see #getExcludedAgentPatterns() - */ - public String[] getIncludedAgentPatterns() - { - Set includes = _agentPatterns.getIncluded(); - return includes.toArray(new String[includes.size()]); + return excluded.toArray(new String[0]); } /** @@ -512,7 +478,7 @@ public String[] getIncludedAgentPatterns() public String[] getIncludedMethods() { Set includes = _methods.getIncluded(); - return includes.toArray(new String[includes.size()]); + return includes.toArray(new String[0]); } /** @@ -524,7 +490,7 @@ public String[] getIncludedMethods() public String[] getIncludedMimeTypes() { Set includes = _mimeTypes.getIncluded(); - return includes.toArray(new String[includes.size()]); + return includes.toArray(new String[0]); } /** @@ -536,7 +502,7 @@ public String[] getIncludedMimeTypes() public String[] getIncludedPaths() { Set includes = _paths.getIncluded(); - return includes.toArray(new String[includes.size()]); + return includes.toArray(new String[0]); } /** @@ -735,20 +701,6 @@ else if (COMMA_GZIP.matcher(field.getValue()).matches()) } } - /** - * Test if the provided User-Agent is allowed based on the User-Agent filters. - * - * @param ua the user agent - * @return whether compressing is allowed for the given user agent - */ - protected boolean isAgentGzipable(String ua) - { - if (ua == null) - return false; - - return _agentPatterns.test(ua); - } - /** * Test if the provided MIME type is allowed based on the MIME type filters. * @@ -781,21 +733,6 @@ public void recycle(Deflater deflater) _deflaterPool.release(deflater); } - /** - * if(isStarted()) - * throw new IllegalStateException(getState()); - * - * Set the excluded filter list of User-Agent patterns (replacing any previously set) - * - * @param patterns Regular expressions list matching user agents to exclude - * @see #setIncludedAgentPatterns(String...) - */ - public void setExcludedAgentPatterns(String... patterns) - { - _agentPatterns.getExcluded().clear(); - addExcludedAgentPatterns(patterns); - } - /** * Set the excluded filter list of HTTP methods (replacing any previously set) * @@ -834,18 +771,6 @@ public void setExcludedPaths(String... pathspecs) _paths.exclude(pathspecs); } - /** - * Set the included filter list of User-Agent patterns (replacing any previously set) - * - * @param patterns Regular expressions matching user agents to include - * @see #setExcludedAgentPatterns(String...) - */ - public void setIncludedAgentPatterns(String... patterns) - { - _agentPatterns.getIncluded().clear(); - addIncludedAgentPatterns(patterns); - } - /** * Set the included filter list of HTTP methods (replacing any previously set) * diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java index d45008076e29..0b47d1688a6a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java @@ -46,7 +46,6 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor public static Logger LOG = LoggerFactory.getLogger(GzipHttpOutputInterceptor.class); private static final byte[] GZIP_HEADER = new byte[]{(byte)0x1f, (byte)0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0}; - public static final HttpField VARY_ACCEPT_ENCODING_USER_AGENT = new PreEncodedHttpField(HttpHeader.VARY, HttpHeader.ACCEPT_ENCODING + ", " + HttpHeader.USER_AGENT); public static final HttpField VARY_ACCEPT_ENCODING = new PreEncodedHttpField(HttpHeader.VARY, HttpHeader.ACCEPT_ENCODING.asString()); private enum GZState @@ -69,7 +68,7 @@ private enum GZState public GzipHttpOutputInterceptor(GzipFactory factory, HttpChannel channel, HttpOutput.Interceptor next, boolean syncFlush) { - this(factory, VARY_ACCEPT_ENCODING_USER_AGENT, channel.getHttpConfiguration().getOutputBufferSize(), channel, next, syncFlush); + this(factory, VARY_ACCEPT_ENCODING, channel.getHttpConfiguration().getOutputBufferSize(), channel, next, syncFlush); } public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, HttpChannel channel, HttpOutput.Interceptor next, boolean syncFlush) diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerBreakEvenSizeTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerBreakEvenSizeTest.java index 2739bbe77d77..5a2b46b56a9c 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerBreakEvenSizeTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerBreakEvenSizeTest.java @@ -59,7 +59,6 @@ public void startServerAndClient() throws Exception server.addConnector(connector); GzipHandler gzipHandler = new GzipHandler(); - gzipHandler.setExcludedAgentPatterns(); gzipHandler.setMinGzipSize(0); ServletContextHandler context = new ServletContextHandler(gzipHandler, "/"); diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java index 95d12675c7ba..efac22d6f068 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java @@ -101,7 +101,6 @@ public void init() throws Exception _server.addConnector(_connector); GzipHandler gzipHandler = new GzipHandler(); - gzipHandler.setExcludedAgentPatterns(); gzipHandler.setMinGzipSize(16); gzipHandler.setInflateBufferSize(4096); @@ -174,7 +173,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse response) throw } @Override - protected void doDelete(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException + protected void doDelete(HttpServletRequest req, HttpServletResponse response) throws IOException { String ifm = req.getHeader("If-Match"); if (ifm != null && ifm.equals(__contentETag))