Disclaimer

My last version of the compression module was working great and able to compress and cache pages, WebResources, CSS and JavaScripts files. It have one problem (that similiar to most of the compression modules you can find). To compress the javascripts files, you have to modify the way you register them and change their links to something like that:

It is not so hard work to do, but it became useless when you use third party controls as 'Telerik' (RadControls) and force it to doesn't use it's scripts as WebResources. In such case, the controls 'Injects' their scripts links into the HTML code and you can't modify them as needed to let the compression handler to compress it. In the new version of my compression component, I added a ResponseFilter that parse the Scripts links in the HTML and convert them into 'compressable' links. By default, this option is disabled in the compression module. To enable it (if you use such third party controls), just add the attribute compressThirdParityScripts="true" to the compression configuration.

Running after the best performance for your web application, one of the biggest improvement you can do is to use compression.
Page, Stylesheet & Javascript files (images are already compressed format).
There are few compression modules out there, all you needs to do is just 'goolge' for it, and the implementation is really very simple.
Not a lot of changes in your code are needed.

ASP.NET bring us a new way to integrate resources into our site - using WebResource.axd handler.
Those are like a 'Virtual containers' for other file that embedded into a DLL file. It can be Css, Javascript, Images...
Actually this is an handler that load the resource from the DLL file.

The Problem

The pages, .css & .js files can be easily compress with one of the modules on the net.
The basic technique is to pass your response through 'Compression Filter'.

WebResource.axd, that became very large (specially when using AJAX) are break down for unknown reason when you try to compress them that way.
That result is you will not compress them, and your client will download big files that contains js or css (sometimes of 70kb and even more)

The Challenge

I couldn't find on the net a compression module that compress this type of file. I decided I will try to do it by myself.

I started to make tests and to play with WebResources files. Only WebResources that contains 'text' content (css, js, text) can be consider as compressible.
Images are not.

I was thinking, maybe I can load the WebResource, get it's content as string, compress the string and send it to the response as string (or as byte[]).
After some tests, It discovered as true. The WebResource was passing to the client completely and without any missing characters.
The problem was to get the WebResource content. For that, I had to make a 'fake' request based on the url,
asking again for the WebResource and then compress it.

Thanks to Mads Kristensen that point me to the performance issue and offer an idea how to improve it, The second try was better.
The first time the WebResource was loading, it was compressed and inserted into the Application object, and next time it was requested, it was served from there directly.

It was actually running on our production servers (Where I work) for a while, and compressed the WebResources.
But I was not happy with that solution. The 'fake' request throw an exception once in a while. Maybe one for some thousands of users.
Also it was not stable enough on some other servers, and throw 404 errors.

Third Try. Success! Working as expected. No errors and really WebResource compression module!

The compression part in the first two tries was perfect. compress the content of the webresource as byte[] and serve it to the client. The only problem is to load the actual resource content.

In this time, I decided to 'attack' the problem from another way.
.NET using System.Web.Handlers.AssemblyResourceLoader to load and serve WebResource requests.
I decided to implement my own AssemblyResourceLoader handler that will support compression. unfortunately, AssemblyResourceLoader handler is registered into the system, and can't be remove. Any try to do so will lead you to an exception.

So, what I did was to create an HttpModule, using the event PreRequestHandlerExecute that is one step before the handle action,
do my stuff here, and then end response and jump over the hander step.
That way I can prevented AssemblyResourceLoader handler to do his stuff.

To create my version for the AssemblyResourceLoader, I used the source code of 'ASP.NET 2.0 AJAX Extensions'
and a reflector to see the code of the original AssemblyResourceLoader.

There are few steps in that module:

1. Parsing the request query string. The request for WebResource has an encrypted query string with the the resource data separate with pipes chars ('|'):
Assemby info, Resource name and a char for the 'assembly type'. The query string is encrypted using the machinekey, and needs to be decrypted using reflection. The problem is, that share hosting servers (such GoDaddy) are not permitting using reflection from within your code. Only from assemblies that are in the GAC. What I had to do is to find a public method from an assembly that in the GAC of every server.

The solution I found (if somebody have a better idea, I will be glad to hear) is creating an empty Membership class, that inherits from MembershipProvider, and implement only one public method that using the 'DecryptPassword' method from the MembershipProvider. it is also using the machinekey.
(To use that decryption, add the attribute reflectionAlloweded="false" to the CompressorSettings section, and machinekey section to the web.config. See Readme.txt file in the source)

2. Load the assembly. The char for the 'assembly type' in the query string indicate if the assembly is the System.Web assembly or the one that encrypted in the query.

3. Load the resource info. Using the resource name from the query, we need to load the resource info - it's content type, if it exist and if it needs to perform substitution calls
(WebResource within another WebResource. If so, we let the original System.Web.Handlers.AssemblyResourceLoader to handle this WebResource). Of course we check in our cache if that resource already asked for, and load the info from the cache.

4. Setting the HttpCachePolicy headers to cache the request, and adding a ETag for checking in the next time.

6. Compress the resource stream if needed. If the resource is 'compressable' it can be read as a string, so we load it as StreamReader, read it to the end, convert the string into byte[] and compress it. If not (in case of images) we just load the resource, and serve it to the client.

To prevent the search engines to cache the axd handlers. (not 100% solution, but not harmful )

Add a machinekey to your Web.Config. ASP.NET use it for validation and encryption/decryption of the view state & the WebResource query strings. Don't relay on the auto generate key. It can lead you to some Cryptographic Exceptions. Use one of the on-line tools to generate key for you.

Implementation

Full instruction can be found in the readme.txt file in the source code.

The Source Code

The source code contain handlers to compress .js & .css files, simple module to compress pages, the module to compress the WebResources and helper classes.
Every module/handler is 'stand alone' and can be use independent in case you choose to use your own implementation for the pages of css/js.

The Readme.txt file include all needed info to implement the handlers/modules.

Important: If you are using the WebResource compression module on share hosting thatnow allowed using reflection from your code (as GoDaddy), you must
add the attribute reflectionAlloweded="false" to the CompressorSettings section.

This post is an update for my previews post about http compression.
In that post, I posted a http module that compress aspx pages and WebResource.axd files.
Thanks to Mads Kristensen, that found a performance issue in my code and had a solution for that,
I improved my compressor component and also I included a complete project to compress asp.net pages,
CSS files, JavaScript files and WebResource.axd files as well.
The implementation in really very simple and not lot of changes are necessary in existing project.
The page compression is very simple, and common to all http compressors:
Registering the httpmodule in the web.config:

The WebResource.axd compression is a bit more complicate, but the idea is to make a 'fake' request
based on the requested url when the client asked for WebResource.axd file,
get the response into byte[], compress it into MemoryStream cache it as a byte[] and send it to the client.
The next time the client ask for this WebResource, it served from the cache (thanks again to Mads Kirstensen).
The compression is only when the WebResource serve css or javascript files. over wise, the WebResource is send to the client 'as it'.
The CSS and the JS files compression is similar to the page compression, the differents are that in those cases, we load the requested file,
read it into string, manipulate it (remove unneeded spaces and stuff), compress it, cache it, and send it to the client.
The 'problem' is that css and js files are served by the IIS itself, and not by ASP.NET engine,
so what we do is, changing the 'src' attribute for js files to jslib.axd or the 'href' attribute for css files to css.axd, and add as query string the real files name
(see in the ReadMe.txt file how it done), and those files are handled by ASP.NET engine and to compress tham - for that, we register the following handlers in the web.config: (another option, is to serve those files via WebResources)

The caching for those files have CacheDependency on the files, so the content is removed from the cache in the moment the files are changed,
and reload again when requested.
Before sending data to the client, we check the ETag, and if its the same as the client sent, so we can stop the response and send only the code 304.

Extra features

By configuration in the web.config, it is possible to disable any compression type, decide the time (by days) the cached file will be in the cache,
and also it is possible to specified mimeTypes content to not compress or specified aspx files to exclude from the compression.
All configuration description in the ReadMe.txt file.

Implementation

All implementation instructions are in the attach zip file in ReadMe.txt file.

Note

The WebResource compression may not work if the request is url that modified in the windows 'host' file.
In that case, you will need to disable WebResource compression (See in the ReadMe.txt)