Tracking session expiration in browser

So, there is that complex heterogeneous web application, with AJAX parts done both manually and by frameworks, multiple pop-up windows, etc. A big respectable client approaches you with a requirement to invalidate, close, or do some other activity on all web application windows once HTTP session times out. Hopefully you know how to control the HTTP session time-out interval, for a J2EE-compliant web-application it is done from a web.xml file (however in lots of app servers it's done not in a standard way). For a 10 minutes time-out it is:


	<session-config>
		<session-timeout>10</session-timeout>
	</session-config>

The client requirement is not absurd at all and makes perfect sense from the end-user perspective, but it can become a terrible pain for a developer because: 1. You can't just start a countdown timer in the browser window each time page loads to close the window upon time-out. This approach worked in a non-AJAX world when each browser-server interaction resulted in browser window reloaded. 2. You can't query the server to check whether HTTP session has timed-out or not, as each such query will be treated as a browser-server interaction prolonging the session. This will lead to a never expiring session. 3. You could create a separate web-application being aware of the primary web-app's HTTP session and intersecting with it. But that's an overkill, and the chances of such solution getting accepted are extremely low due to integration issues likely to arise. 4. You could try to intercept all AJAX browser-server interactions with some advanced hack-like code, and it will help you to deal with your current window. But this doesn't work for the multiple open windows case - you just can't communicate between browser windows. The only way to talk to some open window from a primary one is to use other window's JavaScript reference, and once primary window is reloaded or directed to a different location it loses all JavaScript references to other windows. 5. The most realistic approach is to make periodical JavaScript XMLHTTP requests (from each open window) to the server each {session max inactive interval}+10 seconds. This will eventually close all windows, but may result in windows closed minutes (or even hours depending on web-app session time-out setting) after the HTTP session gets destroyed, e.g. once user logs out from within the primary window. No more options left, you are frustrated and you think that it's just the right time to take your daddy's gun and shoot your classmates at the school tomorrow. No, not yet kid - there is still a way out! The way out is not very straightforward, but is very elegant. Cookies will help us. One could think that cookies expiration time would do the trick. Unfortunately, as described in

this

article, you can't rely on cookie expiration time since it is measured by a client browser, and noone can guarantee that the client system clock is not one year behind. So, here is the System and Method for Tracking HTTP Session Time-Outs in Heterogeneous Web applications. On each request made from a browser to a server two cookies are set by a servlet filter. One holds the server current time, and another holds session expiration time. Server current time is only needed to calculate an offset between client and server. Session expiration time is then gets periodically checked against the _calculated_ current server time (remember the offset). Each time _any_ request is made to the server the expiration time cookie gets updated, and the whole thing just works. In practice this method is realized in just three steps: 1. Create a servlet filter that would filter each and every request to your web-application. Configure it in web.xml like this:


	<filter>
		<filter-name>SessionTimeoutCookieFilter</filter-name>
		<filter-class>some.package.SessionTimeoutCookieFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>SessionTimeoutCookieFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

Don't worry about you web-app performance - this filter is VERY primitive, all it does is adding two cookies to the response:


	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
            HttpServletResponse httpResp = (HttpServletResponse) resp;
            HttpServletRequest httpReq = (HttpServletRequest) req;
            long currTime = System.currentTimeMillis();
            long expiryTime = currTime + session.getMaxInactiveInterval() * 1000;
            Cookie cookie = new Cookie("serverTime", "" + currTime);
            cookie.setPath("/");
            httpResp.addCookie(cookie);
            if (httpReq.getRemoteUser() != null) {
                cookie = new Cookie("sessionExpiry", "" + expiryTime);
            } else {
                cookie = new Cookie("sessionExpiry", "" + currTime);
            }
            cookie.setPath("/");
            httpResponse.addCookie(cookie);
            filterChain.doFilter(req, resp);
	}

Setting path (to "/" in our case) is very important. If you omit path setting the browser will automatically calculate it from the URL which will result in chaos inside your browser cookies storage. 2. We need a small JavaScript on each window to calculate offset between server and client time. It needs to be run just once, but it wouldn't hurt to run it on each page load:



function calcOffset() {
    var serverTime = getCookie('serverTime');
    serverTime = serverTime==null ? null : Math.abs(serverTime);
    var clientTimeOffset = (new Date()).getTime() - serverTime;
    setCookie('clientTimeOffset', clientTimeOffset);
}

window.onLoad = function() { calcOffset(); };

3. And finally we need a function that would actually check whether session has timed-out. It needs to be executed periodically, in our case each 10 seconds (or 10000 milliseconds):


function checkSession() {
    var sessionExpiry = Math.abs(getCookie('sessionExpiry'));
    var timeOffset = Math.abs(getCookie('clientTimeOffset'));
    var localTime = (new Date()).getTime();
    if (localTime - timeOffset > (sessionExpiry+15000)) { // 15 extra seconds to make sure
        window.close();
    } else {
        setTimeout('checkSession()', 10000);
    }
}

Indeed, closing browser windows upon session expiry is pure brutality, and it can and should be accompanied by a warning message popping up like 1 minute before session times out. I'm really interested in receiving your

critical feedback

on my method.