diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java index 32f8a709b8f..6e54ce6d950 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IncludeExcludeSet.java @@ -65,6 +65,7 @@ public class IncludeExcludeSet implements Predicate

*/ public IncludeExcludeSet() { + // noinspection unchecked this(HashSet.class); } @@ -75,8 +76,9 @@ public class IncludeExcludeSet implements Predicate

* one for include patterns and one for exclude patters. If the class is also a {@link Predicate}, * then it is also used as the item test for the set, otherwise a {@link SetContainsPredicate} instance * is created. - * @param The type of a set to use as the backing store + * @param The type of {@link Set} to use as the backing store */ + @SuppressWarnings({"unchecked", "rawtypes"}) public > IncludeExcludeSet(Class setClass) { try @@ -119,8 +121,9 @@ public class IncludeExcludeSet implements Predicate

* @param includePredicate the Predicate for included item testing (null for simple {@link Set#contains(Object)} test) * @param excludeSet the Set of items that represent the excluded space * @param excludePredicate the Predicate for excluded item testing (null for simple {@link Set#contains(Object)} test) - * @param The type of a set to use as the backing store + * @param The type of {@link Set} to use as the backing store */ + @SuppressWarnings("unused") public > IncludeExcludeSet(Set includeSet, Predicate

includePredicate, Set excludeSet, Predicate

excludePredicate) { Objects.requireNonNull(includeSet, "Include Set"); @@ -154,11 +157,35 @@ public class IncludeExcludeSet implements Predicate

_excludes.addAll(Arrays.asList(element)); } + /** + * Test includes and excludes for match. + * + *

+ * Excludes always win over includes. + *

+ * + *

+ * Empty includes means all inputs are allowed. + *

+ * + * @param t the input argument + * @return true if the input matches an include, and is not excluded. + */ @Override public boolean test(P t) { - if (!_includes.isEmpty() && !_includePredicate.test(t)) - return false; + if (!_includes.isEmpty()) + { + if (!_includePredicate.test(t)) + { + // If we have defined includes, but none match then + // return false immediately, no need to check excluded + return false; + } + } + + if (_excludes.isEmpty()) + return true; return !_excludePredicate.test(t); } @@ -166,13 +193,13 @@ public class IncludeExcludeSet implements Predicate

* Test Included and not Excluded * * @param item The item to test - * @return Boolean.TRUE if item is included, Boolean.FALSE if item is excluded or null if neither + * @return {@link Boolean#TRUE} if item is included, {@link Boolean#FALSE} if item is excluded, or null if neither */ public Boolean isIncludedAndNotExcluded(P item) { - if (_excludePredicate.test(item)) + if (!_excludes.isEmpty() && _excludePredicate.test(item)) return Boolean.FALSE; - if (_includePredicate.test(item)) + if (!_includes.isEmpty() && _includePredicate.test(item)) return Boolean.TRUE; return null; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeSetTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeSetTest.java index 1c584a0ea75..630b2f01888 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeSetTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/IncludeExcludeSetTest.java @@ -17,11 +17,90 @@ import java.net.InetAddress; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class IncludeExcludeSetTest { + @Test + public void testEmpty() + { + IncludeExcludeSet set = new IncludeExcludeSet<>(); + assertTrue(set.test("foo")); + + assertTrue(set.isEmpty()); + + assertFalse(set.hasIncludes()); + assertFalse(set.hasExcludes()); + + // Set is empty, so it's neither included nor excluded + assertNull(set.isIncludedAndNotExcluded("foo")); + } + + @Test + public void testOnlyIncludes() + { + IncludeExcludeSet set = new IncludeExcludeSet<>(); + set.getIncluded().add("foo"); + set.include("bar"); + set.include("a", "b", "c"); + + assertTrue(set.test("foo")); + assertTrue(set.test("bar")); + assertFalse(set.test("zed")); + + assertTrue(set.hasIncludes()); + assertFalse(set.hasExcludes()); + + // "foo" is included + assertEquals(set.isIncludedAndNotExcluded("foo"), Boolean.TRUE); + } + + @Test + public void testOnlyExcludes() + { + IncludeExcludeSet set = new IncludeExcludeSet<>(); + set.getExcluded().add("foo"); + set.exclude("bar"); + set.exclude("a", "b", "c"); + + assertFalse(set.test("foo")); + assertFalse(set.test("bar")); + assertTrue(set.test("zed")); + + assertFalse(set.hasIncludes()); + assertTrue(set.hasExcludes()); + + // "foo" is excluded + assertEquals(set.isIncludedAndNotExcluded("foo"), Boolean.FALSE); + } + + @Test + public void testIncludeAndExclude() + { + IncludeExcludeSet set = new IncludeExcludeSet<>(); + set.include("foo"); + set.exclude("bar"); + + assertTrue(set.test("foo")); // specifically included + assertFalse(set.test("bar")); // specifically excluded + assertFalse(set.test("zed")); // not in includes nor excludes + + assertTrue(set.hasIncludes()); + assertTrue(set.hasExcludes()); + + // "foo" is included + assertEquals(set.isIncludedAndNotExcluded("foo"), Boolean.TRUE); + + // "bar" is included + assertEquals(set.isIncludedAndNotExcluded("bar"), Boolean.FALSE); + + // "zed" is neither included nor excluded + assertNull(set.isIncludedAndNotExcluded("zed")); + } + @Test public void testWithInetAddressSet() throws Exception {