:::: MENU ::::

Thursday, February 7, 2013

Cool C# Snippets - Dynamically GZIP compress the HTML response of your website in code, without having to configure IIS directly. Make massive savings on your websites bandwidth.

Dynamic GZIP Compression in Code

I recently found a way to GZIP compress the HTML, CSS and Javascript output from an ASP.NET website, dynamically in code. GZIP compression can drastically reduce the file sizes for any text based file served to the client. This includes the raw HTML, CSS, Javascript files, even plain XML or CSV, but it can't compress images, media and binary files. Normally, you'd set this on the server, but what if you dont have access to the server.
First create a Global.asax file in your web root and put the following line in it, deleting everything thats there currently...
<%@ Application Language="C#" Inherits="Global" %>
Then create a Global.cs file in your APP_CODE folder and put the following code in...
using System;
using System.IO.Compression;

public class Global : System.Web.HttpApplication
{
    public Global()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        this.PostReleaseRequestState +=
            new EventHandler(Global_PostReleaseRequestState);
    }

    private void Global_PostReleaseRequestState(
        object sender, EventArgs e)
    {
        string contentType = Response.ContentType;

        if (contentType == "text/html" ||
            contentType == "text/css")
        {
            Response.Cache.VaryByHeaders["Accept-Encoding"] = true;

            string acceptEncoding =
                Request.Headers["Accept-Encoding"];

            if(acceptEncoding != null)
            {
                if (acceptEncoding.Contains("gzip"))
                {
                    Response.Filter = new GZipStream(
                        Response.Filter, CompressionMode.Compress);
                    Response.AppendHeader(
                        "Content-Encoding", "gzip");
                }
                else if (acceptEncoding.Contains("deflate"))
                {
                    Response.Filter = new DeflateStream(
                        Response.Filter, CompressionMode.Compress);
                    Response.AppendHeader(
                        "Content-Encoding", "deflate");
                }
            }
        }
    }
}
The browser will tell the server whether it supports GZIP or DEFLATE compression (via the Accept-Encoding header). If its an older browser that doesn't support compression, it will just be sent as uncompressed HTML and your site will still work.
The magic happens with the following lines...
Response.Filter = new GZipStream(
    Response.Filter, CompressionMode.Compress);
Response.AppendHeader("Content-Encoding", "gzip");
Response.Filter (also available from your ASPX pages) exposes a Stream which you override with a GZipStream object, this will cause the raw text response to get compressed, while telling the browser this the response is compressed with the second line Response.AppendHeader.
The reason we put this in the Global_PostReleaseRequestState event is because it allows us to target all files, not just ASPX pages.
Pay attention to the line if (contentType == "text/html" || contentType == "text/css"), I had an issue where I was using the ASP.NET AJAX extensions Control Toolkit which was supplying it's own external javascript files that were already GZIP compressed, and since it was trying to compress an already compressed file, it was screwing it up causing issues and throwing script errors on the client, because the browser couldn't read the script files. So be aware of this, if you have complete control over your external JavaScript files then could probably add the code...
|| contentType == "application/x-javascript" || contentType == "text/javascript"
...to that IF statement.
FYI, I tried adding this code to an application I was working on at Conscia. The client wouldn't let us turn GZIP compression on their own servers, and we didn't have any access to them. Page sizes were around 150-200KB but after adding this code they now sit at around 20-30KB, a massive improvement which would benefit the particular slow network this application was being made to run on.
Page loads times were quicker and the application seemed noticably more responsive. So your benefits are lowered costs and an improved user experience.