Windows Phone 7 and Compression using Accept-Encoding Header

As a few of my readers have commented on my post  Windows Phone 7 Data: Json WCF Data Service with IIS 7 Compression, there is a breaking change between the CTP and Beta versions of the Windows Phone developer tools that prevents you from setting the Accept-Encoding header. One could argue that the fact you could set this at all in the ctp was a bug, since Silverlight 3 doesn’t permit this. Unfortunately not being able to set this header is a major pain if you want to take advantage of the built in IIS dynamic and/or static compression. I would really like to see this fixed before the tools ship but given we’re only a couple of weeks away from the ship date of the tools I doubt very much if it will be.

Having spent a few days at TechEd Australia thinking about this issue I went digging to see if we could either force IIS to compress all data (ie that way we wouldn’t have to set the Accept-Encoding header) or better still try to apply compression based on some other request attribute. In the end the fix was actually surprisingly simple.

Firstly, I have to call out a post by Ted Jardine on IE6 gzip bug solved using IIS7’s URL Rewrite Modulewhich put me on the right track. Essentially the process goes like this:

– In the request we set a different header (I chose one called “Compress-Data” and set it to a value of  “true”)
– On the server we define a rewrite rule that will detect this header and set the Accept-Encoding header to gzip,deflate for all matching requests.
– The server would then serve up compressed data – Woot!

Ok, so these are the steps you need to follow:

1) Update the applicationHost.config file to allow you to modify the allowedServerVariables element. This file is typically found at C:WindowsSystem32inetsrvconfig

<configuration>
    <configSections>
        <sectionGroup name="system.webServer">
            <sectionGroup name="rewrite">
                <section name="allowedServerVariables" overrideModeDefault="Allow" />

2) Add a rewrite rule to the web.config file for your application. Note that you have to include allowedServerVariables element in order for the HTTP_ACCEPT_ENCODING server variable to be set by the rule.

<configuration>
    <system.webServer>
        <rewrite>
            <allowedServerVariables>
                <add name="HTTP_ACCEPT_ENCODING" />
            </allowedServerVariables>
            <rules>
                <rule name="RewriteUserFriendlyURL1" patternSyntax="Wildcard" stopProcessing="false">
                    <match url="*" />
                    <conditions>
                        <add input="{HTTP_COMPRESS_DATA}" pattern="true" />
                    </conditions>
                    <action type="None" />
                    <serverVariables>
                        <set name="HTTP_ACCEPT_ENCODING" value="gzip,deflate" />
                    </serverVariables>
                </rule>
            </rules>
        </rewrite>

This will match all urls, looking for the HTTP_COMPRESS_DATA variable set to true. When working with rewrite rules you have to remember that headers are converted to variables by a) making them upper case, b) replacing “-“ with “_” and c) adding HTTP_ as a prefix. In this case when a match is found the HTTP_ACCEPT_ENCODING variable (ie the Accept-Encoding header) is set to gzip,deflate, as if you had specified this in the header when making the request.

3) Now all we need to do is to call this from our Windows Phone application, specifying the appropriate header.

private void MakeRequestButton_Click(object sender, RoutedEventArgs e){
    var request = HttpWebRequest.Create("<request url>”) as HttpWebRequest;
    request.Accept = "application/json";
    request.Headers["Compress-data"] = "true";
    request.BeginGetResponse(RequestCallback, request);
}

private void RequestCallback(IAsyncResult result){
    var request = result.AsyncState as HttpWebRequest;
    var response = request.EndGetResponse(result);
    using(var strm = response.GetResponseStream())
    using (var reader = new StreamReader(strm)){
        var data = reader.ReadToEnd();
        // data will be compressed
    }
}

Although this example specifies the Accept header to request JSON (as per my previous post on compression), this is not required and you can apply the Compress-data header to any request you make. Of course you should also examine the Content-Encoding header on the response to make sure you only decompress a response that is compressed. If the server ignores the Compress-data header (which will be the default case if the rewrite rule is missing or fails) you will get back uncompressed data.

Leave a comment