Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rewrite asp.net response before it gets cached with dynamicCompressionBeforeCache

Tags:

asp.net

My ASP.NET library installs a response filter to parse and alter the HTML output of pages. This is working until the following is enabled in web.config:

<system.webServer>
    <urlCompression doDynamicCompression="true" dynamicCompressionBeforeCache="true" />
</system.webServer>

My response filter is still called, but is passed what seems to be compressed data, not the original HTML.

How can I ensure my rewriting occurs before the page is stored in the output cache?

Setting dynamicCompressionBeforeCache to false is not an option, since this library is designed to be used in other people's web applications who may have good reason to enable compression.

like image 975
Andrew Davey Avatar asked Sep 09 '11 15:09

Andrew Davey


2 Answers

alexb's answer helped me figure out a solution.

The key was realising that my response filter was getting run after the IIS DynamicCompressionModule. This can be fixed by forcing the filter to run by flushing the response immediately after the request handler executes.

public class MyModule : IHttpModule
{
    public void Init(HttpApplication application)
    {
        application.BeginRequest += application_BeginRequest;
        application.PostRequestHandlerExecute += application_PostRequestHandlerExecute;
    }

    void application_BeginRequest(object sender, EventArgs e)
    {
        // Install response filter
        var response = ((HttpApplication)sender).Context.Response;
        response.Filter = new RewritingFilter(response.Filter);
    }

    void application_PostRequestHandlerExecute(object sender, EventArgs e)
    {
        // Flush immediately after the request handler has finished.
        // This is Before the output cache compression happens.
        var response = ((HttpApplication)sender).Context.Response;
        response.Flush();
    }

    public void Dispose()
    {
    }
}
like image 95
Andrew Davey Avatar answered Nov 15 '22 08:11

Andrew Davey


According to this blog post, the DynamicCompressionModule, which is responsible for handling the dynamic compression, runs during the ReleaseRequestState stage (which occurs before the UpdateRequestCache stage, when the page is being stored to the output cache). Therefore:

  • if you want to process the response before dynamic compression is performed, then you should place your filtering code in a HttpModule that hooks up to the PostRequestHandlerExecute event;
  • if you want to process the response caching is performed, but after compression, then you should place your filtering code in a HttpModule that hooks up the PostReleaseRequestState event.

Here's an example on how to do this:

public class SampleModule : IHttpModule
{
    public void Dispose()
    {
        return;
    }

    public void Init(HttpApplication context)
    {
        context.PostRequestHandlerExecute += new EventHandler(App_OnPostRequestHandlerExecute);
        context.PostReleaseRequestState += new EventHandler(App_OnPostReleaseRequestState);
    }

    private void App_OnPostRequestHandlerExecute(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;
        HttpResponse response = app.Context.Response;

        response.Write("<b>Modified!</b>");
    }

    private void App_OnPostReleaseRequestState(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;
        HttpResponse response = app.Context.Response;

        //check if the output has been compressed - make sure to actually test if for whatever content encoding you want to deal with
        if (response.Headers["Content-Encoding"] != null)
        {
            //do stuff
        }
        else
        {
            //do some other stuff
        }
    }
}

And then register your module using the web.config file:

<add name="SampleModule" type="My.ModuleNamespace.SampleModule" />
like image 33
AlexB Avatar answered Nov 15 '22 09:11

AlexB