title: Using both Response.Filter and BufferOutput=true in an ASP.NET MVC action causes a ZLibException
body: <p>I have an ASP.NET MVC action that does the following:</p>
  
  <pre><code>public ActionResult Index()
  {
      /* assuming the client supports gzip since the error doesn't happen without it */
      Response.Filter = new GZipStream(Response.Filter, CompressionMode.Compress);
      Response.AppendHeader("Content-Encoding", "gzip");
  
      Response.BufferOutput = false; // Removing this line allows it to succeed
  
      return Content("Hello world");
  }
  </code></pre>
  
  <p>It consistently fails with the following exception:</p>
  
  <pre><code> System.IO.Compression.ZLibException: The stream state of the underlying compression routine is inconsistent.
   at System.IO.Compression.DeflaterZLib.Deflate(FlushCode flushCode) 
   at System.IO.Compression.DeflaterZLib.ReadDeflateOutput(Byte[] outputBuffer, FlushCode flushCode, Int32&amp; bytesRead)
   at System.IO.Compression.DeflaterZLib.System.IO.Compression.IDeflater.GetDeflateOutput(Byte[] outputBuffer)
   at System.IO.Compression.DeflateStream.WriteDeflaterOutput(Boolean isAsync)
   at System.IO.Compression.DeflateStream.Write(Byte[] array, Int32 offset, Int32 count)
   at System.Web.HttpWriter.FilterIntegrated(Boolean finalFiltering, IIS7WorkerRequest wr)
   at System.Web.HttpResponse.UpdateNativeResponse(Boolean sendHeaders)
   at System.Web.HttpResponse.Flush(Boolean finalFlush, Boolean async)
   at System.Web.HttpWriter.WriteFromStream(Byte[] data, Int32 offset, Int32 size)
   at System.IO.Compression.DeflateStream.PurgeBuffers(Boolean disposing)
   at System.IO.Compression.DeflateStream.Dispose(Boolean disposing)
   at System.IO.Stream.Close()
   at System.IO.Compression.GZipStream.Dispose(Boolean disposing)
   at System.IO.Stream.Close()
   at System.Web.HttpWriter.FilterIntegrated(Boolean finalFiltering, IIS7WorkerRequest wr)
   at System.Web.HttpResponse.FilterOutput()
   at System.Web.HttpApplication.CallFilterExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&amp; completedSynchronously)
   at System.Web.HttpApplication.PipelineStepManager.ResumeSteps(Exception error)
   at System.Web.HttpApplication.BeginProcessRequestNotification(HttpContext context, AsyncCallback cb)
   at System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context)
   at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
   at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
   at System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus&amp; notificationStatus)
   at System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus&amp; notificationStatus)
   at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
   at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
  </code></pre>
  
  <p>I get the same exception when running it from a bare IHttpRequest handler.</p>
  
  <p>Removing the <code>BufferOutput = false</code> line allows it to run without issues, but I'd like to be able to add compression regardless of whether the response is buffered or not.  I also can't modify web.config to turn on compression globally.</p>
  
  <p>It appears that IIS sets <code>Filtering = true</code> on the underlying HttpResponseStreamFilterSink that the GZipStream is writing to, sometime after the request is finished but before it calls <code>Close()</code>.
  Then, when it calls <code>Close()</code>, the GZipStream writes its final few bytes to the underlying stream, and IIS passes the bytes back to its input, believing they have yet to be filtered.  Understandably, the GZipStream refuses to write after being closed and throws an exception.</p>
  
  <p>It's possible to wrap the GZipStream as follows, in order to bypass it while the stream is closing:</p>
  
  <pre><code>class GZipHelper : Stream
  {
      public GZipStream GZip { get; }
      private bool _closing;
  
      public GZipHelper(Stream baseStream)
      {
          GZip = new GZipStream(baseStream, CompressionMode.Compress);
      }
  
      /* unimplemented Stream methods elided */
  
      public override void Flush()
      {
          GZip.Flush();
      }
  
      public override void Write(byte[] buffer, int offset, int count)
      {
          if (!_closing)
          {
              GZip.Write(buffer, offset, count);
          }
          else
          {
              GZip.BaseStream.Write(buffer, offset, count);
          }
  
      }
      public override void Close()
      {
          if (!_closing)
          {
              _closing = true;
              base.Close();
              GZip.Close(); // will call Write()
          }
      }
  }
  </code></pre>
  
  <p>This fixes the exception and produces the correct output.  However, I don't know why this workaround is necessary.</p>
  
  <p>What is going on?</p>