Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoakim Erdfelt2015-12-08 22:51:25 +0000
committerJoakim Erdfelt2015-12-08 22:51:25 +0000
commit5e3fbbccd0df05049f248e5ac3a9207feaeb3b29 (patch)
tree4af1c1748fae358c461b6684f48f5792ad1142f8
parent7cdc58e6b9921d85023d4255ebbce0c091cb6359 (diff)
downloadorg.eclipse.jetty.project-5e3fbbccd0df05049f248e5ac3a9207feaeb3b29.tar.gz
org.eclipse.jetty.project-5e3fbbccd0df05049f248e5ac3a9207feaeb3b29.tar.xz
org.eclipse.jetty.project-5e3fbbccd0df05049f248e5ac3a9207feaeb3b29.zip
Backporting GzipHandler's IncludeExclude configs
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java50
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java9
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java10
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java406
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExclude.java136
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/RegexSet.java103
-rw-r--r--jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeTest.java153
-rw-r--r--jetty-util/src/test/java/org/eclipse/jetty/util/RegexSetTest.java86
8 files changed, 843 insertions, 110 deletions
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java b/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java
index 2c68dcea7f..482c92b9da 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java
@@ -18,14 +18,17 @@
package org.eclipse.jetty.http;
+import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.jetty.util.ArrayTernaryTrie;
+import org.eclipse.jetty.util.IncludeExclude;
import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.URIUtil;
@@ -569,4 +572,51 @@ public class PathMap<O> extends HashMap<String,O>
this.mapped = mapped;
}
}
+
+ public static class PathSet extends AbstractSet<String> implements IncludeExclude.MatchSet<String>
+ {
+ private final PathMap<Boolean> _map = new PathMap<>();
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return _map.keySet().iterator();
+ }
+
+ @Override
+ public int size()
+ {
+ return _map.size();
+ }
+
+ @Override
+ public boolean add(String item)
+ {
+ return _map.put(item,Boolean.TRUE)==null;
+ }
+
+ @Override
+ public boolean remove(Object item)
+ {
+ return _map.remove(item)!=null;
+ }
+
+ @Override
+ public boolean contains(Object o)
+ {
+ return _map.containsKey(o);
+ }
+
+
+ public boolean containsMatch(String s)
+ {
+ return _map.containsMatch(s);
+ }
+
+ @Override
+ public boolean matches(String item)
+ {
+ return _map.containsMatch(item);
+ }
+ }
}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
index 24738a1989..8f559ad9b1 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
@@ -45,6 +45,7 @@ import org.eclipse.jetty.servlets.gzip.AbstractCompressedStream;
import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
import org.eclipse.jetty.servlets.gzip.DeflatedOutputStream;
import org.eclipse.jetty.servlets.gzip.GzipOutputStream;
+import org.eclipse.jetty.util.IncludeExclude;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -539,7 +540,13 @@ public class GzipFilter extends UserAgentFilter
protected void configureWrappedResponse(CompressedResponseWrapper wrappedResponse)
{
- wrappedResponse.setMimeTypes(_mimeTypes,_excludeMimeTypes);
+ IncludeExclude<String> mimeTypeExclusions = new IncludeExclude<>();
+ if(_excludeMimeTypes)
+ mimeTypeExclusions.getExcluded().addAll(_mimeTypes);
+ else
+ mimeTypeExclusions.getIncluded().addAll(_mimeTypes);
+
+ wrappedResponse.setMimeTypes(mimeTypeExclusions);
wrappedResponse.setBufferSize(_bufferSize);
wrappedResponse.setMinCompressSize(_minGzipSize);
}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java
index b9ad084495..fdd7fb60ea 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/CompressedResponseWrapper.java
@@ -23,13 +23,13 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
-import java.util.Set;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
+import org.eclipse.jetty.util.IncludeExclude;
import org.eclipse.jetty.util.StringUtil;
/*------------------------------------------------------------ */
@@ -41,8 +41,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
public static final int DEFAULT_BUFFER_SIZE = 8192;
public static final int DEFAULT_MIN_COMPRESS_SIZE = 256;
- private Set<String> _mimeTypes;
- private boolean _excludeMimeTypes;
+ private IncludeExclude<String> _mimeTypes;
private int _bufferSize=DEFAULT_BUFFER_SIZE;
private int _minCompressSize=DEFAULT_MIN_COMPRESS_SIZE;
protected HttpServletRequest _request;
@@ -95,9 +94,8 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
/* ------------------------------------------------------------ */
/**
*/
- public void setMimeTypes(Set<String> mimeTypes,boolean excludeMimeTypes)
+ public void setMimeTypes(IncludeExclude<String> mimeTypes)
{
- _excludeMimeTypes=excludeMimeTypes;
_mimeTypes = mimeTypes;
}
@@ -138,7 +136,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
if (colon>0)
ct=ct.substring(0,colon);
- if (_mimeTypes.contains(StringUtil.asciiToLowerCase(ct))==_excludeMimeTypes)
+ if (!_mimeTypes.matches(StringUtil.asciiToLowerCase(ct)))
noCompression();
}
}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java
index c9976e4795..30a8d784fc 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java
@@ -23,22 +23,26 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
-import java.util.HashSet;
import java.util.Set;
-import java.util.StringTokenizer;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPOutputStream;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.PathMap;
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.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -63,12 +67,17 @@ public class GzipHandler extends HandlerWrapper
{
private static final Logger LOG = Log.getLogger(GzipHandler.class);
- final protected Set<String> _mimeTypes=new HashSet<>();
- protected boolean _excludeMimeTypes=false;
- protected Set<String> _excludedUA;
+ // final protected Set<String> _mimeTypes=new HashSet<>();
+ // protected boolean _excludeMimeTypes=false;
+ // protected Set<String> _excludedUA;
protected int _bufferSize = 8192;
protected int _minGzipSize = 256;
protected String _vary = "Accept-Encoding, User-Agent";
+
+ private final IncludeExclude<String> _agentPatterns=new IncludeExclude<>(RegexSet.class);
+ private final IncludeExclude<String> _methods = new IncludeExclude<>();
+ private final IncludeExclude<String> _paths = new IncludeExclude<>(PathMap.PathSet.class);
+ private final IncludeExclude<String> _mimeTypes = new IncludeExclude<>();
/* ------------------------------------------------------------ */
/**
@@ -76,6 +85,174 @@ public class GzipHandler extends HandlerWrapper
*/
public GzipHandler()
{
+ _methods.include(HttpMethod.GET.asString());
+ for (String type:MimeTypes.getKnownMimeTypes())
+ {
+ if ("image/svg+xml".equals(type))
+ _paths.exclude("*.svgz");
+ else if (type.startsWith("image/")||
+ type.startsWith("audio/")||
+ type.startsWith("video/"))
+ _mimeTypes.exclude(type);
+ }
+ _mimeTypes.exclude("application/compress");
+ _mimeTypes.exclude("application/zip");
+ _mimeTypes.exclude("application/gzip");
+ _mimeTypes.exclude("application/bzip2");
+ _mimeTypes.exclude("application/x-rar-compressed");
+ LOG.debug("{} mime types {}",this,_mimeTypes);
+
+ _agentPatterns.exclude(".*MSIE 6.0.*");
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param patterns Regular expressions matching user agents to exclude
+ */
+ public void addExcludedAgentPatterns(String... patterns)
+ {
+ _agentPatterns.exclude(patterns);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param methods The methods to exclude in compression
+ */
+ public void addExcludedMethods(String... methods)
+ {
+ for (String m : methods)
+ _methods.exclude(m);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the mime types.
+ * @param types The mime types to exclude (without charset or other parameters).
+ * For backward compatibility the mimetypes may be comma separated strings, but this
+ * will not be supported in future versions.
+ */
+ public void addExcludedMimeTypes(String... types)
+ {
+ for (String t : types)
+ _mimeTypes.exclude(StringUtil.csvSplit(t));
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param pathspecs Path specs (as per servlet spec) to exclude. If a
+ * ServletContext is available, the paths are relative to the context path,
+ * otherwise they are absolute.
+ * For backward compatibility the pathspecs may be comma separated strings, but this
+ * will not be supported in future versions.
+ */
+ public void addExcludedPaths(String... pathspecs)
+ {
+ for (String p : pathspecs)
+ _paths.exclude(StringUtil.csvSplit(p));
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param patterns Regular expressions matching user agents to exclude
+ */
+ public void addIncludedAgentPatterns(String... patterns)
+ {
+ _agentPatterns.include(patterns);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param methods The methods to include in compression
+ */
+ public void addIncludedMethods(String... methods)
+ {
+ for (String m : methods)
+ _methods.include(m);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Add included mime types. Inclusion takes precedence over
+ * exclusion.
+ * @param types The mime types to include (without charset or other parameters)
+ * For backward compatibility the mimetypes may be comma separated strings, but this
+ * will not be supported in future versions.
+ */
+ public void addIncludedMimeTypes(String... types)
+ {
+ for (String t : types)
+ _mimeTypes.include(StringUtil.csvSplit(t));
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Add path specs to include. Inclusion takes precedence over exclusion.
+ * @param pathspecs Path specs (as per servlet spec) to include. If a
+ * ServletContext is available, the paths are relative to the context path,
+ * otherwise they are absolute
+ * For backward compatibility the pathspecs may be comma separated strings, but this
+ * will not be supported in future versions.
+ */
+ public void addIncludedPaths(String... pathspecs)
+ {
+ for (String p : pathspecs)
+ _paths.include(StringUtil.csvSplit(p));
+ }
+
+ /* ------------------------------------------------------------ */
+ public String[] getExcludedAgentPatterns()
+ {
+ Set<String> excluded=_agentPatterns.getExcluded();
+ return excluded.toArray(new String[excluded.size()]);
+ }
+
+ /* ------------------------------------------------------------ */
+ public String[] getExcludedMethods()
+ {
+ Set<String> excluded=_methods.getExcluded();
+ return excluded.toArray(new String[excluded.size()]);
+ }
+
+ /* ------------------------------------------------------------ */
+ public String[] getExcludedMimeTypes()
+ {
+ Set<String> excluded=_mimeTypes.getExcluded();
+ return excluded.toArray(new String[excluded.size()]);
+ }
+
+ /* ------------------------------------------------------------ */
+ public String[] getExcludedPaths()
+ {
+ Set<String> excluded=_paths.getExcluded();
+ return excluded.toArray(new String[excluded.size()]);
+ }
+
+ /* ------------------------------------------------------------ */
+ public String[] getIncludedAgentPatterns()
+ {
+ Set<String> includes=_agentPatterns.getIncluded();
+ return includes.toArray(new String[includes.size()]);
+ }
+
+ /* ------------------------------------------------------------ */
+ public String[] getIncludedMethods()
+ {
+ Set<String> includes=_methods.getIncluded();
+ return includes.toArray(new String[includes.size()]);
+ }
+
+ /* ------------------------------------------------------------ */
+ public String[] getIncludedMimeTypes()
+ {
+ Set<String> includes=_mimeTypes.getIncluded();
+ return includes.toArray(new String[includes.size()]);
+ }
+
+ /* ------------------------------------------------------------ */
+ public String[] getIncludedPaths()
+ {
+ Set<String> includes=_paths.getIncluded();
+ return includes.toArray(new String[includes.size()]);
}
/* ------------------------------------------------------------ */
@@ -84,9 +261,10 @@ public class GzipHandler extends HandlerWrapper
*
* @return mime types to set
*/
+ @Deprecated
public Set<String> getMimeTypes()
{
- return _mimeTypes;
+ throw new UnsupportedOperationException("Use getIncludedMimeTypes or getExcludedMimeTypes instead");
}
/* ------------------------------------------------------------ */
@@ -96,11 +274,10 @@ public class GzipHandler extends HandlerWrapper
* @param mimeTypes
* the mime types to set
*/
+ @Deprecated
public void setMimeTypes(Set<String> mimeTypes)
{
- _excludeMimeTypes=false;
- _mimeTypes.clear();
- _mimeTypes.addAll(mimeTypes);
+ throw new UnsupportedOperationException("Use setIncludedMimeTypes or setExcludedMimeTypes instead");
}
/* ------------------------------------------------------------ */
@@ -110,18 +287,10 @@ public class GzipHandler extends HandlerWrapper
* @param mimeTypes
* the mime types to set
*/
+ @Deprecated
public void setMimeTypes(String mimeTypes)
{
- if (mimeTypes != null)
- {
- _excludeMimeTypes=false;
- _mimeTypes.clear();
- StringTokenizer tok = new StringTokenizer(mimeTypes,",",false);
- while (tok.hasMoreTokens())
- {
- _mimeTypes.add(tok.nextToken());
- }
- }
+ throw new UnsupportedOperationException("Use setIncludedMimeTypes or setExcludedMimeTypes instead");
}
/* ------------------------------------------------------------ */
@@ -130,7 +299,7 @@ public class GzipHandler extends HandlerWrapper
*/
public void setExcludeMimeTypes(boolean exclude)
{
- _excludeMimeTypes=exclude;
+ throw new UnsupportedOperationException("Use setExcludedMimeTypes instead");
}
/* ------------------------------------------------------------ */
@@ -141,7 +310,7 @@ public class GzipHandler extends HandlerWrapper
*/
public Set<String> getExcluded()
{
- return _excludedUA;
+ return _agentPatterns.getExcluded();
}
/* ------------------------------------------------------------ */
@@ -153,7 +322,8 @@ public class GzipHandler extends HandlerWrapper
*/
public void setExcluded(Set<String> excluded)
{
- _excludedUA = excluded;
+ _agentPatterns.getExcluded().clear();
+ _agentPatterns.getExcluded().addAll(excluded);
}
/* ------------------------------------------------------------ */
@@ -165,12 +335,11 @@ public class GzipHandler extends HandlerWrapper
*/
public void setExcluded(String excluded)
{
+ _agentPatterns.getExcluded().clear();
+
if (excluded != null)
{
- _excludedUA = new HashSet<String>();
- StringTokenizer tok = new StringTokenizer(excluded,",",false);
- while (tok.hasMoreTokens())
- _excludedUA.add(tok.nextToken());
+ _agentPatterns.exclude(StringUtil.csvSplit(excluded));
}
}
@@ -249,22 +418,6 @@ public class GzipHandler extends HandlerWrapper
@Override
protected void doStart() throws Exception
{
- if (_mimeTypes.size()==0 && _excludeMimeTypes)
- {
- _excludeMimeTypes = true;
- for (String type:MimeTypes.getKnownMimeTypes())
- {
- if (type.equals("image/svg+xml")) //always compressable (unless .svgz file)
- continue;
- if (type.startsWith("image/")||
- type.startsWith("audio/")||
- type.startsWith("video/"))
- _mimeTypes.add(type);
- }
- _mimeTypes.add("application/compress");
- _mimeTypes.add("application/zip");
- _mimeTypes.add("application/gzip");
- }
super.doStart();
}
@@ -275,80 +428,127 @@ public class GzipHandler extends HandlerWrapper
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
- if (_handler!=null && isStarted())
+ if(_handler == null || !isStarted())
{
- String ae = request.getHeader("accept-encoding");
- if (ae != null && ae.indexOf("gzip")>=0 && !response.containsHeader("Content-Encoding")
- && !HttpMethod.HEAD.is(request.getMethod()))
- {
- if (_excludedUA!=null)
- {
- String ua = request.getHeader("User-Agent");
- if (_excludedUA.contains(ua))
- {
- _handler.handle(target,baseRequest, request, response);
- return;
- }
- }
-
- final CompressedResponseWrapper wrappedResponse = newGzipResponseWrapper(request,response);
+ // do nothing
+ return;
+ }
+
+ if(isGzippable(baseRequest, request, response))
+ {
+ final CompressedResponseWrapper wrappedResponse = newGzipResponseWrapper(request,response);
- boolean exceptional=true;
- try
- {
- _handler.handle(target, baseRequest, request, wrappedResponse);
- exceptional=false;
- }
- finally
+ boolean exceptional=true;
+ try
+ {
+ _handler.handle(target, baseRequest, request, wrappedResponse);
+ exceptional=false;
+ }
+ finally
+ {
+ if (request.isAsyncStarted())
{
- if (request.isAsyncStarted())
+ request.getAsyncContext().addListener(new AsyncListener()
{
- request.getAsyncContext().addListener(new AsyncListener()
+
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException
{
-
- @Override
- public void onTimeout(AsyncEvent event) throws IOException
- {
- }
-
- @Override
- public void onStartAsync(AsyncEvent event) throws IOException
- {
- }
-
- @Override
- public void onError(AsyncEvent event) throws IOException
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException
+ {
+ try
{
+ wrappedResponse.finish();
}
-
- @Override
- public void onComplete(AsyncEvent event) throws IOException
+ catch(IOException e)
{
- try
- {
- wrappedResponse.finish();
- }
- catch(IOException e)
- {
- LOG.warn(e);
- }
+ LOG.warn(e);
}
- });
- }
- else if (exceptional && !response.isCommitted())
- {
- wrappedResponse.resetBuffer();
- wrappedResponse.noCompression();
- }
- else
- wrappedResponse.finish();
+ }
+ });
+ }
+ else if (exceptional && !response.isCommitted())
+ {
+ wrappedResponse.resetBuffer();
+ wrappedResponse.noCompression();
}
+ else
+ wrappedResponse.finish();
}
- else
+ }
+ else
+ {
+ _handler.handle(target,baseRequest, request, response);
+ }
+ }
+
+ private boolean isGzippable(Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ String ae = request.getHeader("accept-encoding");
+ if (ae == null || !ae.contains("gzip"))
+ {
+ // Request not indicated for Gzip
+ return false;
+ }
+
+ if(response.containsHeader("Content-Encoding"))
+ {
+ // Response is already declared, can't gzip
+ LOG.debug("{} excluded as Content-Encoding already declared {}",this,request);
+ return false;
+ }
+
+ if(HttpMethod.HEAD.is(request.getMethod()))
+ {
+ // HEAD is never Gzip'd
+ LOG.debug("{} excluded by method {}",this,request);
+ return false;
+ }
+
+ // Exclude based on Request Method
+ if (!_methods.matches(baseRequest.getMethod()))
+ {
+ LOG.debug("{} excluded by method {}",this,request);
+ return false;
+ }
+
+ // Exclude based on Request Path
+ ServletContext context = baseRequest.getServletContext();
+ String path = context==null?baseRequest.getRequestURI():URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo());
+
+ if(path != null && !_paths.matches(path))
+ {
+ LOG.debug("{} excluded by path {}",this,request);
+ return false;
+ }
+
+
+ // Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded
+ String mimeType = context==null?null:context.getMimeType(path);
+ if (mimeType!=null)
+ {
+ mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
+ if (!_mimeTypes.matches(mimeType))
{
- _handler.handle(target,baseRequest, request, response);
+ LOG.debug("{} excluded by path suffix mime type {}",this,request);
+ return false;
}
}
+
+ return true;
}
/**
@@ -363,7 +563,7 @@ public class GzipHandler extends HandlerWrapper
return new CompressedResponseWrapper(request,response)
{
{
- super.setMimeTypes(GzipHandler.this._mimeTypes,GzipHandler.this._excludeMimeTypes);
+ super.setMimeTypes(GzipHandler.this._mimeTypes);
super.setBufferSize(GzipHandler.this._bufferSize);
super.setMinCompressSize(GzipHandler.this._minGzipSize);
}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExclude.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExclude.java
new file mode 100644
index 0000000000..b5af37356e
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExclude.java
@@ -0,0 +1,136 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// 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.util;
+
+import java.util.HashSet;
+import java.util.Set;
+
+
+/** Utility class to maintain a set of inclusions and exclusions.
+ * <p>Maintains a set of included and excluded elements. The method {@link #matches(Object)}
+ * will return true IFF the passed object is not in the excluded set AND ( either the
+ * included set is empty OR the object is in the included set)
+ * <p>The type of the underlying {@link Set} used may be passed into the
+ * constructor, so special sets like Servlet PathMap may be used.
+ * <p>
+ * @param <ITEM> The type of element
+ */
+public class IncludeExclude<ITEM>
+{
+ public interface MatchSet<ITEM> extends Set<ITEM>
+ {
+ public boolean matches(ITEM item);
+ }
+
+ @SuppressWarnings("serial")
+ protected static class ContainsMatchSet<ITEM> extends HashSet<ITEM> implements MatchSet<ITEM>
+ {
+ @Override
+ public boolean matches(ITEM item)
+ {
+ return contains(item);
+ }
+ }
+
+ private final MatchSet<ITEM> _includes;
+ private final MatchSet<ITEM> _excludes;
+
+ /**
+ * Default constructor over {@link HashSet}
+ */
+ public IncludeExclude()
+ {
+ _includes = new ContainsMatchSet<ITEM>();
+ _excludes = new ContainsMatchSet<ITEM>();
+ }
+
+ /**
+ * Construct an IncludeExclude
+ * @param setClass The type of {@link Set} to using internally
+ * @param matcher A function to test if a passed ITEM is matched by the passed SET, or null to use {@link Set#contains(Object)}
+ */
+ public IncludeExclude(Class<? extends MatchSet<ITEM>> setClass)
+ {
+ try
+ {
+ _includes = setClass.newInstance();
+ _excludes = setClass.newInstance();
+ }
+ catch (InstantiationException | IllegalAccessException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void include(ITEM element)
+ {
+ _includes.add(element);
+ }
+
+ public void include(ITEM... element)
+ {
+ for (ITEM e: element)
+ _includes.add(e);
+ }
+
+ public void exclude(ITEM element)
+ {
+ _excludes.add(element);
+ }
+
+ public void exclude(ITEM... element)
+ {
+ for (ITEM e: element)
+ _excludes.add(e);
+ }
+
+ public boolean matches(ITEM e)
+ {
+ if (_includes.size()>0 && !_includes.matches(e))
+ return false;
+ return !_excludes.matches(e);
+ }
+
+ public int size()
+ {
+ return _includes.size()+_excludes.size();
+ }
+
+ public Set<ITEM> getIncluded()
+ {
+ return _includes;
+ }
+
+ public Set<ITEM> getExcluded()
+ {
+ return _excludes;
+ }
+
+ public void clear()
+ {
+ _includes.clear();
+ _excludes.clear();
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s@%x{i=%s,e=%s}",this.getClass().getSimpleName(),hashCode(),_includes,_excludes);
+ }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/RegexSet.java b/jetty-util/src/main/java/org/eclipse/jetty/util/RegexSet.java
new file mode 100644
index 0000000000..288e5154f2
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/RegexSet.java
@@ -0,0 +1,103 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// 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.util;
+
+import java.util.AbstractSet;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * A Set of Regular expressions strings.
+ * <p>
+ * Provides the efficient {@link #matches(String)} method to check for a match against all the combined Regex's
+ */
+public class RegexSet extends AbstractSet<String> implements IncludeExclude.MatchSet<String>
+{
+ private final Set<String> _patterns=new HashSet<String>();
+ private final Set<String> _unmodifiable=Collections.unmodifiableSet(_patterns);
+ private Pattern _pattern;
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return _unmodifiable.iterator();
+ }
+
+ @Override
+ public int size()
+ {
+ return _patterns.size();
+ }
+
+ @Override
+ public boolean add(String pattern)
+ {
+ boolean added = _patterns.add(pattern);
+ if (added)
+ updatePattern();
+ return added;
+ }
+
+ @Override
+ public boolean remove(Object pattern)
+ {
+ boolean removed = _patterns.remove(pattern);
+
+ if (removed)
+ updatePattern();
+ return removed;
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return _patterns.isEmpty();
+ }
+
+ @Override
+ public void clear()
+ {
+ _patterns.clear();
+ _pattern=null;
+ }
+
+ private void updatePattern()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("^(");
+ for (String pattern: _patterns)
+ {
+ if (builder.length()>2)
+ builder.append('|');
+ builder.append('(');
+ builder.append(pattern);
+ builder.append(')');
+ }
+ builder.append(")$");
+ _pattern = Pattern.compile(builder.toString());
+ }
+
+ public boolean matches(String s)
+ {
+ return _pattern!=null && _pattern.matcher(s).matches();
+ }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeTest.java
new file mode 100644
index 0000000000..c2b4d61559
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeTest.java
@@ -0,0 +1,153 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// 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.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class IncludeExcludeTest
+{
+ @Test
+ public void testEmpty()
+ {
+ IncludeExclude<String> ie = new IncludeExclude<>();
+
+ assertEquals(0,ie.size());
+ assertEquals(true,ie.matches("foo"));
+ }
+
+ @Test
+ public void testIncludeOnly()
+ {
+ IncludeExclude<String> ie = new IncludeExclude<>();
+ ie.include("foo");
+ ie.include("bar");
+
+ assertEquals(2,ie.size());
+ assertEquals(false,ie.matches(""));
+ assertEquals(true,ie.matches("foo"));
+ assertEquals(true,ie.matches("bar"));
+ assertEquals(false,ie.matches("foobar"));
+ }
+
+ @Test
+ public void testExcludeOnly()
+ {
+ IncludeExclude<String> ie = new IncludeExclude<>();
+ ie.exclude("foo");
+ ie.exclude("bar");
+
+ assertEquals(2,ie.size());
+
+ assertEquals(false,ie.matches("foo"));
+ assertEquals(false,ie.matches("bar"));
+ assertEquals(true,ie.matches(""));
+ assertEquals(true,ie.matches("foobar"));
+ assertEquals(true,ie.matches("wibble"));
+ }
+
+ @Test
+ public void testIncludeExclude()
+ {
+ IncludeExclude<String> ie = new IncludeExclude<>();
+ ie.include("foo");
+ ie.include("bar");
+ ie.exclude("bar");
+ ie.exclude("xxx");
+
+ assertEquals(4,ie.size());
+
+ assertEquals(true,ie.matches("foo"));
+ assertEquals(false,ie.matches("bar"));
+ assertEquals(false,ie.matches(""));
+ assertEquals(false,ie.matches("foobar"));
+ assertEquals(false,ie.matches("xxx"));
+ }
+
+
+
+ @Test
+ public void testEmptyRegex()
+ {
+ IncludeExclude<String> ie = new IncludeExclude<>(RegexSet.class);
+
+ assertEquals(0,ie.size());
+ assertEquals(true,ie.matches("foo"));
+ }
+
+ @Test
+ public void testIncludeRegex()
+ {
+ IncludeExclude<String> ie = new IncludeExclude<>(RegexSet.class);
+ ie.include("f..");
+ ie.include("b((ar)|(oo))");
+
+ assertEquals(2,ie.size());
+ assertEquals(false,ie.matches(""));
+ assertEquals(true,ie.matches("foo"));
+ assertEquals(true,ie.matches("far"));
+ assertEquals(true,ie.matches("bar"));
+ assertEquals(true,ie.matches("boo"));
+ assertEquals(false,ie.matches("foobar"));
+ assertEquals(false,ie.matches("xxx"));
+ }
+
+ @Test
+ public void testExcludeRegex()
+ {
+ IncludeExclude<String> ie = new IncludeExclude<>(RegexSet.class);
+ ie.exclude("f..");
+ ie.exclude("b((ar)|(oo))");
+
+ assertEquals(2,ie.size());
+
+ assertEquals(false,ie.matches("foo"));
+ assertEquals(false,ie.matches("far"));
+ assertEquals(false,ie.matches("bar"));
+ assertEquals(false,ie.matches("boo"));
+ assertEquals(true,ie.matches(""));
+ assertEquals(true,ie.matches("foobar"));
+ assertEquals(true,ie.matches("xxx"));
+ }
+
+ @Test
+ public void testIncludeExcludeRegex()
+ {
+ IncludeExclude<String> ie = new IncludeExclude<>(RegexSet.class);
+ ie.include(".*[aeiou].*");
+ ie.include("[AEIOU].*");
+ ie.exclude("f..");
+ ie.exclude("b((ar)|(oo))");
+
+ assertEquals(4,ie.size());
+ assertEquals(false,ie.matches("foo"));
+ assertEquals(false,ie.matches("far"));
+ assertEquals(false,ie.matches("bar"));
+ assertEquals(false,ie.matches("boo"));
+ assertEquals(false,ie.matches(""));
+ assertEquals(false,ie.matches("xxx"));
+
+ assertEquals(true,ie.matches("foobar"));
+ assertEquals(true,ie.matches("Ant"));
+
+ }
+
+
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/RegexSetTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/RegexSetTest.java
new file mode 100644
index 0000000000..f80c2eda5c
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/RegexSetTest.java
@@ -0,0 +1,86 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// 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.util;
+
+import org.junit.Test;
+
+import org.junit.Assert;
+
+public class RegexSetTest
+{
+
+ @Test
+ public void testEmpty()
+ {
+ RegexSet set = new RegexSet();
+
+ Assert.assertEquals(false,set.contains("foo"));
+ Assert.assertEquals(false,set.matches("foo"));
+ Assert.assertEquals(false,set.matches(""));
+
+ }
+
+ @Test
+ public void testSimple()
+ {
+ RegexSet set = new RegexSet();
+ set.add("foo.*");
+
+ Assert.assertEquals(true,set.contains("foo.*"));
+ Assert.assertEquals(true,set.matches("foo"));
+ Assert.assertEquals(true,set.matches("foobar"));
+ Assert.assertEquals(false,set.matches("bar"));
+ Assert.assertEquals(false,set.matches(""));
+
+ }
+
+ @Test
+ public void testSimpleTerminated()
+ {
+ RegexSet set = new RegexSet();
+ set.add("^foo.*$");
+
+ Assert.assertEquals(true,set.contains("^foo.*$"));
+ Assert.assertEquals(true,set.matches("foo"));
+ Assert.assertEquals(true,set.matches("foobar"));
+ Assert.assertEquals(false,set.matches("bar"));
+ Assert.assertEquals(false,set.matches(""));
+ }
+
+ @Test
+ public void testCombined()
+ {
+ RegexSet set = new RegexSet();
+ set.add("^foo.*$");
+ set.add("bar");
+ set.add("[a-z][0-9][a-z][0-9]");
+
+ Assert.assertEquals(true,set.contains("^foo.*$"));
+ Assert.assertEquals(true,set.matches("foo"));
+ Assert.assertEquals(true,set.matches("foobar"));
+ Assert.assertEquals(true,set.matches("bar"));
+ Assert.assertEquals(true,set.matches("c3p0"));
+ Assert.assertEquals(true,set.matches("r2d2"));
+
+ Assert.assertEquals(false,set.matches("wibble"));
+ Assert.assertEquals(false,set.matches("barfoo"));
+ Assert.assertEquals(false,set.matches("2b!b"));
+ Assert.assertEquals(false,set.matches(""));
+ }
+}

Back to the top