Merge pull request #3363 from isopov/comnpression-excluded-useragents
* pr/3363: Add HTTP compression excludeUserAgents property
This commit is contained in:
commit
fd0b1c6332
|
@ -68,6 +68,7 @@ content into your application; rather pick only the properties that you need.
|
|||
server.port=8080
|
||||
server.address= # bind to a specific NIC
|
||||
server.compression.enabled=false # if response compression is enabled
|
||||
server.compression.exclude-user-agents= # list of user-agents to exclude from compression
|
||||
server.compression.mime-types=text/html,text/xml,text/plain,text/css # comma-separated list of MIME types that should be compressed
|
||||
server.compression.min-response-size=2048 # minimum response size that is required for compression to be performed
|
||||
server.context-parameters.*= # Servlet context init parameters, e.g. server.context-parameters.a=alpha
|
||||
|
|
|
@ -37,7 +37,12 @@ public class Compression {
|
|||
"text/css" };
|
||||
|
||||
/**
|
||||
* Minimum response size that is required for compression to be performed
|
||||
* Comma-separated list of user agents for which responses should not be compressed.
|
||||
*/
|
||||
private String[] excludedUserAgents = null;
|
||||
|
||||
/**
|
||||
* Minimum response size that is required for compression to be performed.
|
||||
*/
|
||||
private int minResponseSize = 2048;
|
||||
|
||||
|
@ -65,4 +70,11 @@ public class Compression {
|
|||
this.minResponseSize = minSize;
|
||||
}
|
||||
|
||||
public String[] getExcludedUserAgents() {
|
||||
return this.excludedUserAgents;
|
||||
}
|
||||
|
||||
public void setExcludedUserAgents(String[] excludedUserAgents) {
|
||||
this.excludedUserAgents = excludedUserAgents;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -588,6 +588,12 @@ public class JettyEmbeddedServletContainerFactory extends
|
|||
.invoke(handler,
|
||||
new HashSet<String>(Arrays.asList(compression
|
||||
.getMimeTypes())));
|
||||
if (compression.getExcludedUserAgents() != null) {
|
||||
ReflectionUtils.findMethod(handlerClass, "setExcluded", Set.class)
|
||||
.invoke(handler,
|
||||
new HashSet<String>(Arrays.asList(compression
|
||||
.getExcludedUserAgents())));
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
@ -605,6 +611,10 @@ public class JettyEmbeddedServletContainerFactory extends
|
|||
gzipHandler.setMinGzipSize(compression.getMinResponseSize());
|
||||
gzipHandler.setMimeTypes(new HashSet<String>(Arrays.asList(compression
|
||||
.getMimeTypes())));
|
||||
if (compression.getExcludedUserAgents() != null) {
|
||||
gzipHandler.setExcluded(new HashSet<String>(Arrays.asList(compression
|
||||
.getExcludedUserAgents())));
|
||||
}
|
||||
return gzipHandler;
|
||||
}
|
||||
|
||||
|
@ -623,6 +633,11 @@ public class JettyEmbeddedServletContainerFactory extends
|
|||
ReflectionUtils.findMethod(handlerClass, "setIncludedMimeTypes",
|
||||
String[].class).invoke(handler,
|
||||
new Object[] { compression.getMimeTypes() });
|
||||
if (compression.getExcludedUserAgents() != null) {
|
||||
ReflectionUtils.findMethod(handlerClass, "setExcludedAgentPatterns",
|
||||
String[].class).invoke(handler,
|
||||
new Object[] { compression.getExcludedUserAgents() });
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
|
|
@ -280,6 +280,11 @@ public class TomcatEmbeddedServletContainerFactory extends
|
|||
protocol.setCompressionMinSize(compression.getMinResponseSize());
|
||||
protocol.setCompressableMimeTypes(StringUtils
|
||||
.arrayToCommaDelimitedString(compression.getMimeTypes()));
|
||||
if (getCompression().getExcludedUserAgents() != null) {
|
||||
protocol.setNoCompressionUserAgents(StringUtils
|
||||
.arrayToCommaDelimitedString(getCompression()
|
||||
.getExcludedUserAgents()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.context.embedded.undertow;
|
|||
import io.undertow.Handlers;
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.Undertow.Builder;
|
||||
import io.undertow.attribute.RequestHeaderAttribute;
|
||||
import io.undertow.predicate.Predicate;
|
||||
import io.undertow.predicate.Predicates;
|
||||
import io.undertow.server.HttpHandler;
|
||||
|
@ -27,6 +28,7 @@ import io.undertow.server.handlers.encoding.ContentEncodingRepository;
|
|||
import io.undertow.server.handlers.encoding.EncodingHandler;
|
||||
import io.undertow.server.handlers.encoding.GzipEncodingProvider;
|
||||
import io.undertow.servlet.api.DeploymentManager;
|
||||
import io.undertow.util.HttpString;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.ServerSocket;
|
||||
|
@ -100,8 +102,8 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
|
|||
|
||||
private Undertow createUndertowServer() {
|
||||
try {
|
||||
HttpHandler servletHandler = this.manager.start();
|
||||
this.builder.setHandler(getContextHandler(servletHandler));
|
||||
HttpHandler httpHandler = this.manager.start();
|
||||
this.builder.setHandler(getContextHandler(httpHandler));
|
||||
return this.builder.build();
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
|
@ -110,25 +112,36 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
|
|||
}
|
||||
}
|
||||
|
||||
private HttpHandler getContextHandler(HttpHandler servletHandler) {
|
||||
HttpHandler contextHandler = configurationCompressionIfNecessary(servletHandler);
|
||||
private HttpHandler getContextHandler(HttpHandler httpHandler) {
|
||||
HttpHandler contextHandler = configurationCompressionIfNecessary(httpHandler);
|
||||
if (StringUtils.isEmpty(this.contextPath)) {
|
||||
return contextHandler;
|
||||
}
|
||||
return Handlers.path().addPrefixPath(this.contextPath, contextHandler);
|
||||
}
|
||||
|
||||
private HttpHandler configurationCompressionIfNecessary(HttpHandler servletHandler) {
|
||||
private HttpHandler configurationCompressionIfNecessary(HttpHandler httpHandler) {
|
||||
if (this.compression == null || !this.compression.getEnabled()) {
|
||||
return servletHandler;
|
||||
return httpHandler;
|
||||
}
|
||||
ContentEncodingRepository encodingRepository = new ContentEncodingRepository();
|
||||
Predicate mimeAndSizePredicate = Predicates.and(Predicates
|
||||
.maxContentSize(this.compression.getMinResponseSize()), Predicates
|
||||
.or(new CompressibleMimeTypePredicate(this.compression.getMimeTypes())));
|
||||
encodingRepository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50,
|
||||
mimeAndSizePredicate);
|
||||
return new EncodingHandler(encodingRepository).setNext(servletHandler);
|
||||
ContentEncodingRepository repository = new ContentEncodingRepository();
|
||||
repository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50,
|
||||
Predicates.and(getCompressionPredicates(this.compression)));
|
||||
return new EncodingHandler(repository).setNext(httpHandler);
|
||||
}
|
||||
|
||||
private Predicate[] getCompressionPredicates(Compression compression) {
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(Predicates.maxContentSize(compression.getMinResponseSize()));
|
||||
predicates.add(new CompressibleMimeTypePredicate(compression.getMimeTypes()));
|
||||
if (compression.getExcludedUserAgents() != null) {
|
||||
for (String agent : compression.getExcludedUserAgents()) {
|
||||
RequestHeaderAttribute agentHeader = new RequestHeaderAttribute(
|
||||
new HttpString(HttpHeaders.USER_AGENT));
|
||||
predicates.add(Predicates.not(Predicates.regex(agentHeader, agent)));
|
||||
}
|
||||
}
|
||||
return predicates.toArray(new Predicate[predicates.size()]);
|
||||
}
|
||||
|
||||
private String getPortsDescription() {
|
||||
|
|
|
@ -531,35 +531,43 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
|
|||
|
||||
@Test
|
||||
public void compression() throws Exception {
|
||||
assertTrue(doTestCompression(10000, null));
|
||||
assertTrue(doTestCompression(10000, null, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noCompressionForSmallResponse() throws Exception {
|
||||
assertFalse(doTestCompression(100, null));
|
||||
assertFalse(doTestCompression(100, null, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noCompressionForMimeType() throws Exception {
|
||||
String[] mimeTypes = new String[] { "text/html", "text/xml", "text/css" };
|
||||
assertFalse(doTestCompression(10000, mimeTypes));
|
||||
assertFalse(doTestCompression(10000, mimeTypes, null));
|
||||
}
|
||||
|
||||
private boolean doTestCompression(int contentSize, String[] mimeTypes)
|
||||
throws Exception {
|
||||
String testContent = setUpFactoryForCompression(contentSize, mimeTypes);
|
||||
@Test
|
||||
public void noCompressionForUserAgent() throws Exception {
|
||||
assertFalse(doTestCompression(10000, null, new String[] { "testUserAgent" }));
|
||||
}
|
||||
|
||||
private boolean doTestCompression(int contentSize, String[] mimeTypes,
|
||||
String[] excludedUserAgents) throws Exception {
|
||||
String testContent = setUpFactoryForCompression(contentSize, mimeTypes,
|
||||
excludedUserAgents);
|
||||
TestGzipInputStreamFactory inputStreamFactory = new TestGzipInputStreamFactory();
|
||||
Map<String, InputStreamFactory> contentDecoderMap = singletonMap("gzip",
|
||||
(InputStreamFactory) inputStreamFactory);
|
||||
String response = getResponse(getLocalUrl("/test.txt"),
|
||||
String response = getResponse(
|
||||
getLocalUrl("/test.txt"),
|
||||
new HttpComponentsClientHttpRequestFactory(HttpClientBuilder.create()
|
||||
.setUserAgent("testUserAgent")
|
||||
.setContentDecoderRegistry(contentDecoderMap).build()));
|
||||
assertThat(response, equalTo(testContent));
|
||||
return inputStreamFactory.wasCompressionUsed();
|
||||
}
|
||||
|
||||
protected String setUpFactoryForCompression(int contentSize, String[] mimeTypes)
|
||||
throws Exception {
|
||||
protected String setUpFactoryForCompression(int contentSize, String[] mimeTypes,
|
||||
String[] excludedUserAgents) throws Exception {
|
||||
char[] chars = new char[contentSize];
|
||||
Arrays.fill(chars, 'F');
|
||||
String testContent = new String(chars);
|
||||
|
@ -572,6 +580,9 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
|
|||
if (mimeTypes != null) {
|
||||
compression.setMimeTypes(mimeTypes);
|
||||
}
|
||||
if (excludedUserAgents != null) {
|
||||
compression.setExcludedUserAgents(excludedUserAgents);
|
||||
}
|
||||
factory.setCompression(compression);
|
||||
this.container = factory.getEmbeddedServletContainer();
|
||||
this.container.start();
|
||||
|
|
|
@ -186,8 +186,8 @@ public class JettyEmbeddedServletContainerFactoryTests extends
|
|||
@Override
|
||||
@SuppressWarnings("serial")
|
||||
// Workaround for Jetty issue - https://bugs.eclipse.org/bugs/show_bug.cgi?id=470646
|
||||
protected String setUpFactoryForCompression(final int contentSize, String[] mimeTypes)
|
||||
throws Exception {
|
||||
protected String setUpFactoryForCompression(final int contentSize,
|
||||
String[] mimeTypes, String[] excludedUserAgents) throws Exception {
|
||||
char[] chars = new char[contentSize];
|
||||
Arrays.fill(chars, 'F');
|
||||
final String testContent = new String(chars);
|
||||
|
@ -197,6 +197,9 @@ public class JettyEmbeddedServletContainerFactoryTests extends
|
|||
if (mimeTypes != null) {
|
||||
compression.setMimeTypes(mimeTypes);
|
||||
}
|
||||
if (excludedUserAgents != null) {
|
||||
compression.setExcludedUserAgents(excludedUserAgents);
|
||||
}
|
||||
factory.setCompression(compression);
|
||||
this.container = factory.getEmbeddedServletContainer(new ServletRegistrationBean(
|
||||
new HttpServlet() {
|
||||
|
|
Loading…
Reference in New Issue