我尝试实现一个Tomcat阀门(当前使用7.0.55),它应该拦截到达Tomcat服务的每个请求,无论Connector和whatnot,无论是否有一个具有匹配名称的主机或servlet上下文或其他什么。
阀门的invoke
- 方法如下所示:
public class MyValve extends ValveBase {
public void invoke(Request request, Response response) throws IOException,
ServletException {
LOG.trace("Valve is being invoked");
getNext().invoke(request, response);
}
}
在开发系统上,在本地进行测试,一切工作都是例外。对我的“localhost”tomcat上的任何URI路径的请求都会写入该日志行。在sever.xml
中,阀门配置在任何Host
元素之外:
<Server port="8005" shutdown="SHUTDOWN">
...
<Service name="Catalina">
...
<Engine defaultHost="localhost" name="Catalina">
...
<Realm ... />
<Valve className="a.b.c.MyValve" />
...
<Host ...>
</Host>
</Engine>
</Service>
</Server>
现在在我的系统的hosts文件中说,域test.domain.com
映射到127.0.0.1,并且有一个部署的上下文名为some-webapp
。
如上所述,当我拨打http://localhost:8080/some-webapp/
时,日志行会打印出来,这是预期的,当我拨打http://localhost:8080/non-existing-webapp/
时也会打印出来,这也是预期的。
对于域(在server.xml中未配置)test.domain.com也是如此,因此http://test.domain.com/some-webapp/
打印日志行以及http://test.domain.com/non-existing-webapp
。
但对于我们正在测试的服务器来说,情况并非如此。这里只有在对tomcat“知道”URI的上下文名称时才调用Valve,即调用... / some-webapp /会在调用... / non-existing-webapp时打印日志行/什么都不做 - 根本没有调用阀门。
仍然,tomcat处理该请求,因为在这种情况下发送到客户端的404包含“Apache-Coyote something”作为响应头。
我不知道如何进一步调试这个,特别是tomcat“选择”管道或其他什么的过程 - 有什么想法吗?
谢谢!
答案 0 :(得分:1)
原来这是由Tomcat的webapps-dir中缺少ROOT目录引起的。我认为Tomcat确实会在很早的时间点严格过滤传入的请求,甚至在任何阀门都能处理并弄乱请求之前。
如果没有默认上下文(即没有ROOT-dir),则Tomcat(认为)知道对non-existing-webapp
的请求不能成功,因此甚至不会调用阀门。使用默认上下文,Tomcat无法知道请求会发生什么,因此阀门有机会拦截请求。
答案 1 :(得分:0)
在版本6中(不确定在早期版本中会发生什么) - 8如果Tomcat确定需要重定向,则不会调用Valve,因此令人惊讶的是,只有在映射阶段确定了上下文时,Valves才能可靠地工作。
您可以检查动作发生的org.apache.catalina.connector.CoyoteAdapter
类的源代码(并附上调试器,如果您愿意)
// Parse and set Catalina and configuration specific
// request parameters
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
boolean postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
// Calling the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
正如您所看到的,仅在postParseRequest返回true的情况下调用Valves,例如,如果Tomcat确定它应该在postParseRequest期间返回重定向,则不会发生这种情况。 确定需要重定向的代码:
// Possible redirect
MessageBytes redirectPathMB = request.getMappingData().redirectPath;
if (!redirectPathMB.isNull()) {
String redirectPath = URLEncoder.DEFAULT.encode(redirectPathMB.toString());
String query = request.getQueryString();
if (request.isRequestedSessionIdFromURL()) {
// This is not optimal, but as this is not very common, it
// shouldn't matter
redirectPath = redirectPath + ";" +
SessionConfig.getSessionUriParamName(
request.getContext()) +
"=" + request.getRequestedSessionId();
}
if (query != null) {
// This is not optimal, but as this is not very common, it
// shouldn't matter
redirectPath = redirectPath + "?" + query;
}
response.sendRedirect(redirectPath);
request.getContext().logAccess(request, response, 0, true);
return false;
}
在我的情况下,重定向已在org.apache.catalina.mapper.Mapper.internalMapWrapper(...)
如果您使用RemoteIpValve,这可能会造成麻烦,因为它会导致Tomcat使用错误的架构发送重定向。