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