Paul Hammant's Blog: Some more on 'Strangulation inside Tomcat'
The two web-apps of the previous blog entry
/b/ maintain their own JSESSIONIDs, and have been upgraded to show it. Build and deploy the app as before, and using Chrome visit these two in turn: http://localhost:8080/a/AFilterThatOutputsTheRequestsThreadID http://localhost:8080/b/AServletThatOutputsTheRequestsThreadID
In Chrome’s Cookie Manager (Chrome prefs), here is the cookie for “localhost” pertaining to the
/a/ context, after hitting visiting the URL:
Note that the “Path” field is the same as the webapp’s name.
The one for /b/ is much the same, but a different JSESSIONID string value as you observed when you visited the URLs.
If you go one further and go to one of the URLs that uses getServletDispatcher(..).include(..) to bridge two web apps, then you’ll see only the JSESSIONID for
/a/ be used for both.
Java Servlets versus the browser’s cookie rules.
The browser does not know about server side Java web apps; Specifically the fact that they want to maintain different JSESSIONIDs per “context” (
/b/ are different contexts in the servlet container). The browser thinks of these as directories, and doesn’t make the same distinction that Java Servlet containers do.
The browser could have a single JSESSIONID cookie mounted to root (
/) that would be allowable for both the ‘a’ and ‘b’ webapps’ browser interactions with the server. The browser would think that eligible to send up with the request, and the Tomcat would not know that had happened.
For our ‘a’ and ‘b’ example as is, with two path-separated cookies, the theoretical problem remains just that.
Making the problem a real one
What if application
/b/ were instead mounted on the root context (
/)? Effectively making ‘a’ a subdirectory of ‘b’ as far as the browser is concerned. Well I have a Github branch that shows just that:
git checkout b_is_now_root mvn clean install ./copy_war_files.sh cd apache-tomcat-7.0.41/bin ./catalina.sh run
First things first - delete all the cookies for localhost in the Chrome Cookie Manager.
Next, visit http://localhost:8080/AServletThatOutputsTheRequestsThreadID (note we have dropped the
/b/ part of the URL). We now have a single cookie on localhost, and it is mapped to the
/ (root) path. You can see that in the Cookie Manager.
Now let’s visit http://localhost:8080/a/AFilterThatOutputsTheRequestsThreadID for the other webapp. This time we’ll watch that initial interaction with Chrome inspector’s network tab:
Note the root-context JSESSIONID going up to the Tomcat server, and a second JSESSIONID mapped to
/a/ coming back.
If we hit refresh in the browser for that, then both are sent to the servlet-container with requests, and that’ll stay the same throughout the life of that session & servlet container:
In the end though, the servlet container disambiguates as is it channels requests to ‘a’ or root (former ‘b’) accurately.
Not just for strangulation..
Of course this is wider than a strategy to strangle an older webapp. It affects any situation where you might be doing some clever hosting of two webapps on the same domain from a single servlet container.
It might be nice for a mechanism inside Tomcat’s context.xml to force one JSESSIONID for all hosted webapps on the same domain. Note - I’m not asking for multiple apps to have combined session stores inside their servlet context (request.getSession() etc), just that the IDs would be the same. There’s some stuff with Servlet spec 3.x that looks like you may be able to override cookie path defaults, but I’ve not played with that.
Update: there is such a config item for Tomcat7’s context.xml:
<Context ... sessionCookiePath="/" > ... </Context>