views:

142

answers:

1

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?

+3  A: 

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
Looks solid. I'll give it a whirl. Thanks!
DM
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
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
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
Got it, had to tweak a few things to get exactly what I needed but it works great. Thanks for the help!
DM