From a8b4927427fc37ee547e50b6cd79a8fd7556ec8b Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 17 Feb 2021 22:19:23 +0100 Subject: [PATCH] Fix #5979 by allowing a configurable etag separator. (#5980) * Fix #5979 by allowing a configurable etag separator. Fix #5979 by allowing a configurable etag separator * updates from review * Updates from review Signed-off-by: Greg Wilkins --- .../jetty/http/CompressedContentFormat.java | 38 +++++++++++++------ .../server/handler/gzip/GzipHandler.java | 1 + .../jetty/servlet/GzipHandlerTest.java | 5 ++- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/CompressedContentFormat.java b/jetty-http/src/main/java/org/eclipse/jetty/http/CompressedContentFormat.java index 399eab97a96..1fccb00ad87 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/CompressedContentFormat.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/CompressedContentFormat.java @@ -18,8 +18,21 @@ package org.eclipse.jetty.http; +import java.util.Objects; + +import org.eclipse.jetty.util.StringUtil; + public class CompressedContentFormat { + /** + * The separator within an etag used to indicate a compressed variant. By default the separator is "--" + * So etag for compressed resource that normally has an etag of W/"28c772d6" + * is W/"28c772d6--gzip". The separator may be changed by the + * "org.eclipse.jetty.http.CompressedContentFormat.ETAG_SEPARATOR" System property. If changed, it should be changed to a string + * that will not be found in a normal etag or at least is very unlikely to be a substring of a normal etag. + */ + public static final String ETAG_SEPARATOR = System.getProperty(CompressedContentFormat.class.getName() + ".ETAG_SEPARATOR", "--"); + public static final CompressedContentFormat GZIP = new CompressedContentFormat("gzip", ".gz"); public static final CompressedContentFormat BR = new CompressedContentFormat("br", ".br"); public static final CompressedContentFormat[] NONE = new CompressedContentFormat[0]; @@ -32,11 +45,11 @@ public class CompressedContentFormat public CompressedContentFormat(String encoding, String extension) { - _encoding = encoding; - _extension = extension; - _etag = "--" + encoding; + _encoding = StringUtil.asciiToLowerCase(encoding); + _extension = StringUtil.asciiToLowerCase(extension); + _etag = ETAG_SEPARATOR + _encoding; _etagQuote = _etag + "\""; - _contentEncoding = new PreEncodedHttpField(HttpHeader.CONTENT_ENCODING, encoding); + _contentEncoding = new PreEncodedHttpField(HttpHeader.CONTENT_ENCODING, _encoding); } @Override @@ -45,12 +58,13 @@ public class CompressedContentFormat if (!(o instanceof CompressedContentFormat)) return false; CompressedContentFormat ccf = (CompressedContentFormat)o; - if (_encoding == null && ccf._encoding != null) - return false; - if (_extension == null && ccf._extension != null) - return false; + return Objects.equals(_encoding, ccf._encoding) && Objects.equals(_extension, ccf._extension); + } - return _encoding.equalsIgnoreCase(ccf._encoding) && _extension.equalsIgnoreCase(ccf._extension); + @Override + public int hashCode() + { + return Objects.hash(_encoding, _extension); } public static boolean tagEquals(String etag, String tag) @@ -58,9 +72,9 @@ public class CompressedContentFormat if (etag.equals(tag)) return true; - int dashdash = tag.indexOf("--"); - if (dashdash > 0 && dashdash == etag.length() - 1) - return etag.regionMatches(0, tag, 0, dashdash); + int separator = tag.lastIndexOf(ETAG_SEPARATOR); + if (separator > 0 && separator == etag.length() - 1) + return etag.regionMatches(0, tag, 0, separator); return false; } } 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 e443d474e73..71aa69421ad 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 @@ -123,6 +123,7 @@ import org.eclipse.jetty.util.log.Logger; * If a ETag is present in the Response headers, and GzipHandler is compressing the * contents, it will add the {@code --gzip} suffix before the Response headers are committed * and sent to the User Agent. + * Note that the suffix used is determined by {@link CompressedContentFormat#ETAG_SEPARATOR} *

*

* This implementation relies on an Jetty internal {@link org.eclipse.jetty.server.HttpOutput.Interceptor} 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 4f92ef36952..ff831315414 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 @@ -44,6 +44,7 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.CompressedContentFormat; import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.server.HttpOutput; import org.eclipse.jetty.server.LocalConnector; @@ -87,7 +88,7 @@ public class GzipHandlerTest private static final String __micro = __content.substring(0, 10); private static final String __contentETag = String.format("W/\"%x\"", __content.hashCode()); - private static final String __contentETagGzip = String.format("W/\"%x--gzip\"", __content.hashCode()); + private static final String __contentETagGzip = String.format("W/\"%x" + CompressedContentFormat.GZIP._etag + "\"", __content.hashCode()); private static final String __icontent = "BEFORE" + __content + "AFTER"; private Server _server; @@ -591,7 +592,7 @@ public class GzipHandlerTest request.setURI("/ctx/content"); request.setVersion("HTTP/1.0"); request.setHeader("Host", "tester"); - request.setHeader("If-Match", "WrongEtag--gzip"); + request.setHeader("If-Match", "WrongEtag" + CompressedContentFormat.GZIP._etag); request.setHeader("accept-encoding", "gzip"); response = HttpTester.parseResponse(_connector.getResponse(request.generate()));