From 71fb369dc504ace892cd87a91627705ffea0c834 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) Signed-off-by: Greg Wilkins --- .../jetty/http/CompressedContentFormat.java | 38 +++++++++++++------ .../server/handler/gzip/GzipHandler.java | 1 + .../jetty/servlet/GzipHandlerTest.java | 6 ++- 3 files changed, 31 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 28002b68db59..e72fcf958fd1 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 @@ -13,8 +13,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]; @@ -27,11 +40,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 @@ -40,12 +53,13 @@ public boolean equals(Object o) 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) @@ -53,9 +67,9 @@ public static boolean tagEquals(String etag, String tag) 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 cfae381f10fc..7e508b45d4f4 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 @@ -119,6 +119,7 @@ * 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 1af8cb921e54..ef3850276aa4 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 @@ -26,6 +26,7 @@ import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; +<<<<<<< HEAD import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; import jakarta.servlet.Filter; @@ -39,6 +40,7 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.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; @@ -82,7 +84,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; @@ -585,7 +587,7 @@ public void testDeleteETagGzipHandler() throws Exception 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()));