package org.eclipse.jetty.http.pathmap;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ServletPathSpec extends AbstractPathSpec
{
private static final Logger LOG = LoggerFactory.getLogger(ServletPathSpec.class);
private final String _declaration;
private final PathSpecGroup _group;
private final int _pathDepth;
private final int _specLength;
private final String _prefix;
private final String _suffix;
public static String normalize(String pathSpec)
{
if (StringUtil.isNotBlank(pathSpec) && !pathSpec.startsWith("/") && !pathSpec.startsWith("*"))
return "/" + pathSpec;
return pathSpec;
}
public static boolean match(String pathSpec, String path)
{
return match(pathSpec, path, false);
}
public static boolean match(String pathSpec, String path, boolean noDefault)
{
if (pathSpec.length() == 0)
return "/".equals(path);
char c = pathSpec.charAt(0);
if (c == '/')
{
if (!noDefault && pathSpec.length() == 1 || pathSpec.equals(path))
return true;
if (isPathWildcardMatch(pathSpec, path))
return true;
}
else if (c == '*')
return path.regionMatches(path.length() - pathSpec.length() + 1,
pathSpec, 1, pathSpec.length() - 1);
return false;
}
private static boolean isPathWildcardMatch(String pathSpec, String path)
{
int cpl = pathSpec.length() - 2;
if (pathSpec.endsWith("/*") && path.regionMatches(0, pathSpec, 0, cpl))
{
if (path.length() == cpl || '/' == path.charAt(cpl))
return true;
}
return false;
}
public static String pathMatch(String pathSpec, String path)
{
char c = pathSpec.charAt(0);
if (c == '/')
{
if (pathSpec.length() == 1)
return path;
if (pathSpec.equals(path))
return path;
if (isPathWildcardMatch(pathSpec, path))
return path.substring(0, pathSpec.length() - 2);
}
else if (c == '*')
{
if (path.regionMatches(path.length() - (pathSpec.length() - 1),
pathSpec, 1, pathSpec.length() - 1))
return path;
}
return null;
}
public static String pathInfo(String pathSpec, String path)
{
if ("".equals(pathSpec))
return path;
char c = pathSpec.charAt(0);
if (c == '/')
{
if (pathSpec.length() == 1)
return null;
boolean wildcard = isPathWildcardMatch(pathSpec, path);
if (pathSpec.equals(path) && !wildcard)
return null;
if (wildcard)
{
if (path.length() == pathSpec.length() - 2)
return null;
return path.substring(pathSpec.length() - 2);
}
}
return null;
}
public static String relativePath(String base,
String pathSpec,
String path)
{
String info = pathInfo(pathSpec, path);
if (info == null)
info = path;
if (info.startsWith("./"))
info = info.substring(2);
if (base.endsWith(URIUtil.SLASH))
if (info.startsWith(URIUtil.SLASH))
path = base + info.substring(1);
else
path = base + info;
else if (info.startsWith(URIUtil.SLASH))
path = base + info;
else
path = base + URIUtil.SLASH + info;
return path;
}
public ServletPathSpec(String servletPathSpec)
{
if (servletPathSpec == null)
servletPathSpec = "";
if (servletPathSpec.startsWith("servlet|"))
servletPathSpec = servletPathSpec.substring("servlet|".length());
assertValidServletPathSpec(servletPathSpec);
if (servletPathSpec.isEmpty())
{
_declaration = "";
_group = PathSpecGroup.ROOT;
_pathDepth = -1;
_specLength = 1;
_prefix = null;
_suffix = null;
return;
}
if ("/".equals(servletPathSpec))
{
_declaration = "/";
_group = PathSpecGroup.DEFAULT;
_pathDepth = -1;
_specLength = 1;
_prefix = null;
_suffix = null;
return;
}
int specLength = servletPathSpec.length();
PathSpecGroup group;
String prefix;
String suffix;
if (servletPathSpec.charAt(0) == '/' && servletPathSpec.endsWith("/*"))
{
group = PathSpecGroup.PREFIX_GLOB;
prefix = servletPathSpec.substring(0, specLength - 2);
suffix = null;
}
else if (servletPathSpec.charAt(0) == '*' && servletPathSpec.length() > 1)
{
group = PathSpecGroup.SUFFIX_GLOB;
prefix = null;
suffix = servletPathSpec.substring(2, specLength);
}
else
{
group = PathSpecGroup.EXACT;
prefix = servletPathSpec;
suffix = null;
if (servletPathSpec.endsWith("*"))
{
LOG.warn("Suspicious URL pattern: '{}'; see sections 12.1 and 12.2 of the Servlet specification",
servletPathSpec);
}
}
int pathDepth = 0;
for (int i = 0; i < specLength; i++)
{
int cp = servletPathSpec.codePointAt(i);
if (cp < 128)
{
char c = (char)cp;
if (c == '/')
pathDepth++;
}
}
_declaration = servletPathSpec;
_group = group;
_pathDepth = pathDepth;
_specLength = specLength;
_prefix = prefix;
_suffix = suffix;
}
private static void assertValidServletPathSpec(String servletPathSpec)
{
if ((servletPathSpec == null) || servletPathSpec.equals(""))
{
return;
}
int len = servletPathSpec.length();
if (servletPathSpec.charAt(0) == '/')
{
if (len == 1)
{
return;
}
int idx = servletPathSpec.indexOf('*');
if (idx < 0)
{
return;
}
if (idx != (len - 1))
{
throw new IllegalArgumentException("Servlet Spec 12.2 violation: glob '*' can only exist at end of prefix based matches: bad spec \"" + servletPathSpec + "\"");
}
}
else if (servletPathSpec.startsWith("*."))
{
int idx = servletPathSpec.indexOf('/');
if (idx >= 0)
throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have path separators: bad spec \"" + servletPathSpec + "\"");
idx = servletPathSpec.indexOf('*', 2);
if (idx >= 1)
throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have multiple glob '*': bad spec \"" + servletPathSpec + "\"");
}
else
{
throw new IllegalArgumentException("Servlet Spec 12.2 violation: path spec must start with \"/\" or \"*.\": bad spec \"" + servletPathSpec + "\"");
}
}
@Override
public int getSpecLength()
{
return _specLength;
}
@Override
public PathSpecGroup getGroup()
{
return _group;
}
@Override
public int getPathDepth()
{
return _pathDepth;
}
@Override
public String getPathInfo(String path)
{
switch (_group)
{
case ROOT:
return path;
case PREFIX_GLOB:
if (path.length() == (_specLength - 2))
return null;
return path.substring(_specLength - 2);
default:
return null;
}
}
@Override
public String getPathMatch(String path)
{
switch (_group)
{
case ROOT:
return "";
case EXACT:
if (_declaration.equals(path))
return path;
return null;
case PREFIX_GLOB:
if (isWildcardMatch(path))
return path.substring(0, _specLength - 2);
return null;
case SUFFIX_GLOB:
if (path.regionMatches(path.length() - (_specLength - 1), _declaration, 1, _specLength - 1))
return path;
return null;
case DEFAULT:
return path;
default:
return null;
}
}
@Override
public String getDeclaration()
{
return _declaration;
}
@Override
public String getPrefix()
{
return _prefix;
}
@Override
public String getSuffix()
{
return _suffix;
}
private boolean isWildcardMatch(String path)
{
int cpl = _specLength - 2;
if ((_group == PathSpecGroup.PREFIX_GLOB) && (path.regionMatches(0, _declaration, 0, cpl)))
return (path.length() == cpl) || ('/' == path.charAt(cpl));
return false;
}
@Override
public boolean matches(String path)
{
switch (_group)
{
case EXACT:
return _declaration.equals(path);
case PREFIX_GLOB:
return isWildcardMatch(path);
case SUFFIX_GLOB:
return path.regionMatches((path.length() - _specLength) + 1, _declaration, 1, _specLength - 1);
case ROOT:
return ("/".equals(path));
case DEFAULT:
return true;
default:
return false;
}
}
}