SLING-9222 - Support requiring empty extensions or selectors in paths.strict mode
diff --git a/src/main/java/org/apache/sling/servlets/resolver/internal/PathBasedServletAcceptor.java b/src/main/java/org/apache/sling/servlets/resolver/internal/PathBasedServletAcceptor.java
index 1fea703..5da3f6e 100644
--- a/src/main/java/org/apache/sling/servlets/resolver/internal/PathBasedServletAcceptor.java
+++ b/src/main/java/org/apache/sling/servlets/resolver/internal/PathBasedServletAcceptor.java
@@ -23,6 +23,7 @@
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
+import org.apache.commons.lang3.ArrayUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.servlets.ServletResolverConstants;
import org.apache.sling.engine.RequestUtil;
@@ -41,6 +42,16 @@
// TODO should be in ServletResolverConstants
private static final String STRICT_PATHS_SERVICE_PROPERTY = "sling.servlet.paths.strict";
+ // Used to indicate "accept only an empty set of selectors or extensions - should not be
+ // a valid selector or extension to avoid collisions
+ private static final String EMPTY_VALUE = ".EMPTY.";
+
+ static class InvalidPropertyException extends RuntimeException {
+ InvalidPropertyException(String reason) {
+ super(reason);
+ }
+ }
+
boolean accept(SlingHttpServletRequest request, Servlet servlet) {
// Get OSGi service properties from the SlingServletConfig
final ServletConfig rawCfg = servlet.getServletConfig();
@@ -56,9 +67,9 @@
final Object strictPaths = config.getServiceProperty(STRICT_PATHS_SERVICE_PROPERTY);
if(strictPaths != null && Boolean.valueOf(strictPaths.toString())) {
accepted =
- accept(servletName, config, ServletResolverConstants.SLING_SERVLET_EXTENSIONS, request.getRequestPathInfo().getExtension())
- && accept(servletName, config, ServletResolverConstants.SLING_SERVLET_SELECTORS, request.getRequestPathInfo().getSelectors())
- && accept(servletName, config, ServletResolverConstants.SLING_SERVLET_METHODS, request.getMethod());
+ accept(servletName, config, ServletResolverConstants.SLING_SERVLET_EXTENSIONS, true, request.getRequestPathInfo().getExtension())
+ && accept(servletName, config, ServletResolverConstants.SLING_SERVLET_SELECTORS, true, request.getRequestPathInfo().getSelectors())
+ && accept(servletName, config, ServletResolverConstants.SLING_SERVLET_METHODS, false, request.getMethod());
}
LOGGER.debug("accepted={} for {}", accepted, servletName);
@@ -66,23 +77,34 @@
return accepted;
}
- private boolean accept(String servletName, SlingServletConfig config, String servicePropertyKey, String ... requestValues) {
+ private boolean accept(String servletName, SlingServletConfig config, String servicePropertyKey, boolean emptyValueApplies, String ... requestValues) {
final String [] propValues = toStringArray(config.getServiceProperty(servicePropertyKey));
if(propValues == null) {
LOGGER.debug("Property {} is null or empty, not checking that value for {}", servicePropertyKey, servletName);
return true;
}
- // requestValues must match at least one value in propValue
boolean accepted = false;
- for(String rValue : requestValues) {
- for(String pValue : propValues) {
- if(rValue != null && rValue.equals(pValue)) {
- accepted = true;
- break;
+ if(propValues.length == 1 && EMPTY_VALUE.equals(propValues[0])) {
+ // If supported for this service property, request value must be empty
+ if(!emptyValueApplies) {
+ throw new InvalidPropertyException("Special value " + EMPTY_VALUE
+ + " is not valid for the " + servicePropertyKey + " service property");
+ } else {
+ accepted = requestValues.length == 0 || (requestValues.length == 1 && requestValues[0] == null);
+ }
+ } else {
+ // requestValues must match at least one value in propValue
+ for(String rValue : requestValues) {
+ for(String pValue : propValues) {
+ if(rValue != null && rValue.equals(pValue)) {
+ accepted = true;
+ break;
+ }
}
}
}
+
LOGGER.debug("accepted={} for property {} and servlet {}", accepted, servicePropertyKey, servletName);
return accepted;
}
diff --git a/src/test/java/org/apache/sling/servlets/resolver/internal/PathBasedServletAcceptorTest.java b/src/test/java/org/apache/sling/servlets/resolver/internal/PathBasedServletAcceptorTest.java
index 5fd43aa..0e4bd23 100644
--- a/src/test/java/org/apache/sling/servlets/resolver/internal/PathBasedServletAcceptorTest.java
+++ b/src/test/java/org/apache/sling/servlets/resolver/internal/PathBasedServletAcceptorTest.java
@@ -45,6 +45,8 @@
public static final String [] STRING_ARRAY = new String[0];
+ public static final String V_EMPTY = ".EMPTY.";
+
private final PathBasedServletAcceptor acceptor = new PathBasedServletAcceptor();
private class TestCase {
@@ -248,10 +250,66 @@
}
@Test
+ public void testEmptyExtensionAndSelectorWithEmpty() {
+ new TestCase()
+ .withServiceProperty(ServletResolverTestSupport.P_STRICT_PATHS, true)
+ .withServiceProperty(ServletResolverTestSupport.P_EXTENSIONS, V_EMPTY)
+ .withServiceProperty(ServletResolverTestSupport.P_SELECTORS, V_EMPTY)
+ .assertAccept(true);
+ }
+
+ @Test
+ public void testEmptyExtensionAndSelectorWithSelector() {
+ new TestCase()
+ .withServiceProperty(ServletResolverTestSupport.P_STRICT_PATHS, true)
+ .withServiceProperty(ServletResolverTestSupport.P_EXTENSIONS, V_EMPTY)
+ .withServiceProperty(ServletResolverTestSupport.P_SELECTORS, V_EMPTY)
+ .withSelector("someSel")
+ .assertAccept(false);
+ }
+
+ @Test
+ public void testEmptyExtensionAndSelectorWithExtension() {
+ new TestCase()
+ .withServiceProperty(ServletResolverTestSupport.P_STRICT_PATHS, true)
+ .withServiceProperty(ServletResolverTestSupport.P_EXTENSIONS, V_EMPTY)
+ .withServiceProperty(ServletResolverTestSupport.P_SELECTORS, V_EMPTY)
+ .withExtension("someExt")
+ .assertAccept(false);
+ }
+
+ @Test
+ public void testEmptyExtensionSpecificSelector() {
+ new TestCase()
+ .withServiceProperty(ServletResolverTestSupport.P_STRICT_PATHS, true)
+ .withServiceProperty(ServletResolverTestSupport.P_EXTENSIONS, V_EMPTY)
+ .withServiceProperty(ServletResolverTestSupport.P_SELECTORS, "someSel")
+ .withSelector("someSel")
+ .assertAccept(true);
+ }
+
+ @Test
+ public void testEmptySelectorSpecificExtension() {
+ new TestCase()
+ .withServiceProperty(ServletResolverTestSupport.P_STRICT_PATHS, true)
+ .withServiceProperty(ServletResolverTestSupport.P_EXTENSIONS, "someExt")
+ .withServiceProperty(ServletResolverTestSupport.P_SELECTORS, V_EMPTY)
+ .withExtension("someExt")
+ .assertAccept(true);
+ }
+
+ @Test(expected = PathBasedServletAcceptor.InvalidPropertyException.class)
+ public void testEmptyMethodException() {
+ new TestCase()
+ .withServiceProperty(ServletResolverTestSupport.P_STRICT_PATHS, true)
+ .withServiceProperty(ServletResolverTestSupport.P_METHODS, V_EMPTY)
+ .assertAccept(true);
+ }
+
+ @Test
public void testNoSlingServletConfig() {
final Servlet s = mock(Servlet.class);
when(s.getServletConfig()).thenReturn(mock(ServletConfig.class));
assertTrue(acceptor.accept(null, s));
}
-
}