Alright all, I've been bashing my head bloody over this one...
The Set Up:
The Problem:
I am getting the following error on EVERY Authenticated Request:
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.<CopyResponseStatusAndHeadersAsync>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.<CopyResponseAsync>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.<ProcessRequestAsyncCore>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)
Investigation done so far:
The error listed above is happening only with the OPTIONS verb.
The error says that the headers were already sent before it was asked to set its Status Code.
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.
In order to get past the pre-flight, I'm using this: (global.asax)
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
Response.Flush();
}
}
This passes the responses into my handler, but it ALSO passes the OPTIONS verb into this code, which I think it should:
//Capture incoming request by overriding SendAsync method
protected override Task<HttpResponseMessage> 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 =>
{
HttpResponseMessage response = task.Result;
if (response.StatusCode == HttpStatusCode.Unauthorized && !response.Headers.Contains("WWW-Authenticate"))
{
response.Headers.Add("WWW-Authenticate", "Basic");
}
return response;
});
}
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.
I'm NOT putting OPTIONS on any of my controllers, so that might explain the 405 - but the request shouldn't get that far.
So - this sounds like a config thing? Confused.
My cors config is:
<!-- BEGIN: Enable CORS -->
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="accept, authorization, origin, content-type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<remove name="WebDAV"/>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<!-- END: Enable CORS -->
My Thoughts so far
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!
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?
If that's not it then what the hell IS causing this? Where is it coming from? How can I fix it? No idea...
So I'm leaning on the valiant wisdom of my fellow nerds on Stack Overflow...
HELP!!!!!!!
Put a Response.End();
right after the Response.Flush();
So you should have an empty response for the preflight request.
At least this solved the issue for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With