Cherry-pick of Improvements to PathSpec for Jetty 10.0.x (#8136)
* Cherry-pick of Improvements to PathSpec.
* From commit: 5b4d1dd1c6
* Fixing ConstraintSecurityHandler usage of PathMappings
* Fixing bad INCLUDE logic from cherry-pick in ServletHandler.doScope()
* Cleanup of non ServletPathSpec behaviors in ServletPathMapping class
* Skip optional group name/info lookup if regex fails.
* Prevent NPE on static servletPathMappings
* Update WebSocketMappings to use new PathMappings.getMatched(String)
Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
parent
346136ad6c
commit
8de55150fe
|
@ -31,7 +31,12 @@ public abstract class AbstractPathSpec implements PathSpec
|
||||||
return diff;
|
return diff;
|
||||||
|
|
||||||
// Path Spec Name (alphabetical)
|
// Path Spec Name (alphabetical)
|
||||||
return getDeclaration().compareTo(other.getDeclaration());
|
diff = getDeclaration().compareTo(other.getDeclaration());
|
||||||
|
if (diff != 0)
|
||||||
|
return diff;
|
||||||
|
|
||||||
|
// Path Implementation
|
||||||
|
return getClass().getName().compareTo(other.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -50,7 +55,7 @@ public abstract class AbstractPathSpec implements PathSpec
|
||||||
@Override
|
@Override
|
||||||
public final int hashCode()
|
public final int hashCode()
|
||||||
{
|
{
|
||||||
return Objects.hash(getDeclaration());
|
return Objects.hash(getGroup().ordinal(), getSpecLength(), getDeclaration(), getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under the
|
||||||
|
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||||
|
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.http.pathmap;
|
||||||
|
|
||||||
|
public interface MatchedPath
|
||||||
|
{
|
||||||
|
MatchedPath EMPTY = new MatchedPath()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String getPathMatch()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPathInfo()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return MatchedPath.class.getSimpleName() + ".EMPTY";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static MatchedPath from(String pathMatch, String pathInfo)
|
||||||
|
{
|
||||||
|
return new MatchedPath()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String getPathMatch()
|
||||||
|
{
|
||||||
|
return pathMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPathInfo()
|
||||||
|
{
|
||||||
|
return pathInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return MatchedPath.class.getSimpleName() + "[pathMatch=" + pathMatch + ", pathInfo=" + pathInfo + "]";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the portion of the path that matches a path spec.
|
||||||
|
*
|
||||||
|
* @return the path name portion of the match.
|
||||||
|
*/
|
||||||
|
String getPathMatch();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the portion of the path that is after the path spec.
|
||||||
|
*
|
||||||
|
* @return the path info portion of the match, or null if there is no portion after the {@link #getPathMatch()}
|
||||||
|
*/
|
||||||
|
String getPathInfo();
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under the
|
||||||
|
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||||
|
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.http.pathmap;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The match details when using {@link PathMappings#getMatched(String)}, used to minimize return to the PathSpec or PathMappings for subsequent details
|
||||||
|
* that are now provided by the {@link MatchedPath} instance.
|
||||||
|
*
|
||||||
|
* @param <E> the type of resource (IncludeExclude uses boolean, WebSocket uses endpoint/endpoint config, servlet uses ServletHolder, etc)
|
||||||
|
*/
|
||||||
|
public class MatchedResource<E>
|
||||||
|
{
|
||||||
|
private final E resource;
|
||||||
|
private final PathSpec pathSpec;
|
||||||
|
private final MatchedPath matchedPath;
|
||||||
|
|
||||||
|
public MatchedResource(E resource, PathSpec pathSpec, MatchedPath matchedPath)
|
||||||
|
{
|
||||||
|
this.resource = resource;
|
||||||
|
this.pathSpec = pathSpec;
|
||||||
|
this.matchedPath = matchedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E> MatchedResource<E> of(Map.Entry<PathSpec, E> mapping, MatchedPath matchedPath)
|
||||||
|
{
|
||||||
|
return new MatchedResource<>(mapping.getValue(), mapping.getKey(), matchedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MatchedPath getMatchedPath()
|
||||||
|
{
|
||||||
|
return this.matchedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathSpec getPathSpec()
|
||||||
|
{
|
||||||
|
return this.pathSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public E getResource()
|
||||||
|
{
|
||||||
|
return this.resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the portion of the path that matches a path spec.
|
||||||
|
*
|
||||||
|
* @return the path name portion of the match.
|
||||||
|
*/
|
||||||
|
public String getPathMatch()
|
||||||
|
{
|
||||||
|
return matchedPath.getPathMatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the portion of the path that is after the path spec.
|
||||||
|
*
|
||||||
|
* @return the path info portion of the match, or null if there is no portion after the {@link #getPathMatch()}
|
||||||
|
*/
|
||||||
|
public String getPathInfo()
|
||||||
|
{
|
||||||
|
return matchedPath.getPathInfo();
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,14 +42,17 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(PathMappings.class);
|
private static final Logger LOG = LoggerFactory.getLogger(PathMappings.class);
|
||||||
private final Set<MappedResource<E>> _mappings = new TreeSet<>(Comparator.comparing(MappedResource::getPathSpec));
|
private final Set<MappedResource<E>> _mappings = new TreeSet<>(Comparator.comparing(MappedResource::getPathSpec));
|
||||||
|
|
||||||
|
private boolean _optimizedExact = true;
|
||||||
private final Index.Mutable<MappedResource<E>> _exactMap = new Index.Builder<MappedResource<E>>()
|
private final Index.Mutable<MappedResource<E>> _exactMap = new Index.Builder<MappedResource<E>>()
|
||||||
.caseSensitive(true)
|
.caseSensitive(true)
|
||||||
.mutable()
|
.mutable()
|
||||||
.build();
|
.build();
|
||||||
|
private boolean _optimizedPrefix = true;
|
||||||
private final Index.Mutable<MappedResource<E>> _prefixMap = new Index.Builder<MappedResource<E>>()
|
private final Index.Mutable<MappedResource<E>> _prefixMap = new Index.Builder<MappedResource<E>>()
|
||||||
.caseSensitive(true)
|
.caseSensitive(true)
|
||||||
.mutable()
|
.mutable()
|
||||||
.build();
|
.build();
|
||||||
|
private boolean _optimizedSuffix = true;
|
||||||
private final Index.Mutable<MappedResource<E>> _suffixMap = new Index.Builder<MappedResource<E>>()
|
private final Index.Mutable<MappedResource<E>> _suffixMap = new Index.Builder<MappedResource<E>>()
|
||||||
.caseSensitive(true)
|
.caseSensitive(true)
|
||||||
.mutable()
|
.mutable()
|
||||||
|
@ -90,6 +93,26 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
_mappings.removeIf(predicate);
|
_mappings.removeIf(predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of MatchedResource matches for the specified path.
|
||||||
|
*
|
||||||
|
* @param path the path to return matches on
|
||||||
|
* @return the list of mapped resource the path matches on
|
||||||
|
*/
|
||||||
|
public List<MatchedResource<E>> getMatchedList(String path)
|
||||||
|
{
|
||||||
|
List<MatchedResource<E>> ret = new ArrayList<>();
|
||||||
|
for (MappedResource<E> mr : _mappings)
|
||||||
|
{
|
||||||
|
MatchedPath matchedPath = mr.getPathSpec().matched(path);
|
||||||
|
if (matchedPath != null)
|
||||||
|
{
|
||||||
|
ret.add(new MatchedResource<>(mr.getResource(), mr.getPathSpec(), matchedPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of MappedResource matches for the specified path.
|
* Return a list of MappedResource matches for the specified path.
|
||||||
*
|
*
|
||||||
|
@ -110,11 +133,11 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
ret.add(mr);
|
ret.add(mr);
|
||||||
break;
|
break;
|
||||||
case DEFAULT:
|
case DEFAULT:
|
||||||
if (isRootPath || mr.getPathSpec().matches(path))
|
if (isRootPath || mr.getPathSpec().matched(path) != null)
|
||||||
ret.add(mr);
|
ret.add(mr);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (mr.getPathSpec().matches(path))
|
if (mr.getPathSpec().matched(path) != null)
|
||||||
ret.add(mr);
|
ret.add(mr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -122,20 +145,33 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MappedResource<E> getMatch(String path)
|
public MatchedResource<E> getMatched(String path)
|
||||||
{
|
{
|
||||||
|
MatchedPath matchedPath;
|
||||||
PathSpecGroup lastGroup = null;
|
PathSpecGroup lastGroup = null;
|
||||||
|
|
||||||
|
boolean skipRestOfGroup = false;
|
||||||
// Search all the mappings
|
// Search all the mappings
|
||||||
for (MappedResource<E> mr : _mappings)
|
for (MappedResource<E> mr : _mappings)
|
||||||
{
|
{
|
||||||
PathSpecGroup group = mr.getPathSpec().getGroup();
|
PathSpecGroup group = mr.getPathSpec().getGroup();
|
||||||
|
if (group == lastGroup && skipRestOfGroup)
|
||||||
|
{
|
||||||
|
continue; // skip
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run servlet spec optimizations on first hit of specific groups
|
||||||
if (group != lastGroup)
|
if (group != lastGroup)
|
||||||
{
|
{
|
||||||
|
// New group, reset skip logic
|
||||||
|
skipRestOfGroup = false;
|
||||||
|
|
||||||
// New group in list, so let's look for an optimization
|
// New group in list, so let's look for an optimization
|
||||||
switch (group)
|
switch (group)
|
||||||
{
|
{
|
||||||
case EXACT:
|
case EXACT:
|
||||||
|
{
|
||||||
|
if (_optimizedExact)
|
||||||
{
|
{
|
||||||
int i = path.length();
|
int i = path.length();
|
||||||
while (i >= 0)
|
while (i >= 0)
|
||||||
|
@ -143,14 +179,23 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
MappedResource<E> candidate = _exactMap.getBest(path, 0, i);
|
MappedResource<E> candidate = _exactMap.getBest(path, 0, i);
|
||||||
if (candidate == null)
|
if (candidate == null)
|
||||||
break;
|
break;
|
||||||
if (candidate.getPathSpec().matches(path))
|
|
||||||
return candidate;
|
matchedPath = candidate.getPathSpec().matched(path);
|
||||||
i = candidate.getPathSpec().getPrefix().length() - 1;
|
if (matchedPath != null)
|
||||||
|
{
|
||||||
|
return new MatchedResource<>(candidate.getResource(), candidate.getPathSpec(), matchedPath);
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
// If we reached here, there's NO optimized EXACT Match possible, skip simple match below
|
||||||
|
skipRestOfGroup = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PREFIX_GLOB:
|
case PREFIX_GLOB:
|
||||||
|
{
|
||||||
|
if (_optimizedPrefix)
|
||||||
{
|
{
|
||||||
int i = path.length();
|
int i = path.length();
|
||||||
while (i >= 0)
|
while (i >= 0)
|
||||||
|
@ -158,21 +203,35 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
MappedResource<E> candidate = _prefixMap.getBest(path, 0, i);
|
MappedResource<E> candidate = _prefixMap.getBest(path, 0, i);
|
||||||
if (candidate == null)
|
if (candidate == null)
|
||||||
break;
|
break;
|
||||||
if (candidate.getPathSpec().matches(path))
|
|
||||||
return candidate;
|
matchedPath = candidate.getPathSpec().matched(path);
|
||||||
i = candidate.getPathSpec().getPrefix().length() - 1;
|
if (matchedPath != null)
|
||||||
|
return new MatchedResource<>(candidate.getResource(), candidate.getPathSpec(), matchedPath);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
// If we reached here, there's NO optimized PREFIX Match possible, skip simple match below
|
||||||
|
skipRestOfGroup = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SUFFIX_GLOB:
|
case SUFFIX_GLOB:
|
||||||
|
{
|
||||||
|
if (_optimizedSuffix)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while ((i = path.indexOf('.', i + 1)) > 0)
|
while ((i = path.indexOf('.', i + 1)) > 0)
|
||||||
{
|
{
|
||||||
MappedResource<E> candidate = _suffixMap.get(path, i + 1, path.length() - i - 1);
|
MappedResource<E> candidate = _suffixMap.get(path, i + 1, path.length() - i - 1);
|
||||||
if (candidate != null && candidate.getPathSpec().matches(path))
|
if (candidate == null)
|
||||||
return candidate;
|
break;
|
||||||
|
|
||||||
|
matchedPath = candidate.getPathSpec().matched(path);
|
||||||
|
if (matchedPath != null)
|
||||||
|
return new MatchedResource<>(candidate.getResource(), candidate.getPathSpec(), matchedPath);
|
||||||
|
}
|
||||||
|
// If we reached here, there's NO optimized SUFFIX Match possible, skip simple match below
|
||||||
|
skipRestOfGroup = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -181,8 +240,9 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mr.getPathSpec().matches(path))
|
matchedPath = mr.getPathSpec().matched(path);
|
||||||
return mr;
|
if (matchedPath != null)
|
||||||
|
return new MatchedResource<>(mr.getResource(), mr.getPathSpec(), matchedPath);
|
||||||
|
|
||||||
lastGroup = group;
|
lastGroup = group;
|
||||||
}
|
}
|
||||||
|
@ -190,21 +250,28 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #getMatched(String)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public MappedResource<E> getMatch(String path)
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("Use .getMatched(String) instead");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<MappedResource<E>> iterator()
|
public Iterator<MappedResource<E>> iterator()
|
||||||
{
|
{
|
||||||
return _mappings.iterator();
|
return _mappings.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link PathSpec#from(String)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public static PathSpec asPathSpec(String pathSpecString)
|
public static PathSpec asPathSpec(String pathSpecString)
|
||||||
{
|
{
|
||||||
if (pathSpecString == null)
|
return PathSpec.from(pathSpecString);
|
||||||
throw new RuntimeException("Path Spec String must start with '^', '/', or '*.': got [" + pathSpecString + "]");
|
|
||||||
|
|
||||||
if (pathSpecString.length() == 0)
|
|
||||||
return new ServletPathSpec("");
|
|
||||||
|
|
||||||
return pathSpecString.charAt(0) == '^' ? new RegexPathSpec(pathSpecString) : new ServletPathSpec(pathSpecString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public E get(PathSpec spec)
|
public E get(PathSpec spec)
|
||||||
|
@ -212,66 +279,84 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
return _mappings.stream()
|
return _mappings.stream()
|
||||||
.filter(mappedResource -> mappedResource.getPathSpec().equals(spec))
|
.filter(mappedResource -> mappedResource.getPathSpec().equals(spec))
|
||||||
.map(MappedResource::getResource)
|
.map(MappedResource::getResource)
|
||||||
.findFirst().orElse(null);
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean put(String pathSpecString, E resource)
|
public boolean put(String pathSpecString, E resource)
|
||||||
{
|
{
|
||||||
return put(asPathSpec(pathSpecString), resource);
|
return put(PathSpec.from(pathSpecString), resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean put(PathSpec pathSpec, E resource)
|
public boolean put(PathSpec pathSpec, E resource)
|
||||||
{
|
{
|
||||||
MappedResource<E> entry = new MappedResource<>(pathSpec, resource);
|
MappedResource<E> entry = new MappedResource<>(pathSpec, resource);
|
||||||
switch (pathSpec.getGroup())
|
|
||||||
{
|
|
||||||
case EXACT:
|
|
||||||
String exact = pathSpec.getPrefix();
|
|
||||||
if (exact != null)
|
|
||||||
_exactMap.put(exact, entry);
|
|
||||||
break;
|
|
||||||
case PREFIX_GLOB:
|
|
||||||
String prefix = pathSpec.getPrefix();
|
|
||||||
if (prefix != null)
|
|
||||||
_prefixMap.put(prefix, entry);
|
|
||||||
break;
|
|
||||||
case SUFFIX_GLOB:
|
|
||||||
String suffix = pathSpec.getSuffix();
|
|
||||||
if (suffix != null)
|
|
||||||
_suffixMap.put(suffix, entry);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean added = _mappings.add(entry);
|
boolean added = _mappings.add(entry);
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{} {} to {}", added ? "Added" : "Ignored", entry, this);
|
LOG.debug("{} {} to {}", added ? "Added" : "Ignored", entry, this);
|
||||||
|
|
||||||
|
if (added)
|
||||||
|
{
|
||||||
|
switch (pathSpec.getGroup())
|
||||||
|
{
|
||||||
|
case EXACT:
|
||||||
|
if (pathSpec instanceof ServletPathSpec)
|
||||||
|
{
|
||||||
|
String exact = pathSpec.getDeclaration();
|
||||||
|
if (exact != null)
|
||||||
|
_exactMap.put(exact, entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This is not a Servlet mapping, turn off optimization on Exact
|
||||||
|
// TODO: see if we can optimize all Regex / UriTemplate versions here too.
|
||||||
|
// Note: Example exact in Regex that can cause problems `^/a\Q/b\E/` (which is only ever matching `/a/b/`)
|
||||||
|
// Note: UriTemplate can handle exact easily enough
|
||||||
|
_optimizedExact = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PREFIX_GLOB:
|
||||||
|
if (pathSpec instanceof ServletPathSpec)
|
||||||
|
{
|
||||||
|
String prefix = pathSpec.getPrefix();
|
||||||
|
if (prefix != null)
|
||||||
|
_prefixMap.put(prefix, entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This is not a Servlet mapping, turn off optimization on Prefix
|
||||||
|
// TODO: see if we can optimize all Regex / UriTemplate versions here too.
|
||||||
|
// Note: Example Prefix in Regex that can cause problems `^/a/b+` or `^/a/bb*` ('b' one or more times)
|
||||||
|
// Note: Example Prefix in UriTemplate that might cause problems `/a/{b}/{c}`
|
||||||
|
_optimizedPrefix = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SUFFIX_GLOB:
|
||||||
|
if (pathSpec instanceof ServletPathSpec)
|
||||||
|
{
|
||||||
|
String suffix = pathSpec.getSuffix();
|
||||||
|
if (suffix != null)
|
||||||
|
_suffixMap.put(suffix, entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This is not a Servlet mapping, turn off optimization on Suffix
|
||||||
|
// TODO: see if we can optimize all Regex / UriTemplate versions here too.
|
||||||
|
// Note: Example suffix in Regex that can cause problems `^.*/path/name.ext` or `^/a/.*(ending)`
|
||||||
|
// Note: Example suffix in UriTemplate that can cause problems `/{a}/name.ext`
|
||||||
|
_optimizedSuffix = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("incomplete-switch")
|
@SuppressWarnings("incomplete-switch")
|
||||||
public boolean remove(PathSpec pathSpec)
|
public boolean remove(PathSpec pathSpec)
|
||||||
{
|
{
|
||||||
String prefix = pathSpec.getPrefix();
|
|
||||||
String suffix = pathSpec.getSuffix();
|
|
||||||
switch (pathSpec.getGroup())
|
|
||||||
{
|
|
||||||
case EXACT:
|
|
||||||
if (prefix != null)
|
|
||||||
_exactMap.remove(prefix);
|
|
||||||
break;
|
|
||||||
case PREFIX_GLOB:
|
|
||||||
if (prefix != null)
|
|
||||||
_prefixMap.remove(prefix);
|
|
||||||
break;
|
|
||||||
case SUFFIX_GLOB:
|
|
||||||
if (suffix != null)
|
|
||||||
_suffixMap.remove(suffix);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator<MappedResource<E>> iter = _mappings.iterator();
|
Iterator<MappedResource<E>> iter = _mappings.iterator();
|
||||||
boolean removed = false;
|
boolean removed = false;
|
||||||
while (iter.hasNext())
|
while (iter.hasNext())
|
||||||
|
@ -283,11 +368,54 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{} {} to {}", removed ? "Removed" : "Ignored", pathSpec, this);
|
LOG.debug("{} {} to {}", removed ? "Removed" : "Ignored", pathSpec, this);
|
||||||
|
|
||||||
|
if (removed)
|
||||||
|
{
|
||||||
|
switch (pathSpec.getGroup())
|
||||||
|
{
|
||||||
|
case EXACT:
|
||||||
|
String exact = pathSpec.getDeclaration();
|
||||||
|
if (exact != null)
|
||||||
|
{
|
||||||
|
_exactMap.remove(exact);
|
||||||
|
// Recalculate _optimizeExact
|
||||||
|
_optimizedExact = canBeOptimized(PathSpecGroup.EXACT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PREFIX_GLOB:
|
||||||
|
String prefix = pathSpec.getPrefix();
|
||||||
|
if (prefix != null)
|
||||||
|
{
|
||||||
|
_prefixMap.remove(prefix);
|
||||||
|
// Recalculate _optimizePrefix
|
||||||
|
_optimizedPrefix = canBeOptimized(PathSpecGroup.PREFIX_GLOB);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SUFFIX_GLOB:
|
||||||
|
String suffix = pathSpec.getSuffix();
|
||||||
|
if (suffix != null)
|
||||||
|
{
|
||||||
|
_suffixMap.remove(suffix);
|
||||||
|
// Recalculate _optimizeSuffix
|
||||||
|
_optimizedSuffix = canBeOptimized(PathSpecGroup.SUFFIX_GLOB);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canBeOptimized(PathSpecGroup suffixGlob)
|
||||||
|
{
|
||||||
|
return _mappings.stream()
|
||||||
|
.filter((mapping) -> mapping.getPathSpec().getGroup() == suffixGlob)
|
||||||
|
.allMatch((mapping) -> mapping.getPathSpec() instanceof ServletPathSpec);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.http.pathmap;
|
package org.eclipse.jetty.http.pathmap;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A path specification is a URI path template that can be matched against.
|
* A path specification is a URI path template that can be matched against.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -20,6 +22,16 @@ package org.eclipse.jetty.http.pathmap;
|
||||||
*/
|
*/
|
||||||
public interface PathSpec extends Comparable<PathSpec>
|
public interface PathSpec extends Comparable<PathSpec>
|
||||||
{
|
{
|
||||||
|
static PathSpec from(String pathSpecString)
|
||||||
|
{
|
||||||
|
Objects.requireNonNull(pathSpecString, "null PathSpec not supported");
|
||||||
|
|
||||||
|
if (pathSpecString.length() == 0)
|
||||||
|
return new ServletPathSpec("");
|
||||||
|
|
||||||
|
return pathSpecString.charAt(0) == '^' ? new RegexPathSpec(pathSpecString) : new ServletPathSpec(pathSpecString);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the spec.
|
* The length of the spec.
|
||||||
*
|
*
|
||||||
|
@ -48,7 +60,9 @@ public interface PathSpec extends Comparable<PathSpec>
|
||||||
*
|
*
|
||||||
* @param path the path to match against
|
* @param path the path to match against
|
||||||
* @return the path info portion of the string
|
* @return the path info portion of the string
|
||||||
|
* @deprecated use {@link #matched(String)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
String getPathInfo(String path);
|
String getPathInfo(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,7 +70,9 @@ public interface PathSpec extends Comparable<PathSpec>
|
||||||
*
|
*
|
||||||
* @param path the path to match against
|
* @param path the path to match against
|
||||||
* @return the match, or null if no match at all
|
* @return the match, or null if no match at all
|
||||||
|
* @deprecated use {@link #matched(String)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
String getPathMatch(String path);
|
String getPathMatch(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,6 +101,16 @@ public interface PathSpec extends Comparable<PathSpec>
|
||||||
*
|
*
|
||||||
* @param path the path to test
|
* @param path the path to test
|
||||||
* @return true if the path matches this path spec, false otherwise
|
* @return true if the path matches this path spec, false otherwise
|
||||||
|
* @deprecated use {@link #matched(String)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
boolean matches(String path);
|
boolean matches(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the complete matched details of the provided path.
|
||||||
|
*
|
||||||
|
* @param path the path to test
|
||||||
|
* @return the matched details, if a match was possible, or null if not able to be matched.
|
||||||
|
*/
|
||||||
|
MatchedPath matched(String path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.http.pathmap;
|
||||||
|
|
||||||
import java.util.AbstractSet;
|
import java.util.AbstractSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +30,7 @@ public class PathSpecSet extends AbstractSet<String> implements Predicate<String
|
||||||
@Override
|
@Override
|
||||||
public boolean test(String s)
|
public boolean test(String s)
|
||||||
{
|
{
|
||||||
return specs.getMatch(s) != null;
|
return specs.getMatched(s) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,17 +49,14 @@ public class PathSpecSet extends AbstractSet<String> implements Predicate<String
|
||||||
{
|
{
|
||||||
return (PathSpec)o;
|
return (PathSpec)o;
|
||||||
}
|
}
|
||||||
if (o instanceof String)
|
|
||||||
{
|
return PathSpec.from(Objects.toString(o));
|
||||||
return PathMappings.asPathSpec((String)o);
|
|
||||||
}
|
|
||||||
return PathMappings.asPathSpec(o.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean add(String s)
|
public boolean add(String s)
|
||||||
{
|
{
|
||||||
return specs.put(PathMappings.asPathSpec(s), Boolean.TRUE);
|
return specs.put(PathSpec.from(s), Boolean.TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -13,11 +13,32 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.http.pathmap;
|
package org.eclipse.jetty.http.pathmap;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
public class RegexPathSpec extends AbstractPathSpec
|
public class RegexPathSpec extends AbstractPathSpec
|
||||||
{
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(UriTemplatePathSpec.class);
|
||||||
|
|
||||||
|
private static final Map<Character, String> FORBIDDEN_ESCAPED = new HashMap<>();
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
FORBIDDEN_ESCAPED.put('s', "any whitespace");
|
||||||
|
FORBIDDEN_ESCAPED.put('n', "newline");
|
||||||
|
FORBIDDEN_ESCAPED.put('r', "carriage return");
|
||||||
|
FORBIDDEN_ESCAPED.put('t', "tab");
|
||||||
|
FORBIDDEN_ESCAPED.put('f', "form-feed");
|
||||||
|
FORBIDDEN_ESCAPED.put('b', "bell");
|
||||||
|
FORBIDDEN_ESCAPED.put('e', "escape");
|
||||||
|
FORBIDDEN_ESCAPED.put('c', "control char");
|
||||||
|
}
|
||||||
|
|
||||||
private final String _declaration;
|
private final String _declaration;
|
||||||
private final PathSpecGroup _group;
|
private final PathSpecGroup _group;
|
||||||
private final int _pathDepth;
|
private final int _pathDepth;
|
||||||
|
@ -33,35 +54,81 @@ public class RegexPathSpec extends AbstractPathSpec
|
||||||
declaration = regex;
|
declaration = regex;
|
||||||
int specLength = declaration.length();
|
int specLength = declaration.length();
|
||||||
// build up a simple signature we can use to identify the grouping
|
// build up a simple signature we can use to identify the grouping
|
||||||
boolean inGrouping = false;
|
boolean inTextList = false;
|
||||||
|
boolean inQuantifier = false;
|
||||||
StringBuilder signature = new StringBuilder();
|
StringBuilder signature = new StringBuilder();
|
||||||
|
|
||||||
int pathDepth = 0;
|
int pathDepth = 0;
|
||||||
|
char last = 0;
|
||||||
for (int i = 0; i < declaration.length(); i++)
|
for (int i = 0; i < declaration.length(); i++)
|
||||||
{
|
{
|
||||||
char c = declaration.charAt(i);
|
char c = declaration.charAt(i);
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case '[':
|
case '^': // ignore anchors
|
||||||
inGrouping = true;
|
case '$': // ignore anchors
|
||||||
|
case '\'': // ignore escaping
|
||||||
|
case '(': // ignore grouping
|
||||||
|
case ')': // ignore grouping
|
||||||
break;
|
break;
|
||||||
case ']':
|
case '+': // single char quantifier
|
||||||
inGrouping = false;
|
case '?': // single char quantifier
|
||||||
|
case '*': // single char quantifier
|
||||||
|
case '|': // alternate match token
|
||||||
|
case '.': // any char token
|
||||||
signature.append('g'); // glob
|
signature.append('g'); // glob
|
||||||
break;
|
break;
|
||||||
case '*':
|
case '{':
|
||||||
|
inQuantifier = true;
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
inQuantifier = false;
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
inTextList = true;
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
inTextList = false;
|
||||||
signature.append('g'); // glob
|
signature.append('g'); // glob
|
||||||
break;
|
break;
|
||||||
case '/':
|
case '/':
|
||||||
if (!inGrouping)
|
if (!inTextList && !inQuantifier)
|
||||||
pathDepth++;
|
pathDepth++;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!inGrouping && Character.isLetterOrDigit(c))
|
if (!inTextList && !inQuantifier && Character.isLetterOrDigit(c))
|
||||||
|
{
|
||||||
|
if (last == '\\') // escaped
|
||||||
|
{
|
||||||
|
String forbiddenReason = FORBIDDEN_ESCAPED.get(c);
|
||||||
|
if (forbiddenReason != null)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException(String.format("%s does not support \\%c (%s) for \"%s\"",
|
||||||
|
this.getClass().getSimpleName(), c, forbiddenReason, declaration));
|
||||||
|
}
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'S': // any non-whitespace
|
||||||
|
case 'd': // any digits
|
||||||
|
case 'D': // any non-digits
|
||||||
|
case 'w': // any word
|
||||||
|
case 'W': // any non-word
|
||||||
|
signature.append('g'); // glob
|
||||||
|
break;
|
||||||
|
default:
|
||||||
signature.append('l'); // literal (exact)
|
signature.append('l'); // literal (exact)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else // not escaped
|
||||||
|
{
|
||||||
|
signature.append('l'); // literal (exact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last = c;
|
||||||
|
}
|
||||||
Pattern pattern = Pattern.compile(declaration);
|
Pattern pattern = Pattern.compile(declaration);
|
||||||
|
|
||||||
// Figure out the grouping based on the signature
|
// Figure out the grouping based on the signature
|
||||||
|
@ -72,7 +139,7 @@ public class RegexPathSpec extends AbstractPathSpec
|
||||||
group = PathSpecGroup.EXACT;
|
group = PathSpecGroup.EXACT;
|
||||||
else if (Pattern.matches("^l*g+", sig))
|
else if (Pattern.matches("^l*g+", sig))
|
||||||
group = PathSpecGroup.PREFIX_GLOB;
|
group = PathSpecGroup.PREFIX_GLOB;
|
||||||
else if (Pattern.matches("^g+l+$", sig))
|
else if (Pattern.matches("^g+l+.*", sig))
|
||||||
group = PathSpecGroup.SUFFIX_GLOB;
|
group = PathSpecGroup.SUFFIX_GLOB;
|
||||||
else
|
else
|
||||||
group = PathSpecGroup.MIDDLE_GLOB;
|
group = PathSpecGroup.MIDDLE_GLOB;
|
||||||
|
@ -82,12 +149,28 @@ public class RegexPathSpec extends AbstractPathSpec
|
||||||
_pathDepth = pathDepth;
|
_pathDepth = pathDepth;
|
||||||
_specLength = specLength;
|
_specLength = specLength;
|
||||||
_pattern = pattern;
|
_pattern = pattern;
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
{
|
||||||
|
LOG.debug("Creating RegexPathSpec[{}] (signature: [{}], group: {})",
|
||||||
|
_declaration, sig, _group);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Matcher getMatcher(String path)
|
protected Matcher getMatcher(String path)
|
||||||
{
|
{
|
||||||
|
int idx = path.indexOf('?');
|
||||||
|
if (idx >= 0)
|
||||||
|
{
|
||||||
|
// match only non-query part
|
||||||
|
return _pattern.matcher(path.substring(0, idx));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// match entire path
|
||||||
return _pattern.matcher(path);
|
return _pattern.matcher(path);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSpecLength()
|
public int getSpecLength()
|
||||||
|
@ -176,16 +259,102 @@ public class RegexPathSpec extends AbstractPathSpec
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(final String path)
|
public boolean matches(final String path)
|
||||||
{
|
{
|
||||||
int idx = path.indexOf('?');
|
|
||||||
if (idx >= 0)
|
|
||||||
{
|
|
||||||
// match only non-query part
|
|
||||||
return getMatcher(path.substring(0, idx)).matches();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// match entire path
|
|
||||||
return getMatcher(path).matches();
|
return getMatcher(path).matches();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatchedPath matched(String path)
|
||||||
|
{
|
||||||
|
Matcher matcher = getMatcher(path);
|
||||||
|
if (matcher.matches())
|
||||||
|
{
|
||||||
|
return new RegexMatchedPath(this, path, matcher);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RegexMatchedPath implements MatchedPath
|
||||||
|
{
|
||||||
|
private final RegexPathSpec pathSpec;
|
||||||
|
private final String path;
|
||||||
|
private final Matcher matcher;
|
||||||
|
|
||||||
|
public RegexMatchedPath(RegexPathSpec regexPathSpec, String path, Matcher matcher)
|
||||||
|
{
|
||||||
|
this.pathSpec = regexPathSpec;
|
||||||
|
this.path = path;
|
||||||
|
this.matcher = matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPathMatch()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String p = matcher.group("name");
|
||||||
|
if (p != null)
|
||||||
|
{
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException ignore)
|
||||||
|
{
|
||||||
|
// ignore if group name not found.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathSpec.getGroup() == PathSpecGroup.PREFIX_GLOB && matcher.groupCount() >= 1)
|
||||||
|
{
|
||||||
|
int idx = matcher.start(1);
|
||||||
|
if (idx > 0)
|
||||||
|
{
|
||||||
|
if (this.path.charAt(idx - 1) == '/')
|
||||||
|
idx--;
|
||||||
|
return this.path.substring(0, idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default is the full path
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPathInfo()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String p = matcher.group("info");
|
||||||
|
if (p != null)
|
||||||
|
{
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException ignore)
|
||||||
|
{
|
||||||
|
// ignore if group info not found.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path Info only valid for PREFIX_GLOB
|
||||||
|
if (pathSpec.getGroup() == PathSpecGroup.PREFIX_GLOB && matcher.groupCount() >= 1)
|
||||||
|
{
|
||||||
|
String pathInfo = matcher.group(1);
|
||||||
|
if ("".equals(pathInfo))
|
||||||
|
return "/";
|
||||||
|
else
|
||||||
|
return pathInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// default is null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return getClass().getSimpleName() + "[" +
|
||||||
|
"pathSpec=" + pathSpec +
|
||||||
|
", path=\"" + path + "\"" +
|
||||||
|
", matcher=" + matcher +
|
||||||
|
']';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,10 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
private final PathSpecGroup _group;
|
private final PathSpecGroup _group;
|
||||||
private final int _pathDepth;
|
private final int _pathDepth;
|
||||||
private final int _specLength;
|
private final int _specLength;
|
||||||
|
private final int _matchLength;
|
||||||
private final String _prefix;
|
private final String _prefix;
|
||||||
private final String _suffix;
|
private final String _suffix;
|
||||||
|
private final MatchedPath _preMatchedPath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a servlet or filter path mapping isn't a suffix mapping, ensure
|
* If a servlet or filter path mapping isn't a suffix mapping, ensure
|
||||||
|
@ -202,8 +204,10 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
_group = PathSpecGroup.ROOT;
|
_group = PathSpecGroup.ROOT;
|
||||||
_pathDepth = -1; // Set pathDepth to -1 to force this to be at the end of the sort order.
|
_pathDepth = -1; // Set pathDepth to -1 to force this to be at the end of the sort order.
|
||||||
_specLength = 1;
|
_specLength = 1;
|
||||||
|
_matchLength = 0;
|
||||||
_prefix = null;
|
_prefix = null;
|
||||||
_suffix = null;
|
_suffix = null;
|
||||||
|
_preMatchedPath = MatchedPath.from("", "/");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,8 +218,10 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
_group = PathSpecGroup.DEFAULT;
|
_group = PathSpecGroup.DEFAULT;
|
||||||
_pathDepth = -1; // Set pathDepth to -1 to force this to be at the end of the sort order.
|
_pathDepth = -1; // Set pathDepth to -1 to force this to be at the end of the sort order.
|
||||||
_specLength = 1;
|
_specLength = 1;
|
||||||
|
_matchLength = 0;
|
||||||
_prefix = null;
|
_prefix = null;
|
||||||
_suffix = null;
|
_suffix = null;
|
||||||
|
_preMatchedPath = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,6 +229,7 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
PathSpecGroup group;
|
PathSpecGroup group;
|
||||||
String prefix;
|
String prefix;
|
||||||
String suffix;
|
String suffix;
|
||||||
|
MatchedPath preMatchedPath;
|
||||||
|
|
||||||
// prefix based
|
// prefix based
|
||||||
if (servletPathSpec.charAt(0) == '/' && servletPathSpec.endsWith("/*"))
|
if (servletPathSpec.charAt(0) == '/' && servletPathSpec.endsWith("/*"))
|
||||||
|
@ -230,6 +237,7 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
group = PathSpecGroup.PREFIX_GLOB;
|
group = PathSpecGroup.PREFIX_GLOB;
|
||||||
prefix = servletPathSpec.substring(0, specLength - 2);
|
prefix = servletPathSpec.substring(0, specLength - 2);
|
||||||
suffix = null;
|
suffix = null;
|
||||||
|
preMatchedPath = MatchedPath.from(prefix, null);
|
||||||
}
|
}
|
||||||
// suffix based
|
// suffix based
|
||||||
else if (servletPathSpec.charAt(0) == '*' && servletPathSpec.length() > 1)
|
else if (servletPathSpec.charAt(0) == '*' && servletPathSpec.length() > 1)
|
||||||
|
@ -237,6 +245,7 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
group = PathSpecGroup.SUFFIX_GLOB;
|
group = PathSpecGroup.SUFFIX_GLOB;
|
||||||
prefix = null;
|
prefix = null;
|
||||||
suffix = servletPathSpec.substring(2, specLength);
|
suffix = servletPathSpec.substring(2, specLength);
|
||||||
|
preMatchedPath = null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -248,15 +257,15 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
LOG.warn("Suspicious URL pattern: '{}'; see sections 12.1 and 12.2 of the Servlet specification",
|
LOG.warn("Suspicious URL pattern: '{}'; see sections 12.1 and 12.2 of the Servlet specification",
|
||||||
servletPathSpec);
|
servletPathSpec);
|
||||||
}
|
}
|
||||||
|
preMatchedPath = MatchedPath.from(servletPathSpec, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pathDepth = 0;
|
int pathDepth = 0;
|
||||||
for (int i = 0; i < specLength; i++)
|
for (int i = 0; i < specLength; i++)
|
||||||
{
|
{
|
||||||
int cp = servletPathSpec.codePointAt(i);
|
char c = servletPathSpec.charAt(i);
|
||||||
if (cp < 128)
|
if (c < 128)
|
||||||
{
|
{
|
||||||
char c = (char)cp;
|
|
||||||
if (c == '/')
|
if (c == '/')
|
||||||
pathDepth++;
|
pathDepth++;
|
||||||
}
|
}
|
||||||
|
@ -266,8 +275,17 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
_group = group;
|
_group = group;
|
||||||
_pathDepth = pathDepth;
|
_pathDepth = pathDepth;
|
||||||
_specLength = specLength;
|
_specLength = specLength;
|
||||||
|
_matchLength = prefix == null ? 0 : prefix.length();
|
||||||
_prefix = prefix;
|
_prefix = prefix;
|
||||||
_suffix = suffix;
|
_suffix = suffix;
|
||||||
|
_preMatchedPath = preMatchedPath;
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
{
|
||||||
|
LOG.debug("Creating {}[{}] (group: {}, prefix: \"{}\", suffix: \"{}\")",
|
||||||
|
getClass().getSimpleName(),
|
||||||
|
_declaration, _group, _prefix, _suffix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertValidServletPathSpec(String servletPathSpec)
|
private static void assertValidServletPathSpec(String servletPathSpec)
|
||||||
|
@ -334,6 +352,10 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
return _pathDepth;
|
return _pathDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #matched(String)}#{@link MatchedPath#getPathInfo()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public String getPathInfo(String path)
|
public String getPathInfo(String path)
|
||||||
{
|
{
|
||||||
|
@ -343,15 +365,19 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
return path;
|
return path;
|
||||||
|
|
||||||
case PREFIX_GLOB:
|
case PREFIX_GLOB:
|
||||||
if (path.length() == (_specLength - 2))
|
if (path.length() == _matchLength)
|
||||||
return null;
|
return null;
|
||||||
return _specLength == 2 ? path : path.substring(_specLength - 2);
|
return path.substring(_matchLength);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #matched(String)}#{@link MatchedPath#getPathMatch()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public String getPathMatch(String path)
|
public String getPathMatch(String path)
|
||||||
{
|
{
|
||||||
|
@ -367,7 +393,7 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
|
|
||||||
case PREFIX_GLOB:
|
case PREFIX_GLOB:
|
||||||
if (isWildcardMatch(path))
|
if (isWildcardMatch(path))
|
||||||
return path.substring(0, _specLength - 2);
|
return path.substring(0, _matchLength);
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case SUFFIX_GLOB:
|
case SUFFIX_GLOB:
|
||||||
|
@ -404,12 +430,44 @@ public class ServletPathSpec extends AbstractPathSpec
|
||||||
private boolean isWildcardMatch(String path)
|
private boolean isWildcardMatch(String path)
|
||||||
{
|
{
|
||||||
// For a spec of "/foo/*" match "/foo" , "/foo/..." but not "/foobar"
|
// For a spec of "/foo/*" match "/foo" , "/foo/..." but not "/foobar"
|
||||||
int cpl = _specLength - 2;
|
if (_group == PathSpecGroup.PREFIX_GLOB && path.length() >= _matchLength && path.regionMatches(0, _declaration, 0, _matchLength))
|
||||||
if ((_group == PathSpecGroup.PREFIX_GLOB) && (path.regionMatches(0, _declaration, 0, cpl)))
|
return path.length() == _matchLength || path.charAt(_matchLength) == '/';
|
||||||
return (path.length() == cpl) || ('/' == path.charAt(cpl));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatchedPath matched(String path)
|
||||||
|
{
|
||||||
|
switch (_group)
|
||||||
|
{
|
||||||
|
case EXACT:
|
||||||
|
if (_declaration.equals(path))
|
||||||
|
return _preMatchedPath;
|
||||||
|
break;
|
||||||
|
case PREFIX_GLOB:
|
||||||
|
if (isWildcardMatch(path))
|
||||||
|
{
|
||||||
|
if (path.length() == _matchLength)
|
||||||
|
return _preMatchedPath;
|
||||||
|
return MatchedPath.from(path.substring(0, _matchLength), path.substring(_matchLength));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SUFFIX_GLOB:
|
||||||
|
if (path.regionMatches((path.length() - _specLength) + 1, _declaration, 1, _specLength - 1))
|
||||||
|
return MatchedPath.from(path, null);
|
||||||
|
break;
|
||||||
|
case ROOT:
|
||||||
|
// Only "/" matches
|
||||||
|
if ("/".equals(path))
|
||||||
|
return _preMatchedPath;
|
||||||
|
break;
|
||||||
|
case DEFAULT:
|
||||||
|
// If we reached this point, then everything matches
|
||||||
|
return MatchedPath.from(path, null);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(String path)
|
public boolean matches(String path)
|
||||||
{
|
{
|
||||||
|
|
|
@ -197,6 +197,12 @@ public class UriTemplatePathSpec extends AbstractPathSpec
|
||||||
_pattern = pattern;
|
_pattern = pattern;
|
||||||
_variables = variables;
|
_variables = variables;
|
||||||
_logicalDeclaration = logicalSignature.toString();
|
_logicalDeclaration = logicalSignature.toString();
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
{
|
||||||
|
LOG.debug("Creating UriTemplatePathSpec[{}] (regex: \"{}\", signature: [{}], group: {}, variables: [{}])",
|
||||||
|
_declaration, regex, sig, _group, String.join(", ", _variables));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -296,7 +302,9 @@ public class UriTemplatePathSpec extends AbstractPathSpec
|
||||||
Map<String, String> ret = new HashMap<>();
|
Map<String, String> ret = new HashMap<>();
|
||||||
int groupCount = matcher.groupCount();
|
int groupCount = matcher.groupCount();
|
||||||
for (int i = 1; i <= groupCount; i++)
|
for (int i = 1; i <= groupCount; i++)
|
||||||
|
{
|
||||||
ret.put(_variables[i - 1], matcher.group(i));
|
ret.put(_variables[i - 1], matcher.group(i));
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -304,8 +312,18 @@ public class UriTemplatePathSpec extends AbstractPathSpec
|
||||||
|
|
||||||
protected Matcher getMatcher(String path)
|
protected Matcher getMatcher(String path)
|
||||||
{
|
{
|
||||||
|
int idx = path.indexOf('?');
|
||||||
|
if (idx >= 0)
|
||||||
|
{
|
||||||
|
// match only non-query part
|
||||||
|
return _pattern.matcher(path.substring(0, idx));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// match entire path
|
||||||
return _pattern.matcher(path);
|
return _pattern.matcher(path);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSpecLength()
|
public int getSpecLength()
|
||||||
|
@ -394,17 +412,18 @@ public class UriTemplatePathSpec extends AbstractPathSpec
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(final String path)
|
public boolean matches(final String path)
|
||||||
{
|
{
|
||||||
int idx = path.indexOf('?');
|
|
||||||
if (idx >= 0)
|
|
||||||
{
|
|
||||||
// match only non-query part
|
|
||||||
return getMatcher(path.substring(0, idx)).matches();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// match entire path
|
|
||||||
return getMatcher(path).matches();
|
return getMatcher(path).matches();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatchedPath matched(String path)
|
||||||
|
{
|
||||||
|
Matcher matcher = getMatcher(path);
|
||||||
|
if (matcher.matches())
|
||||||
|
{
|
||||||
|
return new UriTemplatePathSpec.UriTemplateMatchedPath(this, path, matcher);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getVariableCount()
|
public int getVariableCount()
|
||||||
|
@ -416,4 +435,62 @@ public class UriTemplatePathSpec extends AbstractPathSpec
|
||||||
{
|
{
|
||||||
return _variables;
|
return _variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class UriTemplateMatchedPath implements MatchedPath
|
||||||
|
{
|
||||||
|
private final UriTemplatePathSpec pathSpec;
|
||||||
|
private final String path;
|
||||||
|
private final Matcher matcher;
|
||||||
|
|
||||||
|
public UriTemplateMatchedPath(UriTemplatePathSpec uriTemplatePathSpec, String path, Matcher matcher)
|
||||||
|
{
|
||||||
|
this.pathSpec = uriTemplatePathSpec;
|
||||||
|
this.path = path;
|
||||||
|
this.matcher = matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPathMatch()
|
||||||
|
{
|
||||||
|
// TODO: UriTemplatePathSpec has no concept of prefix/suffix, this should be simplified
|
||||||
|
// TODO: Treat all UriTemplatePathSpec matches as exact when it comes to pathMatch/pathInfo
|
||||||
|
if (pathSpec.getGroup() == PathSpecGroup.PREFIX_GLOB && matcher.groupCount() >= 1)
|
||||||
|
{
|
||||||
|
int idx = matcher.start(1);
|
||||||
|
if (idx > 0)
|
||||||
|
{
|
||||||
|
if (path.charAt(idx - 1) == '/')
|
||||||
|
idx--;
|
||||||
|
return path.substring(0, idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPathInfo()
|
||||||
|
{
|
||||||
|
// TODO: UriTemplatePathSpec has no concept of prefix/suffix, this should be simplified
|
||||||
|
// TODO: Treat all UriTemplatePathSpec matches as exact when it comes to pathMatch/pathInfo
|
||||||
|
if (pathSpec.getGroup() == PathSpecGroup.PREFIX_GLOB && matcher.groupCount() >= 1)
|
||||||
|
{
|
||||||
|
String pathInfo = matcher.group(1);
|
||||||
|
if ("".equals(pathInfo))
|
||||||
|
return "/";
|
||||||
|
else
|
||||||
|
return pathInfo;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return getClass().getSimpleName() + "[" +
|
||||||
|
"pathSpec=" + pathSpec +
|
||||||
|
", path=\"" + path + "\"" +
|
||||||
|
", matcher=" + matcher +
|
||||||
|
']';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
package org.eclipse.jetty.http.pathmap;
|
package org.eclipse.jetty.http.pathmap;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
@ -24,32 +22,23 @@ import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
// @checkstyle-disable-check : AvoidEscapedUnicodeCharactersCheck
|
// @checkstyle-disable-check : AvoidEscapedUnicodeCharactersCheck
|
||||||
public class PathMappingsTest
|
public class PathMappingsTest
|
||||||
{
|
{
|
||||||
private void assertMatch(PathMappings<String> pathmap, String path, String expectedValue)
|
private void assertMatch(PathMappings<String> pathmap, String path, String expectedValue)
|
||||||
{
|
{
|
||||||
String msg = String.format(".getMatch(\"%s\")", path);
|
String msg = String.format(".getMatched(\"%s\")", path);
|
||||||
MappedResource<String> match = pathmap.getMatch(path);
|
MatchedResource<String> matched = pathmap.getMatched(path);
|
||||||
assertThat(msg, match, notNullValue());
|
assertThat(msg, matched, notNullValue());
|
||||||
String actualMatch = match.getResource();
|
String actualMatch = matched.getResource();
|
||||||
assertEquals(expectedValue, actualMatch, msg);
|
assertEquals(expectedValue, actualMatch, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dumpMappings(PathMappings<String> p)
|
|
||||||
{
|
|
||||||
for (MappedResource<String> res : p)
|
|
||||||
{
|
|
||||||
System.out.printf(" %s%n", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the match order rules with a mixed Servlet and regex path specs
|
* Test the match order rules with a mixed Servlet and regex path specs
|
||||||
* <p>
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Exact match</li>
|
* <li>Exact match</li>
|
||||||
* <li>Longest prefix match</li>
|
* <li>Longest prefix match</li>
|
||||||
|
@ -70,8 +59,6 @@ public class PathMappingsTest
|
||||||
p.put(new RegexPathSpec("^/animal/.*/cam$"), "animalCam");
|
p.put(new RegexPathSpec("^/animal/.*/cam$"), "animalCam");
|
||||||
p.put(new RegexPathSpec("^/entrance/cam$"), "entranceCam");
|
p.put(new RegexPathSpec("^/entrance/cam$"), "entranceCam");
|
||||||
|
|
||||||
// dumpMappings(p);
|
|
||||||
|
|
||||||
assertMatch(p, "/animal/bird/eagle", "birds");
|
assertMatch(p, "/animal/bird/eagle", "birds");
|
||||||
assertMatch(p, "/animal/fish/bass/sea", "fishes");
|
assertMatch(p, "/animal/fish/bass/sea", "fishes");
|
||||||
assertMatch(p, "/animal/peccary/javalina/evolution", "animals");
|
assertMatch(p, "/animal/peccary/javalina/evolution", "animals");
|
||||||
|
@ -102,7 +89,7 @@ public class PathMappingsTest
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the match order rules with a mixed Servlet and URI Template path specs
|
* Test the match order rules with a mixed Servlet and URI Template path specs
|
||||||
* <p>
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Exact match</li>
|
* <li>Exact match</li>
|
||||||
* <li>Longest prefix match</li>
|
* <li>Longest prefix match</li>
|
||||||
|
@ -122,8 +109,6 @@ public class PathMappingsTest
|
||||||
p.put(new UriTemplatePathSpec("/animal/{type}/{name}/cam"), "animalCam");
|
p.put(new UriTemplatePathSpec("/animal/{type}/{name}/cam"), "animalCam");
|
||||||
p.put(new UriTemplatePathSpec("/entrance/cam"), "entranceCam");
|
p.put(new UriTemplatePathSpec("/entrance/cam"), "entranceCam");
|
||||||
|
|
||||||
// dumpMappings(p);
|
|
||||||
|
|
||||||
assertMatch(p, "/animal/bird/eagle", "birds");
|
assertMatch(p, "/animal/bird/eagle", "birds");
|
||||||
assertMatch(p, "/animal/fish/bass/sea", "fishes");
|
assertMatch(p, "/animal/fish/bass/sea", "fishes");
|
||||||
assertMatch(p, "/animal/peccary/javalina/evolution", "animals");
|
assertMatch(p, "/animal/peccary/javalina/evolution", "animals");
|
||||||
|
@ -136,7 +121,7 @@ public class PathMappingsTest
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the match order rules for URI Template based specs
|
* Test the match order rules for URI Template based specs
|
||||||
* <p>
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Exact match</li>
|
* <li>Exact match</li>
|
||||||
* <li>Longest prefix match</li>
|
* <li>Longest prefix match</li>
|
||||||
|
@ -154,8 +139,6 @@ public class PathMappingsTest
|
||||||
p.put(new UriTemplatePathSpec("/{var1}/d"), "endpointD");
|
p.put(new UriTemplatePathSpec("/{var1}/d"), "endpointD");
|
||||||
p.put(new UriTemplatePathSpec("/b/{var2}"), "endpointE");
|
p.put(new UriTemplatePathSpec("/b/{var2}"), "endpointE");
|
||||||
|
|
||||||
// dumpMappings(p);
|
|
||||||
|
|
||||||
assertMatch(p, "/a/b/c", "endpointB");
|
assertMatch(p, "/a/b/c", "endpointB");
|
||||||
assertMatch(p, "/a/d/c", "endpointA");
|
assertMatch(p, "/a/d/c", "endpointA");
|
||||||
assertMatch(p, "/a/x/y", "endpointC");
|
assertMatch(p, "/a/x/y", "endpointC");
|
||||||
|
@ -163,8 +146,28 @@ public class PathMappingsTest
|
||||||
assertMatch(p, "/b/d", "endpointE");
|
assertMatch(p, "/b/d", "endpointE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the match order rules for mixed Servlet and Regex path specs
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testPathMap() throws Exception
|
public void testServletAndRegexMatchOrder()
|
||||||
|
{
|
||||||
|
PathMappings<String> p = new PathMappings<>();
|
||||||
|
|
||||||
|
p.put(new ServletPathSpec("/a/*"), "endpointA");
|
||||||
|
p.put(new RegexPathSpec("^.*/middle/.*$"), "middle");
|
||||||
|
p.put(new ServletPathSpec("*.do"), "endpointDo");
|
||||||
|
p.put(new ServletPathSpec("/"), "default");
|
||||||
|
|
||||||
|
assertMatch(p, "/a/b/c", "endpointA");
|
||||||
|
assertMatch(p, "/a/middle/c", "endpointA");
|
||||||
|
assertMatch(p, "/b/middle/c", "middle");
|
||||||
|
assertMatch(p, "/x/y.do", "endpointDo");
|
||||||
|
assertMatch(p, "/b/d", "default");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPathMap()
|
||||||
{
|
{
|
||||||
PathMappings<String> p = new PathMappings<>();
|
PathMappings<String> p = new PathMappings<>();
|
||||||
|
|
||||||
|
@ -178,76 +181,40 @@ public class PathMappingsTest
|
||||||
p.put(new ServletPathSpec("/"), "8");
|
p.put(new ServletPathSpec("/"), "8");
|
||||||
// p.put(new ServletPathSpec("/XXX:/YYY"), "9"); // special syntax from Jetty 3.1.x
|
// p.put(new ServletPathSpec("/XXX:/YYY"), "9"); // special syntax from Jetty 3.1.x
|
||||||
p.put(new ServletPathSpec(""), "10");
|
p.put(new ServletPathSpec(""), "10");
|
||||||
|
// @checkstyle-disable-check : AvoidEscapedUnicodeCharactersCheck
|
||||||
p.put(new ServletPathSpec("/\u20ACuro/*"), "11");
|
p.put(new ServletPathSpec("/\u20ACuro/*"), "11");
|
||||||
|
// @checkstyle-enable-check : AvoidEscapedUnicodeCharactersCheck
|
||||||
assertEquals("/Foo/bar", new ServletPathSpec("/Foo/bar").getPathMatch("/Foo/bar"), "pathMatch exact");
|
|
||||||
assertEquals("/Foo", new ServletPathSpec("/Foo/*").getPathMatch("/Foo/bar"), "pathMatch prefix");
|
|
||||||
assertEquals("/Foo", new ServletPathSpec("/Foo/*").getPathMatch("/Foo/"), "pathMatch prefix");
|
|
||||||
assertEquals("/Foo", new ServletPathSpec("/Foo/*").getPathMatch("/Foo"), "pathMatch prefix");
|
|
||||||
assertEquals("/Foo/bar.ext", new ServletPathSpec("*.ext").getPathMatch("/Foo/bar.ext"), "pathMatch suffix");
|
|
||||||
assertEquals("/Foo/bar.ext", new ServletPathSpec("/").getPathMatch("/Foo/bar.ext"), "pathMatch default");
|
|
||||||
|
|
||||||
assertEquals(null, new ServletPathSpec("/Foo/bar").getPathInfo("/Foo/bar"), "pathInfo exact");
|
|
||||||
assertEquals("/bar", new ServletPathSpec("/Foo/*").getPathInfo("/Foo/bar"), "pathInfo prefix");
|
|
||||||
assertEquals("/*", new ServletPathSpec("/Foo/*").getPathInfo("/Foo/*"), "pathInfo prefix");
|
|
||||||
assertEquals("/", new ServletPathSpec("/Foo/*").getPathInfo("/Foo/"), "pathInfo prefix");
|
|
||||||
assertEquals(null, new ServletPathSpec("/Foo/*").getPathInfo("/Foo"), "pathInfo prefix");
|
|
||||||
assertEquals(null, new ServletPathSpec("*.ext").getPathInfo("/Foo/bar.ext"), "pathInfo suffix");
|
|
||||||
assertEquals(null, new ServletPathSpec("/").getPathInfo("/Foo/bar.ext"), "pathInfo default");
|
|
||||||
|
|
||||||
p.put(new ServletPathSpec("/*"), "0");
|
p.put(new ServletPathSpec("/*"), "0");
|
||||||
|
|
||||||
// assertEquals("1", p.get("/abs/path"), "Get absolute path");
|
assertEquals("/abs/path", p.getMatched("/abs/path").getPathSpec().getDeclaration(), "Match absolute path");
|
||||||
assertEquals("/abs/path", p.getMatch("/abs/path").getPathSpec().getDeclaration(), "Match absolute path");
|
assertEquals("1", p.getMatched("/abs/path").getResource(), "Match absolute path");
|
||||||
assertEquals("1", p.getMatch("/abs/path").getResource(), "Match absolute path");
|
assertEquals("0", p.getMatched("/abs/path/xxx").getResource(), "Mismatch absolute path");
|
||||||
assertEquals("0", p.getMatch("/abs/path/xxx").getResource(), "Mismatch absolute path");
|
assertEquals("0", p.getMatched("/abs/pith").getResource(), "Mismatch absolute path");
|
||||||
assertEquals("0", p.getMatch("/abs/pith").getResource(), "Mismatch absolute path");
|
assertEquals("2", p.getMatched("/abs/path/longer").getResource(), "Match longer absolute path");
|
||||||
assertEquals("2", p.getMatch("/abs/path/longer").getResource(), "Match longer absolute path");
|
assertEquals("0", p.getMatched("/abs/path/").getResource(), "Not exact absolute path");
|
||||||
assertEquals("0", p.getMatch("/abs/path/").getResource(), "Not exact absolute path");
|
assertEquals("0", p.getMatched("/abs/path/xxx").getResource(), "Not exact absolute path");
|
||||||
assertEquals("0", p.getMatch("/abs/path/xxx").getResource(), "Not exact absolute path");
|
|
||||||
|
|
||||||
assertEquals("3", p.getMatch("/animal/bird/eagle/bald").getResource(), "Match longest prefix");
|
assertEquals("3", p.getMatched("/animal/bird/eagle/bald").getResource(), "Match longest prefix");
|
||||||
assertEquals("4", p.getMatch("/animal/fish/shark/grey").getResource(), "Match longest prefix");
|
assertEquals("4", p.getMatched("/animal/fish/shark/grey").getResource(), "Match longest prefix");
|
||||||
assertEquals("5", p.getMatch("/animal/insect/bug").getResource(), "Match longest prefix");
|
assertEquals("5", p.getMatched("/animal/insect/bug").getResource(), "Match longest prefix");
|
||||||
assertEquals("5", p.getMatch("/animal").getResource(), "mismatch exact prefix");
|
assertEquals("5", p.getMatched("/animal").getResource(), "mismatch exact prefix");
|
||||||
assertEquals("5", p.getMatch("/animal/").getResource(), "mismatch exact prefix");
|
assertEquals("5", p.getMatched("/animal/").getResource(), "mismatch exact prefix");
|
||||||
|
|
||||||
assertEquals("0", p.getMatch("/suffix/path.tar.gz").getResource(), "Match longest suffix");
|
assertEquals("0", p.getMatched("/suffix/path.tar.gz").getResource(), "Match longest suffix");
|
||||||
assertEquals("0", p.getMatch("/suffix/path.gz").getResource(), "Match longest suffix");
|
assertEquals("0", p.getMatched("/suffix/path.gz").getResource(), "Match longest suffix");
|
||||||
assertEquals("5", p.getMatch("/animal/path.gz").getResource(), "prefix rather than suffix");
|
assertEquals("5", p.getMatched("/animal/path.gz").getResource(), "prefix rather than suffix");
|
||||||
|
|
||||||
assertEquals("0", p.getMatch("/Other/path").getResource(), "default");
|
assertEquals("0", p.getMatched("/Other/path").getResource(), "default");
|
||||||
|
|
||||||
assertEquals("", new ServletPathSpec("/*").getPathMatch("/xxx/zzz"), "pathMatch /*");
|
assertEquals("10", p.getMatched("/").getResource(), "match / with ''");
|
||||||
assertEquals("/xxx/zzz", new ServletPathSpec("/*").getPathInfo("/xxx/zzz"), "pathInfo /*");
|
|
||||||
|
|
||||||
assertTrue(new ServletPathSpec("/").matches("/anything"), "match /");
|
|
||||||
assertTrue(new ServletPathSpec("/*").matches("/anything"), "match /*");
|
|
||||||
assertTrue(new ServletPathSpec("/foo").matches("/foo"), "match /foo");
|
|
||||||
assertTrue(!new ServletPathSpec("/foo").matches("/bar"), "!match /foo");
|
|
||||||
assertTrue(new ServletPathSpec("/foo/*").matches("/foo"), "match /foo/*");
|
|
||||||
assertTrue(new ServletPathSpec("/foo/*").matches("/foo/"), "match /foo/*");
|
|
||||||
assertTrue(new ServletPathSpec("/foo/*").matches("/foo/anything"), "match /foo/*");
|
|
||||||
assertTrue(!new ServletPathSpec("/foo/*").matches("/bar"), "!match /foo/*");
|
|
||||||
assertTrue(!new ServletPathSpec("/foo/*").matches("/bar/"), "!match /foo/*");
|
|
||||||
assertTrue(!new ServletPathSpec("/foo/*").matches("/bar/anything"), "!match /foo/*");
|
|
||||||
assertTrue(new ServletPathSpec("*.foo").matches("anything.foo"), "match *.foo");
|
|
||||||
assertTrue(!new ServletPathSpec("*.foo").matches("anything.bar"), "!match *.foo");
|
|
||||||
assertTrue(new ServletPathSpec("/On*").matches("/On*"), "match /On*");
|
|
||||||
assertTrue(!new ServletPathSpec("/On*").matches("/One"), "!match /One");
|
|
||||||
|
|
||||||
assertEquals("10", p.getMatch("/").getResource(), "match / with ''");
|
|
||||||
|
|
||||||
assertTrue(new ServletPathSpec("").matches("/"), "match \"\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See JIRA issue: JETTY-88.
|
* See JIRA issue: JETTY-88.
|
||||||
*
|
|
||||||
* @throws Exception failed test
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testPathMappingsOnlyMatchOnDirectoryNames() throws Exception
|
public void testPathMappingsOnlyMatchOnDirectoryNames()
|
||||||
{
|
{
|
||||||
ServletPathSpec spec = new ServletPathSpec("/xyz/*");
|
ServletPathSpec spec = new ServletPathSpec("/xyz/*");
|
||||||
|
|
||||||
|
@ -266,40 +233,25 @@ public class PathMappingsTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPrecidenceVsOrdering() throws Exception
|
public void testPrecedenceVsOrdering()
|
||||||
{
|
{
|
||||||
PathMappings<String> p = new PathMappings<>();
|
PathMappings<String> p = new PathMappings<>();
|
||||||
p.put(new ServletPathSpec("/dump/gzip/*"), "prefix");
|
p.put(new ServletPathSpec("/dump/gzip/*"), "prefix");
|
||||||
p.put(new ServletPathSpec("*.txt"), "suffix");
|
p.put(new ServletPathSpec("*.txt"), "suffix");
|
||||||
|
|
||||||
assertEquals(null, p.getMatch("/foo/bar"));
|
assertNull(p.getMatched("/foo/bar"));
|
||||||
assertEquals("prefix", p.getMatch("/dump/gzip/something").getResource());
|
assertEquals("prefix", p.getMatched("/dump/gzip/something").getResource());
|
||||||
assertEquals("suffix", p.getMatch("/foo/something.txt").getResource());
|
assertEquals("suffix", p.getMatched("/foo/something.txt").getResource());
|
||||||
assertEquals("prefix", p.getMatch("/dump/gzip/something.txt").getResource());
|
assertEquals("prefix", p.getMatched("/dump/gzip/something.txt").getResource());
|
||||||
|
|
||||||
p = new PathMappings<>();
|
p = new PathMappings<>();
|
||||||
p.put(new ServletPathSpec("*.txt"), "suffix");
|
p.put(new ServletPathSpec("*.txt"), "suffix");
|
||||||
p.put(new ServletPathSpec("/dump/gzip/*"), "prefix");
|
p.put(new ServletPathSpec("/dump/gzip/*"), "prefix");
|
||||||
|
|
||||||
assertEquals(null, p.getMatch("/foo/bar"));
|
assertNull(p.getMatched("/foo/bar"));
|
||||||
assertEquals("prefix", p.getMatch("/dump/gzip/something").getResource());
|
assertEquals("prefix", p.getMatched("/dump/gzip/something").getResource());
|
||||||
assertEquals("suffix", p.getMatch("/foo/something.txt").getResource());
|
assertEquals("suffix", p.getMatched("/foo/something.txt").getResource());
|
||||||
assertEquals("prefix", p.getMatch("/dump/gzip/something.txt").getResource());
|
assertEquals("prefix", p.getMatched("/dump/gzip/something.txt").getResource());
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@ValueSource(strings = {
|
|
||||||
"*",
|
|
||||||
"/foo/*/bar",
|
|
||||||
"*/foo",
|
|
||||||
"*.foo/*"
|
|
||||||
})
|
|
||||||
public void testBadPathSpecs(String str)
|
|
||||||
{
|
|
||||||
assertThrows(IllegalArgumentException.class, () ->
|
|
||||||
{
|
|
||||||
new ServletPathSpec(str);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -310,8 +262,8 @@ public class PathMappingsTest
|
||||||
assertThat(p.put(new UriTemplatePathSpec("/a/{var2}/c"), "resourceAA"), is(false));
|
assertThat(p.put(new UriTemplatePathSpec("/a/{var2}/c"), "resourceAA"), is(false));
|
||||||
assertThat(p.put(new UriTemplatePathSpec("/a/b/c"), "resourceB"), is(true));
|
assertThat(p.put(new UriTemplatePathSpec("/a/b/c"), "resourceB"), is(true));
|
||||||
assertThat(p.put(new UriTemplatePathSpec("/a/b/c"), "resourceBB"), is(false));
|
assertThat(p.put(new UriTemplatePathSpec("/a/b/c"), "resourceBB"), is(false));
|
||||||
assertThat(p.put(new ServletPathSpec("/a/b/c"), "resourceBB"), is(false));
|
assertThat(p.put(new ServletPathSpec("/a/b/c"), "resourceBB"), is(true));
|
||||||
assertThat(p.put(new RegexPathSpec("/a/b/c"), "resourceBB"), is(false));
|
assertThat(p.put(new RegexPathSpec("/a/b/c"), "resourceBB"), is(true));
|
||||||
|
|
||||||
assertThat(p.put(new ServletPathSpec("/*"), "resourceC"), is(true));
|
assertThat(p.put(new ServletPathSpec("/*"), "resourceC"), is(true));
|
||||||
assertThat(p.put(new RegexPathSpec("/(.*)"), "resourceCC"), is(true));
|
assertThat(p.put(new RegexPathSpec("/(.*)"), "resourceCC"), is(true));
|
||||||
|
@ -461,14 +413,14 @@ public class PathMappingsTest
|
||||||
@Test
|
@Test
|
||||||
public void testAsPathSpec()
|
public void testAsPathSpec()
|
||||||
{
|
{
|
||||||
assertThat(PathMappings.asPathSpec(""), instanceOf(ServletPathSpec.class));
|
assertThat(PathSpec.from(""), instanceOf(ServletPathSpec.class));
|
||||||
assertThat(PathMappings.asPathSpec("/"), instanceOf(ServletPathSpec.class));
|
assertThat(PathSpec.from("/"), instanceOf(ServletPathSpec.class));
|
||||||
assertThat(PathMappings.asPathSpec("/*"), instanceOf(ServletPathSpec.class));
|
assertThat(PathSpec.from("/*"), instanceOf(ServletPathSpec.class));
|
||||||
assertThat(PathMappings.asPathSpec("/foo/*"), instanceOf(ServletPathSpec.class));
|
assertThat(PathSpec.from("/foo/*"), instanceOf(ServletPathSpec.class));
|
||||||
assertThat(PathMappings.asPathSpec("*.jsp"), instanceOf(ServletPathSpec.class));
|
assertThat(PathSpec.from("*.jsp"), instanceOf(ServletPathSpec.class));
|
||||||
|
|
||||||
assertThat(PathMappings.asPathSpec("^$"), instanceOf(RegexPathSpec.class));
|
assertThat(PathSpec.from("^$"), instanceOf(RegexPathSpec.class));
|
||||||
assertThat(PathMappings.asPathSpec("^.*"), instanceOf(RegexPathSpec.class));
|
assertThat(PathSpec.from("^.*"), instanceOf(RegexPathSpec.class));
|
||||||
assertThat(PathMappings.asPathSpec("^/"), instanceOf(RegexPathSpec.class));
|
assertThat(PathSpec.from("^/"), instanceOf(RegexPathSpec.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class RegexPathSpecTest
|
public class RegexPathSpecTest
|
||||||
|
@ -28,13 +30,13 @@ public class RegexPathSpecTest
|
||||||
public static void assertMatches(PathSpec spec, String path)
|
public static void assertMatches(PathSpec spec, String path)
|
||||||
{
|
{
|
||||||
String msg = String.format("Spec(\"%s\").matches(\"%s\")", spec.getDeclaration(), path);
|
String msg = String.format("Spec(\"%s\").matches(\"%s\")", spec.getDeclaration(), path);
|
||||||
assertThat(msg, spec.matches(path), is(true));
|
assertNotNull(spec.matched(path), msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void assertNotMatches(PathSpec spec, String path)
|
public static void assertNotMatches(PathSpec spec, String path)
|
||||||
{
|
{
|
||||||
String msg = String.format("!Spec(\"%s\").matches(\"%s\")", spec.getDeclaration(), path);
|
String msg = String.format("!Spec(\"%s\").matches(\"%s\")", spec.getDeclaration(), path);
|
||||||
assertThat(msg, spec.matches(path), is(false));
|
assertNull(spec.matched(path), msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -127,7 +129,7 @@ public class RegexPathSpecTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuffixSpec()
|
public void testSuffixSpecTraditional()
|
||||||
{
|
{
|
||||||
RegexPathSpec spec = new RegexPathSpec("^(.*).do$");
|
RegexPathSpec spec = new RegexPathSpec("^(.*).do$");
|
||||||
assertEquals("^(.*).do$", spec.getDeclaration(), "Spec.pathSpec");
|
assertEquals("^(.*).do$", spec.getDeclaration(), "Spec.pathSpec");
|
||||||
|
@ -144,6 +146,105 @@ public class RegexPathSpecTest
|
||||||
assertNotMatches(spec, "/aa");
|
assertNotMatches(spec, "/aa");
|
||||||
assertNotMatches(spec, "/aa/bb");
|
assertNotMatches(spec, "/aa/bb");
|
||||||
assertNotMatches(spec, "/aa/bb.do/more");
|
assertNotMatches(spec, "/aa/bb.do/more");
|
||||||
|
|
||||||
|
assertThat(spec.getPathMatch("/a/b/c.do"), equalTo("/a/b/c.do"));
|
||||||
|
assertThat(spec.getPathInfo("/a/b/c.do"), nullValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A suffix type path spec, where the beginning of the path is evaluated
|
||||||
|
* but the rest of the path is ignored.
|
||||||
|
* The beginning is starts with a glob, contains a literal, and no terminal "$".
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSuffixSpecGlobish()
|
||||||
|
{
|
||||||
|
RegexPathSpec spec = new RegexPathSpec("^/[Hh]ello");
|
||||||
|
assertEquals("^/[Hh]ello", spec.getDeclaration(), "Spec.pathSpec");
|
||||||
|
assertEquals("^/[Hh]ello", spec.getPattern().pattern(), "Spec.pattern");
|
||||||
|
assertEquals(1, spec.getPathDepth(), "Spec.pathDepth");
|
||||||
|
assertEquals(PathSpecGroup.SUFFIX_GLOB, spec.getGroup(), "Spec.group");
|
||||||
|
|
||||||
|
assertMatches(spec, "/hello");
|
||||||
|
assertMatches(spec, "/Hello");
|
||||||
|
|
||||||
|
assertNotMatches(spec, "/Hello/World");
|
||||||
|
assertNotMatches(spec, "/a");
|
||||||
|
assertNotMatches(spec, "/aa");
|
||||||
|
assertNotMatches(spec, "/aa/bb");
|
||||||
|
assertNotMatches(spec, "/aa/bb.do/more");
|
||||||
|
|
||||||
|
assertThat(spec.getPathMatch("/hello"), equalTo("/hello"));
|
||||||
|
assertThat(spec.getPathInfo("/hello"), nullValue());
|
||||||
|
|
||||||
|
assertThat(spec.getPathMatch("/Hello"), equalTo("/Hello"));
|
||||||
|
assertThat(spec.getPathInfo("/Hello"), nullValue());
|
||||||
|
|
||||||
|
MatchedPath matchedPath = spec.matched("/hello");
|
||||||
|
assertThat(matchedPath.getPathMatch(), equalTo("/hello"));
|
||||||
|
assertThat(matchedPath.getPathInfo(), nullValue());
|
||||||
|
|
||||||
|
matchedPath = spec.matched("/Hello");
|
||||||
|
assertThat(matchedPath.getPathMatch(), equalTo("/Hello"));
|
||||||
|
assertThat(matchedPath.getPathInfo(), nullValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuffixSpecMiddle()
|
||||||
|
{
|
||||||
|
RegexPathSpec spec = new RegexPathSpec("^.*/middle/.*$");
|
||||||
|
assertEquals("^.*/middle/.*$", spec.getDeclaration(), "Spec.pathSpec");
|
||||||
|
assertEquals("^.*/middle/.*$", spec.getPattern().pattern(), "Spec.pattern");
|
||||||
|
assertEquals(2, spec.getPathDepth(), "Spec.pathDepth");
|
||||||
|
assertEquals(PathSpecGroup.SUFFIX_GLOB, spec.getGroup(), "Spec.group");
|
||||||
|
|
||||||
|
assertMatches(spec, "/a/middle/c.do");
|
||||||
|
assertMatches(spec, "/a/b/c/d/middle/e/f");
|
||||||
|
assertMatches(spec, "/middle/");
|
||||||
|
|
||||||
|
assertNotMatches(spec, "/a.do");
|
||||||
|
assertNotMatches(spec, "/a/middle");
|
||||||
|
assertNotMatches(spec, "/middle");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuffixSpecMiddleWithGroupings()
|
||||||
|
{
|
||||||
|
RegexPathSpec spec = new RegexPathSpec("^(.*)/middle/(.*)$");
|
||||||
|
assertEquals("^(.*)/middle/(.*)$", spec.getDeclaration(), "Spec.pathSpec");
|
||||||
|
assertEquals("^(.*)/middle/(.*)$", spec.getPattern().pattern(), "Spec.pattern");
|
||||||
|
assertEquals(2, spec.getPathDepth(), "Spec.pathDepth");
|
||||||
|
assertEquals(PathSpecGroup.SUFFIX_GLOB, spec.getGroup(), "Spec.group");
|
||||||
|
|
||||||
|
assertMatches(spec, "/a/middle/c.do");
|
||||||
|
assertMatches(spec, "/a/b/c/d/middle/e/f");
|
||||||
|
assertMatches(spec, "/middle/");
|
||||||
|
|
||||||
|
assertNotMatches(spec, "/a.do");
|
||||||
|
assertNotMatches(spec, "/a/middle");
|
||||||
|
assertNotMatches(spec, "/middle");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNamedRegexGroup()
|
||||||
|
{
|
||||||
|
RegexPathSpec spec = new RegexPathSpec("^(?<name>(.*)/middle/)(?<info>.*)$");
|
||||||
|
assertEquals("^(?<name>(.*)/middle/)(?<info>.*)$", spec.getDeclaration(), "Spec.pathSpec");
|
||||||
|
assertEquals("^(?<name>(.*)/middle/)(?<info>.*)$", spec.getPattern().pattern(), "Spec.pattern");
|
||||||
|
assertEquals(2, spec.getPathDepth(), "Spec.pathDepth");
|
||||||
|
assertEquals(PathSpecGroup.SUFFIX_GLOB, spec.getGroup(), "Spec.group");
|
||||||
|
|
||||||
|
assertMatches(spec, "/a/middle/c.do");
|
||||||
|
assertMatches(spec, "/a/b/c/d/middle/e/f");
|
||||||
|
assertMatches(spec, "/middle/");
|
||||||
|
|
||||||
|
assertNotMatches(spec, "/a.do");
|
||||||
|
assertNotMatches(spec, "/a/middle");
|
||||||
|
assertNotMatches(spec, "/middle");
|
||||||
|
|
||||||
|
MatchedPath matchedPath = spec.matched("/a/middle/c.do");
|
||||||
|
assertThat(matchedPath.getPathMatch(), is("/a/middle/"));
|
||||||
|
assertThat(matchedPath.getPathInfo(), is("c.do"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -24,7 +24,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests of {@link PathMappings#getMatch(String)}, with a focus on correct mapping selection order
|
* Tests of {@link PathMappings#getMatched(String)}, with a focus on correct mapping selection order
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("Duplicates")
|
@SuppressWarnings("Duplicates")
|
||||||
public class ServletPathSpecOrderTest
|
public class ServletPathSpecOrderTest
|
||||||
|
@ -53,6 +53,7 @@ public class ServletPathSpecOrderTest
|
||||||
data.add(Arguments.of("/Other/path", "default"));
|
data.add(Arguments.of("/Other/path", "default"));
|
||||||
// @checkstyle-disable-check : AvoidEscapedUnicodeCharactersCheck
|
// @checkstyle-disable-check : AvoidEscapedUnicodeCharactersCheck
|
||||||
data.add(Arguments.of("/\u20ACuro/path", "money"));
|
data.add(Arguments.of("/\u20ACuro/path", "money"));
|
||||||
|
// @checkstyle-enable-check : AvoidEscapedUnicodeCharactersCheck
|
||||||
data.add(Arguments.of("/", "root"));
|
data.add(Arguments.of("/", "root"));
|
||||||
|
|
||||||
// Extra tests
|
// Extra tests
|
||||||
|
@ -87,6 +88,6 @@ public class ServletPathSpecOrderTest
|
||||||
@MethodSource("data")
|
@MethodSource("data")
|
||||||
public void testMatch(String inputPath, String expectedResource)
|
public void testMatch(String inputPath, String expectedResource)
|
||||||
{
|
{
|
||||||
assertThat("Match on [" + inputPath + "]", mappings.getMatch(inputPath).getResource(), is(expectedResource));
|
assertThat("Match on [" + inputPath + "]", mappings.getMatched(inputPath).getResource(), is(expectedResource));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,70 +14,48 @@
|
||||||
package org.eclipse.jetty.http.pathmap;
|
package org.eclipse.jetty.http.pathmap;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class ServletPathSpecTest
|
public class ServletPathSpecTest
|
||||||
{
|
{
|
||||||
private void assertBadServletPathSpec(String pathSpec)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
new ServletPathSpec(pathSpec);
|
|
||||||
fail("Expected IllegalArgumentException for a bad servlet pathspec on: " + pathSpec);
|
|
||||||
}
|
|
||||||
catch (IllegalArgumentException e)
|
|
||||||
{
|
|
||||||
// expected path
|
|
||||||
System.out.println(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertMatches(ServletPathSpec spec, String path)
|
private void assertMatches(ServletPathSpec spec, String path)
|
||||||
{
|
{
|
||||||
String msg = String.format("Spec(\"%s\").matches(\"%s\")", spec.getDeclaration(), path);
|
String msg = String.format("Spec(\"%s\").matches(\"%s\")", spec.getDeclaration(), path);
|
||||||
assertThat(msg, spec.matches(path), is(true));
|
assertThat(msg, spec.matched(path), not(nullValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertNotMatches(ServletPathSpec spec, String path)
|
private void assertNotMatches(ServletPathSpec spec, String path)
|
||||||
{
|
{
|
||||||
String msg = String.format("!Spec(\"%s\").matches(\"%s\")", spec.getDeclaration(), path);
|
String msg = String.format("!Spec(\"%s\").matches(\"%s\")", spec.getDeclaration(), path);
|
||||||
assertThat(msg, spec.matches(path), is(false));
|
assertThat(msg, spec.matched(path), is(nullValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void testBadServletPathSpecA()
|
@ValueSource(strings = {
|
||||||
|
"foo",
|
||||||
|
"/foo/*.do",
|
||||||
|
"foo/*.do",
|
||||||
|
"foo/*.*do",
|
||||||
|
"*",
|
||||||
|
"*do",
|
||||||
|
"/foo/*/bar",
|
||||||
|
"*/foo",
|
||||||
|
"*.foo/*"
|
||||||
|
})
|
||||||
|
public void testBadPathSpecs(String str)
|
||||||
{
|
{
|
||||||
assertBadServletPathSpec("foo");
|
assertThrows(IllegalArgumentException.class, () -> new ServletPathSpec(str));
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBadServletPathSpecB()
|
|
||||||
{
|
|
||||||
assertBadServletPathSpec("/foo/*.do");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBadServletPathSpecC()
|
|
||||||
{
|
|
||||||
assertBadServletPathSpec("foo/*.do");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBadServletPathSpecD()
|
|
||||||
{
|
|
||||||
assertBadServletPathSpec("foo/*.*do");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBadServletPathSpecE()
|
|
||||||
{
|
|
||||||
assertBadServletPathSpec("*do");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -107,16 +85,27 @@ public class ServletPathSpecTest
|
||||||
@Test
|
@Test
|
||||||
public void testGetPathInfo()
|
public void testGetPathInfo()
|
||||||
{
|
{
|
||||||
assertEquals(null, new ServletPathSpec("/Foo/bar").getPathInfo("/Foo/bar"), "pathInfo exact");
|
ServletPathSpec spec = new ServletPathSpec("/Foo/bar");
|
||||||
assertEquals("/bar", new ServletPathSpec("/Foo/*").getPathInfo("/Foo/bar"), "pathInfo prefix");
|
assertThat("PathInfo exact", spec.matched("/Foo/bar").getPathInfo(), is(nullValue()));
|
||||||
assertEquals("/*", new ServletPathSpec("/Foo/*").getPathInfo("/Foo/*"), "pathInfo prefix");
|
|
||||||
assertEquals("/", new ServletPathSpec("/Foo/*").getPathInfo("/Foo/"), "pathInfo prefix");
|
spec = new ServletPathSpec("/Foo/*");
|
||||||
assertEquals(null, new ServletPathSpec("/Foo/*").getPathInfo("/Foo"), "pathInfo prefix");
|
assertThat("PathInfo prefix", spec.matched("/Foo/bar").getPathInfo(), is("/bar"));
|
||||||
assertEquals(null, new ServletPathSpec("*.ext").getPathInfo("/Foo/bar.ext"), "pathInfo suffix");
|
assertThat("PathInfo prefix", spec.matched("/Foo/*").getPathInfo(), is("/*"));
|
||||||
assertEquals(null, new ServletPathSpec("/").getPathInfo("/Foo/bar.ext"), "pathInfo default");
|
assertThat("PathInfo prefix", spec.matched("/Foo/").getPathInfo(), is("/"));
|
||||||
assertEquals("/", new ServletPathSpec("").getPathInfo("/"), "pathInfo root");
|
assertThat("PathInfo prefix", spec.matched("/Foo").getPathInfo(), is(nullValue()));
|
||||||
assertEquals("", new ServletPathSpec("").getPathInfo(""), "pathInfo root");
|
|
||||||
assertEquals("/xxx/zzz", new ServletPathSpec("/*").getPathInfo("/xxx/zzz"), "pathInfo default");
|
spec = new ServletPathSpec("*.ext");
|
||||||
|
assertThat("PathInfo suffix", spec.matched("/Foo/bar.ext").getPathInfo(), is(nullValue()));
|
||||||
|
|
||||||
|
spec = new ServletPathSpec("/");
|
||||||
|
assertThat("PathInfo default", spec.matched("/Foo/bar.ext").getPathInfo(), is(nullValue()));
|
||||||
|
|
||||||
|
spec = new ServletPathSpec("");
|
||||||
|
assertThat("PathInfo root", spec.matched("/").getPathInfo(), is("/"));
|
||||||
|
assertThat("PathInfo root", spec.matched(""), is(nullValue())); // does not match // TODO: verify with greg
|
||||||
|
|
||||||
|
spec = new ServletPathSpec("/*");
|
||||||
|
assertThat("PathInfo default", spec.matched("/xxx/zzz").getPathInfo(), is("/xxx/zzz"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -138,15 +127,26 @@ public class ServletPathSpecTest
|
||||||
@Test
|
@Test
|
||||||
public void testPathMatch()
|
public void testPathMatch()
|
||||||
{
|
{
|
||||||
assertEquals("/Foo/bar", new ServletPathSpec("/Foo/bar").getPathMatch("/Foo/bar"), "pathMatch exact");
|
ServletPathSpec spec = new ServletPathSpec("/Foo/bar");
|
||||||
assertEquals("/Foo", new ServletPathSpec("/Foo/*").getPathMatch("/Foo/bar"), "pathMatch prefix");
|
assertThat("PathMatch exact", spec.matched("/Foo/bar").getPathMatch(), is("/Foo/bar"));
|
||||||
assertEquals("/Foo", new ServletPathSpec("/Foo/*").getPathMatch("/Foo/"), "pathMatch prefix");
|
|
||||||
assertEquals("/Foo", new ServletPathSpec("/Foo/*").getPathMatch("/Foo"), "pathMatch prefix");
|
spec = new ServletPathSpec("/Foo/*");
|
||||||
assertEquals("/Foo/bar.ext", new ServletPathSpec("*.ext").getPathMatch("/Foo/bar.ext"), "pathMatch suffix");
|
assertThat("PathMatch prefix", spec.matched("/Foo/bar").getPathMatch(), is("/Foo"));
|
||||||
assertEquals("/Foo/bar.ext", new ServletPathSpec("/").getPathMatch("/Foo/bar.ext"), "pathMatch default");
|
assertThat("PathMatch prefix", spec.matched("/Foo/").getPathMatch(), is("/Foo"));
|
||||||
assertEquals("", new ServletPathSpec("").getPathMatch("/"), "pathInfo root");
|
assertThat("PathMatch prefix", spec.matched("/Foo").getPathMatch(), is("/Foo"));
|
||||||
assertEquals("", new ServletPathSpec("").getPathMatch(""), "pathInfo root");
|
|
||||||
assertEquals("", new ServletPathSpec("/*").getPathMatch("/xxx/zzz"), "pathMatch default");
|
spec = new ServletPathSpec("*.ext");
|
||||||
|
assertThat("PathMatch suffix", spec.matched("/Foo/bar.ext").getPathMatch(), is("/Foo/bar.ext"));
|
||||||
|
|
||||||
|
spec = new ServletPathSpec("/");
|
||||||
|
assertThat("PathMatch default", spec.matched("/Foo/bar.ext").getPathMatch(), is("/Foo/bar.ext"));
|
||||||
|
|
||||||
|
spec = new ServletPathSpec("");
|
||||||
|
assertThat("PathMatch root", spec.matched("/").getPathMatch(), is(""));
|
||||||
|
assertThat("PathMatch root", spec.matched(""), is(nullValue())); // does not match // TODO: verify with greg
|
||||||
|
|
||||||
|
spec = new ServletPathSpec("/*");
|
||||||
|
assertThat("PathMatch default", spec.matched("/xxx/zzz").getPathMatch(), is(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -163,9 +163,39 @@ public class ServletPathSpecTest
|
||||||
|
|
||||||
assertMatches(spec, "/downloads");
|
assertMatches(spec, "/downloads");
|
||||||
|
|
||||||
assertEquals("/", spec.getPathInfo("/downloads/"), "Spec.pathInfo");
|
MatchedPath matched = spec.matched("/downloads/");
|
||||||
assertEquals("/distribution.zip", spec.getPathInfo("/downloads/distribution.zip"), "Spec.pathInfo");
|
assertThat("matched.pathMatch", matched.getPathMatch(), is("/downloads"));
|
||||||
assertEquals("/dist/9.0/distribution.tar.gz", spec.getPathInfo("/downloads/dist/9.0/distribution.tar.gz"), "Spec.pathInfo");
|
assertThat("matched.pathInfo", matched.getPathInfo(), is("/"));
|
||||||
|
|
||||||
|
matched = spec.matched("/downloads/distribution.zip");
|
||||||
|
assertThat("matched.pathMatch", matched.getPathMatch(), is("/downloads"));
|
||||||
|
assertThat("matched.pathInfo", matched.getPathInfo(), is("/distribution.zip"));
|
||||||
|
|
||||||
|
matched = spec.matched("/downloads/dist/9.0/distribution.tar.gz");
|
||||||
|
assertThat("matched.pathMatch", matched.getPathMatch(), is("/downloads"));
|
||||||
|
assertThat("matched.pathInfo", matched.getPathInfo(), is("/dist/9.0/distribution.tar.gz"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMatches()
|
||||||
|
{
|
||||||
|
assertTrue(new ServletPathSpec("/").matches("/anything"), "match /");
|
||||||
|
assertTrue(new ServletPathSpec("/*").matches("/anything"), "match /*");
|
||||||
|
assertTrue(new ServletPathSpec("/foo").matches("/foo"), "match /foo");
|
||||||
|
assertFalse(new ServletPathSpec("/foo").matches("/bar"), "!match /foo");
|
||||||
|
assertTrue(new ServletPathSpec("/foo/*").matches("/foo"), "match /foo/*");
|
||||||
|
assertTrue(new ServletPathSpec("/foo/*").matches("/foo/"), "match /foo/*");
|
||||||
|
assertTrue(new ServletPathSpec("/foo/*").matches("/foo/anything"), "match /foo/*");
|
||||||
|
assertFalse(new ServletPathSpec("/foo/*").matches("/bar"), "!match /foo/*");
|
||||||
|
assertFalse(new ServletPathSpec("/foo/*").matches("/bar/"), "!match /foo/*");
|
||||||
|
assertFalse(new ServletPathSpec("/foo/*").matches("/bar/anything"), "!match /foo/*");
|
||||||
|
assertTrue(new ServletPathSpec("*.foo").matches("anything.foo"), "match *.foo");
|
||||||
|
assertFalse(new ServletPathSpec("*.foo").matches("anything.bar"), "!match *.foo");
|
||||||
|
assertTrue(new ServletPathSpec("/On*").matches("/On*"), "match /On*");
|
||||||
|
assertFalse(new ServletPathSpec("/On*").matches("/One"), "!match /One");
|
||||||
|
|
||||||
|
assertTrue(new ServletPathSpec("").matches("/"), "match \"\"");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -182,7 +212,9 @@ public class ServletPathSpecTest
|
||||||
assertNotMatches(spec, "/downloads/distribution.tgz");
|
assertNotMatches(spec, "/downloads/distribution.tgz");
|
||||||
assertNotMatches(spec, "/abs/path");
|
assertNotMatches(spec, "/abs/path");
|
||||||
|
|
||||||
assertEquals(null, spec.getPathInfo("/downloads/distribution.tar.gz"), "Spec.pathInfo");
|
MatchedPath matched = spec.matched("/downloads/distribution.tar.gz");
|
||||||
|
assertThat("Suffix.pathMatch", matched.getPathMatch(), is("/downloads/distribution.tar.gz"));
|
||||||
|
assertThat("Suffix.pathInfo", matched.getPathInfo(), is(nullValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -196,5 +228,4 @@ public class ServletPathSpecTest
|
||||||
assertThat(new ServletPathSpec("/bar/foo"), not(equalTo(new ServletPathSpec("/foo/bar"))));
|
assertThat(new ServletPathSpec("/bar/foo"), not(equalTo(new ServletPathSpec("/foo/bar"))));
|
||||||
assertThat(new ServletPathSpec("/foo"), not(equalTo(new RegexPathSpec("/foo"))));
|
assertThat(new ServletPathSpec("/foo"), not(equalTo(new RegexPathSpec("/foo"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
#org.eclipse.jetty.LEVEL=DEBUG
|
#org.eclipse.jetty.LEVEL=DEBUG
|
||||||
#org.eclipse.jetty.server.LEVEL=DEBUG
|
#org.eclipse.jetty.server.LEVEL=DEBUG
|
||||||
#org.eclipse.jetty.http.LEVEL=DEBUG
|
#org.eclipse.jetty.http.LEVEL=DEBUG
|
||||||
|
#org.eclipse.jetty.http.pathmap.LEVEL=DEBUG
|
||||||
|
|
|
@ -34,6 +34,7 @@ import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.pathmap.MappedResource;
|
import org.eclipse.jetty.http.pathmap.MappedResource;
|
||||||
|
import org.eclipse.jetty.http.pathmap.MatchedResource;
|
||||||
import org.eclipse.jetty.http.pathmap.PathMappings;
|
import org.eclipse.jetty.http.pathmap.PathMappings;
|
||||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
import org.eclipse.jetty.server.HttpConfiguration;
|
||||||
|
@ -575,7 +576,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
@Override
|
@Override
|
||||||
protected RoleInfo prepareConstraintInfo(String pathInContext, Request request)
|
protected RoleInfo prepareConstraintInfo(String pathInContext, Request request)
|
||||||
{
|
{
|
||||||
MappedResource<Map<String, RoleInfo>> resource = _constraintRoles.getMatch(pathInContext);
|
MatchedResource<Map<String, RoleInfo>> resource = _constraintRoles.getMatched(pathInContext);
|
||||||
if (resource == null)
|
if (resource == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|
|
@ -388,14 +388,14 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
|
||||||
@Override
|
@Override
|
||||||
public void log(Request request, Response response)
|
public void log(Request request, Response response)
|
||||||
{
|
{
|
||||||
if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null)
|
try
|
||||||
|
{
|
||||||
|
if (_ignorePathMap != null && _ignorePathMap.getMatched(request.getRequestURI()) != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_filter != null && !_filter.test(request, response))
|
if (_filter != null && !_filter.test(request, response))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
StringBuilder sb = _buffers.get();
|
StringBuilder sb = _buffers.get();
|
||||||
sb.setLength(0);
|
sb.setLength(0);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import javax.servlet.http.HttpServletMapping;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.MappingMatch;
|
import javax.servlet.http.MappingMatch;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.pathmap.MatchedPath;
|
||||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||||
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||||
|
|
||||||
|
@ -39,13 +40,52 @@ public class ServletPathMapping implements HttpServletMapping
|
||||||
private final String _servletPath;
|
private final String _servletPath;
|
||||||
private final String _pathInfo;
|
private final String _pathInfo;
|
||||||
|
|
||||||
public ServletPathMapping(PathSpec pathSpec, String servletName, String pathInContext)
|
public ServletPathMapping(PathSpec pathSpec, String servletName, String pathInContext, MatchedPath matchedPath)
|
||||||
{
|
{
|
||||||
_servletName = (servletName == null ? "" : servletName);
|
_servletName = (servletName == null ? "" : servletName);
|
||||||
_pattern = pathSpec == null ? null : pathSpec.getDeclaration();
|
|
||||||
|
|
||||||
if (pathSpec instanceof ServletPathSpec && pathInContext != null)
|
if (pathSpec == null)
|
||||||
{
|
{
|
||||||
|
_pattern = null;
|
||||||
|
_mappingMatch = null;
|
||||||
|
_matchValue = "";
|
||||||
|
_servletPath = pathInContext;
|
||||||
|
_pathInfo = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathInContext == null)
|
||||||
|
{
|
||||||
|
_pattern = pathSpec.getDeclaration();
|
||||||
|
_mappingMatch = null;
|
||||||
|
_matchValue = "";
|
||||||
|
_servletPath = "";
|
||||||
|
_pathInfo = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path Spec types that are not ServletPathSpec
|
||||||
|
if (!(pathSpec instanceof ServletPathSpec))
|
||||||
|
{
|
||||||
|
_pattern = pathSpec.getDeclaration();
|
||||||
|
_mappingMatch = null;
|
||||||
|
if (matchedPath != null)
|
||||||
|
{
|
||||||
|
_servletPath = matchedPath.getPathMatch();
|
||||||
|
_pathInfo = matchedPath.getPathInfo();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_servletPath = pathInContext;
|
||||||
|
_pathInfo = null;
|
||||||
|
}
|
||||||
|
_matchValue = _servletPath.substring(_servletPath.charAt(0) == '/' ? 1 : 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from here down is ServletPathSpec behavior
|
||||||
|
_pattern = pathSpec.getDeclaration();
|
||||||
|
|
||||||
switch (pathSpec.getGroup())
|
switch (pathSpec.getGroup())
|
||||||
{
|
{
|
||||||
case ROOT:
|
case ROOT:
|
||||||
|
@ -74,7 +114,7 @@ public class ServletPathMapping implements HttpServletMapping
|
||||||
_servletPath = pathSpec.getPrefix();
|
_servletPath = pathSpec.getPrefix();
|
||||||
// TODO avoid the substring on the known servletPath!
|
// TODO avoid the substring on the known servletPath!
|
||||||
_matchValue = _servletPath.startsWith("/") ? _servletPath.substring(1) : _servletPath;
|
_matchValue = _servletPath.startsWith("/") ? _servletPath.substring(1) : _servletPath;
|
||||||
_pathInfo = pathSpec.getPathInfo(pathInContext);
|
_pathInfo = matchedPath != null ? matchedPath.getPathInfo() : null;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SUFFIX_GLOB:
|
case SUFFIX_GLOB:
|
||||||
|
@ -87,23 +127,13 @@ public class ServletPathMapping implements HttpServletMapping
|
||||||
|
|
||||||
case MIDDLE_GLOB:
|
case MIDDLE_GLOB:
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException("ServletPathSpec of type MIDDLE_GLOB");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pathSpec != null)
|
|
||||||
|
public ServletPathMapping(PathSpec pathSpec, String servletName, String pathInContext)
|
||||||
{
|
{
|
||||||
_mappingMatch = null;
|
this(pathSpec, servletName, pathInContext, null);
|
||||||
_servletPath = pathSpec.getPathMatch(pathInContext);
|
|
||||||
_matchValue = _servletPath.startsWith("/") ? _servletPath.substring(1) : _servletPath;
|
|
||||||
_pathInfo = pathSpec.getPathInfo(pathInContext);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_mappingMatch = null;
|
|
||||||
_matchValue = "";
|
|
||||||
_servletPath = pathInContext;
|
|
||||||
_pathInfo = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -62,6 +62,7 @@ import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.http.MetaData;
|
import org.eclipse.jetty.http.MetaData;
|
||||||
import org.eclipse.jetty.http.MimeTypes;
|
import org.eclipse.jetty.http.MimeTypes;
|
||||||
import org.eclipse.jetty.http.UriCompliance;
|
import org.eclipse.jetty.http.UriCompliance;
|
||||||
|
import org.eclipse.jetty.http.pathmap.MatchedPath;
|
||||||
import org.eclipse.jetty.http.pathmap.RegexPathSpec;
|
import org.eclipse.jetty.http.pathmap.RegexPathSpec;
|
||||||
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
|
@ -2014,11 +2015,13 @@ public class RequestTest
|
||||||
{
|
{
|
||||||
ServletPathSpec spec;
|
ServletPathSpec spec;
|
||||||
String uri;
|
String uri;
|
||||||
|
MatchedPath matched;
|
||||||
ServletPathMapping m;
|
ServletPathMapping m;
|
||||||
|
|
||||||
spec = null;
|
spec = null;
|
||||||
uri = null;
|
uri = null;
|
||||||
m = new ServletPathMapping(spec, null, uri);
|
matched = null;
|
||||||
|
m = new ServletPathMapping(spec, null, uri, matched);
|
||||||
assertThat(m.getMappingMatch(), nullValue());
|
assertThat(m.getMappingMatch(), nullValue());
|
||||||
assertThat(m.getMatchValue(), is(""));
|
assertThat(m.getMatchValue(), is(""));
|
||||||
assertThat(m.getPattern(), nullValue());
|
assertThat(m.getPattern(), nullValue());
|
||||||
|
@ -2028,71 +2031,78 @@ public class RequestTest
|
||||||
|
|
||||||
spec = new ServletPathSpec("");
|
spec = new ServletPathSpec("");
|
||||||
uri = "/";
|
uri = "/";
|
||||||
m = new ServletPathMapping(spec, "Something", uri);
|
matched = spec.matched(uri);
|
||||||
|
m = new ServletPathMapping(spec, "Something", uri, matched);
|
||||||
assertThat(m.getMappingMatch(), is(MappingMatch.CONTEXT_ROOT));
|
assertThat(m.getMappingMatch(), is(MappingMatch.CONTEXT_ROOT));
|
||||||
assertThat(m.getMatchValue(), is(""));
|
assertThat(m.getMatchValue(), is(""));
|
||||||
assertThat(m.getPattern(), is(""));
|
assertThat(m.getPattern(), is(""));
|
||||||
assertThat(m.getServletName(), is("Something"));
|
assertThat(m.getServletName(), is("Something"));
|
||||||
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
|
assertThat(m.getServletPath(), is(""));
|
||||||
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
|
assertThat(m.getPathInfo(), is("/"));
|
||||||
|
|
||||||
spec = new ServletPathSpec("/");
|
spec = new ServletPathSpec("/");
|
||||||
uri = "/some/path";
|
uri = "/some/path";
|
||||||
m = new ServletPathMapping(spec, "Default", uri);
|
matched = spec.matched(uri);
|
||||||
|
m = new ServletPathMapping(spec, "Default", uri, matched);
|
||||||
assertThat(m.getMappingMatch(), is(MappingMatch.DEFAULT));
|
assertThat(m.getMappingMatch(), is(MappingMatch.DEFAULT));
|
||||||
assertThat(m.getMatchValue(), is(""));
|
assertThat(m.getMatchValue(), is(""));
|
||||||
assertThat(m.getPattern(), is("/"));
|
assertThat(m.getPattern(), is("/"));
|
||||||
assertThat(m.getServletName(), is("Default"));
|
assertThat(m.getServletName(), is("Default"));
|
||||||
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
|
assertThat(m.getServletPath(), is("/some/path"));
|
||||||
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
|
assertThat(m.getPathInfo(), nullValue());
|
||||||
|
|
||||||
spec = new ServletPathSpec("/foo/*");
|
spec = new ServletPathSpec("/foo/*");
|
||||||
uri = "/foo/bar";
|
uri = "/foo/bar";
|
||||||
m = new ServletPathMapping(spec, "FooServlet", uri);
|
matched = spec.matched(uri);
|
||||||
|
m = new ServletPathMapping(spec, "FooServlet", uri, matched);
|
||||||
assertThat(m.getMappingMatch(), is(MappingMatch.PATH));
|
assertThat(m.getMappingMatch(), is(MappingMatch.PATH));
|
||||||
assertThat(m.getMatchValue(), is("foo"));
|
assertThat(m.getMatchValue(), is("foo"));
|
||||||
assertThat(m.getPattern(), is("/foo/*"));
|
assertThat(m.getPattern(), is("/foo/*"));
|
||||||
assertThat(m.getServletName(), is("FooServlet"));
|
assertThat(m.getServletName(), is("FooServlet"));
|
||||||
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
|
assertThat(m.getServletPath(), is("/foo"));
|
||||||
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
|
assertThat(m.getPathInfo(), is("/bar"));
|
||||||
|
|
||||||
uri = "/foo/";
|
uri = "/foo/";
|
||||||
m = new ServletPathMapping(spec, "FooServlet", uri);
|
matched = spec.matched(uri);
|
||||||
|
m = new ServletPathMapping(spec, "FooServlet", uri, matched);
|
||||||
assertThat(m.getMappingMatch(), is(MappingMatch.PATH));
|
assertThat(m.getMappingMatch(), is(MappingMatch.PATH));
|
||||||
assertThat(m.getMatchValue(), is("foo"));
|
assertThat(m.getMatchValue(), is("foo"));
|
||||||
assertThat(m.getPattern(), is("/foo/*"));
|
assertThat(m.getPattern(), is("/foo/*"));
|
||||||
assertThat(m.getServletName(), is("FooServlet"));
|
assertThat(m.getServletName(), is("FooServlet"));
|
||||||
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
|
assertThat(m.getServletPath(), is("/foo"));
|
||||||
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
|
assertThat(m.getPathInfo(), is("/"));
|
||||||
|
|
||||||
uri = "/foo";
|
uri = "/foo";
|
||||||
m = new ServletPathMapping(spec, "FooServlet", uri);
|
matched = spec.matched(uri);
|
||||||
|
m = new ServletPathMapping(spec, "FooServlet", uri, matched);
|
||||||
assertThat(m.getMappingMatch(), is(MappingMatch.PATH));
|
assertThat(m.getMappingMatch(), is(MappingMatch.PATH));
|
||||||
assertThat(m.getMatchValue(), is("foo"));
|
assertThat(m.getMatchValue(), is("foo"));
|
||||||
assertThat(m.getPattern(), is("/foo/*"));
|
assertThat(m.getPattern(), is("/foo/*"));
|
||||||
assertThat(m.getServletName(), is("FooServlet"));
|
assertThat(m.getServletName(), is("FooServlet"));
|
||||||
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
|
assertThat(m.getServletPath(), is("/foo"));
|
||||||
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
|
assertThat(m.getPathInfo(), nullValue());
|
||||||
|
|
||||||
spec = new ServletPathSpec("*.jsp");
|
spec = new ServletPathSpec("*.jsp");
|
||||||
uri = "/foo/bar.jsp";
|
uri = "/foo/bar.jsp";
|
||||||
m = new ServletPathMapping(spec, "JspServlet", uri);
|
matched = spec.matched(uri);
|
||||||
|
m = new ServletPathMapping(spec, "JspServlet", uri, matched);
|
||||||
assertThat(m.getMappingMatch(), is(MappingMatch.EXTENSION));
|
assertThat(m.getMappingMatch(), is(MappingMatch.EXTENSION));
|
||||||
assertThat(m.getMatchValue(), is("foo/bar"));
|
assertThat(m.getMatchValue(), is("foo/bar"));
|
||||||
assertThat(m.getPattern(), is("*.jsp"));
|
assertThat(m.getPattern(), is("*.jsp"));
|
||||||
assertThat(m.getServletName(), is("JspServlet"));
|
assertThat(m.getServletName(), is("JspServlet"));
|
||||||
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
|
assertThat(m.getServletPath(), is("/foo/bar.jsp"));
|
||||||
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
|
assertThat(m.getPathInfo(), nullValue());
|
||||||
|
|
||||||
spec = new ServletPathSpec("/catalog");
|
spec = new ServletPathSpec("/catalog");
|
||||||
uri = "/catalog";
|
uri = "/catalog";
|
||||||
m = new ServletPathMapping(spec, "CatalogServlet", uri);
|
matched = spec.matched(uri);
|
||||||
|
m = new ServletPathMapping(spec, "CatalogServlet", uri, matched);
|
||||||
assertThat(m.getMappingMatch(), is(MappingMatch.EXACT));
|
assertThat(m.getMappingMatch(), is(MappingMatch.EXACT));
|
||||||
assertThat(m.getMatchValue(), is("catalog"));
|
assertThat(m.getMatchValue(), is("catalog"));
|
||||||
assertThat(m.getPattern(), is("/catalog"));
|
assertThat(m.getPattern(), is("/catalog"));
|
||||||
assertThat(m.getServletName(), is("CatalogServlet"));
|
assertThat(m.getServletName(), is("CatalogServlet"));
|
||||||
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
|
assertThat(m.getServletPath(), is("/catalog"));
|
||||||
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
|
assertThat(m.getPathInfo(), nullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2102,7 +2112,7 @@ public class RequestTest
|
||||||
ServletPathMapping m;
|
ServletPathMapping m;
|
||||||
|
|
||||||
spec = new RegexPathSpec("^/.*$");
|
spec = new RegexPathSpec("^/.*$");
|
||||||
m = new ServletPathMapping(spec, "Something", "/some/path");
|
m = new ServletPathMapping(spec, "Something", "/some/path", spec.matched("/some/path"));
|
||||||
assertThat(m.getMappingMatch(), nullValue());
|
assertThat(m.getMappingMatch(), nullValue());
|
||||||
assertThat(m.getPattern(), is(spec.getDeclaration()));
|
assertThat(m.getPattern(), is(spec.getDeclaration()));
|
||||||
assertThat(m.getServletName(), is("Something"));
|
assertThat(m.getServletName(), is("Something"));
|
||||||
|
@ -2111,7 +2121,7 @@ public class RequestTest
|
||||||
assertThat(m.getMatchValue(), is("some/path"));
|
assertThat(m.getMatchValue(), is("some/path"));
|
||||||
|
|
||||||
spec = new RegexPathSpec("^/some(/.*)?$");
|
spec = new RegexPathSpec("^/some(/.*)?$");
|
||||||
m = new ServletPathMapping(spec, "Something", "/some/path");
|
m = new ServletPathMapping(spec, "Something", "/some/path", spec.matched("/some/path"));
|
||||||
assertThat(m.getMappingMatch(), nullValue());
|
assertThat(m.getMappingMatch(), nullValue());
|
||||||
assertThat(m.getPattern(), is(spec.getDeclaration()));
|
assertThat(m.getPattern(), is(spec.getDeclaration()));
|
||||||
assertThat(m.getServletName(), is("Something"));
|
assertThat(m.getServletName(), is("Something"));
|
||||||
|
@ -2119,7 +2129,7 @@ public class RequestTest
|
||||||
assertThat(m.getPathInfo(), is("/path"));
|
assertThat(m.getPathInfo(), is("/path"));
|
||||||
assertThat(m.getMatchValue(), is("some"));
|
assertThat(m.getMatchValue(), is("some"));
|
||||||
|
|
||||||
m = new ServletPathMapping(spec, "Something", "/some");
|
m = new ServletPathMapping(spec, "Something", "/some", spec.matched("/some"));
|
||||||
assertThat(m.getMappingMatch(), nullValue());
|
assertThat(m.getMappingMatch(), nullValue());
|
||||||
assertThat(m.getPattern(), is(spec.getDeclaration()));
|
assertThat(m.getPattern(), is(spec.getDeclaration()));
|
||||||
assertThat(m.getServletName(), is("Something"));
|
assertThat(m.getServletName(), is("Something"));
|
||||||
|
|
|
@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletRequestWrapper;
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.pathmap.MatchedResource;
|
||||||
import org.eclipse.jetty.server.Dispatcher;
|
import org.eclipse.jetty.server.Dispatcher;
|
||||||
import org.eclipse.jetty.server.Handler;
|
import org.eclipse.jetty.server.Handler;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
|
@ -66,7 +67,7 @@ public class Invoker extends HttpServlet
|
||||||
|
|
||||||
private ContextHandler _contextHandler;
|
private ContextHandler _contextHandler;
|
||||||
private ServletHandler _servletHandler;
|
private ServletHandler _servletHandler;
|
||||||
private ServletHandler.MappedServlet _invokerEntry;
|
private MatchedResource<ServletHandler.MappedServlet> _invokerEntry;
|
||||||
private Map<String, String> _parameters;
|
private Map<String, String> _parameters;
|
||||||
private boolean _nonContextServlets;
|
private boolean _nonContextServlets;
|
||||||
private boolean _verbose;
|
private boolean _verbose;
|
||||||
|
@ -162,16 +163,16 @@ public class Invoker extends HttpServlet
|
||||||
try (AutoLock l = _servletHandler.lock())
|
try (AutoLock l = _servletHandler.lock())
|
||||||
{
|
{
|
||||||
// find the entry for the invoker (me)
|
// find the entry for the invoker (me)
|
||||||
_invokerEntry = _servletHandler.getMappedServlet(servletPath);
|
_invokerEntry = _servletHandler.getMatchedServlet(servletPath);
|
||||||
|
|
||||||
// Check for existing mapping (avoid threaded race).
|
// Check for existing mapping (avoid threaded race).
|
||||||
String path = URIUtil.addPaths(servletPath, servlet);
|
String path = URIUtil.addPaths(servletPath, servlet);
|
||||||
ServletHandler.MappedServlet entry = _servletHandler.getMappedServlet(path);
|
MatchedResource<ServletHandler.MappedServlet> entry = _servletHandler.getMatchedServlet(path);
|
||||||
|
|
||||||
if (entry != null && !entry.equals(_invokerEntry))
|
if (entry != null && !entry.getResource().equals(_invokerEntry.getResource()))
|
||||||
{
|
{
|
||||||
// Use the holder
|
// Use the holder
|
||||||
holder = (ServletHolder)entry.getServletHolder();
|
holder = entry.getResource().getServletHolder();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,6 +45,8 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.pathmap.MappedResource;
|
import org.eclipse.jetty.http.pathmap.MappedResource;
|
||||||
|
import org.eclipse.jetty.http.pathmap.MatchedPath;
|
||||||
|
import org.eclipse.jetty.http.pathmap.MatchedResource;
|
||||||
import org.eclipse.jetty.http.pathmap.PathMappings;
|
import org.eclipse.jetty.http.pathmap.PathMappings;
|
||||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||||
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||||
|
@ -353,6 +355,24 @@ public class ServletHandler extends ScopedHandler
|
||||||
return _filters.toArray(new FilterHolder[0]);
|
return _filters.toArray(new FilterHolder[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServletHolder matching path.
|
||||||
|
*
|
||||||
|
* @param target Path within _context or servlet name
|
||||||
|
* @return PathMap Entries pathspec to ServletHolder
|
||||||
|
* @deprecated Use {@link #getMatchedServlet(String)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public MappedResource<MappedServlet> getHolderEntry(String target)
|
||||||
|
{
|
||||||
|
if (target.startsWith("/"))
|
||||||
|
{
|
||||||
|
MatchedResource<MappedServlet> matchedResource = getMatchedServlet(target);
|
||||||
|
return new MappedResource<>(matchedResource.getPathSpec(), matchedResource.getResource());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public ServletContext getServletContext()
|
public ServletContext getServletContext()
|
||||||
{
|
{
|
||||||
return _servletContext;
|
return _servletContext;
|
||||||
|
@ -439,14 +459,15 @@ public class ServletHandler extends ScopedHandler
|
||||||
ServletHolder servletHolder = null;
|
ServletHolder servletHolder = null;
|
||||||
UserIdentity.Scope oldScope = null;
|
UserIdentity.Scope oldScope = null;
|
||||||
|
|
||||||
MappedServlet mappedServlet = getMappedServlet(target);
|
MatchedResource<MappedServlet> matched = getMatchedServlet(target);
|
||||||
if (mappedServlet != null)
|
if (matched != null)
|
||||||
{
|
{
|
||||||
|
MappedServlet mappedServlet = matched.getResource();
|
||||||
servletHolder = mappedServlet.getServletHolder();
|
servletHolder = mappedServlet.getServletHolder();
|
||||||
ServletPathMapping servletPathMapping = mappedServlet.getServletPathMapping(target);
|
ServletPathMapping servletPathMapping = mappedServlet.getServletPathMapping(target, matched.getMatchedPath());
|
||||||
|
|
||||||
if (servletPathMapping != null)
|
if (servletPathMapping != null)
|
||||||
{
|
{
|
||||||
// Setting the servletPathMapping also provides the servletPath and pathInfo
|
|
||||||
baseRequest.setServletPathMapping(servletPathMapping);
|
baseRequest.setServletPathMapping(servletPathMapping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -516,25 +537,39 @@ public class ServletHandler extends ScopedHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get MappedServlet for target.
|
* ServletHolder matching target path.
|
||||||
*
|
*
|
||||||
* @param target Path within _context or servlet name
|
* @param target Path within _context or servlet name
|
||||||
* @return MappedServlet matched by path or name. Named servlets have a null PathSpec
|
* @return MatchedResource, pointing to the {@link MappedResource} for the {@link ServletHolder}, and also the pathspec specific name/info sections for the match.
|
||||||
|
* Named servlets have a null PathSpec and {@link MatchedResource}.
|
||||||
*/
|
*/
|
||||||
public MappedServlet getMappedServlet(String target)
|
public MatchedResource<MappedServlet> getMatchedServlet(String target)
|
||||||
{
|
{
|
||||||
if (target.startsWith("/"))
|
if (target.startsWith("/"))
|
||||||
{
|
{
|
||||||
if (_servletPathMap == null)
|
if (_servletPathMap == null)
|
||||||
return null;
|
return null;
|
||||||
|
return _servletPathMap.getMatched(target);
|
||||||
MappedResource<MappedServlet> match = _servletPathMap.getMatch(target);
|
|
||||||
if (match == null)
|
|
||||||
return null;
|
|
||||||
return match.getResource();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _servletNameMap.get(target);
|
MappedServlet holder = _servletNameMap.get(target);
|
||||||
|
if (holder == null)
|
||||||
|
return null;
|
||||||
|
return new MatchedResource<>(holder, null, MatchedPath.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServletHolder matching path.
|
||||||
|
*
|
||||||
|
* @param target Path within _context or servlet name
|
||||||
|
* @return MappedResource to the ServletHolder. Named servlets have a null PathSpec
|
||||||
|
* @deprecated use {@link #getMatchedServlet(String)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public MappedServlet getMappedServlet(String target)
|
||||||
|
{
|
||||||
|
MatchedResource<MappedServlet> matchedResource = getMatchedServlet(target);
|
||||||
|
return matchedResource.getResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
|
protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
|
||||||
|
@ -1528,7 +1563,7 @@ public class ServletHandler extends ScopedHandler
|
||||||
switch (pathSpec.getGroup())
|
switch (pathSpec.getGroup())
|
||||||
{
|
{
|
||||||
case EXACT:
|
case EXACT:
|
||||||
_servletPathMapping = new ServletPathMapping(_pathSpec, _servletHolder.getName(), _pathSpec.getPrefix());
|
_servletPathMapping = new ServletPathMapping(_pathSpec, _servletHolder.getName(), _pathSpec.getDeclaration());
|
||||||
break;
|
break;
|
||||||
case ROOT:
|
case ROOT:
|
||||||
_servletPathMapping = new ServletPathMapping(_pathSpec, _servletHolder.getName(), "/");
|
_servletPathMapping = new ServletPathMapping(_pathSpec, _servletHolder.getName(), "/");
|
||||||
|
@ -1554,12 +1589,12 @@ public class ServletHandler extends ScopedHandler
|
||||||
return _servletHolder;
|
return _servletHolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServletPathMapping getServletPathMapping(String pathInContext)
|
public ServletPathMapping getServletPathMapping(String pathInContext, MatchedPath matchedPath)
|
||||||
{
|
{
|
||||||
if (_servletPathMapping != null)
|
if (_servletPathMapping != null)
|
||||||
return _servletPathMapping;
|
return _servletPathMapping;
|
||||||
if (_pathSpec != null)
|
if (_pathSpec != null)
|
||||||
return new ServletPathMapping(_pathSpec, _servletHolder.getName(), pathInContext);
|
return new ServletPathMapping(_pathSpec, _servletHolder.getName(), pathInContext, matchedPath);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.pathmap.PathMappings;
|
|
||||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||||
import org.eclipse.jetty.server.LocalConnector;
|
import org.eclipse.jetty.server.LocalConnector;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
|
@ -48,7 +47,7 @@ public class RegexServletTest
|
||||||
@Override
|
@Override
|
||||||
protected PathSpec asPathSpec(String pathSpec)
|
protected PathSpec asPathSpec(String pathSpec)
|
||||||
{
|
{
|
||||||
return PathMappings.asPathSpec(pathSpec);
|
return PathSpec.from(pathSpec);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.pathmap.MappedResource;
|
import org.eclipse.jetty.http.pathmap.MappedResource;
|
||||||
|
import org.eclipse.jetty.http.pathmap.MatchedResource;
|
||||||
import org.eclipse.jetty.http.pathmap.PathMappings;
|
import org.eclipse.jetty.http.pathmap.PathMappings;
|
||||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||||
import org.eclipse.jetty.http.pathmap.RegexPathSpec;
|
import org.eclipse.jetty.http.pathmap.RegexPathSpec;
|
||||||
|
@ -208,7 +209,7 @@ public class WebSocketMappings implements Dumpable, LifeCycle.Listener
|
||||||
*/
|
*/
|
||||||
public WebSocketNegotiator getMatchedNegotiator(String target, Consumer<PathSpec> pathSpecConsumer)
|
public WebSocketNegotiator getMatchedNegotiator(String target, Consumer<PathSpec> pathSpecConsumer)
|
||||||
{
|
{
|
||||||
MappedResource<WebSocketNegotiator> mapping = this.mappings.getMatch(target);
|
MatchedResource<WebSocketNegotiator> mapping = this.mappings.getMatched(target);
|
||||||
if (mapping == null)
|
if (mapping == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue