Introduction
Compressing files before they are sent to the browser has many benefits. The page will load faster and less bandwidth is used, resulting in better user experience and reduced cost. There are a few different HTTP Compression module implementations out there, but I wanted one that would compress WebResource.axd. I couldn't find any that did, so I tried to find a way to do it myself.
This implementation has three parts to it. One is the compression of Web pages. For this I used the Flanders implementation found here, or on The Code Project here. I started with Ben Lowery's code, but Flanders code was much leaner, cleaner, and specific to .NET 2.0. It has been modified to use a custom filter to replace JavaScript and stylesheet links.
Next is combining multiple JavaScript and CSS files into one file and compressing them. The technique is described here. To summarize the article, it is better to have one JavaScript file and one CSS file for the browser to download. This is because the browser can't use parallel downloading for these types of files. Each one must be downloaded before another can begin. But it is also difficult to maintain large JavaScript and CSS files. The solution is to keep them separate, and then combine them into one file dynamically. And then on top of that, compress them. I've changed the way this is done a little, but I think it is slightly better. I have updated the HttpModule
to parse through the HTML of the page and replace the links to JavaScript and CSS files, combining wherever possible. It is now possible to write your pages like you normally would (in the past, to compress a script you would have to link to js.axd?files=jsfiletocompress.js) and the HttpModule
will replace the link with an appropriate js.axd?files=jsfiletocompress or css.axd?files=csstocompress.css. So the page will work with or without the compression enabled. This makes it a lot easier and allows us to take advantage of Visual Studio 2008's designer.
Imagine, instead of including five JavaScript files equaling 177 KB, you can include one file equaling 52 KB. For example, in the sample Web site, there are 5 JavaScript files: 2 ScriptResource.axd, 2 WebResource.axd, and 1 *.js file that combines 5 *.js files in the JavaScript folder. Uncompressed, these files would equal 329 KB, but compressed they equal 89 KB. Those are huge savings.
The third is the compression of WebResource.axd. I will give a quick explanation of the Web resource compression. A call to WebResource.axd using the compression module breaks it for some reason. I tried, to no avail, a lot of different ways to get around this problem. I am able to compress the WebResource.axd by replacing any JavaScript or CSS links with something that looks like this: js.axd?files=WebResource.axd?d=sldkf09sdf098&t=9896987987. Then when the HttpHandler
runs, it checks to see if the file is a local file, if not then it downloads it. All files are cached on the server and on the browser (including WebResource.axd). This way also enables you to include JavaScript or CSS from another domain and still compress and cache the files.
There are a couple of changes that I made to the original Flanders HttpCompression
module. First and foremost is instead of having the code being all inclusive, I changed it to be all exclusive. Meaning, instead of having a long list of excluded mime types that you want to exclude, I have included mime types and included path section in the Web config. So, most likely you will only want files of mime type text/html to be compressed with the module (this won't affect the JavaScript, CSS, or WebResource.axd from being compressed). I did leave the excluded mime type and paths intact allowing for exclusion of items that have been included. Also, I added the ability to use '*' to include all mime types (this makes it work like the original implementation and you must exclude all types you don't want compressed).
I might mention that if you are running a page from Visual Studio 2005's development server, all mime types will go through the compression module, but if you run it from IIS, they will not. Images, JS, and CSS files, and static pages do not go through the ASP.NET pipeline but are served up directly.
I also added code to make it work with update panels. This is done by adding...
if (app.Request["HTTP_X_MICROSOFTAJAX"] != null)
return;
... to the beginning of the context_PostReleaseRequestState
method.
I do not claim to be a C# guru, and as such, there may be better ways to do what I am doing. I welcome any comments or suggestions to make this code better. This code should be considered beta software. Also, there are many improvements and features that may be added, but I don't have time to do so. I have been successful in using this code at the University where I work, which uses load balanced servers running IIS6, and on my personal Website running in a medium trust environment (hosted by GoDaddy). I cannot, however, guarantee that it will work for you.
This project was written using Visual Studio 2005 and C# 2.0 and is not meant to run in a .NET 1.1 environment.
Using the Code
Add a reference to the DLL or drag and drop the DLL into the bin folder of the Web application. Add the following to the web.config. That's it! It should work transparently, meaning you can develop your site without the compression module, then add the compression later; or develop with the compression module and then remove it later without having to make any changes besides deleting the web.config items and the DLL.
Web.config
The only settings that have been added is the use of the <IncludedMimeTypes>
, <IncludedPaths>
(IncludedPaths
has not been fully tested), <CacheSettings>
, and <PathSettings>
.
<IncludedMimeTypes>
is described above and tells the module what mime types to compress. Again, you may use '*' to compress all mime types that may pass through the module. <CacheSettings>
is for the caching of the JavaScript and CSS files (including WebResource.axd). <PathSettings>
is where the JavaScript and CSS files are located. I will talk more about this later. Defaults are: cacheFiles
=false, path
=cache, jsPath
=javascript, and cssPath
=css.
<configSections>
<sectionGroup name="DCWeb">
<section name="HttpCompress" type="DC.Web.HttpCompress.Configuration,
DC.Web.HttpCompress"/>
</sectionGroup>
</configSections>
<DCWeb>
<HttpCompress compressionType="GZip">
<IncludedMimeTypes>
<add mime="text/html" />
</IncludedMimeTypes>
-->
<ExcludedPaths>
<add path="~/NoCompress/Default.aspx" />
</ExcludedPaths>
</HttpCompress>
</DCWeb>
-->
<httpHandlers>
<add verb="*" path="js.axd,css.axd"
type="DC.Web.HttpCompress.CompressionHandler,DC.Web.HttpCompress"/>
</httpHandlers>
-->
<httpModules>
<add name="HttpCompressModule"
type="DC.Web.HttpCompress.HttpModule,DC.Web.HttpCompress"/>
</httpModules>
Including JavaScript and CSS
This has changed since the previous version. Just include the JavaScript and CSS like you normally would. You can even use stylesheet themes, this is demonstrated in the included solution.
Caching the Files
The files are cached under the context.Cache
on the server and on the browser using Etags.
Conclusion
It's that easy to enable compression of Web pages, JavaScript, CSS, and the WebResource.axd. I hope you find this code as useful as I have.
History
- 03/18/2008 - Article, source code and demo updated
- 02/06/2008 - Major update to the code. Changed to use a custom filter to replace JavaScript includes and CSS links with ones pointing to a
HttpHandler
. Changed the HttpHandler
to cache differently on the server and to compress the output differently.
- 05/17/2007 - Updated code which caused CSS errors
- 04/30/2007 - Created article