Fix #6305 Optimise isProtectedTarget (#6306)

* Fix #6305 Optimise isProtectedTarget

Fix #6305 Optimise isProtectedTarget by using case insensitive Index.

Signed-off-by: Greg Wilkins <gregw@webtide.com>

* Fix #6305 Optimise isProtectedTarget

updates from review

Signed-off-by: Greg Wilkins <gregw@webtide.com>

* Fix #6305 Optimise isProtectedTarget

updates from review

Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
Greg Wilkins 2021-05-28 18:01:25 +10:00 committed by GitHub
parent 9e03775a7f
commit 0d71185e10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 28 deletions

View File

@ -73,6 +73,7 @@ import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap; import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.Index;
import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
@ -179,6 +180,16 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
DESTROYED DESTROYED
} }
/**
* The type of protected target match
* @see #_protectedTargets
*/
private enum ProtectedTargetType
{
EXACT,
PREFIX
}
protected ContextStatus _contextStatus = ContextStatus.NOTSET; protected ContextStatus _contextStatus = ContextStatus.NOTSET;
protected Context _scontext; protected Context _scontext;
private final AttributesMap _attributes; private final AttributesMap _attributes;
@ -214,7 +225,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
private final List<ServletRequestAttributeListener> _servletRequestAttributeListeners = new CopyOnWriteArrayList<>(); private final List<ServletRequestAttributeListener> _servletRequestAttributeListeners = new CopyOnWriteArrayList<>();
private final List<ContextScopeListener> _contextListeners = new CopyOnWriteArrayList<>(); private final List<ContextScopeListener> _contextListeners = new CopyOnWriteArrayList<>();
private final Set<EventListener> _durableListeners = new HashSet<>(); private final Set<EventListener> _durableListeners = new HashSet<>();
private String[] _protectedTargets; private Index<ProtectedTargetType> _protectedTargets = Index.empty(false);
private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<>(); private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<>();
public enum Availability public enum Availability
@ -1474,30 +1485,17 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
*/ */
public boolean isProtectedTarget(String target) public boolean isProtectedTarget(String target)
{ {
if (target == null || _protectedTargets == null) if (target == null || _protectedTargets.isEmpty())
return false; return false;
while (target.startsWith("//")) if (target.startsWith("//"))
{ // ignore empty segments which may be discard by file system
target = URIUtil.compactPath(target); target = URIUtil.compactPath(target);
}
for (int i = 0; i < _protectedTargets.length; i++) ProtectedTargetType type = _protectedTargets.getBest(target);
{
String t = _protectedTargets[i];
if (StringUtil.startsWithIgnoreCase(target, t))
{
if (target.length() == t.length())
return true;
// Check that the target prefix really is a path segment, thus return type == ProtectedTargetType.PREFIX ||
// it can end with /, a query, a target or a parameter type == ProtectedTargetType.EXACT && _protectedTargets.get(target) == ProtectedTargetType.EXACT;
char c = target.charAt(t.length());
if (c == '/' || c == '?' || c == '#' || c == ';')
return true;
}
}
return false;
} }
/** /**
@ -1505,13 +1503,22 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
*/ */
public void setProtectedTargets(String[] targets) public void setProtectedTargets(String[] targets)
{ {
if (targets == null) Index.Builder<ProtectedTargetType> builder = new Index.Builder<>();
if (targets != null)
{ {
_protectedTargets = null; for (String t : targets)
return; {
} if (!t.startsWith("/"))
throw new IllegalArgumentException("Bad protected target: " + t);
_protectedTargets = Arrays.copyOf(targets, targets.length); builder.with(t, ProtectedTargetType.EXACT);
builder.with(t + "/", ProtectedTargetType.PREFIX);
builder.with(t + "?", ProtectedTargetType.PREFIX);
builder.with(t + "#", ProtectedTargetType.PREFIX);
builder.with(t + ";", ProtectedTargetType.PREFIX);
}
}
_protectedTargets = builder.caseSensitive(false).build();
} }
public String[] getProtectedTargets() public String[] getProtectedTargets()
@ -1519,7 +1526,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
if (_protectedTargets == null) if (_protectedTargets == null)
return null; return null;
return Arrays.copyOf(_protectedTargets, _protectedTargets.length); return _protectedTargets.keySet().stream()
.filter(s -> _protectedTargets.get(s) == ProtectedTargetType.EXACT)
.toArray(String[]::new);
} }
@Override @Override

View File

@ -646,9 +646,20 @@ public class ContextHandlerTest
String[] protectedTargets = {"/foo-inf", "/bar-inf"}; String[] protectedTargets = {"/foo-inf", "/bar-inf"};
handler.setProtectedTargets(protectedTargets); handler.setProtectedTargets(protectedTargets);
assertTrue(handler.isProtectedTarget("/foo-inf"));
assertTrue(handler.isProtectedTarget("/Foo-Inf"));
assertTrue(handler.isProtectedTarget("/FOO-INF"));
assertTrue(handler.isProtectedTarget("/foo-inf/"));
assertTrue(handler.isProtectedTarget("/FOO-inf?"));
assertTrue(handler.isProtectedTarget("/FOO-INF;"));
assertTrue(handler.isProtectedTarget("/foo-INF#"));
assertTrue(handler.isProtectedTarget("//foo-inf"));
assertTrue(handler.isProtectedTarget("//foo-inf//some//path"));
assertTrue(handler.isProtectedTarget("///foo-inf"));
assertTrue(handler.isProtectedTarget("/foo-inf/x/y/z")); assertTrue(handler.isProtectedTarget("/foo-inf/x/y/z"));
assertFalse(handler.isProtectedTarget("/foo/x/y/z"));
assertTrue(handler.isProtectedTarget("/foo-inf?x=y&z=1")); assertTrue(handler.isProtectedTarget("/foo-inf?x=y&z=1"));
assertFalse(handler.isProtectedTarget("/foo/x/y/z"));
assertFalse(handler.isProtectedTarget("/foo-inf-bar")); assertFalse(handler.isProtectedTarget("/foo-inf-bar"));
protectedTargets = new String[4]; protectedTargets = new String[4];

View File

@ -73,7 +73,8 @@ public interface Index<V>
V getBest(String s, int offset, int len); V getBest(String s, int offset, int len);
/** /**
* Get the best match from key in a String. * Get the best match from key in a String, which may be
* a prefix match or an exact match.
* *
* @param s The string * @param s The string
* @return The value or null if not found * @return The value or null if not found
@ -267,6 +268,11 @@ public interface Index<V>
return new ArrayTrie<>(true, maxCapacity); return new ArrayTrie<>(true, maxCapacity);
} }
static <V> Index<V> empty(boolean caseSensitive)
{
return EmptyTrie.instance(caseSensitive);
}
/** /**
* Builder of {@link Index} instances. * Builder of {@link Index} instances.
* @param <V> the entry type * @param <V> the entry type