Hi there,
I've been investigating bug #7686 since we have similar problems with our
application. I think I've found the problem and a way to solve it, but this
requires changes to several catalina core files, so I'd like to hear some
opinions before sending a patch.
The Bug
=======
Here's the problem (easily reproduceable using the attached war file): A
servlet ("BugtestServlet") includes another servlet ("IncludedServlet")
using ServletContext.getRequestDispatcher. Now IncludedServlet forwards to
"/jsp/forwarded.jsp". So in summary we have:
"/BugtestServlet" --- includes ---> "/IncludedServlet" --- forwards to --->
"/jsp/forwarded.jsp"
Now if you request "/BugtestServlet", Tomcat returns an exception message
saying the file "/IncludedServlet" cannot be found. Note that this problem
does not occur when using Orion or Resin servlet engines.
The Analysis
============
The problem originates from chapter 8 of the Servlet 2.3 spec. In short, it
says that
- in case of a forward, the resource that is forwarded to sees the servlet
path (via getServletPath) used in the request dispatcher
- in case of an include, the included resource sees the original servlet
path (e.g. "/BugtestServlet") and can retrieve the path used in the request
dispatcher (e.g. "/IncludedServlet") via special request attributes
The problem starts when Jasper's JspServlet tries to determine which page to
deliver. It cannot simply use getServletPath() since this will return the
original path when doing an include. Thus, it checks if
"javax.servlet.include.servlet_path" is present as a request attribute. If
it is, JspServlet uses it instead of getServletPath(). This works in simple
cases (e.g. a Servlet including a JSP), but fails for the bugtest webapp.
Explanation:
- in IncludedServlet, getServletPath() returns "/BugtestServlet" and the
"javax.servlet.include.servlet_path" attribute is "/IncludedServlet"
- in JspServlet (indirectly invoked by forwarding to "/jsp/forwarded.jsp"),
getServletPath() returns "/jsp/forwarded.jsp" and the
"javax.servlet.include.servlet_path" attribute is "/IncludedServlet"
- since the additional request attribute is present, JspServlet thinks it
has to use it instead of getServletPath()
- processing "/IncludedServlet" as a JSP fails with a FileNotFoundException
This is only the harmless case. As stated in the original bug report, you
can easily produce endless forwarding loops when you replace the servlets of
the bugtest app with JSPs. And it still get's worse: Let's say you have a
JSP "test.jsp" that is included from somewhere. Now this JSP forwards to
"/html/test.html". The result is that the ***JSP source code*** of
"test.jsp" arrives at the browser! (To reproduce the last case with the
attached war file: request "/jsp/including.jsp" and look at the source code
of the returned page in your browser.)
Proposed Solution
=================
I would suggest to add a request attribute
"org.apache.catalina.actual_servlet_path" that _always_ contains the path
used when retrieving a request dispatcher. This way, the correct path could
be determined no matter whether include or forward is used:
- check if "org.apache.catalina.actual_servlet_path" is present; if so, use
it as the path for accessing resources etc.
- if the attribute is not present, fall back to the standard behaviour (i.e.
check for "javax.servlet.include.servlet_path" and use getServletPath if
this is not present)
I've already tested this with JspServlet, and it works fine. However, to be
consistent, several source files would have to be changed, for example:
- DefaultServlet.java (necessary to solve the "forward to html" problem
described above)
- HttpRequestBase.java since it uses "javax.servlet.include.servlet_path" to
convert a request-relative path to a context-relative one (currently, this
probably failes within include/forward chains)
- a bunch of other files (basically all that deal with the case of an
include specifically)
Also, the problem is not limited to "javax.servlet.include.servlet_path" but
applies to all request attributes set in an include. For all of these,
counterparts that reflect the correct path should be introduced. The best
solution would be if the spec mandated such attributes, e.g.
"javax.servlet.actual_path.servlet_path" etc. Should I write to
servletapi-feedback@eng.sun.com about it or is there a better place? Or
maybe I'm missing something and the issue can be solved more elegantly?
Final remarks
=============
If I receive positive feedback on this, I'll happily send a comprehensive
patch. Until now, I've only added an attribute for the servlet path (and not
path info etc.) and a check for it in JspServlet.
Thanks for your time
Andreas Junghans
PS Maybe this bug is also present in Tomcat 3.3 (haven't tested that).