Click here to Skip to main content
13,344,281 members (56,194 online)
Click here to Skip to main content
Articles » Web Development » ASP.NET » General » Downloads


152 bookmarked
Posted 30 Apr 2007

HTTP Compression Module

, 20 Mar 2008
A compression module for ASP.NET that works with WebResource.axd, JavaScript, and CSS
/// As a starting point for this module i used the Blowery module written by Ben Lowery.
/// his blog is located at :
/// After studying the module that he wrote I couldn't see the point of all the inherited streams
/// because the one we use is already limited with a bunch of constraints.
/// The version he has for .net 2.0 is ported from .net 1.1 and the configuration properties don't follow 
/// the new schema anymore. 
/// Because of the change from #ziplib to the native .net 2.0 everything that has to do with quality 
/// doesn't count anymore.
/// I decided to rewrite his library to a .net 2.0 version of it which turned out to be much shorter.
/// So this module is copyrighted by Ivan Porto Carrero, Flanders International Marketing Ltd. 2006
/// Blog :
/// You are free to use this module as long as you keep this notice in the source files
/// 23/01/2006
/// I changed the module to do work at the post release request state event in stead of at begin request.
/// When fired with begin request the parameters get zipped and form values get affected which kills the correct processing of the page.
/// The compression now takes place after the entire content has been created.
/// The above was written by Flanders.
/// The Items I added are marked with DC.
/// A quick explanation of the web resource compression.  A call to WebResource.axd using
/// the module breaks it for some reason.  I tried a lot of different things to get around this
/// but to no avail.  Finally I came up with the following solution.  Hook into the begin request
/// and check if it is a webResource.axd.  If it is then call CompleteRequest so it goes straight
/// to the EndRequest method.  This is so no processing is done on the request.  In EndRequest,
/// send it to the CompressWebResource object.  This then checks to see if there is an Etag (cached on browser),
/// if there is it sends a Not Modified header. (The webresource is loaded every time normally, this adds
/// browser cache ability to WebResource files.)  Then I check if a file is cached, if it is then I 
/// send the cached file.  Otherwise, I create a HttpWebRequest and request the WebResource.axd, but add a 'u=1'
/// to the query string so I know to let it pass through the above and retrieves the WebResource.
/// Then I check the content-type, if it is neither javascript or css then I send it to the 
/// browser, first adding an Etag to allow browser caching.  If it is javascript or css,
/// then get the allowed encoding and encode the data and cache it on disk.
/// I expect the credit to be given where credit is due.  The module is created by Flanders
/// and the above copyright applies.  The Compress.cs,CompressHandler.cs,and CompressWebResource.cs
/// were created by me, Darick Carpenter.  The CompressHandler is a port from PHP obtained here:
/// and the copyright from that port is on CompressHandler.cs.  The CompressWebResource was
/// created by me and I echo the copyright notice in CompressHandler.cs offering no warranty.  
/// Darick Carpenter
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
using System.IO.Compression;
using System.Configuration;

namespace DC.Web.HttpCompress
    /// <summary>
    /// The Http Module that will compress the outputstream to the browser if it is supported by the browser.
    /// </summary>
    public class HttpModule : IHttpModule
        #region IHttpModule Members

        public void Dispose()

        public void Init(HttpApplication context)
            /* The Post Release Request State is the event most fitted for the task of adding a filter
            // Everything else is too soon or too late. At this point in the execution phase the entire 
             * response content is created and the page has fully executed but still has a few modules to go through
             * from an perspective.  We filter the content here and all of the javascript renders correctly.*/
            context.PostReleaseRequestState += new EventHandler(context_PostReleaseRequestState);
            // DC
            context.BeginRequest += new EventHandler(context_BeginRequest);
            context.EndRequest += new EventHandler(context_EndRequest);

        // DC
        void context_EndRequest(object sender, EventArgs e)
            // Check if the request is for a webResource.  If it doesn't have a query paramenter
            // 'u', send it to the CompressResource object. CompressResource adds the 'u'
            // so we know if it's already come through.
            HttpApplication app = (HttpApplication)sender;
            HttpContext context = app.Context;
            if (context.Request.Path.Contains("WebResource.axd") && app.Context.Request.QueryString["u"] == null)
                CompressWebResource compress = new CompressWebResource();

        // DC
        void context_BeginRequest(object sender, EventArgs e)
            // Check if the request is for a webResource. If it is, call complete request
            // so it doesn't do any unnecessary processing.
            // Also, if a query parameter 'u' is included, do not call complete.
            // This was added so I could call a webResource without processing it.
            HttpApplication app = (HttpApplication)sender;
            if (app.Context.Request.Path.Contains("WebResource.axd") && app.Context.Request.QueryString["u"] == null)

        void context_PostReleaseRequestState(object sender, EventArgs e)
            HttpApplication app = (HttpApplication)sender;

            // DC - Added so update panel (AspAtlas) works
            if (app.Request["HTTP_X_MICROSOFTAJAX"] != null)


            string realPath = "";
            Configuration settings = null;
            // get the config settings
            if (app.Context.Cache["DCCompressModuleConfig"] == null)
                settings = (Configuration)ConfigurationManager.GetSection("DCWeb/HttpCompress");
                app.Context.Cache["DCCompressModuleConfig"] = settings;
                settings = (Configuration)app.Context.Cache["DCCompressModuleConfig"];
            if (settings != null)
                app.Context.Cache.Insert("DCCompressModuleConfig", settings);
                if (settings.CompressionType == CompressionType.None)
                    // skip if the CompressionLevel is set to 'None'
                    return ;
                realPath = app.Request.Path.Remove(0, app.Request.ApplicationPath.Length);
                realPath = (realPath.StartsWith("/")) ? realPath.Remove(0, 1) : realPath;

                // DC - added to use included paths and mime types
                bool isIncludedPath,isIncludedMime;

                isIncludedPath = (settings.IncludedPaths.Contains(realPath) | settings.IncludedPaths.Contains("~/" + realPath));
                isIncludedMime = (settings.IncludedMimeTypes.Contains(app.Response.ContentType));

                if (!isIncludedPath && !isIncludedMime)
                    // path was not included, so skip it

                if (settings.ExcludedPaths.Contains(realPath) | settings.ExcludedPaths.Contains("~/" + realPath))
                    // skip if the file path excludes compression
                    return ;

                if (settings.ExcludedMimeTypes.Contains(app.Response.ContentType))
                    // skip if the MimeType excludes compression
                    return ;
            // fix to handle caching appropriately
            // see
            // Note, this header is added only when the request
            // has the possibility of being compressed...
            // i.e. it is not added when the request is excluded from
            // compression by CompressionLevel, Path, or MimeType          

            // DC - I had to move this because it was messing up the compressHandler and 
            //      compressWebResource Etag values.  This affected the caching of the files
            app.Context.Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
            string acceptedTypes = app.Request.Headers["Accept-Encoding"];

            // if we couldn't find the header, bail out
            if (acceptedTypes == null)

            // Current response stream
            Stream baseStream = app.Response.Filter;

            // If there are more than one possibility offered by the browser default to the preffered one from the web.config
            // If nothing is specified in the web.config default to GZip
            acceptedTypes = acceptedTypes.ToLower();

            if ((acceptedTypes.Contains("gzip") || acceptedTypes.Contains("x-gzip") || acceptedTypes.Contains("*")) && (settings.CompressionType != CompressionType.Deflate))
                app.Response.Filter = new GZipStream(baseStream, CompressionMode.Compress);
                //This won't show up in a trace log but if you use fiddler or nikhil kothari's web dev helper BHO you can see it appended
                app.Response.AppendHeader("Content-Encoding", "gzip");
            else if (acceptedTypes.Contains("deflate"))
                app.Response.Filter = new DeflateStream(baseStream, CompressionMode.Compress);
                //This won't show up in a trace log but if you use fiddler or nikhil kothari's web dev helper BHO you can see it appended
                app.Response.AppendHeader("Content-Encoding", "deflate");

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Web Developer
United States United States
No Biography provided

You may also be interested in...

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.180111.1 | Last Updated 20 Mar 2008
Article Copyright 2007 by darick_c
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid