Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x
Signed-off-by: gregw <gregw@webtide.com>
This commit is contained in:
commit
bb5fefdaeb
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.http;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
||||
public class CompressedContentFormat
|
||||
|
@ -32,18 +33,18 @@ public class CompressedContentFormat
|
|||
public static final CompressedContentFormat BR = new CompressedContentFormat("br", ".br");
|
||||
public static final CompressedContentFormat[] NONE = new CompressedContentFormat[0];
|
||||
|
||||
public final String _encoding;
|
||||
public final String _extension;
|
||||
public final String _etag;
|
||||
public final String _etagQuote;
|
||||
public final PreEncodedHttpField _contentEncoding;
|
||||
private final String _encoding;
|
||||
private final String _extension;
|
||||
private final String _etagSuffix;
|
||||
private final String _etagSuffixQuote;
|
||||
private final PreEncodedHttpField _contentEncoding;
|
||||
|
||||
public CompressedContentFormat(String encoding, String extension)
|
||||
{
|
||||
_encoding = StringUtil.asciiToLowerCase(encoding);
|
||||
_extension = StringUtil.asciiToLowerCase(extension);
|
||||
_etag = ETAG_SEPARATOR + _encoding;
|
||||
_etagQuote = _etag + "\"";
|
||||
_etagSuffix = StringUtil.isEmpty(ETAG_SEPARATOR) ? "" : (ETAG_SEPARATOR + _encoding);
|
||||
_etagSuffixQuote = _etagSuffix + "\"";
|
||||
_contentEncoding = new PreEncodedHttpField(HttpHeader.CONTENT_ENCODING, _encoding);
|
||||
}
|
||||
|
||||
|
@ -56,20 +57,104 @@ public class CompressedContentFormat
|
|||
return Objects.equals(_encoding, ccf._encoding) && Objects.equals(_extension, ccf._extension);
|
||||
}
|
||||
|
||||
public String getEncoding()
|
||||
{
|
||||
return _encoding;
|
||||
}
|
||||
|
||||
public String getExtension()
|
||||
{
|
||||
return _extension;
|
||||
}
|
||||
|
||||
public String getEtagSuffix()
|
||||
{
|
||||
return _etagSuffix;
|
||||
}
|
||||
|
||||
public HttpField getContentEncoding()
|
||||
{
|
||||
return _contentEncoding;
|
||||
}
|
||||
|
||||
/** Get an etag with suffix that represents this compressed type.
|
||||
* @param etag An etag
|
||||
* @return An etag with compression suffix, or the etag itself if no suffix is configured.
|
||||
*/
|
||||
public String etag(String etag)
|
||||
{
|
||||
if (StringUtil.isEmpty(ETAG_SEPARATOR))
|
||||
return etag;
|
||||
int end = etag.length() - 1;
|
||||
if (etag.charAt(end) == '"')
|
||||
return etag.substring(0, end) + _etagSuffixQuote;
|
||||
return etag + _etagSuffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(_encoding, _extension);
|
||||
}
|
||||
|
||||
public static boolean tagEquals(String etag, String tag)
|
||||
/** Check etags for equality, accounting for quoting and compression suffixes.
|
||||
* @param etag An etag without a compression suffix
|
||||
* @param etagWithSuffix An etag optionally with a compression suffix.
|
||||
* @return True if the tags are equal.
|
||||
*/
|
||||
public static boolean tagEquals(String etag, String etagWithSuffix)
|
||||
{
|
||||
if (etag.equals(tag))
|
||||
// Handle simple equality
|
||||
if (etag.equals(etagWithSuffix))
|
||||
return true;
|
||||
|
||||
int separator = tag.lastIndexOf(ETAG_SEPARATOR);
|
||||
if (separator > 0 && separator == etag.length() - 1)
|
||||
return etag.regionMatches(0, tag, 0, separator);
|
||||
return false;
|
||||
// If no separator defined, then simple equality is only possible positive
|
||||
if (StringUtil.isEmpty(ETAG_SEPARATOR))
|
||||
return false;
|
||||
|
||||
// Are both tags quoted?
|
||||
boolean etagQuoted = etag.endsWith("\"");
|
||||
boolean etagSuffixQuoted = etagWithSuffix.endsWith("\"");
|
||||
|
||||
// Look for a separator
|
||||
int separator = etagWithSuffix.lastIndexOf(ETAG_SEPARATOR);
|
||||
|
||||
// If both tags are quoted the same (the norm) then any difference must be the suffix
|
||||
if (etagQuoted == etagSuffixQuoted)
|
||||
return separator > 0 && etag.regionMatches(0, etagWithSuffix, 0, separator);
|
||||
|
||||
// If either tag is weak then we can't match because weak tags must be quoted
|
||||
if (etagWithSuffix.startsWith("W/") || etag.startsWith("W/"))
|
||||
return false;
|
||||
|
||||
// compare unquoted strong etags
|
||||
etag = etagQuoted ? QuotedStringTokenizer.unquote(etag) : etag;
|
||||
etagWithSuffix = etagSuffixQuoted ? QuotedStringTokenizer.unquote(etagWithSuffix) : etagWithSuffix;
|
||||
separator = etagWithSuffix.lastIndexOf(ETAG_SEPARATOR);
|
||||
if (separator > 0)
|
||||
return etag.regionMatches(0, etagWithSuffix, 0, separator);
|
||||
|
||||
return Objects.equals(etag, etagWithSuffix);
|
||||
}
|
||||
|
||||
public String stripSuffixes(String etagsList)
|
||||
{
|
||||
if (StringUtil.isEmpty(ETAG_SEPARATOR))
|
||||
return etagsList;
|
||||
|
||||
// This is a poor implementation that ignores list and tag structure
|
||||
while (true)
|
||||
{
|
||||
int i = etagsList.lastIndexOf(_etagSuffix);
|
||||
if (i < 0)
|
||||
return etagsList;
|
||||
etagsList = etagsList.substring(0, i) + etagsList.substring(i + _etagSuffix.length());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return _encoding;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ public class PrecompressedHttpContent implements HttpContent
|
|||
@Override
|
||||
public String getETagValue()
|
||||
{
|
||||
return _content.getResource().getWeakETag(_format._etag);
|
||||
return _content.getResource().getWeakETag(_format.getEtagSuffix());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,13 +96,13 @@ public class PrecompressedHttpContent implements HttpContent
|
|||
@Override
|
||||
public HttpField getContentEncoding()
|
||||
{
|
||||
return _format._contentEncoding;
|
||||
return _format.getContentEncoding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentEncodingValue()
|
||||
{
|
||||
return _format._contentEncoding.getValue();
|
||||
return _format.getContentEncoding().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -162,7 +162,9 @@ public class PrecompressedHttpContent implements HttpContent
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("PrecompressedHttpContent@%x{e=%s,r=%s|%s,lm=%s|%s,ct=%s}", hashCode(), _format._encoding,
|
||||
return String.format("%s@%x{e=%s,r=%s|%s,lm=%s|%s,ct=%s}",
|
||||
this.getClass().getSimpleName(), hashCode(),
|
||||
_format,
|
||||
_content.getResource(), _precompressedContent.getResource(),
|
||||
_content.getResource().lastModified(), _precompressedContent.getResource().lastModified(),
|
||||
getContentType());
|
||||
|
|
|
@ -30,6 +30,8 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.eclipse.jetty.http.CompressedContentFormat.BR;
|
||||
import static org.eclipse.jetty.http.CompressedContentFormat.GZIP;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -75,9 +77,25 @@ public class GZIPContentDecoderTest
|
|||
{
|
||||
assertTrue(CompressedContentFormat.tagEquals("tag", "tag"));
|
||||
assertTrue(CompressedContentFormat.tagEquals("\"tag\"", "\"tag\""));
|
||||
assertTrue(CompressedContentFormat.tagEquals("\"tag\"", "\"tag--gzip\""));
|
||||
assertFalse(CompressedContentFormat.tagEquals("Zag", "Xag--gzip"));
|
||||
assertTrue(CompressedContentFormat.tagEquals("\"tag\"", "\"tag" + GZIP.getEtagSuffix() + "\""));
|
||||
assertTrue(CompressedContentFormat.tagEquals("\"tag\"", "\"tag" + BR.getEtagSuffix() + "\""));
|
||||
assertTrue(CompressedContentFormat.tagEquals("W/\"1234567\"", "W/\"1234567\""));
|
||||
assertTrue(CompressedContentFormat.tagEquals("W/\"1234567\"", "W/\"1234567" + GZIP.getEtagSuffix() + "\""));
|
||||
|
||||
assertFalse(CompressedContentFormat.tagEquals("Zag", "Xag" + GZIP.getEtagSuffix()));
|
||||
assertFalse(CompressedContentFormat.tagEquals("xtag", "tag"));
|
||||
assertFalse(CompressedContentFormat.tagEquals("W/\"1234567\"", "W/\"1234111\""));
|
||||
assertFalse(CompressedContentFormat.tagEquals("W/\"1234567\"", "W/\"1234111" + GZIP.getEtagSuffix() + "\""));
|
||||
|
||||
assertTrue(CompressedContentFormat.tagEquals("12345", "\"12345\""));
|
||||
assertTrue(CompressedContentFormat.tagEquals("\"12345\"", "12345"));
|
||||
assertTrue(CompressedContentFormat.tagEquals("12345", "\"12345" + GZIP.getEtagSuffix() + "\""));
|
||||
assertTrue(CompressedContentFormat.tagEquals("\"12345\"", "12345" + GZIP.getEtagSuffix()));
|
||||
|
||||
assertThat(GZIP.stripSuffixes("12345"), is("12345"));
|
||||
assertThat(GZIP.stripSuffixes("12345, 666" + GZIP.getEtagSuffix()), is("12345, 666"));
|
||||
assertThat(GZIP.stripSuffixes("12345, 666" + GZIP.getEtagSuffix() + ",W/\"9999" + GZIP.getEtagSuffix() + "\""),
|
||||
is("12345, 666,W/\"9999\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.rewrite.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
|
||||
public class ForceRequestHeaderValueRule extends Rule
|
||||
{
|
||||
private String headerName;
|
||||
private String forcedValue;
|
||||
|
||||
public String getHeaderName()
|
||||
{
|
||||
return headerName;
|
||||
}
|
||||
|
||||
public void setHeaderName(String headerName)
|
||||
{
|
||||
this.headerName = headerName;
|
||||
}
|
||||
|
||||
public String getForcedValue()
|
||||
{
|
||||
return forcedValue;
|
||||
}
|
||||
|
||||
public void setForcedValue(String forcedValue)
|
||||
{
|
||||
this.forcedValue = forcedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String matchAndApply(String target, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException
|
||||
{
|
||||
String existingValue = httpServletRequest.getHeader(headerName);
|
||||
if (existingValue == null)
|
||||
{
|
||||
// no hit, skip this rule.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (existingValue.equals(forcedValue))
|
||||
{
|
||||
// already what we expect, skip this rule.
|
||||
return null;
|
||||
}
|
||||
|
||||
Request baseRequest = Request.getBaseRequest(httpServletRequest);
|
||||
baseRequest.getHttpFields().remove(headerName);
|
||||
baseRequest.getHttpFields().add(headerName, forcedValue);
|
||||
return target;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.rewrite.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Collections;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerList;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ForceRequestHeaderValueRuleTest
|
||||
{
|
||||
private Server server;
|
||||
private LocalConnector connector;
|
||||
private ForceRequestHeaderValueRule rule;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = new LocalConnector(server);
|
||||
server.addConnector(connector);
|
||||
|
||||
HandlerList handlers = new HandlerList();
|
||||
|
||||
RewriteHandler rewriteHandler = new RewriteHandler();
|
||||
rule = new ForceRequestHeaderValueRule();
|
||||
rewriteHandler.addRule(rule);
|
||||
|
||||
Handler handler = new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setContentType("text/plain");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
OutputStream stream = response.getOutputStream();
|
||||
OutputStreamWriter out = new OutputStreamWriter(stream);
|
||||
out.append("Echo\n");
|
||||
for (String headerName : Collections.list(request.getHeaderNames()))
|
||||
{
|
||||
// Combine all values for header into single output on response body
|
||||
out.append("Request Header[").append(headerName).append("]: [")
|
||||
.append(request.getHeader(headerName)).append("]\n");
|
||||
}
|
||||
out.flush();
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
};
|
||||
|
||||
handlers.addHandler(rewriteHandler);
|
||||
handlers.addHandler(handler);
|
||||
server.setHandler(handlers);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void teardown()
|
||||
{
|
||||
LifeCycle.stop(server);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalRequest() throws Exception
|
||||
{
|
||||
rule.setHeaderName("Accept");
|
||||
rule.setForcedValue("*/*");
|
||||
|
||||
StringBuilder request = new StringBuilder();
|
||||
request.append("GET /echo/foo HTTP/1.1\r\n");
|
||||
request.append("Host: local\r\n");
|
||||
request.append("Connection: closed\r\n");
|
||||
request.append("\r\n");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(request.toString()));
|
||||
assertEquals(200, response.getStatus());
|
||||
assertThat(response.getContent(), not(containsString("[Accept]")));
|
||||
assertThat(response.getContent(), containsString("[Host]: [local]"));
|
||||
assertThat(response.getContent(), containsString("[Connection]: [closed]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneAcceptHeaderRequest() throws Exception
|
||||
{
|
||||
rule.setHeaderName("Accept");
|
||||
rule.setForcedValue("*/*");
|
||||
|
||||
StringBuilder request = new StringBuilder();
|
||||
request.append("GET /echo/foo HTTP/1.1\r\n");
|
||||
request.append("Host: local\r\n");
|
||||
request.append("Accept: */*\r\n");
|
||||
request.append("Connection: closed\r\n");
|
||||
request.append("\r\n");
|
||||
|
||||
String rawResponse = connector.getResponse(request.toString());
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
assertEquals(200, response.getStatus());
|
||||
assertThat(response.getContent(), containsString("[Accept]: [*/*]"));
|
||||
assertThat(response.getContent(), containsString("[Host]: [local]"));
|
||||
assertThat(response.getContent(), containsString("[Connection]: [closed]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThreeAcceptHeadersRequest() throws Exception
|
||||
{
|
||||
rule.setHeaderName("Accept");
|
||||
rule.setForcedValue("*/*");
|
||||
|
||||
StringBuilder request = new StringBuilder();
|
||||
request.append("GET /echo/foo HTTP/1.1\r\n");
|
||||
request.append("Host: local\r\n");
|
||||
request.append("Accept: images/jpeg\r\n");
|
||||
request.append("Accept: text/plain\r\n");
|
||||
request.append("Accept: */*\r\n");
|
||||
request.append("Connection: closed\r\n");
|
||||
request.append("\r\n");
|
||||
|
||||
String rawResponse = connector.getResponse(request.toString());
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
assertEquals(200, response.getStatus());
|
||||
assertThat(response.getContent(), containsString("[Accept]: [*/*]"));
|
||||
assertThat(response.getContent(), containsString("[Host]: [local]"));
|
||||
assertThat(response.getContent(), containsString("[Connection]: [closed]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterleavedAcceptHeadersRequest() throws Exception
|
||||
{
|
||||
rule.setHeaderName("Accept");
|
||||
rule.setForcedValue("*/*");
|
||||
|
||||
StringBuilder request = new StringBuilder();
|
||||
request.append("GET /echo/foo HTTP/1.1\r\n");
|
||||
request.append("Host: local\r\n");
|
||||
request.append("Accept: images/jpeg\r\n"); // not value intended to be forced
|
||||
request.append("Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0\r\n");
|
||||
request.append("accept: text/plain\r\n"); // interleaved with other headers shouldn't matter
|
||||
request.append("Accept-Charset: iso-8859-5, unicode-1-1;q=0.8\r\n");
|
||||
request.append("ACCEPT: */*\r\n"); // case shouldn't matter
|
||||
request.append("Connection: closed\r\n");
|
||||
request.append("\r\n");
|
||||
|
||||
String rawResponse = connector.getResponse(request.toString());
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
assertEquals(200, response.getStatus());
|
||||
assertThat(response.getContent(), containsString("[Accept]: [*/*]"));
|
||||
assertThat(response.getContent(), containsString("[Accept-Charset]: [iso-8859-5, unicode-1-1;q=0.8]"));
|
||||
assertThat(response.getContent(), containsString("[Accept-Encoding]: [gzip;q=1.0, identity; q=0.5, *;q=0]"));
|
||||
assertThat(response.getContent(), containsString("[Host]: [local]"));
|
||||
assertThat(response.getContent(), containsString("[Connection]: [closed]"));
|
||||
}
|
||||
}
|
|
@ -224,7 +224,7 @@ public class CachedContentFactory implements HttpContent.ContentFactory
|
|||
Map<CompressedContentFormat, CachedHttpContent> precompresssedContents = new HashMap<>(_precompressedFormats.length);
|
||||
for (CompressedContentFormat format : _precompressedFormats)
|
||||
{
|
||||
String compressedPathInContext = pathInContext + format._extension;
|
||||
String compressedPathInContext = pathInContext + format.getExtension();
|
||||
CachedHttpContent compressedContent = _cache.get(compressedPathInContext);
|
||||
if (compressedContent == null || compressedContent.isValid())
|
||||
{
|
||||
|
@ -269,7 +269,7 @@ public class CachedContentFactory implements HttpContent.ContentFactory
|
|||
Map<CompressedContentFormat, HttpContent> compressedContents = new HashMap<>();
|
||||
for (CompressedContentFormat format : _precompressedFormats)
|
||||
{
|
||||
String compressedPathInContext = pathInContext + format._extension;
|
||||
String compressedPathInContext = pathInContext + format.getExtension();
|
||||
CachedHttpContent compressedContent = _cache.get(compressedPathInContext);
|
||||
if (compressedContent != null && compressedContent.isValid() && compressedContent.getResource().lastModified() >= resource.lastModified())
|
||||
compressedContents.put(format, compressedContent);
|
||||
|
@ -682,7 +682,7 @@ public class CachedContentFactory implements HttpContent.ContentFactory
|
|||
_content = content;
|
||||
_precompressedContent = precompressedContent;
|
||||
|
||||
_etag = (CachedContentFactory.this._etags) ? new PreEncodedHttpField(HttpHeader.ETAG, _content.getResource().getWeakETag(format._etag)) : null;
|
||||
_etag = (CachedContentFactory.this._etags) ? new PreEncodedHttpField(HttpHeader.ETAG, _content.getResource().getWeakETag(format.getEtagSuffix())) : null;
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
|
|
|
@ -85,7 +85,7 @@ public class ResourceContentFactory implements ContentFactory
|
|||
Map<CompressedContentFormat, HttpContent> compressedContents = new HashMap<>(_precompressedFormats.length);
|
||||
for (CompressedContentFormat format : _precompressedFormats)
|
||||
{
|
||||
String compressedPathInContext = pathInContext + format._extension;
|
||||
String compressedPathInContext = pathInContext + format.getExtension();
|
||||
Resource compressedResource = _factory.getResource(compressedPathInContext);
|
||||
if (compressedResource != null && compressedResource.exists() && compressedResource.lastModified() >= resource.lastModified() &&
|
||||
compressedResource.length() < resource.length())
|
||||
|
|
|
@ -138,7 +138,7 @@ public class ResourceService
|
|||
public void setPrecompressedFormats(CompressedContentFormat[] precompressedFormats)
|
||||
{
|
||||
_precompressedFormats = precompressedFormats;
|
||||
_preferredEncodingOrder = stream(_precompressedFormats).map(f -> f._encoding).toArray(String[]::new);
|
||||
_preferredEncodingOrder = stream(_precompressedFormats).map(f -> f.getEncoding()).toArray(String[]::new);
|
||||
}
|
||||
|
||||
public void setEncodingCacheSize(int encodingCacheSize)
|
||||
|
@ -279,7 +279,7 @@ public class ResourceService
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("precompressed={}", precompressedContent);
|
||||
content = precompressedContent;
|
||||
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), precompressedContentEncoding._encoding);
|
||||
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), precompressedContentEncoding.getEncoding());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +352,7 @@ public class ResourceService
|
|||
{
|
||||
for (CompressedContentFormat format : availableFormats)
|
||||
{
|
||||
if (format._encoding.equals(encoding))
|
||||
if (format.getEncoding().equals(encoding))
|
||||
return format;
|
||||
}
|
||||
|
||||
|
@ -526,9 +526,9 @@ public class ResourceService
|
|||
if (etag != null)
|
||||
{
|
||||
QuotedCSV quoted = new QuotedCSV(true, ifm);
|
||||
for (String tag : quoted)
|
||||
for (String etagWithSuffix : quoted)
|
||||
{
|
||||
if (CompressedContentFormat.tagEquals(etag, tag))
|
||||
if (CompressedContentFormat.tagEquals(etag, etagWithSuffix))
|
||||
{
|
||||
match = true;
|
||||
break;
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.eclipse.jetty.http.CompressedContentFormat;
|
|||
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.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
|
@ -150,13 +149,13 @@ import org.slf4j.LoggerFactory;
|
|||
public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||
{
|
||||
public static final EnumSet<HttpHeader> ETAG_HEADERS = EnumSet.of(HttpHeader.IF_MATCH, HttpHeader.IF_NONE_MATCH);
|
||||
public static final String GZIP_HANDLER_ETAGS = "o.e.j.s.h.gzip.GzipHandler.etag";
|
||||
public static final String GZIP = "gzip";
|
||||
public static final String DEFLATE = "deflate";
|
||||
public static final int DEFAULT_MIN_GZIP_SIZE = 32;
|
||||
public static final int BREAK_EVEN_GZIP_SIZE = 23;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GzipHandler.class);
|
||||
private static final HttpField X_CE_GZIP = new PreEncodedHttpField("X-Content-Encoding", "gzip");
|
||||
private static final HttpField TE_CHUNKED = new PreEncodedHttpField(HttpHeader.TRANSFER_ENCODING, HttpHeaderValue.CHUNKED.asString());
|
||||
private static final Pattern COMMA_GZIP = Pattern.compile(".*, *gzip");
|
||||
|
||||
private InflaterPool _inflaterPool;
|
||||
|
@ -600,23 +599,17 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
|||
case IF_MATCH:
|
||||
case IF_NONE_MATCH:
|
||||
{
|
||||
String etag = field.getValue();
|
||||
int i = etag.indexOf(CompressedContentFormat.GZIP._etagQuote);
|
||||
if (i <= 0 || alreadyGzipped)
|
||||
String etags = field.getValue();
|
||||
String etagsNoSuffix = CompressedContentFormat.GZIP.stripSuffixes(etags);
|
||||
if (etagsNoSuffix.equals(etags))
|
||||
newFields.add(field);
|
||||
else
|
||||
{
|
||||
baseRequest.setAttribute("o.e.j.s.h.gzip.GzipHandler.etag", etag);
|
||||
while (i >= 0)
|
||||
{
|
||||
etag = etag.substring(0, i) + etag.substring(i + CompressedContentFormat.GZIP._etag.length());
|
||||
i = etag.indexOf(CompressedContentFormat.GZIP._etagQuote, i);
|
||||
}
|
||||
newFields.add(new HttpField(field.getHeader(), etag));
|
||||
newFields.add(new HttpField(field.getHeader(), etagsNoSuffix));
|
||||
baseRequest.setAttribute(GZIP_HANDLER_ETAGS, etags);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CONTENT_LENGTH:
|
||||
newFields.add(inflated ? new HttpField("X-Content-Length", field.getValue()) : field);
|
||||
break;
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.zip.Deflater;
|
|||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
|
@ -139,9 +140,9 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
|||
LOG.debug("{} exclude by status {}", this, sc);
|
||||
noCompression();
|
||||
|
||||
if (sc == 304)
|
||||
if (sc == HttpStatus.NOT_MODIFIED_304)
|
||||
{
|
||||
String requestEtags = (String)_channel.getRequest().getAttribute("o.e.j.s.h.gzip.GzipHandler.etag");
|
||||
String requestEtags = (String)_channel.getRequest().getAttribute(GzipHandler.GZIP_HANDLER_ETAGS);
|
||||
String responseEtag = response.getHttpFields().get(HttpHeader.ETAG);
|
||||
if (requestEtags != null && responseEtag != null)
|
||||
{
|
||||
|
@ -200,7 +201,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
|||
return;
|
||||
}
|
||||
|
||||
fields.put(GZIP._contentEncoding);
|
||||
fields.put(GZIP.getContentEncoding());
|
||||
_crc.reset();
|
||||
|
||||
// Adjust headers
|
||||
|
@ -228,8 +229,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
|||
|
||||
private String etagGzip(String etag)
|
||||
{
|
||||
int end = etag.length() - 1;
|
||||
return (etag.charAt(end) == '"') ? etag.substring(0, end) + GZIP._etag + '"' : etag + GZIP._etag;
|
||||
return GZIP.etag(etag);
|
||||
}
|
||||
|
||||
public void noCompression()
|
||||
|
|
|
@ -83,7 +83,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" + CompressedContentFormat.GZIP._etag + "\"", __content.hashCode());
|
||||
private static final String __contentETagGzip = String.format("W/\"%x" + CompressedContentFormat.GZIP.getEtagSuffix() + "\"", __content.hashCode());
|
||||
private static final String __icontent = "BEFORE" + __content + "AFTER";
|
||||
|
||||
private Server _server;
|
||||
|
@ -586,7 +586,7 @@ public class GzipHandlerTest
|
|||
request.setURI("/ctx/content");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host", "tester");
|
||||
request.setHeader("If-Match", "WrongEtag" + CompressedContentFormat.GZIP._etag);
|
||||
request.setHeader("If-Match", "WrongEtag" + CompressedContentFormat.GZIP.getEtagSuffix());
|
||||
request.setHeader("accept-encoding", "gzip");
|
||||
|
||||
response = HttpTester.parseResponse(_connector.getResponse(request.generate()));
|
||||
|
|
|
@ -118,7 +118,7 @@ public class GzipDefaultServletTest extends AbstractGzipTest
|
|||
// Response Content-Encoding check
|
||||
assertThat("Response[Content-Encoding]", response.get("Content-Encoding"), containsString("gzip"));
|
||||
assertThat("Response[ETag]", response.get("ETag"), startsWith("W/"));
|
||||
assertThat("Response[ETag]", response.get("ETag"), containsString(CompressedContentFormat.GZIP._etag));
|
||||
assertThat("Response[ETag]", response.get("ETag"), containsString(CompressedContentFormat.GZIP.getEtagSuffix()));
|
||||
|
||||
assertThat("Response[Content-Length]", response.get("Content-Length"), is(nullValue()));
|
||||
// A HEAD request should have similar headers, but no body
|
||||
|
@ -320,7 +320,7 @@ public class GzipDefaultServletTest extends AbstractGzipTest
|
|||
// Response Content-Encoding check
|
||||
assertThat("Response[Content-Encoding]", response.get("Content-Encoding"), containsString("gzip"));
|
||||
assertThat("Response[ETag]", response.get("ETag"), startsWith("W/"));
|
||||
assertThat("Response[ETag]", response.get("ETag"), containsString(CompressedContentFormat.GZIP._etag));
|
||||
assertThat("Response[ETag]", response.get("ETag"), containsString(CompressedContentFormat.GZIP.getEtagSuffix()));
|
||||
assertThat("Response[Vary]", response.get("Vary"), containsString("Accept-Encoding"));
|
||||
|
||||
// Response Content checks
|
||||
|
@ -439,7 +439,7 @@ public class GzipDefaultServletTest extends AbstractGzipTest
|
|||
// Response Content-Encoding check
|
||||
assertThat("Response[Content-Encoding]", response.get("Content-Encoding"), not(containsString("gzip")));
|
||||
assertThat("Response[ETag]", response.get("ETag"), startsWith("W/"));
|
||||
assertThat("Response[ETag]", response.get("ETag"), not(containsString(CompressedContentFormat.GZIP._etag)));
|
||||
assertThat("Response[ETag]", response.get("ETag"), not(containsString(CompressedContentFormat.GZIP.getEtagSuffix())));
|
||||
|
||||
// Response Content checks
|
||||
UncompressedMetadata metadata = parseResponseContent(response);
|
||||
|
|
Loading…
Reference in New Issue