9

So I am compressing my output stream via an action filter:

var response = filterContext.HttpContext.Response;
response.Filter = new DeflateStream(response.Filter), CompressionMode.Compress);

Which works great. Now, I would also like to remove the excess whitespace present. I found Mads Kristensen's http module http://madskristensen.net/post/A-whitespace-removal-HTTP-module-for-ASPNET-20.aspx.

public class WhitespaceFilter : Stream {

    // using Mads Kristensen httpModule
    // http://madskristensen.net/post/A-whitespace-removal-HTTP-module-for-ASPNET-20.aspx

    private Stream os;
    private static Regex reg = new Regex(@"^\s+", RegexOptions.Multiline | RegexOptions.Compiled); 

    public WhitespaceFilter(Stream os) {
        this.os = os;
    }

    //methods that need to be overridden from stream
    public override bool CanRead {
        get { return true; }
    }

    public override bool CanSeek {
        get { return true; }
    }

    public override bool CanWrite {
        get { return true; }
    }

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

    public override long Length {
        get { return 0; }
    }

    private long _position;
    public override long Position {
        get { return _position; }
        set { _position = value; }
    }

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

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

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

    public override void Close() {
        os.Close();
    }

    public override void Write(byte[] buffer, int offset, int count) {
        string html = System.Text.Encoding.Default.GetString(buffer);

        //remove whitespace
        html = reg.Replace(html, string.Empty);

        byte[] outdata = System.Text.Encoding.Default.GetBytes(html);

        //write bytes to stream
        os.Write(outdata, 0, outdata.GetLength(0));
    } 

I added the WhitespaceFilter class and added a new filter like the compression:

var response = filterContext.HttpContext.Response;
response.Filter = new WhitepaperFilter(response.Filter);

This also works great. However, I seem to be having problems combining the two!

I tried:

var response = filterContext.HttpContext.Response;
response.Filter = new DeflateStream(new WhitespaceFilter(response.Filter), CompressionMode.Compress);

However this results in some major issues. The html gets completely messed up and sometimes I get an 330 error.

It seems that the Whitespace filter write method gets called multiple times. The first time the html string is fine, but on subsequent calls its just random characters.

I thought it might be because the stream had been deflated, but isnt the whitespace filter using the untouched stream and then passing the resulting stream to the DeflateStream call?

Any ideas?

Jason Berkan
  • 8,734
  • 7
  • 29
  • 39
James Hull
  • 3,669
  • 2
  • 27
  • 36
  • 4
    Why bother removing it if you are just going to compress it anyway? I'd only worry about it if I were delivering the results uncompressed. Whitespace compresses really well. – tvanfosson May 29 '10 at 15:50
  • 2
    Fiddler does show that with whitespace removed the file size is quite a bit smaller, however in a real world project, you're right; the difference would be negligible. I am just interested as to why this isn't working when it seems like it should. – James Hull May 29 '10 at 16:05
  • See this guy's findings if you compress and remove whitespace: http://stackoverflow.com/questions/855526/removing-extra-whitespace-from-generated-html-in-mvc – eadam Aug 25 '13 at 04:53

2 Answers2

7

For those who get this far... you can do it... just swap the order of the stream chaining:

   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
        var response = filterContext.HttpContext.Response;

        // - COMPRESS
        HttpRequestBase request = filterContext.HttpContext.Request;
        string acceptEncoding = request.Headers["Accept-Encoding"];
        if (!String.IsNullOrEmpty(acceptEncoding))
        {
            acceptEncoding = acceptEncoding.ToUpperInvariant();

            if (acceptEncoding.Contains("GZIP"))
            {
                response.AppendHeader("Content-encoding", "gzip");
                response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
            }
            else if (acceptEncoding.Contains("DEFLATE"))
            {
                response.AppendHeader("Content-encoding", "deflate");
                response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
            }
        }

        // - REMOVE WHITE SPACE
        response.Filter = new WhitespaceFilter(response.Filter);
    }
PuzSol
  • 86
  • 1
  • 2
  • 6
    I made a test with performance. Trimming whitespaces in runtime is horrible. From 200 rps it dropped to 20 rps. – BrunoLM Jan 29 '11 at 18:16
0

I'm not seeing much wrong with the code above however you may want to try this approach:

var response = filterContext.HttpContext.Response; 
using(var wsf = new WhitespaceFilter(response.Filter))
{
   wsf.Flush();
   response.Filter = new DefalteStream(wsf, CompressMode.Compress);
}

BTW are you using this attribute approach when applying the compressing and white space removal:

http://www.avantprime.com/articles/view-article/7/compress-httpresponse-for-your-controller-actions-using-attributes

DaTribe