A project I am working with implements three JAVA script "timers" from a Master Page to track the session time out and to do a "heartbeat" check. The "setTimerout" timer redirects the user to a warning page if the time out count reaches 1 and a half minutes before a session time out will occur. The two "setInterval" timers are implemented to track if the user's browser is sill active on the site. Following are several code snippets that implement the "timers" and the "Heartbeat" and "Heartbeat Check" handlers.
Master Page code to activate timers:
Dim BaseAccountTimeout As Single = CType((CType(CurrentSessionTimeout, Single) - 1.5), Single)
If Session.Item("UseSessionTimeout") Then
Dim TimeoutAcount As String = CType(((BaseAccountTimeout * 60) * 1000), Integer).ToString
Page.ClientScript.RegisterStartupScript(Me.GetType, "TimeoutScript", "setTimeout(""top.location.href = '" & GotoTimeoutPage & "'""," & TimeoutAcount & ");", True)
End If
If Session.Item("UseHeartBeat") Then
Dim BaseHeartBeatTimeout As Single = CType(CurrentHeartBeatTimeout, Single)
Dim BaseHeartBeatCheckTimeout As Single = CType((BaseHeartBeatTimeout + 1.5), Single)
Dim TimeoutHeartBeat As String = CType(((BaseHeartBeatTimeout * 60) * 1000), Integer).ToString
Dim TimeoutHeartBeatCheck As String = CType(((BaseHeartBeatCheckTimeout * 60) * 1000), Integer).ToString
Page.ClientScript.RegisterStartupScript(Me.GetType, "HeartBeatScript", "setInterval(""ajax.post('/pages/heartbeat.ashx', {id: '" + Session.SessionID + "'}, function() {})""," & TimeoutHeartBeat & ");", True)
Page.ClientScript.RegisterStartupScript(Me.GetType, "HeartBeatCheckScript", "setInterval(""ajax.post('/pages/heartbeatcheck.ashx', {id: '" + Session.SessionID + "'}, function() {})""," & TimeoutHeartBeatCheck & ");", True)
End If
Heartbeat handler code:
<%@ webhandler language="VB" class="HeartbeatHandler" %>
Imports System
Imports System.Web
Imports System.Data
Imports System.Data.SqlClient
Public Class HeartbeatHandler
Implements IHttpHandler
Public ReadOnly Property IsReusable As Boolean Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
Public Sub ProcessRequest(ByVal ctx As HttpContext) Implements IHttpHandler.ProcessRequest
Dim sqlConnection1 As New System.Data.SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings("ApplicationServices").ConnectionString)
Dim cmd As New System.Data.SqlClient.SqlCommand
Dim dreader As System.Data.SqlClient.SqlDataReader
Dim GotOne As Boolean = False
Dim ValuesString As String = ""
Dim FieldsString As String = ""
Dim id As String = ""
id = ctx.Request.Params.Item("id")
If IsNothing(id) Then id = ""
If id = "" Then GoTo EXITHEARTBEAT
cmd.Connection = sqlConnection1
cmd.CommandType = System.Data.CommandType.Text
If cmd.Parameters.Count = 0 Then
cmd.Parameters.Add("@uID", System.Data.SqlDbType.NVarChar)
cmd.Parameters.Add("@Date", System.Data.SqlDbType.DateTime)
cmd.Parameters.Add("@IP", System.Data.SqlDbType.NVarChar)
End If
cmd.Parameters("@uID").Value = id
cmd.Parameters("@Date").Value = CType(Now, DateTime)
cmd.Parameters("@IP").Value = HttpContext.Current.Request.UserHostAddress
Try
cmd.CommandText = "SELECT SessionID FROM aspnet_Sessions WHERE SessionID = @uID"
sqlConnection1.Open()
dreader = cmd.ExecuteReader()
If dreader.Read() Then GotOne = True
dreader.Close()
Catch ex As Exception
' do nothing here
Finally
sqlConnection1.Close()
End Try
If GotOne Then
Try
ValuesString = "LastRequest = @Date"
cmd.CommandText = "UPDATE aspnet_Sessions SET " + ValuesString + " WHERE SessionID = @uID"
sqlConnection1.Open()
cmd.ExecuteNonQuery()
Catch ex As Exception
' do nothing here
Finally
sqlConnection1.Close()
End Try
Else
Try
FieldsString = "(Record, LiveDate, SessionID, LastRequest, IPAddress)"
ValuesString = "(NEWID(), @Date, @uID, @Date, @IP)"
cmd.CommandText = "INSERT aspnet_Sessions " + FieldsString + " VALUES " + ValuesString
sqlConnection1.Open()
cmd.ExecuteNonQuery()
Catch ex As Exception
' do nothing here
Finally
sqlConnection1.Close()
End Try
End If
EXITHEARTBEAT:
End Sub
End Class
Heartbeat check handler code:
<%@ webhandler language="VB" class="HeartbeatCheckHandler" %>
Imports System
Imports System.Web
Imports System.Data
Imports System.Data.SqlClient
Public Class HeartbeatCheckHandler
Implements IHttpHandler
Public ReadOnly Property IsReusable As Boolean Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
Public Sub ProcessRequest(ByVal ctx As HttpContext) Implements IHttpHandler.ProcessRequest
Dim sqlConnection1 As New System.Data.SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings("ApplicationServices").ConnectionString)
Dim cmd As New System.Data.SqlClient.SqlCommand
Dim dreader As System.Data.SqlClient.SqlDataReader
Dim GotOne As Boolean = Nothing
Dim ValuesString As String = ""
Dim FieldsString As String = ""
Dim LastRequest As DateTime = Nothing
Dim TimeDifference As Long = 0
Dim HeartBeatTimeout As Single = CommonCode.Common.DefaultHeartBeatTimeout
Dim MissedHeartBeats As Single = CommonCode.Common.DefaultMissedHeartBeats
Dim id As String = ""
id = ctx.Request.Params.Item("id")
If IsNothing(id) Then id = ""
If id = "" Then GoTo EXITHEARTBEATCHECK
cmd.Connection = sqlConnection1
cmd.CommandType = System.Data.CommandType.Text
Try
cmd.CommandText = "SELECT HeartBeatTimeout, MaximumMissedHeartBeats FROM aspnet_WebSiteSettings"
cmd.CommandType = System.Data.CommandType.Text
sqlConnection1.Open()
dreader = cmd.ExecuteReader()
If dreader.Read() Then
HeartBeatTimeout = dreader("HeartBeatTimeout")
MissedHeartBeats = dreader("MaximumMissedHeartBeats")
End If
dreader.Close()
Catch ex As Exception
' do nothing here
Finally
sqlConnection1.Close()
End Try
Dim MaximumDifference As Single = (HeartBeatTimeout * MissedHeartBeats) + 0.5
If cmd.Parameters.Count = 0 Then cmd.Parameters.Add("@uID", System.Data.SqlDbType.NVarChar)
cmd.Parameters("@uID").Value = id
Try
cmd.CommandText = "SELECT SessionID, LastRequest FROM aspnet_Sessions WHERE SessionID = @uID"
cmd.CommandType = System.Data.CommandType.Text
sqlConnection1.Open()
dreader = cmd.ExecuteReader()
If dreader.Read() Then
If Not IsDBNull(dreader("LastRequest")) Then
If dreader("LastRequest").ToString.Trim <> "" Then
LastRequest = dreader("LastRequest")
TimeDifference = DateDiff(DateInterval.Minute, LastRequest, CType(Now, DateTime))
If TimeDifference > MaximumDifference Then GotOne = True
Else
GotOne = False
End If
Else
GotOne = False
End If
Else
GotOne = False
End If
dreader.Close()
Catch ex As Exception
' do nothing here
Finally
sqlConnection1.Close()
End Try
If GotOne Then
Try
cmd.CommandText = "DELETE aspnet_Sessions WHERE SessionID = @uID"
cmd.CommandType = System.Data.CommandType.Text
sqlConnection1.Open()
cmd.ExecuteNonQuery()
Catch ex As Exception
' do nothing here
Finally
sqlConnection1.Close()
End Try
CommonCode.Common.DeleteFromSessionIDCollection(id)
CommonCode.Common.SessionData(CommonCode.Common.DeleteSessionData, id)
CommonCode.Common.DisposeSession()
End If
EXITHEARTBEATCHECK:
End Sub
End Class
Following is the code on the Master page:
var ajax = {};
ajax.x = function () {
try {
return new ActiveXObject('Msxml2.XMLHTTP')
} catch (e1) {
try {
return new ActiveXObject('Microsoft.XMLHTTP')
} catch (e2) {
return new XMLHttpRequest()
}
}
};
ajax.send = function (url, callback, method, data, sync) {
var x = ajax.x();
x.open(method, url, sync);
x.onreadystatechange = function () {
if (x.readyState == 4) {
callback(x.responseText)
}
};
if (method == 'POST') {
x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
}
x.send(data)
};
ajax.get = function (url, data, callback, sync) {
var query = [];
for (var key in data) {
query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
}
ajax.send(url + '?' + query.join('&'), callback, 'GET', null, sync)
};
ajax.post = function (url, data, callback, sync) {
var query = [];
for (var key in data) {
query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
}
ajax.send(url, callback, 'POST', query.join('&'), sync)
};
I have discovered that the JAVA script timers only continue to fire while a browser is on the site. As soon as the user closes the browser or navigates away from the site, even though the web application is still running, the only event that files is the ASP.NET "Session_End" event in Global.asax when the session times out. None of the "timers" will fire which I am assuming is caused by the fact that the Master Page is no longer loaded so the scripts on the Master do not fire.
I don't really care about the "setTimeout" timer, since there is no need to redirect to a timeout page if the browser is not longer active on the site, so that script can stay on the Master page. However, the two "setInterval" timers are what I am trying to keep running so the app can detect if the user has left the site and then the app can remove the session data and temp directories for that session.
My question is this: Does anyone know of a way to run these two scripts from a service or some other way to implement them so they will continue to fire even though the browser is closed or has gone to another site?
Thanks! David
PS. A little background: The four global variables 'CurrentSessionTimeout', 'CurrentHeartBeatTimeout', 'DefaultHeartBeatTimeout', & 'DefaultMissedHeartBeats' and the two session variables 'UseSessionTimeout' & 'UseHeartBeat' are set when the app starts from values stored in an SQL database. However, neither of the handlers use session variables, but instead get any values they need directly from global variables or the database since the session could have ended before the handler fires, that is if a page is still loaded, which goes back to the original reason for this post. :)