Enabling JSON Compression (and Gibraltar) in Windows Azure
A friend of mine posted to Twitter a bit ago that he was having trouble dealing with HttpCompression on Windows Azure. Specifically, only certain default MimeTypes were being compressed (like ASPX and CSS), while other important ones like JSON and SOAP were not.
During another Azure project, I also ran into an issue where logins stopped working after upgrading to Azure OS 2.X Azure SDK 1.3. It turns out that there is a bug in that release that doesn’t allow configuration inheritance for the MachineKey, and the workaround was to manipulate the server configuration in the RoleEntryPoint.
Since I’m already having to manipulate the server config to synchronize the MachineKey settings, I thought I’d whip up some code real quick to solve this problem during the same process, using the httpConpression topic on IIS.net as a guide.
If you don’t already have a RoleEntryPoint in your app… well, you should. Copy the code below into a new class file in your main application, and add a reference to Microsoft.Web.Administration.dll, as specified here. You’ll also notice Gibraltar-specific code peppered throughout… this is all that you need to get Gibraltar working on Windows Azure (as long as you’re also using the tip from my previous post as well).
(The code is long, and it’s in VB… because that’s the language this particular app was in. If you need it converted to C#, you can do so here.
UPDATE: My colleage Kendall pointed out that the role event handlers should be declared at the very beginning, and that, even though the RoleInstance’s OnStart event is called on a separate thread from the main app, it should still run the WarmUp loop on a separate thread, so that the function can return MyBase.OnStart() and exit properly. Good call.
Imports System.Linq
Imports Microsoft.WindowsAzure.Diagnostics
Imports Microsoft.WindowsAzure.ServiceRuntime
Imports Microsoft.Web.Administration
Imports System.Threading
Namespace Fon.Apps
Public Class WebRole Inherits RoleEntryPoint
Public Overrides Function OnStart() As Boolean
' For information on handling configuration changes
' see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
AddHandler RoleEnvironment.Changing, AddressOf RoleEnvironment_Changing
AddHandler RoleEnvironment.Stopping, AddressOf RoleEnvironment_Stopping
Gibraltar.Agent.Log.SendSessionsOnExit = True
Using server As New ServerManager()
'Cycle through the sites and set the MachineKey for each one.
For Each site In server.Sites
Dim siteConfig = site.GetWebConfiguration()
'Get the appSettings section
Dim appSettings = siteConfig.GetSection("appSettings").GetCollection().ToDictionary(Function(e) DirectCast(e("key"), String), Function(e) DirectCast(e("value"), String))
'Reconfigure the machine key
Dim machineKeySection = siteConfig.GetSection("system.web/machineKey")
machineKeySection.SetAttributeValue("validationKey", appSettings("validationKey"))
machineKeySection.SetAttributeValue("validation", appSettings("validation"))
machineKeySection.SetAttributeValue("decryptionKey", appSettings("decryptionKey"))
machineKeySection.SetAttributeValue("decryption", appSettings("decryption"))
Next
'Turn on compression for JSON and SOAP.
Dim config As Configuration = server.GetApplicationHostConfiguration()
Dim httpCompressionSection As ConfigurationSection = config.GetSection("system.webServer/httpCompression")
Dim dynamicTypesCollection As ConfigurationElementCollection = httpCompressionSection.GetCollection("dynamicTypes")
Dim e1 As ConfigurationElement = dynamicTypesCollection.CreateElement("add")
e1("mimeType") = "application/json"
e1("enabled") = True
dynamicTypesCollection.AddAt(0, e1)
Dim e2 As ConfigurationElement = dynamicTypesCollection.CreateElement("add")
e2("mimeType") = "application/json;charset=utf-8"
e2("enabled") = True
dynamicTypesCollection.AddAt(0, e2)
Dim e3 As ConfigurationElement = dynamicTypesCollection.CreateElement("add")
e3("mimeType") = "application/soap+xml"
e3("enabled") = True
dynamicTypesCollection.AddAt(0, e3)
server.CommitChanges()
End Using
'Poor man's Application Warm-up here
Tasks.Task.Factory.StartNew(Sub()
Try
While (True)
Trace.WriteLine("Warming up web site...")
Dim webClient As New Net.WebClient()
webClient.DownloadString("http://mysite.com/WarmUp.ashx?WarmUp=True")
Thread.Sleep(TimeSpan.FromMinutes(9))
End While
Catch ex As Exception
Gibraltar.Agent.Log.RecordException(ex, "WindowsAzure.WebRole.Startup", True)
End Try
End Sub)
Return MyBase.OnStart()
End Function
Private Sub RoleEnvironment_Stopping(sender As Object, e As RoleEnvironmentStoppingEventArgs)
Gibraltar.Agent.Log.EndSession(Gibraltar.Agent.SessionStatus.Normal, "Azure's configuration settings have changed.")
End Sub
Private Sub RoleEnvironment_Changing(sender As Object, e As RoleEnvironmentChangingEventArgs)
' If a configuration setting is changing
If e.Changes.Any(Function(change) TypeOf change Is RoleEnvironmentConfigurationSettingChange) Then
' Set e.Cancel to true to restart this role instance
Gibraltar.Agent.Log.EndSession(Gibraltar.Agent.SessionStatus.Normal, "Azure's configuration settings have changed.")
e.Cancel = True
End If
End Sub
End Class
End Namespace
You might have noticed the While loop that cycles every 9 minutes and makes a request to a specific website. Though the actual site details have been abstracted out, this is a trick that I use to keep my application “warmed up,” and is a variation on the technique used by Christian Weyer.
For the MachineKey part, you’ll also need to add the following entries to the appSettings section of each of the web.configs in your Role, being sure to replace your values where appropriate:
<add key="decryption" value="Auto" />
<add key="decryptionKey" value="YOURVALUEHERE" />
<add key="validation" value="SHA1" />
<add key="validationKey" value="YOURVALUEHERE" />
You’ll also notice that it’s also pretty easy to add additional MimeTypes to compress. Just continue to copy the pattern for any of the items you think are missing. Be sure not to add ones that don’t already exist, or you’ll likely crash your role.
Anyways, I I hope that helps some of you that are trying to resolve various Azure issues.