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