Force ASP.NET Apps to Keep-Alive

The following code from Larry Silverman is an extension of my Keep-Alive / Pre-Compile code:

GlobalBase.vb
Note: There are some references to TAConfig which is just a small helper class for reading config file settings.

Imports System
Imports System.IO
Imports System.Net
Imports System.Web
Imports System.Web.UI

Namespace Wilson.WebCompile
    Public Class GlobalBase
        Inherits System.Web.HttpApplication

        Private Shared _needsCompile As Boolean = True
        Private Shared _applicationPath As String = ""
        Private Shared _physicalPath As String = ""
        Private Shared _applicationURL As String = ""

        Private Shared _thread As System.Threading.Thread = Nothing
        Private Shared _timer As System.Timers.Timer = Nothing

        '--------------------------------------------------------------------------
        ' TA Customization
        '--------------------------------------------------------------------------
        Private Shared _precompileUser As TAUser = Nothing
        Private Shared _lastResponseCookies As CookieCollection
        Private Shared _server As String
        Private Shared _precompileProblems As New ArrayList
        '--------------------------------------------------------------------------
        ' End TA Customizations
        '--------------------------------------------------------------------------

        Public Event Elapsed As EventHandler

        ' Override and Indicate Time in Minutes to Force the Keep-Alive
        Protected Overridable ReadOnly Property KeepAliveMinutes() As Integer
            Get
                Return TAConfig.GlobalBaseKeepAliveCycle
            End Get
        End Property

        ' Override and Indicate Files to Skip with Semi-Colon Delimiter
        Protected Overridable ReadOnly Property SkipFiles() As String
            Get
                Return ""
            End Get
        End Property

        ' Override and Indicate Folders to Skip with Semi-Colon Delimiter
        Protected Overridable ReadOnly Property SkipFolders() As String
            Get
                Return "_vti_cnf;test"
            End Get
        End Property

        Public Overrides Sub Init()
            _applicationPath = HttpContext.Current.Request.ApplicationPath
            If Not _applicationPath.EndsWith("/") Then _applicationPath += "/"

            _server = HttpContext.Current.Request.ServerVariables("SERVER_NAME")
            Dim https As Boolean = (HttpContext.Current.Request.ServerVariables("HTTPS") <> "off")
            _applicationURL = CStr(IIf(https, CObj("https://"), CObj("http://"))) + _server + _applicationPath

            _physicalPath = HttpContext.Current.Request.PhysicalApplicationPath


            If TAConfig.DoPrecompileOnAppInit AndAlso GlobalBase._needsCompile Then
                GlobalBase._needsCompile = False
                _thread = New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf CompileApp))
                _thread.Start()
            End If

            If Me.KeepAliveMinutes > 0 Then
                _timer = New System.Timers.Timer(60000 * Me.KeepAliveMinutes)
                AddHandler _timer.Elapsed, AddressOf KeepAlive
                _timer.Start()
            End If
        End Sub

        Private Sub KeepAlive(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
            _timer.Enabled = False
            ' Use the login page to keep the site alive.
            Dim url As String = _applicationURL & "public/login.aspx"
            Dim myWebResponse As WebResponse = Nothing
            Try
                RaiseEvent Elapsed(Me, e)
                myWebResponse = HttpWebRequest.Create(url).GetResponse()
            Finally
                _timer.Enabled = True
                If Not myWebResponse Is Nothing Then myWebResponse.Close()
                System.Diagnostics.Debug.WriteLine(url, "KeepAlive")
            End Try
        End Sub

        Private Sub CompileApp()
            CompileFolder(_physicalPath)
            CommunicateProblems()
        End Sub

        Private Sub CommunicateProblems()
            If _precompileProblems.Count > 0 Then
                Dim problem As String
                Dim msg As New StringBuilder
                msg.Append("The following pages threw unhandled exceptions during precompile.  They should be investigated." & vbCrLf)
                For Each problem In _precompileProblems
                    msg.Append(problem & vbCrLf)
                Next
                System.Web.Mail.SmtpMail.Send(TAConfig.SupportEmail, TAConfig.SupportEmail, "Precompile Problems", msg.ToString)
            End If
        End Sub

        Private Sub CompileFolder(ByVal Folder As String)
            Dim _file As String
            For Each _file In Directory.GetFiles(Folder, "*.as?x")
                CompileFile(_file)
            Next

            Dim _folder As String
            For Each _folder In Directory.GetDirectories(Folder)
                Dim skipFolder As Boolean = False
                Dim item As String
                For Each item In Me.SkipFolders.Split(";"c)
                    If item <> "" And _folder.ToUpper().EndsWith(item.ToUpper()) Then
                        skipFolder = True
                        Exit For
                    End If
                Next
                If Not skipFolder Then
                    CompileFolder(_folder)
                End If
            Next
        End Sub

        '--------------------------------------------------------------------------
        ' Lots of customizations in this method.
        ' We needed to add code to create Forms Auth cookies, and
        ' save the cookies returned from every request to be passed up with
        ' the next request.
        '--------------------------------------------------------------------------
        Private Sub CompileFile(ByVal File As String)
            Dim skipFile As Boolean = False
            Dim item As String
            For Each item In Me.SkipFiles.Split(";"c)
                If item <> "" And File.ToUpper().EndsWith(item.ToUpper()) Then
                    skipFile = True
                    Exit For
                End If
            Next

            If Not skipFile Then
                Dim path As String = File.Remove(0, _physicalPath.Length)
                If File.ToLower().EndsWith(".ascx") Then
                    Dim virtualPath As String = _applicationPath + path.Replace("\", "/")
                    System.Diagnostics.Debug.WriteLine(virtualPath, "Control")

                    Dim controlLoader As Page = New Page
                    Try
                        controlLoader.LoadControl(virtualPath)
                    Catch ex As Exception
                        ' Ignore failures in loading user controls.
                        ' Lots of them seem to fail.
                        ' Not sure if they all fail, and if they get compiled even if they fail.
                    End Try
                ElseIf Not File.ToLower().EndsWith(".asax") Then
                    Dim url As String = _applicationURL + path.Replace("\", "/")
                    ' Append our precompile querystring so the page exits immediately.
                    url &= "?precompile=true"

                    System.Diagnostics.Debug.WriteLine(url, "Page")

                    Dim myWebResponse As WebResponse = Nothing
                    Dim myWebReq As HttpWebRequest
                    Try
                        myWebReq = CType(HttpWebRequest.Create(url), HttpWebRequest)
                        myWebReq.AllowAutoRedirect = False
                        ' Add a new CookieContainer to the web request.
                        ' The default property is Nothing (i.e. no container), so we must do this.
                        myWebReq.CookieContainer = New CookieContainer

                        ' Our site requires Forms Authentication on most pages.
                        ' Here we set up a cookie with a valid Forms Auth ticket, and
                        ' attach it to the first request.
                        ' We're going to need to instantiate our default precompile super user, who has access to
                        ' view every page in the site.

                        If _precompileUser Is Nothing Then
                            ' Load the precompile user.
                            _precompileUser = New TAUser(TAUser.PRECOMPILE_USER_ID)

                            ' Initialize FormsAuthentication, for what it's worth
                            FormsAuthentication.Initialize()

                            Dim ticket As New FormsAuthenticationTicket(1, _
                                _precompileUser.Email, _
                                DateTime.Now, _
                                DateTime.Now.AddMinutes(TAConfig.SessionTimeout), _
                                False, _
                                _precompileUser.RoleId.ToString, _
                                FormsAuthentication.FormsCookiePath)

                            ' Hash the cookie for transport
                            Dim hash As String = FormsAuthentication.Encrypt(ticket)

                            ' Create a System.Net.Cookie.
                            Dim myFormsAuthSystemNetCookie As New System.Net.Cookie(FormsAuthentication.FormsCookieName, hash, "/", _server)
                            myWebReq.CookieContainer.Add(myFormsAuthSystemNetCookie)
                        End If

                        ' Attach the cookies we're hanging onto from previous requests.
                        If Not _lastResponseCookies Is Nothing AndAlso _lastResponseCookies.Count > 0 Then
                            Dim myCookie As System.Net.Cookie
                            Dim i As Integer
                            For i = 0 To _lastResponseCookies.Count - 1
                                myCookie = _lastResponseCookies.Item(i)
                                myWebReq.CookieContainer.Add(myCookie)
                            Next
                        End If

                        ' Send the request and get the response.
                        myWebResponse = myWebReq.GetResponse()

                        ' Here we save the cookies returned from the site for the next go-round,
                        ' if any are returned.
                        If Not myWebResponse Is Nothing Then
                            Dim myHttpWebResponse As HttpWebResponse = CType(myWebResponse, HttpWebResponse)
                            If myHttpWebResponse.Cookies.Count > 0 Then
                                Dim myCookie As System.Net.Cookie

                                If _lastResponseCookies Is Nothing Then
                                    _lastResponseCookies = New CookieCollection
                                End If

                                Dim i As Integer
                                For i = 0 To myHttpWebResponse.Cookies.Count - 1
                                    _lastResponseCookies.Add(myHttpWebResponse.Cookies.Item(i))
                                Next
                            End If
                        End If
                    Catch ex As Exception
                        _precompileProblems.Add("Page: " & url & " " & ex.Message)
                    Finally
                        If Not myWebResponse Is Nothing Then myWebResponse.Close()
                    End Try

                End If
            End If
        End Sub
    End Class
End Namespace

PrecompilablePage.vb
Note:  This is the base page that all pages should inherit from in order to have a pre-compile only type of action.

Public Class PrecompilablePage
    Inherits Page

    Protected Overrides Sub OnInit(ByVal e As EventArgs)
        If Not Request.QueryString("precompile") Is Nothing Then
            Response.Clear()
            Response.Write(Wilson.WebCompile.GlobalBase.PRECOMPILE_SUCCESS_MSG)
            Response.Flush()
            Response.End()
            Page.Dispose()
        Else
            MyBase.OnInit(e)
        End If
    End Sub
End Class

1 Comment

  • The article code is good. but the document doesn't cotain the details of the TAConfig file. As it plays imp role, it is difficult to understand without it

Comments have been disabled for this content.