Answered by:
Scripting events no longer fire when a user leaves site or closes browser even though app is still running.

Question
-
User-1335950151 posted
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, MaximumHeartBeats FROM aspnet_WebSiteSettings" cmd.CommandType = System.Data.CommandType.Text sqlConnection1.Open() dreader = cmd.ExecuteReader() If dreader.Read() Then HeartBeatTimeout = dreader("HeartBeatTimeout") MissedHeartBeats = dreader("MaximumHeartBeats") 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
Code on 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 no 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!
DavidTuesday, December 24, 2013 11:01 AM
Answers
-
User753101303 posted
Hi,
No, it runs client side so by definition if the user goes to another page or closes his browser, your content is unloaded and doesn't run any more. Keep in mind that the "web app" is server side (and doesn't even really "run" it just render pages as requested and only "on demand". You could keep Session_End and possibly use a safe guard measure such as removing a directory if its too old (or even don't use at all session bound directories, not sure about their usage).
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Thursday, December 26, 2013 3:12 PM
All replies
-
User753101303 posted
Hi,
No, it runs client side so by definition if the user goes to another page or closes his browser, your content is unloaded and doesn't run any more. Keep in mind that the "web app" is server side (and doesn't even really "run" it just render pages as requested and only "on demand". You could keep Session_End and possibly use a safe guard measure such as removing a directory if its too old (or even don't use at all session bound directories, not sure about their usage).
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Thursday, December 26, 2013 3:12 PM -
User-1335950151 posted
Thanks for your reply. I have come to the conclusion that there isn't a way to fire an event when a browser is not on the site. It appears that as 'cool' as the heart beat idea might be, there is no practical way to make it work from an ASP.NET application web site that uses code behind with page generated Javascripts. Hopefully this post will be helpful to other who might be considering this and keep them from wasting their time ;)
Currently the application's routines are being reworked to use the Session_End event instead. When the Session_End event fires, the session data is eventually gone, so you cannot use session variables for any cleanup routines. Instead the plan is to create some global App_Code variables that will be set in the Application_Start event and then use them in the Session_End event to remove any inactive session data and temp directories.
Thursday, December 26, 2013 5:35 PM