Nancy Street ReleaseRequestState Filter
Click to see the Site Map
HomeInfoMusicGalleryPetsGeoHobbiesGeo
Site MapWhat's NewRecent ChangesContactsServer StatisticsSite Information Home « Computers « DevBlog

Back to: Development Blog Contents

Http Modules & ReleaseRequestState Filter

In March 2005 I had to find a way of performing string replacement in ASP.NET application web pages just before they were rendered. It was too tedious to change the existing code to perform the processing at page creation time, so I was hoping to find a blanket solution by hooking in somewhere after the pages had been composed and were about to be rendered by the browser.

Thanks to Tatham in the aus-dotnet mailing who sent me to a sample that showed how a Http Module could put a custom filter on the ReleaseRequestState event to do what I needed.

Add this to your Web.config file:

<httpModules>
  <add type="mycompany.myproject.MacroModule, mycompany.myproject" name="MacroModule" />
</httpModules>

Following is a skeleton of the Http Module which processes the ReleaseRequestState event and passes each text response through a custom filter. Note that the Encoding is passed into the filter.

namespace mycompany.myproject
{
public class MacroModule : IHttpModule
{
public void Init(HttpApplication context) { context.ReleaseRequestState += new EventHandler(context_ReleaseRequestState); }
    public void Dispose()
    {
      HttpApplication application = HttpContext.Current.ApplicationInstance;
      application.ReleaseRequestState -= new EventHandler(context_ReleaseRequestState);
    }
    private void context_ReleaseRequestState(object sender, EventArgs e)
    {
      HttpResponse response = HttpContext.Current.Response;
      if (string.Compare(response.ContentType,"text/html",true) == 0)
      {
        response.Filter = new MacroFilter(response.Filter,response.ContentEncoding);
      }
    }
  }
}

This is the skeleton code of the custom filter, which is simply a facade over the original Stream that modifies the buffer that is being passed into the Write method. See the notes below.

namespace mycompany.myproject
{
  internal class MacroFilter : Stream
  {
    private Stream s = null;
    private Encoding enc = null;
    private StringBuilder sb = null;

public MacroFilter(Stream s, Encoding enc) { this.s = s; this.enc = enc; }

public override bool CanRead { get { return s.CanRead; } }

public override bool CanSeek { get { return s.CanSeek; } }

public override bool CanWrite { get { return s.CanWrite; } }
  (-- method bodies omitted --)
public override long Length
public override long Position
public override void Flush()
public override long Seek(long offset, SeekOrigin origin)
public override void SetLength(long value)
public override int Read(byte[] buffer, int offset, int count)
public override void Write(byte[] buffer, int offset, int count) { sb = new StringBuilder(count + 1024); sb.Append(enc.GetString(buffer,offset,count)); // <---- Modify the sb contents here ----> byte[] buff = enc.GetBytes(sb.ToString()); s.Write(buff,0,buff.Length); } } }

The class overrides and implements each abstract member of Stream (is there an easier way?). The middle methods in italics have the bodies omitted for brevity, but they just pass processing down to the original 's' Stream object.

In the Write method, a StringBuilder is constructed by encoding the bytes in the write buffer to a string. The comment indicates where the string can be modified as needed by your processing. The string will probably consist of mutiple text records delimited by CRLF or similar (don't make assumptions about the exact format). The bytes are then encoded back to bytes and written in place of the original ones.

This processing allows you to inspect and modify the raw buffer after it has been constructed by the application and before it is sent to the browser.

Some questions remain:

* How can we be sure the buffer will contain the entire page? The HttpResponse Buffer and BufferOutput properties control this, but it's not clear if or when the Write method might get a single page in arbitrary multiple buffer pieces, which could crash the sample code.

* Is there an easier way of coding the filter than using a raw Stream and overriding all the abstract methods?

* Why the ReleaseRequestState event? There are so many events, it would have taken me hours to find the right one without a hint up-front to save time.

Back to: Development Blog Contents


Contact Information | PGP Keys | Site Map | What's New | Visitor Book
Last Updated: 06-Aug-2007 21:10
Copyright © 1999-2007 Orthogonal Programming