Author: markt Date: Thu Oct 28 22:29:25 2010 New Revision: 1028521 URL: http://svn.apache.org/viewvc?rev=1028521&view=rev Log: 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.
Added: tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapperWelcomeFiles.java (with props) tomcat/trunk/test/webapp-3.0/welcome-files/ tomcat/trunk/test/webapp-3.0/welcome-files/index.jsp (with props) tomcat/trunk/test/webapp-3.0/welcome-files/sub/ Modified: tomcat/trunk/java/org/apache/catalina/Context.java tomcat/trunk/java/org/apache/catalina/connector/MapperListener.java tomcat/trunk/java/org/apache/catalina/core/StandardContext.java tomcat/trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapper.java tomcat/trunk/webapps/docs/config/context.xml tomcat/trunk/webapps/docs/config/systemprops.xml Modified: tomcat/trunk/java/org/apache/catalina/Context.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Context.java?rev=1028521&r1=1028520&r2=1028521&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/Context.java (original) +++ tomcat/trunk/java/org/apache/catalina/Context.java Thu Oct 28 22:29:25 2010 @@ -1252,5 +1252,28 @@ public interface Context extends Contain */ Set<String> 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 <code>true</code> if the Servlet expects a resource, + * otherwise <code>false</code> + */ + public boolean isResourceOnlyServlet(String servletName); } Modified: tomcat/trunk/java/org/apache/catalina/connector/MapperListener.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/MapperListener.java?rev=1028521&r1=1028520&r2=1028521&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/connector/MapperListener.java (original) +++ tomcat/trunk/java/org/apache/catalina/connector/MapperListener.java Thu Oct 28 22:29:25 2010 @@ -156,18 +156,18 @@ public class MapperListener implements C } 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 C 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 C boolean jspWildCard = (wrapperName.equals("jsp") && mapping.endsWith("/*")); mapper.addWrapper(hostName, contextName, mapping, wrapper, - jspWildCard); + jspWildCard, + context.isResourceOnlyServlet(wrapperName)); } if(log.isDebugEnabled()) { Modified: tomcat/trunk/java/org/apache/catalina/core/StandardContext.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardContext.java?rev=1028521&r1=1028520&r2=1028521&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/StandardContext.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/StandardContext.java Thu Oct 28 22:29:25 2010 @@ -144,7 +144,12 @@ public class StandardContext extends Con 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 Con private JspConfigDescriptor jspConfigDescriptor = new ApplicationJspConfigDescriptor(); + private Set<String> resourceOnlyServlets = new HashSet<String>(); + + // ----------------------------------------------------- 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 Con wrapper.addMapping(pattern); // Update context mapper - mapper.addWrapper(pattern, wrapper, jspWildCard); + mapper.addWrapper(pattern, wrapper, jspWildCard, + resourceOnlyServlets.contains(name)); fireContainerEvent("addServletMapping", pattern); Modified: tomcat/trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java?rev=1028521&r1=1028520&r2=1028521&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java Thu Oct 28 22:29:25 2010 @@ -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; } } Modified: tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapper.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapper.java?rev=1028521&r1=1028520&r2=1028521&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapper.java (original) +++ tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapper.java Thu Oct 28 22:29:25 2010 @@ -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); } Added: tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapperWelcomeFiles.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapperWelcomeFiles.java?rev=1028521&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapperWelcomeFiles.java (added) +++ tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapperWelcomeFiles.java Thu Oct 28 22:29:25 2010 @@ -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<String,List<String>>()); + assertEquals(HttpServletResponse.SC_OK, rc); + assertTrue(bc.toString().contains("JSP")); + + rc = getUrl("http://localhost:" + getPort() + + "/test/welcome-files/sub", bc, + new HashMap<String,List<String>>()); + 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<String,List<String>>()); + assertEquals(HttpServletResponse.SC_OK, rc); + assertTrue(bc.toString().contains("JSP")); + + rc = getUrl("http://localhost:" + getPort() + + "/test/welcome-files/sub", bc, + new HashMap<String,List<String>>()); + 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"); + } + } +} Propchange: tomcat/trunk/test/org/apache/tomcat/util/http/mapper/TestMapperWelcomeFiles.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/test/webapp-3.0/welcome-files/index.jsp URL: http://svn.apache.org/viewvc/tomcat/trunk/test/webapp-3.0/welcome-files/index.jsp?rev=1028521&view=auto ============================================================================== --- tomcat/trunk/test/webapp-3.0/welcome-files/index.jsp (added) +++ tomcat/trunk/test/webapp-3.0/welcome-files/index.jsp Thu Oct 28 22:29:25 2010 @@ -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. +--%> +<html> + <body> + <p>OK-JSP</p> + </body> +</html> \ No newline at end of file Propchange: tomcat/trunk/test/webapp-3.0/welcome-files/index.jsp ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/webapps/docs/config/context.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/context.xml?rev=1028521&r1=1028520&r2=1028521&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/context.xml (original) +++ tomcat/trunk/webapps/docs/config/context.xml Thu Oct 28 22:29:25 2010 @@ -237,6 +237,20 @@ on demand.</p> </attribute> + <attribute name="resourceOnlyServlets" required="false"> + <p>Comma separated list of Servlet names (as used in + <code>/WEB-INF/web.xml</code>) 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 + <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code> + <a href="systemprops.html">system property</a> is set to + <code>true</code>, the default value of this attribute will be the empty + string, else the default value will be <code>jsp</code>. + </attribute> + <attribute name="sessionCookieDomain" required="false"> <p>The domain to be used for all session cookies created for this context. If set, this overrides any domain set by the web application. Modified: tomcat/trunk/webapps/docs/config/systemprops.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/systemprops.xml?rev=1028521&r1=1028520&r2=1028521&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/systemprops.xml (original) +++ tomcat/trunk/webapps/docs/config/systemprops.xml Thu Oct 28 22:29:25 2010 @@ -258,6 +258,8 @@ <li><code>org.apache.tomcat.util.http.ServerCookie.ALWAYS_ADD_EXPIRES</code>.</li> <li><code>org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR</code>.</li> <li><code>org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING</code>.</li> + <li>The <code>resourceOnlyServlets</code> attribute of any + <a href="context.html">Context</a> element.</li> <li>The <code>tldNamespaceAware</code> attribute of any <a href="context.html">Context</a> element.</li> <li>The <code>tldValidation</code> attribute of any --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org