Question

Tomcat, per the Java EE standard, allows for requests to specify their existing session ID in two ways: 1) via a cookie; 2) via a "path parameter" (not a regular parameter; a path parameter has the format http://host/path/file.ext;jsessionid=xxx?a=b&c=d... - note the ";" and the fact that the query string only starts AFTER the path parameter). What I WANT is to pass the session ID in the request as a REGULAR param, in the query string after the "?", like http://host/path/file.ext?jsessionid=xxx.

By the time the request reaches a place where I could intercept it and change the way the container determines session ID (in a Filter or Servlet, e.g.), it's too late. The behavior I want to alter is in the initial processing of the request from the client. What I want to avoid doing is altering Coyote or Tomcat code and rebuilding Tomcat myself, for all of the obvious reasons. What I'd prefer to do is override the appropriate code and config Tomcat to use that code to determine the requested session ID. That doesn't seem to be possible, but I hope I'm wrong.

I know that getting the session ID in this way is nonstandard; I know that using a cookie or a path param are both GREAT ways to track session state; I know that putting the session ID in the actual query string presents potential problems of its own. I need to do it anyway.

Running Tomcat 7, btw.

Was it helpful?

Solution

The easiest way is it build Tomcat from source, and hack the CoyoteAdapter.java class.

This is where this code resides, but it's not in an easily extensible part of the code where you can just plug in a little bit of code to intercept this part of the request pipeline.

You could get clever and implement your own Connector that relies on your own CoyoteAdapter, rather than just hacking it "in place", but in the end you'll still have a maintenance issue of keeping up with updates, etc.

You also need to be careful, since normally the request parameters are not "parsed" until something asks for them, and usually this is not done until actual user code (I don't believe it's done in the pipeline prior to hitting user code).

This is important because while for GETs the parameter parsing is done off of the request header, for POST, it's based on the content. And if you ask the request for a parameter, it doesn't care if it's a POST or a GET and will "do the right thing" automatically.

This means that if you ask for a request parameter from a POST, the pipeline will be consuming the input stream (which begins pointing at the request payload, after the headers) before the users application code. Depends on the use case, but some apps want the raw stream, so you'd have to be careful using the built in request parameter logic, rather than acting on the raw URL itself.

You may also be able to get away with this with a simple filter, and then you can cast the HttpServletRequest to the underlying Tomcat implementation (who's name eludes me at the moment), and then call the "setSessionID" code on that. Dunno if that's too late in the processing cycle or not, it may well be.

So, it can be done, rather simply it seems, but not through any really prescribed means of extension. The Official Way would be to clone the tomcat connector to call your own version of the CoyoteAdapter, and configure that in the server.xml. But if you're willing to maintain your own version of tomcat, hacking CoyoteAdapter directly would be a lot easier.

OTHER TIPS

You can do this without altering any Tomcat code. Here is an example http filter. Obviously it should run before any other filters:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    final String sessionId = servletRequest.getParameter("jsessionId");
    if (servletRequest instanceof org.apache.catalina.connector.RequestFacade) {
        try {
            Field tomcatRequestField = servletRequest.getClass().getDeclaredField("request");
            tomcatRequestField.setAccessible(true);
            Request tomcatRequest = (Request) tomcatRequestField.get(servletRequest);
            if (sessionId != null) {
                tomcatRequest.setRequestedSessionId(sessionId);
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            LOG.error("Error", e);
        }
    }
    filterChain.doFilter(servletRequest,servletResponse);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top