Hi Guys,
I recently had the pleasure to work more with web applications and am now finding my way back to the server source. First impression: tomcat grew big, compared to JServ times .. but it seems, that its actual main aim, being a small, robust and fast servlet engine - isn't as dominant as it used to be ... (any folks here from the good ol' JServ times ? At least I see Pier quite often in the list). Anyway, while making the wingS application framework (http://wings.mercatis.de/) work with Tomcat 4.x, I've found two bugs in TC, that go hand in hand, but basically make working with URL encoded sessions unpredictable or basically not working. (If applications rely on URL-encoding, then these can be regarded as critical bugs). Both bugs show up, if invalidated sessions come via cookies from the browser. Bug #2 can have an impact as well if multiple session cookies for different servlet contextes on that hosts are available: only the first cookie is accepted. Didn't verify this, but if failing as expected, then this is a severe bug, because only the first servlet context will work with a session ... These bugs are in both CVS versions of tomcat 4.0 and 4.1. One of the bugs can be easily fixed (fix given), the other bug probably needs a bit more work. An example servlet that exposes both bugs is available. ********* Bug #1 ; fix available ********* The logic to determine whether a URL needs to be encoded in HttpServletResponse.encodeURL() is broken. In HttpServletResponseBase.isEncodeable(String location), it decides, that the URL needn't be encoded in the URL, if the current ID comes from the cookie; see code-snippet from HttpServletResponseBase: ------- if (hreq.isRequestedSessionIdFromCookie()) { return (false); } ------ However, this does not take into account, that the session ID we got might have been from some previous session that already is invalidated, i.e. is not valid. In this case isRequestedSessionIdFromCookie() will return true, but this does not say anything if future (valid) sessions will come through the cookie. The fix is easy: So the only way to check this correctly is: --------- if (hreq.isRequestedSessionIdFromCookie() && hreq.isRequestedSessionIdValid()) { return (false); } --------- ********* Bug #2 ; detailed explanation but no fix yet ********* There is a bug in the way, the session id is grabbed from the request. If there is more than one session id in the request -- in the URL and a Cookie, for instance -- the session id found in the cookie _always_ wins. This is a problem, if the browsers sends an invalidated cookie and you choose to use URL-encoding in a later session: even if the session id from the URL (via encodeURL(), that works only after fixing Bug #1) is valid, the application always gets the old and invalid session from the cookie instead of the valid session from the URL. The expected behaviour of course is: give preference to valid session id's if we get more than one session id. The current session id grabbing-from-http-request algorithm is as follows (from HttpProcessor.java) -------- 1. get the session ID from the URL, if any. [HttpProcessor.parseRequest()] 2. go through the cookies. If there is _any_ jsessionid - grab the _first one_ and override the jsession-id found in the URL unconditionally. And set request.setRequestedSessionCookie(true); request.setRequestedSessionURL(false); even if the jsession id found in the cookie is the _same_ as found in the URL, in that case it should be setRequestedSessionURL(true). [HttpProcessor.parseHeaders()] --------- However, it should be something like: --------- 1. get the jsessionid from the URL, if any. if found there, setRequestedSessionURL(true) else setRequestedSessionURL(false) 2. go through the cookies. FOREACH jsessionid found in the cookies: IF the sessionid found is valid in that context IF found session id equals id already in request setRequestedSessionCookie(true) ELSE (* see below) override the session id in request with the cookie-value setRequestedSessionCookie(true) setRequestedSessionURL(false) ENDIF BREAK FOREACH ELSE IF we have not found any session id before (either from URL or a previous cookie) // set at least some session id set the session id from the cookie setRequestedSessionCookie(true) END FOREACH --------- This makes sure, that we find the valid session id, if there is more than one session. <discussion> I'd even suggest to give a higher priority to the URL encoded session: if the session id found in the URL is _valid_, then ignore any valid session id in the cookies unless it is the same. This enables to have two independant web-application instances in the same browser: one with cookie, and one with URL-encoding (otherwise this mode only works with two applications both with URL-encoding). This behaviour can be implemented by adding -- IF not request.isRequestedSessionIdValid() -- at the point denoted with (*) above. </discussion> In an attempt to fix this bug myself, I found, that at that stage it is not yet possible to check whether isRequestedSessionIdValid() [ implementation in HttpRequestBase ], since the context is not yet set in the HttpProcessor.process() stage -- so we don't have the manager and cannot check the session ID in that context for validity. The context is set much later in the processing in StandardHostMapper.map() after having gone through several valves/Pipelines. Since I don't know the internals of the tomcat (yet) I have no quick fix at hand, but for you guys its probably no big deal. Or give me an hint - then I'll fix it myself. To demonstrate both of these bugs, I've written a small servlet, that goes through several steps to create two sessions; one as cookie, one with URL rewriting - just follow the instructions the servlet gives. Note, that Bug #2 can only be checked thoroughly if Bug #1 has been fixed; otherwise Bug #1 does not do URL-encoding in the first place. <http://www.freiheit.com/users/hzeller/SessionBugDemonstration.java> ciao, Henner. PS: Interestingly, in tomcat 3.x the servlet works as expected; however even there, the session id from the cookie seems to win, so that getRequestedSessionId() returns a bogus (old) session-id and isRequestedSessionIdFromCookie() returns true, even though the _valid_ requested session id came from the URL. Still we receive the correct HttpSession, whose getId() call returns the correct id. -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>