From: markt
Date: Thu, 28 Oct 2010 22:29:25 +0000 (+0000)
Subject: Provide configuration option to work around new welcome file mapping requirements...
X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=35c45f212097d8f759aeb65d105e7f8ad1cf647e;p=tomcat7.0
Provide configuration option to work around new welcome file mapping requirements of section 10.10 Servlet 3.0 that break a lot of existing apps. The default configuration retains the current Tomcat 6.0.x behaviour. Enabling STRICT_SERVLET_COMPLILANCE enforces the new requirements by default. Includes test cases.
git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1028521 13f79535-47bb-0310-9956-ffa450edef68
---
diff --git a/java/org/apache/catalina/Context.java b/java/org/apache/catalina/Context.java
index c12d7b50e..cfb77bb80 100644
--- a/java/org/apache/catalina/Context.java
+++ b/java/org/apache/catalina/Context.java
@@ -1252,5 +1252,28 @@ public interface Context extends Container {
*/
Set addServletSecurity(ApplicationServletRegistration registration,
ServletSecurityElement servletSecurityElement);
+
+ /**
+ * Sets the (comma separated) list of Servlets that expect a resource to be
+ * present. Used to ensure that welcome files associated with Servlets that
+ * expect a resource to be present are not mapped when there is no resource.
+ */
+ public void setResourceOnlyServlets(String resourceOnlyServlets);
+
+ /**
+ * Obtains the list of Servlets that expect a resource to be present.
+ *
+ * @return A comma separated list of Servlet names as used in web.xml
+ */
+ public String getResourceOnlyServlets();
+
+ /**
+ * Checks the named Servlet to see if it expects a resource to be present.
+ *
+ * @param servletName Name of the Servlet (as per web.xml) to check
+ * @return true if the Servlet expects a resource,
+ * otherwise false
+ */
+ public boolean isResourceOnlyServlet(String servletName);
}
diff --git a/java/org/apache/catalina/connector/MapperListener.java b/java/org/apache/catalina/connector/MapperListener.java
index 27650ec18..6fc53675b 100644
--- a/java/org/apache/catalina/connector/MapperListener.java
+++ b/java/org/apache/catalina/connector/MapperListener.java
@@ -156,18 +156,18 @@ public class MapperListener implements ContainerListener, LifecycleListener {
} else if (event.getType() == Wrapper.ADD_MAPPING_EVENT) {
// Handle dynamically adding wrappers
Wrapper wrapper = (Wrapper) event.getSource();
-
- String contextName = wrapper.getParent().getName();
+ Context context = (Context) wrapper.getParent();
+ String contextName = context.getName();
if ("/".equals(contextName)) {
contextName = "";
}
- String hostName = wrapper.getParent().getParent().getName();
-
+ String hostName = context.getParent().getName();
+ String wrapperName = wrapper.getName();
String mapping = (String) event.getData();
- boolean jspWildCard = ("jsp".equals(wrapper.getName())
+ boolean jspWildCard = ("jsp".equals(wrapperName)
&& mapping.endsWith("/*"));
mapper.addWrapper(hostName, contextName, mapping, wrapper,
- jspWildCard);
+ jspWildCard, context.isResourceOnlyServlet(wrapperName));
} else if (event.getType() == Wrapper.REMOVE_MAPPING_EVENT) {
// Handle dynamically removing wrappers
Wrapper wrapper = (Wrapper) event.getSource();
@@ -380,11 +380,12 @@ public class MapperListener implements ContainerListener, LifecycleListener {
private void registerWrapper(Wrapper wrapper) {
String wrapperName = wrapper.getName();
- String contextName = wrapper.getParent().getName();
+ Context context = (Context) wrapper.getParent();
+ String contextName = context.getName();
if ("/".equals(contextName)) {
contextName = "";
}
- String hostName = wrapper.getParent().getParent().getName();
+ String hostName = context.getParent().getName();
String[] mappings = wrapper.findMappings();
@@ -392,7 +393,8 @@ public class MapperListener implements ContainerListener, LifecycleListener {
boolean jspWildCard = (wrapperName.equals("jsp")
&& mapping.endsWith("/*"));
mapper.addWrapper(hostName, contextName, mapping, wrapper,
- jspWildCard);
+ jspWildCard,
+ context.isResourceOnlyServlet(wrapperName));
}
if(log.isDebugEnabled()) {
diff --git a/java/org/apache/catalina/core/StandardContext.java b/java/org/apache/catalina/core/StandardContext.java
index 5433c3dcb..099795c08 100644
--- a/java/org/apache/catalina/core/StandardContext.java
+++ b/java/org/apache/catalina/core/StandardContext.java
@@ -144,7 +144,12 @@ public class StandardContext extends ContainerBase
super();
pipeline.setBasic(new StandardContextValve());
broadcaster = new NotificationBroadcasterSupport();
-
+ // Set defaults
+ if (!Globals.STRICT_SERVLET_COMPLIANCE) {
+ // Strict servlet compliance requires all extension mapped servlets
+ // to be checked against welcome files
+ resourceOnlyServlets.add("jsp");
+ }
}
@@ -799,9 +804,46 @@ public class StandardContext extends ContainerBase
private JspConfigDescriptor jspConfigDescriptor =
new ApplicationJspConfigDescriptor();
+ private Set resourceOnlyServlets = new HashSet();
+
+
// ----------------------------------------------------- Context Properties
@Override
+ public String getResourceOnlyServlets() {
+ StringBuilder result = new StringBuilder();
+ boolean first = true;
+ for (String servletName : resourceOnlyServlets) {
+ if (!first) {
+ result.append(',');
+ }
+ result.append(servletName);
+ }
+ return result.toString();
+ }
+
+
+ @Override
+ public void setResourceOnlyServlets(String resourceOnlyServlets) {
+ this.resourceOnlyServlets.clear();
+ if (resourceOnlyServlets == null ||
+ resourceOnlyServlets.length() == 0) {
+ return;
+ }
+ String[] servletNames = resourceOnlyServlets.split(",");
+ for (String servletName : servletNames) {
+ this.resourceOnlyServlets.add(servletName);
+ }
+ }
+
+
+ @Override
+ public boolean isResourceOnlyServlet(String servletName) {
+ return resourceOnlyServlets.contains(servletName);
+ }
+
+
+ @Override
public int getEffectiveMajorVersion() {
return effectiveMajorVersion;
}
@@ -2921,7 +2963,8 @@ public class StandardContext extends ContainerBase
wrapper.addMapping(pattern);
// Update context mapper
- mapper.addWrapper(pattern, wrapper, jspWildCard);
+ mapper.addWrapper(pattern, wrapper, jspWildCard,
+ resourceOnlyServlets.contains(name));
fireContainerEvent("addServletMapping", pattern);
diff --git a/java/org/apache/tomcat/util/http/mapper/Mapper.java b/java/org/apache/tomcat/util/http/mapper/Mapper.java
index b95be5bf8..0565814c9 100644
--- a/java/org/apache/tomcat/util/http/mapper/Mapper.java
+++ b/java/org/apache/tomcat/util/http/mapper/Mapper.java
@@ -279,22 +279,9 @@ public final class Mapper {
}
- /**
- * Add a new Wrapper to an existing Context.
- *
- * @param hostName Virtual host name this wrapper belongs to
- * @param contextPath Context path this wrapper belongs to
- * @param path Wrapper mapping
- * @param wrapper Wrapper object
- */
public void addWrapper(String hostName, String contextPath, String path,
- Object wrapper) {
- addWrapper(hostName, contextPath, path, wrapper, false);
- }
-
-
- public void addWrapper(String hostName, String contextPath, String path,
- Object wrapper, boolean jspWildCard) {
+ Object wrapper, boolean jspWildCard,
+ boolean resourceOnly) {
Host[] hosts = this.hosts;
int pos = find(hosts, hostName);
if (pos < 0) {
@@ -310,30 +297,15 @@ public final class Mapper {
}
Context context = contexts[pos2];
if (context.name.equals(contextPath)) {
- addWrapper(context, path, wrapper, jspWildCard);
+ addWrapper(context, path, wrapper, jspWildCard, resourceOnly);
}
}
}
- /**
- * Add a wrapper to the context associated with this wrapper.
- *
- * @param path Wrapper mapping
- * @param wrapper The Wrapper object
- */
- public void addWrapper(String path, Object wrapper) {
- addWrapper(context, path, wrapper);
- }
-
-
- public void addWrapper(String path, Object wrapper, boolean jspWildCard) {
- addWrapper(context, path, wrapper, jspWildCard);
- }
-
-
- protected void addWrapper(Context context, String path, Object wrapper) {
- addWrapper(context, path, wrapper, false);
+ public void addWrapper(String path, Object wrapper, boolean jspWildCard,
+ boolean resourceOnly) {
+ addWrapper(context, path, wrapper, jspWildCard, resourceOnly);
}
@@ -344,15 +316,18 @@ public final class Mapper {
* @param path Wrapper mapping
* @param wrapper The Wrapper object
* @param jspWildCard true if the wrapper corresponds to the JspServlet
+ * @param resourceOnly true if this wrapper always expects a physical
+ * resource to be present (such as a JSP)
* and the mapping path contains a wildcard; false otherwise
*/
protected void addWrapper(Context context, String path, Object wrapper,
- boolean jspWildCard) {
+ boolean jspWildCard, boolean resourceOnly) {
synchronized (context) {
Wrapper newWrapper = new Wrapper();
newWrapper.object = wrapper;
newWrapper.jspWildCard = jspWildCard;
+ newWrapper.resourceOnly = resourceOnly;
if (path.endsWith("/*")) {
// Wildcard wrapper
newWrapper.name = path.substring(0, path.length() - 2);
@@ -801,7 +776,8 @@ public final class Mapper {
// Rule 3 -- Extension Match
Wrapper[] extensionWrappers = context.extensionWrappers;
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
- internalMapExtensionWrapper(extensionWrappers, path, mappingData);
+ internalMapExtensionWrapper(extensionWrappers, path, mappingData,
+ true);
}
// Rule 4 -- Welcome resources processing for servlets
@@ -842,8 +818,8 @@ public final class Mapper {
// Swallow not found, since this is normal
}
if (file != null && !(file instanceof DirContext) ) {
- internalMapExtensionWrapper(extensionWrappers,
- path, mappingData);
+ internalMapExtensionWrapper(extensionWrappers, path,
+ mappingData, true);
if (mappingData.wrapper == null
&& context.defaultWrapper != null) {
mappingData.wrapper =
@@ -888,8 +864,8 @@ public final class Mapper {
path.append(context.welcomeResources[i], 0,
context.welcomeResources[i].length());
path.setOffset(servletPath);
- internalMapExtensionWrapper(extensionWrappers,
- path, mappingData);
+ internalMapExtensionWrapper(extensionWrappers, path,
+ mappingData, false);
}
path.setOffset(servletPath);
@@ -1005,9 +981,14 @@ public final class Mapper {
/**
* Extension mappings.
+ *
+ * @param wrappers Set of wrappers to check for matches
+ * @param path Path to map
+ * @param mappingData Mapping data for result
+ * @param resourceExpected Is this mapping expecting to find a resource
*/
- private final void internalMapExtensionWrapper
- (Wrapper[] wrappers, CharChunk path, MappingData mappingData) {
+ private final void internalMapExtensionWrapper(Wrapper[] wrappers,
+ CharChunk path, MappingData mappingData, boolean resourceExpected) {
char[] buf = path.getBuffer();
int pathEnd = path.getEnd();
int servletPath = path.getOffset();
@@ -1030,8 +1011,8 @@ public final class Mapper {
path.setOffset(period + 1);
path.setEnd(pathEnd);
int pos = find(wrappers, path);
- if ((pos != -1)
- && (path.equals(wrappers[pos].name))) {
+ if ((pos != -1) && (path.equals(wrappers[pos].name)) &&
+ (resourceExpected || !wrappers[pos].resourceOnly)) {
mappingData.wrapperPath.setChars
(buf, servletPath, pathEnd - servletPath);
mappingData.requestPath.setChars
@@ -1412,5 +1393,6 @@ public final class Mapper {
public String path = null;
public boolean jspWildCard = false;
+ public boolean resourceOnly = false;
}
}
diff --git a/test/org/apache/tomcat/util/http/mapper/TestMapper.java b/test/org/apache/tomcat/util/http/mapper/TestMapper.java
index 6785cb008..db5347d2f 100644
--- a/test/org/apache/tomcat/util/http/mapper/TestMapper.java
+++ b/test/org/apache/tomcat/util/http/mapper/TestMapper.java
@@ -60,14 +60,22 @@ public class TestMapper extends TestCase {
mapper.addContext("iowejoiejfoiew", "blah7", "/foo/bar/bla",
"context3", new String[0], null);
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/fo/*", "wrapper0");
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/", "wrapper1");
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blh", "wrapper2");
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.jsp", "wrapper3");
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blah/bou/*", "wrapper4");
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blah/bobou/*", "wrapper5");
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.htm", "wrapper6");
- mapper.addWrapper("iowejoiejfoiew", "/foo/bar/bla", "/bobou/*", "wrapper7");
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/fo/*",
+ "wrapper0", false, false);
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/",
+ "wrapper1", false, false);
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blh",
+ "wrapper2", false, false);
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.jsp",
+ "wrapper3", false, false);
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blah/bou/*",
+ "wrapper4", false, false);
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blah/bobou/*",
+ "wrapper5", false, false);
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.htm",
+ "wrapper6", false, false);
+ mapper.addWrapper("iowejoiejfoiew", "/foo/bar/bla", "/bobou/*",
+ "wrapper7", false, false);
}
diff --git a/test/org/apache/tomcat/util/http/mapper/TestMapperWelcomeFiles.java b/test/org/apache/tomcat/util/http/mapper/TestMapperWelcomeFiles.java
new file mode 100644
index 000000000..bba040b36
--- /dev/null
+++ b/test/org/apache/tomcat/util/http/mapper/TestMapperWelcomeFiles.java
@@ -0,0 +1,87 @@
+package org.apache.tomcat.util.http.mapper;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class TestMapperWelcomeFiles extends TomcatBaseTest {
+
+ public void testWelcomeFileNotStrict() throws Exception {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ File appDir = new File("test/webapp-3.0");
+
+ StandardContext ctxt = (StandardContext) tomcat.addWebapp(null, "/test",
+ appDir.getAbsolutePath());
+ Tomcat.addServlet(ctxt, "Ok", new OkServlet());
+ ctxt.setReplaceWelcomeFiles(true);
+ ctxt.addWelcomeFile("index.jsp");
+ ctxt.addWelcomeFile("index.do");
+
+ tomcat.start();
+ ByteChunk bc = new ByteChunk();
+ int rc = getUrl("http://localhost:" + getPort() +
+ "/test/welcome-files", bc, new HashMap>());
+ assertEquals(HttpServletResponse.SC_OK, rc);
+ assertTrue(bc.toString().contains("JSP"));
+
+ rc = getUrl("http://localhost:" + getPort() +
+ "/test/welcome-files/sub", bc,
+ new HashMap>());
+ assertEquals(HttpServletResponse.SC_OK, rc);
+ assertTrue(bc.toString().contains("Servlet"));
+ }
+
+ public void testWelcomeFileStrict() throws Exception {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ File appDir = new File("test/webapp-3.0");
+
+ StandardContext ctxt = (StandardContext) tomcat.addWebapp(null, "/test",
+ appDir.getAbsolutePath());
+ Tomcat.addServlet(ctxt, "Ok", new OkServlet());
+ ctxt.setReplaceWelcomeFiles(true);
+ ctxt.addWelcomeFile("index.jsp");
+ ctxt.addWelcomeFile("index.do");
+
+ // Simulate STRICT_SERVLET_COMPLIANCE
+ ctxt.setResourceOnlyServlets("");
+
+ tomcat.start();
+ ByteChunk bc = new ByteChunk();
+ int rc = getUrl("http://localhost:" + getPort() +
+ "/test/welcome-files", bc, new HashMap>());
+ assertEquals(HttpServletResponse.SC_OK, rc);
+ assertTrue(bc.toString().contains("JSP"));
+
+ rc = getUrl("http://localhost:" + getPort() +
+ "/test/welcome-files/sub", bc,
+ new HashMap>());
+ assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
+ }
+
+ private static class OkServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/plain");
+ resp.getWriter().write("OK-Servlet");
+ }
+ }
+}
diff --git a/test/webapp-3.0/welcome-files/index.jsp b/test/webapp-3.0/welcome-files/index.jsp
new file mode 100644
index 000000000..92c9eab4f
--- /dev/null
+++ b/test/webapp-3.0/welcome-files/index.jsp
@@ -0,0 +1,21 @@
+<%--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+--%>
+
+
+ OK-JSP
+
+
\ No newline at end of file
diff --git a/webapps/docs/config/context.xml b/webapps/docs/config/context.xml
index 20d59e53e..ce9dce627 100644
--- a/webapps/docs/config/context.xml
+++ b/webapps/docs/config/context.xml
@@ -237,6 +237,20 @@
on demand.
+
+ Comma separated list of Servlet names (as used in
+ /WEB-INF/web.xml) that expect a resource to be present.
+ Ensures that welcome files associated with Servlets that expect a
+ resource to be present (such as the JSP Servlet) are not used when there
+ is no resource present. This prevents issues caused by the clarification
+ of welcome file mapping in section 10.10 of the Servlet 3.0
+ specification. If the
+ org.apache.catalina.STRICT_SERVLET_COMPLIANCE
+ system property is set to
+ true, the default value of this attribute will be the empty
+ string, else the default value will be jsp.
+
+
The domain to be used for all session cookies created for this
context. If set, this overrides any domain set by the web application.
diff --git a/webapps/docs/config/systemprops.xml b/webapps/docs/config/systemprops.xml
index 33dffdd05..69c59ec70 100644
--- a/webapps/docs/config/systemprops.xml
+++ b/webapps/docs/config/systemprops.xml
@@ -258,6 +258,8 @@
org.apache.tomcat.util.http.ServerCookie.ALWAYS_ADD_EXPIRES.
org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR.
org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING.
+ The resourceOnlyServlets attribute of any
+ Context element.
The tldNamespaceAware attribute of any
Context element.
The tldValidation attribute of any