using System; using System.IO; using System.Web; using System.Collections; using System.Collections.Specialized; namespace blowery.Web.HttpModules { /// /// An HttpModule that hooks onto the Response.Filter property of the /// current request and tries to compress the output, based on what /// the browser supports /// /// ///

This HttpModule uses classes that inherit from . /// We already support gzip and deflate (aka zlib), if you'd like to add /// support for compress (which uses LZW, which is licensed), add in another /// class that inherits from HttpFilter to do the work.

/// ///

This module checks the Accept-Encoding HTTP header to determine if the /// client actually supports any notion of compression. Currently, we support /// the deflate (zlib) and gzip compression schemes. I chose not to implement /// compress, because it's uses lzw, which generally requires a license from /// Unisys. For more information about the common compression types supported, /// see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11 for details.

///
/// /// public sealed class HttpCompressionModule : IHttpModule { /// /// Init the handler and fulfill /// /// /// This implementation hooks the BeginRequest event on the . /// This should be fine. /// /// The this handler is working for. void IHttpModule.Init(HttpApplication context) { context.BeginRequest += new EventHandler(this.CompressContent); } /// /// Implementation of /// /// /// Currently empty. Nothing to really do, as I have no member variables. /// void IHttpModule.Dispose() { } /// /// EventHandler that gets ahold of the current request context and attempts to compress the output. /// /// The that is firing this event. /// Arguments to the event void CompressContent(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; // get the accepted types of compression // i'm getting them all because the string[] that GetValues was returning // contained on member with , delimited items. rather silly, so i just go for the whole thing now // also, the get call is case-insensitive, i just use the pretty casing cause it looks nice string acceptedTypes = app.Request.Headers.Get("Accept-Encoding"); // this will happen if the header wasn't found. just bail out because the client doesn't want compression if(acceptedTypes == null) { return; } // try and find a viable filter for this request HttpCompressingFilter filter = GetFilterForScheme(acceptedTypes, app.Response.Filter); // the filter will be null if no filter was found. if no filter, just bail. if(filter == null) { app.Context.Trace.Write("HttpCompressionModule", "Cannot find filter to support any of the client's desired compression schemes"); return; } // if we get here, we found a viable filter. // set the filter and change the Content-Encoding header to match so the client can decode the response app.Response.Filter = filter; app.Response.AppendHeader("Content-Encoding", filter.NameOfContentEncoding); } /// /// Get ahold of a for the given encoding scheme. /// If no encoding scheme can be found, it returns null. /// HttpCompressingFilter GetFilterForScheme(string schemes, Stream currentFilterStream) { // this makes a copy of the string. i hate making copies. schemes = schemes.ToLower(); bool foundDeflate = schemes.IndexOf("deflate") >= 0 ? true : false; bool foundGZip = schemes.IndexOf("gzip") >= 0 ? true : false; HttpCompressionModuleSettings settings = HttpCompressionModuleSettings.GetSettings(); // if they support the preferred algorithm, use it if(settings.PreferredAlgorithm == CompressionTypes.Deflate && foundDeflate) return new DeflateFilter(currentFilterStream, settings.CompressionLevel); if(settings.PreferredAlgorithm == CompressionTypes.GZip && foundGZip) return new GZipFilter(currentFilterStream); // otherwise, fall back to something they support, which preference to deflate (no reason) if(foundDeflate) return new DeflateFilter(currentFilterStream, settings.CompressionLevel); if(foundGZip) return new GZipFilter(currentFilterStream); // return null. we couldn't find a filter. return null; } } }