问题
I try to implement a Tomcat valve (currently using 7.0.55) that should intercept every request that reaches the Tomcat serivce, regardless of Connector and whatnot, regardless of wether there's a Host with matching name or a servlet context or whatever.
The invoke
-method of the valve looks like this:
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);
}
}
On the dev system, testing locally, everything's working as excepted. A request to any URI path on my "localhost" tomcat writes that log line. In the sever.xml
, the valve is configured outside of any Host
element:
<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>
Now say in my system's hosts file, the domain test.domain.com
is mapped to 127.0.0.1, and there's one context deployed named some-webapp
.
As said above the log line gets printet when I call http://localhost:8080/some-webapp/
, which is as expected, and it gets also printed when I call http://localhost:8080/non-existing-webapp/
, which is also as expected.
The same goes for the domain (that is not configured in server.xml) test.domain.com, thus http://test.domain.com/some-webapp/
prints the log line as well as http://test.domain.com/non-existing-webapp
.
But this is not true for the server we're testing on. Here the Valve is only invoked if the context name of the URI is "known" to tomcat, i.e. a call to .../some-webapp/ would print the log line while a call to .../non-existing-webapp/ would simply do nothing - the valve is not invoked at all.
Still, tomcat handles that request as the 404 that gets sent to the client in this case contains "Apache-Coyote something" as a response header.
I'm out of ideas of how to debug this further, especially the process of tomcat's "selection" of a Pipeline or whatever - any thoughts out there?
Thanks!
回答1:
Turns out it is caused by a missing ROOT-directory in Tomcat's webapps-dir. I think that Tomcat does filter incoming requests rather rigorously at a very early point in time, even before any valves can handle and mess with the request.
And if there's no default context (i.e. no ROOT-dir) then Tomcat (thinks to) know(s) that a request to non-existing-webapp
cannot succeed and thus does not even call the valve(s). With a default context Tomcat cannot know what will happen to the request and thus the valve gets its chance to intercept the request.
回答2:
In versions 6(not sure what happens in earlier versions) - 8 Tomcat does not call Valve if it determines that redirect is required, so suprisingly Valves work reliably only when context is determined during mapping phase.
You can check the source code(and attach a debugger if you wish) of org.apache.catalina.connector.CoyoteAdapter
class where the action happens
// 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);
As you can see Valves are called only in case postParseRequest returned true, which does not happen for example if Tomcat determines that it should return redirect during postParseRequest. Code determining that redirect is required:
// 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;
}
In my case redirect was set in org.apache.catalina.mapper.Mapper.internalMapWrapper(...)
This may become a nuisance if you use RemoteIpValve as it causes Tomcat to send redirects using the wrong schema.
来源:https://stackoverflow.com/questions/26975994/why-is-my-tomcat-valve-not-being-invoked