tags:

views:

582

answers:

1

I have an ISAPI filter for IIS 6 which does some custom processing using the bytes-sent field of the response. I'd like to update that for IIS 7, but I'm running into a problem. None of the IIS 7 events seem to have access to the content-length, bytes sent, or any data which would let me calculate the content-length or bytes sent. (I know the content-length header and bytes sent are not the same, but either will work for this purpose.)

From what I can tell, the content-length header is added by HTTP.SYS after the managed modules have finished executing. Right now I have an event handler which runs on EndRequest. If I could get at the output stream I could calculate what I need myself but the managed pipeline doesn't seem to have access to that either.

Is there some way of getting the content-length or bytes sent in the managed pipeline? Failing that, is there some way I can calculate content-length or bytes sent from objects available in the managed pipeline?

+3  A: 

To get at the bytes sent you can use the HttpResponse.Filter property. As the MSDN docs say this property gets or sets a wrapping filter object that is used to modify the HTTP entity body before transmission.

You can create a new System.IO.Stream that wraps the existing HttpResponse.Filter stream and counts the bytes passed in to the Write method before passing them on. For example:

public class ContentLengthModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.BeginRequest += OnBeginRequest;
        context.EndRequest += OnEndRequest;
    }

    void OnBeginRequest(object sender, EventArgs e)
    {
        var application = (HttpApplication) sender;
        application.Response.Filter = new ContentLengthFilter(application.Response.Filter);
    }

    void OnEndRequest(object sender, EventArgs e)
    {
        var application = (HttpApplication) sender;
        var contentLengthFilter = (ContentLengthFilter) application.Response.Filter;
        var contentLength = contentLengthFilter.BytesWritten;
    }

    public void Dispose()
    {
    }
}

public class ContentLengthFilter : Stream
{
    private readonly Stream _responseFilter;

    public int BytesWritten { get; set; }

    public ContentLengthFilter(Stream responseFilter)
    {
        _responseFilter = responseFilter;
    }

    public override void Flush()
    {
        _responseFilter.Flush();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return _responseFilter.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        _responseFilter.SetLength(value);
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return _responseFilter.Read(buffer, offset, count);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        BytesWritten += count;
        _responseFilter.Write(buffer, offset, count);
    }

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

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

    public override bool CanWrite
    {
        get { return _responseFilter.CanWrite; }
    }

    public override long Length
    {
        get { return _responseFilter.Length; }
    }

    public override long Position
    {
        get { return _responseFilter.Position; }
        set { _responseFilter.Position = value; }
    }
}
Daniel Richardson
Thanks for the very complete answer and code sample. Now I just need a chance to plug it in and try it out.
matt.mercieca
That's exactly what I needed. Thank you again.
matt.mercieca