最近tomcat升级版本时,遇到了ssi解析的问题,记录下解决的过程,还有tomcat ssi配置的要点。

tomcat 配置SSI的两种方式

Tomcat有两种方式支持SSI:Servlet和Filter。

SSIServlet

通过Servlet,org.apache.catalina.ssi.SSIServlet,默认处理”*.shtml”的URL。

配置方式:

修改tomcat的 conf/web.xml文件,去掉下面配置的注释:

<servlet>
<servlet-name>ssi</servlet-name>
<servlet-class>
org.apache.catalina.ssi.SSIServlet
</servlet-class>
<init-param>
<param-name>buffered</param-name>
<param-value>1</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>expires</param-name>
<param-value>666</param-value>
</init-param>
<init-param>
<param-name>isVirtualWebappRelative</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>4</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>ssi</servlet-name>
<url-pattern>*.shtml</url-pattern>
</servlet-mapping>

SSIFilter

通过Filter,org.apache.catalina.ssi.SSIFilter,默认处理”*.shtml”的URL。

配置方式:

修改tomcat的 conf/web.xml文件,打开去掉下面配置的注释:

<filter>
<filter-name>ssi</filter-name>
<filter-class>
org.apache.catalina.ssi.SSIFilter
</filter-class>
<init-param>
<param-name>contentType</param-name>
<param-value>text/x-server-parsed-html(;.*)?</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>expires</param-name>
<param-value>666</param-value>
</init-param>
<init-param>
<param-name>isVirtualWebappRelative</param-name>
<param-value>false</param-value>
</init-param>
</filter> <filter-mapping>
<filter-name>ssi</filter-name>
<url-pattern>*.shtml</url-pattern>
</filter-mapping>

注意事项

注意:两种配置方式最好不要同时打开,除非很清楚是怎样配置的。

另外,在Tomcat的conf/context.xml里要配置privileged=”true”,否则有些SSI特性不能生效。

<Context privileged="true">
  • 1
  • 1

历史代码里处理SSI的办法

在公司的历史代码里,在一个公共的jar包里通过自定义一个EnhancedSSIServlet,继承了Tomcat的org.apache.catalina.ssi.SSIServlet来实现SSI功能的。

@WebServlet(name="ssi",
initParams={@WebInitParam(name="buffered", value="1"), @WebInitParam(name="debug", value="0"),
@WebInitParam(name="expires", value="666"), @WebInitParam(name="isVirtualWebappRelative", value="0"),
@WebInitParam(name="inputEncoding", value="UTF-8"), @WebInitParam(name="outputEncoding", value="UTF-8") },
loadOnStartup=1, urlPatterns={"*.shtml"}, asyncSupported=true)
public class EnhancedSSIServlet extends SSIServlet {

其中@WebServlet是Servlet3.0规范里的,所以使用到web-common的web项目的web.xml文件都要配置为3.0版本以上,例如:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> </web-app>

Tomcat是启动Web应用时,会扫描所有@WebServlet的类,并初始化。

所以在使用到历史代码的项目都只能使用Tomcat服务器,并且不能在tomcat的conf/web.xml里打开SSI相关的配置。

Tomcat版本升级的问题

Tomcat版本从7.0.57升级到7.0.59过程中,出现了无法解析SSI include指令的错误:

SEVERE: #include--Couldn't include file: /pages/test/intelFilter.shtml
java.io.IOException: Couldn't get context for path: /pages/test/intelFilter.shtml
at org.apache.catalina.ssi.SSIServletExternalResolver.getServletContextAndPathFromVirtualPath(SSIServletExternalResolver.java:422)
at org.apache.catalina.ssi.SSIServletExternalResolver.getServletContextAndPath(SSIServletExternalResolver.java:465)
at org.apache.catalina.ssi.SSIServletExternalResolver.getFileText(SSIServletExternalResolver.java:522)
at org.apache.catalina.ssi.SSIMediator.getFileText(SSIMediator.java:161)
at org.apache.catalina.ssi.SSIInclude.process(SSIInclude.java:50)
at org.apache.catalina.ssi.SSIProcessor.process(SSIProcessor.java:159)
at com.test.webcommon.servlet.EnhancedSSIServlet.processSSI(EnhancedSSIServlet.java:72)
at org.apache.catalina.ssi.SSIServlet.requestHandler(SSIServlet.java:181)
at org.apache.catalina.ssi.SSIServlet.doPost(SSIServlet.java:137)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:604)
at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:543)
at org.apache.jasper.runtime.JspRuntimeLibrary.include(JspRuntimeLibrary.java:954)
at org.apache.jsp.pages.lottery.jczq.index_jsp._jspService(index_jsp.java:107)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:395)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:339)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)

仔细查看源代码后,发现不能处理的include指令代码如下:

<!--#include virtual="/pages/test/intelFilter.shtml"-->

经过对比调试Tomcat的代码,发现是在7.0.58版本时,改变了处理URL的方法,关键的处理函数是

org.apache.catalina.core.ApplicationContext.getContext( String uri)

在7.0.57版本前,Tomcat在处理处理像/pages/test/intelFilter.shtml这样的路径时,恰好循环处理了”/”字符,使得childContext等于StandardContext,最终由StandardContext处理了/pages/test/intelFilter.shtml的请求。

这个代码实际上是错误的,不过恰好处理了include virtual的情况。

在7.0.58版本修改了处理uri的代码,所以在升级Tomcat到7.0.59时出错了。

7.0.57版的代码: 
https://svn.apache.org/repos/asf/tomcat/tc7.0.x/tags/TOMCAT_7_0_57/java/org/apache/catalina/core/ApplicationContext.java

/**
* Return a <code>ServletContext</code> object that corresponds to a
* specified URI on the server. This method allows servlets to gain
* access to the context for various parts of the server, and as needed
* obtain <code>RequestDispatcher</code> objects or resources from the
* context. The given path must be absolute (beginning with a "/"),
* and is interpreted based on our virtual host's document root.
*
* @param uri Absolute URI of a resource on the server
*/
@Override
public ServletContext getContext(String uri) {
// Validate the format of the specified argument
if ((uri == null) || (!uri.startsWith("/")))
return (null);
Context child = null;
try {
Host host = (Host) context.getParent();
String mapuri = uri;
while (true) {
child = (Context) host.findChild(mapuri);
if (child != null)
break;
int slash = mapuri.lastIndexOf('/');
if (slash < 0)
break;
mapuri = mapuri.substring(0, slash);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
return (null);
}
if (child == null)
return (null);
if (context.getCrossContext()) {
// If crossContext is enabled, can always return the context
return child.getServletContext();
} else if (child == context) {
// Can still return the current context
return context.getServletContext();
} else {
// Nothing to return
return (null);
}
}

7.0.58的代码: 
https://svn.apache.org/repos/asf/tomcat/tc7.0.x/tags/TOMCAT_7_0_58/java/org/apache/catalina/core/ApplicationContext.java

那么正确的处理办法是怎样的?

仔细查看Tomcat的SSI配置的说明文档,发现有一个isVirtualWebappRelative的配置,而这个配置默认是false的。

isVirtualWebappRelative - Should "virtual" SSI directive paths be interpreted as relative to the context root, instead of the server root? Default false.

**也就是说,如果要支持“#include virtual=”/b.shtml”绝对路径这种指令,就要配置isVirtualWebappRelative为true。 
但是tomcat默认的SSI配置,以及上面的EnhancedSSIServlet类默认都配置isVirtualWebappRelative为false。**

因此,把EnhancedSSIServlet类里的isVirtualWebappRelative配置为true,重新测试,发现已经可以正常处理”#include virtual=”/b.shtml”指令了。

相关的逻辑处理的代码在org.apache.catalina.ssi.SSIServletExternalResolver.getServletContextAndPathFromVirtualPath( String virtualPath):

protected ServletContextAndPath getServletContextAndPathFromVirtualPath(
String virtualPath) throws IOException {
if (!virtualPath.startsWith("/") && !virtualPath.startsWith("\\")) {
return new ServletContextAndPath(context,
getAbsolutePath(virtualPath));
}
String normalized = RequestUtil.normalize(virtualPath);
if (isVirtualWebappRelative) {
return new ServletContextAndPath(context, normalized);
}
ServletContext normContext = context.getContext(normalized);
if (normContext == null) {
throw new IOException("Couldn't get context for path: "
+ normalized);
}

总结

之前的EnhancedSSIServlet类的配置就不支持”#include virtual=”/b.shtml”,这种绝对路径的SSI指令,而以前版本的Tomcat因为恰好处理了”/test.shtml”这种以”/”开头的url,因此以前版本的Tomcat没有报错。而升级后的Tomcat修正了代码,不再处理这种不合理的绝对路径请求了,所以报“ Couldn’t get context for path”的异常。

把tomcat的ssi配置里的isVirtualWebappRelative设置为true就可以了。

最后,留一个小问题:

tomcat是如何知道处理*.jsp请求的?是哪个servlet在起作用?

tomcat ssi配置及升级导致ssi include错误问题解决的更多相关文章

  1. Apache下开启SSI配置,使html支持include包含

    有的时候,我们的页面有公共的导航栏navbar,公共的脚注footer,那么我们就想把这些公共部分独立成一个html文件,在要引用的地方像引用js,css一样,给包含进来. Apache下开启SSI配 ...

  2. nginx配置 yii2 URL重写规则 SSI配置使shtml

    location / { // 加上红色部分 重写url try_files $uri $uri/ /index.php?$args; if (!-e $request_filename){ rewr ...

  3. Apache下开启SSI配置使html支持include包含

    写页面的同学通常会遇到这样的烦恼,就是页面上的 html 标签越来越多的时候,寻找指定的部分就会很困难,那么能不能像 javascript 一样写在不同的文件中引入呢?答案是有的,apache 能做到 ...

  4. 开启SSI配置使shtml支持include公用的页头页脚

    编写编写项目众多静态文件时,能像php等开发语言一样使用include将页面公有的header/footer/sidebar作为公用.独立.单一的文件引入到各页面上,这样修改这些页面公用部分时就能单独 ...

  5. Windows 的Apache支持SSI配置

    配置SSI什么是shtml? 使用SSI(Server Side Include)的html文件扩展名,SSI(Server Side Include),通常称为"服务器端嵌入"或 ...

  6. 在Apache下开启SSI配置

    开启SSI:html.shtml页面include网页文件 使用SSI(Server Side Include)的html文件扩展名,SSI(Server Side Include),通常称为&quo ...

  7. 生产环境中tomcat的配置

    生产环境中要以daemon方式运行tomcat 通常在开发环境中,我们使用$CATALINA_HOME/bin/startup.sh来启动tomcat, 使用$CATALINA_HOME/bin/sh ...

  8. SSI简介 与 nginx开启SSI

    Server Side Include : 服务器端嵌入 原理 : 将内容发送到浏览器之前,可以使用“服务器端包含 (SSI)”指令将文本.图形或应用程序信息包含到网页中.因为包含 SSI 指令的文件 ...

  9. tomcat 安全配置文档

    1.配置文档中使用$CATALINA_HOME变量声明为tomcat的安装目录并明确写出了tomcat的配置文件路径,此路径为测试环境的路径,线上系统对应配置文件的路径可能不一样,在进行相关配置时,应 ...

随机推荐

  1. 洛谷P2312解方程

    传送门 思路分析 怎么求解呢? 其实我们可以把左边的式子当成一个算式来计算,从1到 $ m $ 枚举,只要结果是0,那么当前枚举到的值就是这个等式的解了.可以通过编写一个 $ bool $ 函数来判断 ...

  2. sqlserver sp_spaceused用法

    sp_spaceused显示行数.保留的磁盘空间以及当前数据库中的表所使用的磁盘空间,或显示由整个数据库保留和使用的磁盘空间. 语法sp_spaceused [[@objname =] 'objnam ...

  3. 第一次ActiveX Fuzzing测试

    接着上一篇的看雪Exploit me试题. 这道题给出了一个ActiveX的DLL,挖掘这个DLL中的漏洞. 由于从来没有接触过ActiveX的Fuzzing,所以找了一些文章来看.自己动手试验了一下 ...

  4. 常用SQL Server 语句

       常用SQL语句大全 1.//创建数据库 CREATE DATABASE DBName 2.//删除数据库 DROP DATABASE DBName 3.//备份SQL SERVER --- 创建 ...

  5. C#控制台应用程序之选课系统

    本程序以文本文件作为存储媒介,实现了一个简化版的选课系统,主要实现了以下功能 对学生信息的增删改查 对课程信息的增删改查 对学生选课的增删改查 using System; using System.C ...

  6. SQL之PROCEDURE(存储过程)

    先来看一小段代码 create procedure pr_bank(@bank_id int) as BEGIN select *from bank where bank_ID = @bank_id ...

  7. Winsock—I/O模型之选择模型(一)

    Winsock中提供了一些I/O模型帮助应用程序以异步方式在一个或多个套接字上管理I/O. 这样的I/O模型有六种:阻塞(blocking)模型,选择(select)模型,WSAAsyncSelect ...

  8. sqoop遇到的问题

    我使用的是CDH版本的 这是我的sqoop脚本 sudo -u hive sqoop import --connect jdbc:mysql://xxxx/rom3 --username xxx -- ...

  9. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

  10. leetcode 无重复字符的最长子串 python实现

    这道题需要借助哈希查找key的O(n) 时间复杂度, 否则就会超时 初始化一个 哈希表\字典  dic 头指针start 初始为0 当前指针 cur 初始为0 最大长度变量 l 初始为0 用cur变量 ...