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