AJAX session keep-alive with expiration detection
This AJAX keepalive script can be used to keep sessions alive on a web server by polling it every minute (using POST requests to avoid caching). The page itself is not reloaded, the requests to the server are happening in the background. A hashed ID based on the server-side session ID is returned and the client-side script checks if it matches the last received session ID.Embedded in a page, it looks and works like this:
Session status
The system consists of four elements: The client-side HTML that loads the Javascript (example.html), the Javascript itself (keepalive.js), the server-side code for returning the hashed session ID (session.asp) and the hashing algoritm (sha256.asp).
Note that although classic ASP is used in this example for returning a hashed server-side session ID, the server-side part can be implemented on any modern session-aware platform.
example.html (lines needed to be included in the HTML content highlighted):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252" />
<title>Ajax session keep-alive with expiration detection</title>
</head>
<body>
<span id="sessionstatus">Session status</span>
<script type="text/javascript" src="keepalive.js"></script>
</body>
</html>
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252" />
<title>Ajax session keep-alive with expiration detection</title>
</head>
<body>
<span id="sessionstatus">Session status</span>
<script type="text/javascript" src="keepalive.js"></script>
</body>
</html>
...Or like this, if the default options need to be tweaked:
<span id="sessionstatus">Session status</span>
<script type="text/javascript">
kaDebug=false; // Set to true to show the full server response
kaServerPage='session.asp'; // a page returning the server-side session ID
kaInterval=60; // keep-alive interval in seconds
kaOkMessage='session alive';
kaExpiredMessage='session expired';
kaErrorMessage='session check error';
kaStatusElementID='sessionstatus'; // DOM element ID to update with status
</script>
<script type="text/javascript" src="keepalive.js"></script>
<script type="text/javascript">
kaDebug=false; // Set to true to show the full server response
kaServerPage='session.asp'; // a page returning the server-side session ID
kaInterval=60; // keep-alive interval in seconds
kaOkMessage='session alive';
kaExpiredMessage='session expired';
kaErrorMessage='session check error';
kaStatusElementID='sessionstatus'; // DOM element ID to update with status
</script>
<script type="text/javascript" src="keepalive.js"></script>
keepalive.js: (based on the generic AJAX example)
var kaHttpRequest = false;
var kaOldSessionId = '';
if (typeof kaDebug == 'undefined')
{
var kaDebug = false;
}
if (typeof kaServerPage == 'undefined')
{
var kaServerPage = 'session.asp';
}
if (typeof kaInterval == 'undefined')
{
var kaInterval = 60;
}
if (typeof kaOkMessage == 'undefined')
{
var kaOkMessage = '<span style="color: #41930a;">Session alive</span>';
}
if (typeof kaExpiredMessage == 'undefined')
{
var kaExpiredMessage = '<span style="color: #b82c06;">Session expired</span>';
}
if (typeof kaErrorMessage == 'undefined')
{
var kaErrorMessage = '<span style="color: #b82c06;">Session check error</span>';
}
if (typeof kaStatusElementID == 'undefined')
{
var kaStatusElementID = 'sessionstatus';
}
kaAjax('POST', kaServerPage, '', kaStatusElementID);
setInterval("kaAjax('POST', kaServerPage, '', kaStatusElementID)", kaInterval * 1000);
function kaAjax(httpRequestMethod, url, parameters, target)
{
kaHttpRequest = false;
document.getElementById(target).innerHTML = 'Wait...'
if (window.XMLHttpRequest)
{ // For Mozilla, Safari, Opera, IE7+
kaHttpRequest = new XMLHttpRequest();
if (kaHttpRequest.overrideMimeType)
{
kaHttpRequest.overrideMimeType('text/plain');
//Change MimeType to match the data type of the server response.
//Examples: "text/xml", "text/html", "text/plain"
}
}
else if (window.ActiveXObject)
{ // For IE6
try
{
kaHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
try
{
kaHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{}
}
}
if (!kaHttpRequest)
{
alert('Giving up :( Cannot create an XMLHTTP instance');
return false;
}
kaHttpRequest.onreadystatechange = function() {updateElement(target);};
if (httpRequestMethod == 'GET')
{
var ser = Math.round(Math.random()*1000000); // Anti-caching random number
kaHttpRequest.open('GET', url + '?' + parameters + '&random=' + ser, true);
kaHttpRequest.send(null);
}
else if (httpRequestMethod == 'POST')
{
kaHttpRequest.open('POST', url, true);
kaHttpRequest.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');
kaHttpRequest.send(parameters);
}
else
{
alert('Sorry, unsupported HTTP method');
}
}
function updateElement(target)
{
if (kaHttpRequest.readyState == 4)
{
if (kaDebug == true)
{
alert(kaHttpRequest.responseText);
}
if (kaHttpRequest.status == 200)
{
if (kaOldSessionId == '')
{
kaOldSessionId = kaHttpRequest.responseText;
}
if (kaHttpRequest.responseText == kaOldSessionId)
{
document.getElementById(target).innerHTML = kaOkMessage;
}
else
{
document.getElementById(target).innerHTML = kaExpiredMessage;
}
}
else
{
document.getElementById(target).innerHTML = kaErrorMessage;
}
}
}
var kaOldSessionId = '';
if (typeof kaDebug == 'undefined')
{
var kaDebug = false;
}
if (typeof kaServerPage == 'undefined')
{
var kaServerPage = 'session.asp';
}
if (typeof kaInterval == 'undefined')
{
var kaInterval = 60;
}
if (typeof kaOkMessage == 'undefined')
{
var kaOkMessage = '<span style="color: #41930a;">Session alive</span>';
}
if (typeof kaExpiredMessage == 'undefined')
{
var kaExpiredMessage = '<span style="color: #b82c06;">Session expired</span>';
}
if (typeof kaErrorMessage == 'undefined')
{
var kaErrorMessage = '<span style="color: #b82c06;">Session check error</span>';
}
if (typeof kaStatusElementID == 'undefined')
{
var kaStatusElementID = 'sessionstatus';
}
kaAjax('POST', kaServerPage, '', kaStatusElementID);
setInterval("kaAjax('POST', kaServerPage, '', kaStatusElementID)", kaInterval * 1000);
function kaAjax(httpRequestMethod, url, parameters, target)
{
kaHttpRequest = false;
document.getElementById(target).innerHTML = 'Wait...'
if (window.XMLHttpRequest)
{ // For Mozilla, Safari, Opera, IE7+
kaHttpRequest = new XMLHttpRequest();
if (kaHttpRequest.overrideMimeType)
{
kaHttpRequest.overrideMimeType('text/plain');
//Change MimeType to match the data type of the server response.
//Examples: "text/xml", "text/html", "text/plain"
}
}
else if (window.ActiveXObject)
{ // For IE6
try
{
kaHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
try
{
kaHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{}
}
}
if (!kaHttpRequest)
{
alert('Giving up :( Cannot create an XMLHTTP instance');
return false;
}
kaHttpRequest.onreadystatechange = function() {updateElement(target);};
if (httpRequestMethod == 'GET')
{
var ser = Math.round(Math.random()*1000000); // Anti-caching random number
kaHttpRequest.open('GET', url + '?' + parameters + '&random=' + ser, true);
kaHttpRequest.send(null);
}
else if (httpRequestMethod == 'POST')
{
kaHttpRequest.open('POST', url, true);
kaHttpRequest.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');
kaHttpRequest.send(parameters);
}
else
{
alert('Sorry, unsupported HTTP method');
}
}
function updateElement(target)
{
if (kaHttpRequest.readyState == 4)
{
if (kaDebug == true)
{
alert(kaHttpRequest.responseText);
}
if (kaHttpRequest.status == 200)
{
if (kaOldSessionId == '')
{
kaOldSessionId = kaHttpRequest.responseText;
}
if (kaHttpRequest.responseText == kaOldSessionId)
{
document.getElementById(target).innerHTML = kaOkMessage;
}
else
{
document.getElementById(target).innerHTML = kaExpiredMessage;
}
}
else
{
document.getElementById(target).innerHTML = kaErrorMessage;
}
}
}
session.asp:
<%
Option Explicit
%>
<!-- #include file="sha256.asp" -->
<%
' For security, the server-side session ID is salted and
' hashed before being sent to the client
Dim strSalt
strSalt = "rsq8SwDAmEMDkRv5sMvDe3C" ' Just some random data
' The hash is only calculated once and stored in a session variable:
If Session("SaltedSessionID") = "" Then
Session("SaltedSessionID") = sha256(CStr(Session.SessionID) & strSalt)
End If
Response.Write(Session("SaltedSessionID"))
%>
Option Explicit
%>
<!-- #include file="sha256.asp" -->
<%
' For security, the server-side session ID is salted and
' hashed before being sent to the client
Dim strSalt
strSalt = "rsq8SwDAmEMDkRv5sMvDe3C" ' Just some random data
' The hash is only calculated once and stored in a session variable:
If Session("SaltedSessionID") = "" Then
Session("SaltedSessionID") = sha256(CStr(Session.SessionID) & strSalt)
End If
Response.Write(Session("SaltedSessionID"))
%>
Note: If you don't care about hashing the session ID, session.asp can be as simple as this one-liner:
<% Response.Write(Session.SessionID) %>
sha256.asp (only needed if the session ID should be hashed):
The SHA256 ASP implementation (sha256.asp) used in this example can be downloaded at http://www.frez.co.uk/vb6.aspx.
Page last updated 2009-08-29 21:19. Some rights reserved (CC by 3.0)