5

There may be an easy way to do this but I can't see it...

I created a simple Http Module that starts a timer on the PreRequestHandler and stops the timer on the PostRequestHandler to calculate the time it took the page to load.

I then create some simple html and write my results to Response.Write. Since I'm doing this in the PostRequestHandler it's adding my results after the </html> tag. That's fine for testing but I need in a scenario where the page needs to validate.

I can't seem to figure out how I could manipulate the Response object to insert my results before the </body> tag. Response.Write and Response.Output.Write don't have that flexibility and I couldn't see a way to work with the Response as a string. Am I missing something easy?

DM.
  • 1,837
  • 1
  • 13
  • 18

1 Answers1

8

To do this, you'd have to implement your own stream object and use that as a filter for your response.

For isntance:

public class TimerStream : Stream
{
    private Stream inner { get; set; }
    private StringBuilder   responseHtml;

    public TimerStream(Stream inputStream) { 
        inner = inputStream; 
        responseHtml = new StringBuilder();
        // Setup your timer
    }

    /* Filter overrides should pass through to inner, all but Write */
    public override void Write(byte[] buffer, int offset, int count)
    {
        string bufferedHtml = System.Text.UTF8Encoding.UTF8.GetString (buffer, offset, count);
        Regex endTag = new Regex ("</html>", RegexOptions.IgnoreCase);

        if (!endTag.IsMatch (bufferedHtml))
        {
            responseHtml.Append(bufferedHtml);
        }
        else
        {
            // insert timer html into buffer, then...
            responseHtml.Append (bufferedHtml);
            byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes (responseHtml.ToString ());            
            inner.Write (data, 0, data.Length);            
        }
    }
}

Then, in your HttpModule, you'd add this to your BeginRequest:

// Change the Stream filter
HttpResponse response = context.Response;
response.Filter = new TimerStream(context.Response.Filter);
Jim Schubert
  • 19,627
  • 5
  • 56
  • 67
  • Looks solid. I'll give it a whirl. Thanks! – DM. May 07 '10 at 20:44
  • No problem. I edited my post because I was using `buffer` as an input parameter and a local variable, so I changed it. – Jim Schubert May 08 '10 at 12:57
  • Ok, so I tried to implement this and ran into a few road blocks... at the end of your overriden "Write" method you call "responseStream.Write(...)" where are you getting the responseStream from? You don't pass or initialize it anywhere? – DM. May 11 '10 at 21:03
  • oh, that should probably be inner. When I do this, I have an inputStream and an outputStream (responseStream). At the end, I write to a new/empty MemoryStream, which you can work with in your HttpModule. – Jim Schubert May 11 '10 at 21:24
  • Got it, had to tweak a few things to get exactly what I needed but it works great. Thanks for the help! – DM. May 11 '10 at 22:24
  • Isn't this depending on that the entire html end tag is written in the buffer? – ZNS Dec 08 '11 at 20:49
  • @ZNS Yes, for the last call to the method it would assume the buffer hasn't split ` – Jim Schubert Dec 09 '11 at 16:45