title: Server cannot set status after HTTP headers have been sent - web api CORS
body: <p>Alright all, I've been bashing my head bloody over this one...</p>
  
  <p><strong>The Set Up:</strong></p>
  
  <ul>
  <li>I have a Web Api 2.0 Project with Basic Authentication set up.  </li>
  <li>I have CORS enabled in the web.config</li>
  <li>I have ELMAH logging errors</li>
  <li>I have a DelegatingHandler handling the incoming request - (code below)</li>
  <li>I have a very specific data structure so I'm not using Membership or Identity by Microsoft.</li>
  <li>I'm flushing every request that comes in that uses the OPTIONS verb</li>
  </ul>
  
  <p><strong>The Problem:</strong></p>
  
  <p>I am getting the following error on EVERY Authenticated Request:</p>
  
  <pre><code>System.Web.HttpException (0x80004005): Server cannot set status after HTTP headers have been sent.
     at System.Web.HttpResponse.set_StatusCode(Int32 value)
     at System.Web.HttpResponseWrapper.set_StatusCode(Int32 value)
     at System.Web.Http.WebHost.HttpControllerHandler.&lt;CopyResponseStatusAndHeadersAsync&gt;d__31.MoveNext()
  --- End of stack trace from previous location where exception was thrown ---
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
     at System.Web.Http.WebHost.HttpControllerHandler.&lt;CopyResponseAsync&gt;d__7.MoveNext()
  --- End of stack trace from previous location where exception was thrown ---
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
     at System.Web.Http.WebHost.HttpControllerHandler.&lt;ProcessRequestAsyncCore&gt;d__0.MoveNext()
  --- End of stack trace from previous location where exception was thrown ---
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
     at System.Web.TaskAsyncHelper.EndTask(IAsyncResult ar)
     at System.Web.HttpTaskAsyncHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
     at System.Web.HttpApplication.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar)
  </code></pre>
  
  <p><strong>Investigation done so far:</strong></p>
  
  <p>The error listed above is happening only with the OPTIONS verb.</p>
  
  <p>The error says that the headers were already sent before it was asked to set its Status Code.</p>
  
  <p>Only 1 error record appears - in elmah, the "SendAsync" method is hit twice - once for the OPTIONS pre-flight and once for the GET or POST.  That tells me that this is only happening on one request - the OPTIONS request.</p>
  
  <p>In order to get past the pre-flight, I'm using this: (global.asax)</p>
  
  <pre><code>protected void Application_BeginRequest()
  {
    if (Request.Headers.AllKeys.Contains("Origin") &amp;&amp; Request.HttpMethod == "OPTIONS")
      {
          Response.Flush();
      }
  }
  </code></pre>
  
  <p>This passes the responses into my handler, but it ALSO passes the OPTIONS verb into this code, which I think it should:</p>
  
  <pre><code>//Capture incoming request by overriding SendAsync method
  
  protected override Task&lt;HttpResponseMessage&gt; SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  {
      //validate credentials, set CurrentPrincipal and Current.User if valid
      if (ValidateCredentials(request.Headers.Authorization))
      {
          Thread.CurrentPrincipal = new MyPrincipal(_userName);
          HttpContext.Current.User = new MyPrincipal(_userName);
      }
  
  
      // Execute base.SendAsync to execute default actions
      // and once it is completed, capture the response object and add
      // WWW-Authenticate header if the request was marked as (401) unauthorized.
  
      return base.SendAsync(request, cancellationToken).ContinueWith(task =&gt;
      {
          HttpResponseMessage response = task.Result;
          if (response.StatusCode == HttpStatusCode.Unauthorized &amp;&amp; !response.Headers.Contains("WWW-Authenticate"))
          {
              response.Headers.Add("WWW-Authenticate", "Basic");
          }
          return response;
      });
  
  }
  </code></pre>
  
  <p>Now in the debugger, I'm seeing that the OPTIONS goes through and fires a 405 - method not allowed, but when it comes through to elmah it's a 500 error - Server cannot set status after HTTP headers have been sent.</p>
  
  <p>I'm NOT putting OPTIONS on any of my controllers, so that might explain the 405 - but the request shouldn't get that far.</p>
  
  <p>So - this sounds like a config thing?  Confused.</p>
  
  <p>My cors config is:</p>
  
  <pre><code>&lt;!-- BEGIN: Enable CORS --&gt;
  &lt;httpProtocol&gt;
    &lt;customHeaders&gt;
      &lt;add name="Access-Control-Allow-Origin" value="*" /&gt;
      &lt;add name="Access-Control-Allow-Headers" value="accept, authorization, origin, content-type" /&gt;
      &lt;add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" /&gt;
    &lt;/customHeaders&gt;
  &lt;/httpProtocol&gt;
  
  &lt;handlers&gt;
    &lt;remove name="ExtensionlessUrlHandler-Integrated-4.0" /&gt;
    &lt;remove name="OPTIONSVerbHandler" /&gt;
    &lt;remove name="TRACEVerbHandler" /&gt;
    &lt;remove name="WebDAV"/&gt;
    &lt;add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /&gt;
  &lt;/handlers&gt;
  &lt;!-- END: Enable CORS --&gt;
  </code></pre>
  
  <p><strong>My Thoughts so far</strong></p>
  
  <p>The error is not a "FatalError" and the program still fires because the preflight request doesn't effect anything that I'm aware of - so can the error be ignored?  Well, NO!  It's an error!</p>
  
  <p>Now - with the research I've done, it seems like this is an IIS thing that is leaking into my app that I may be able to stop through config - but how?</p>
  
  <p>If that's not it then what the hell IS causing this?  Where is it coming from? How can I fix it?  No idea...</p>
  
  <p>So I'm leaning on the valiant wisdom of my fellow nerds on Stack Overflow...</p>
  
  <p>HELP!!!!!!!</p>