Issue with HttpSelfHostServer and xUnit console runner

Topics: ASP.NET Web API
Jun 28, 2012 at 8:46 AM
Edited Jun 28, 2012 at 8:52 AM

I'm coding a project that uses WebApi to expose RESTFul api on a server side. Most use cases is to upload/download some binary data from a client to a server. My testing project uses xUnit framework. Every test methods uses new instance of HttpSelfHostServer (created in ctor of test class). HttpSelfHostServer is closed in Dispose of test classes, i.e.: 

public class WebApiTest : IDisposable
{
  private HttpSelfHostConfiguration config;
  private HttpSelfHostServer server;

  public WebApiTest()
  {
  config = new HttpSelfHostConfiguration("http://localhost:8081")
				{
					MaxReceivedMessageSize = Int64.MaxValue,
					TransferMode = TransferMode.Streamed
				};
  server = new HttpSelfHostServer(config);
  server.OpenAsync().Wait();
  }
  public void Dispose()
  {
    server.CloseAsync().Wait();
server.Dispose();
config.Dispose();
 } }
The issue that I'm experiencing is that when tests are executed by xUnit console runner I got exceptions from HttpSelfHostServer handled by the runner inside OnUnhandledException method:
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
The issue occures only when all tests are performed, never when they are executed one by one manually. My question is how to handle that to make my tests passing when running from console runner (xUnit plugin for Resharper ignore UnhandledException of AppDomain so all tests pass)? Is it a HttpSelfHostServer bug or not? The exceptions that I got are:

System.Runtime.CallbackException: An AsyncCallback threw an exception. ---> System.ServiceModel.CommunicationObjectAbortedException: The HTTP request context was aborted while writing the response. As a result, the response may not have been completely written to the network. This can be remedied by gracefully closing the request context rather than aborting it. ---> System.ApplicationException: The handle is invalid. (Exception from HRESULT: 0x80070006 (E_HANDLE))
   at System.Threading.ThreadPool.BindIOCompletionCallbackNative(IntPtr fileHandle)
   at System.Threading.ThreadPool.BindHandle(IntPtr osHandle)
   at System.Net.HttpListener.EnsureBoundHandle()
   at System.Net.HttpListenerResponse.SendHeaders(HTTP_DATA_CHUNK* pDataChunk, HttpResponseStreamAsyncResult asyncResult, HTTP_FLAGS flags)
   at System.Net.HttpResponseStream.BeginWrite(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
   at System.ServiceModel.Channels.BytesReadPositionStream.BeginWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state)
   at System.ServiceModel.Channels.HttpOutput.ListenerResponseHttpOutput.ListenerResponseOutputStream.BeginWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state)
   --- End of inner exception stack trace ---
   at System.ServiceModel.Channels.HttpOutput.ListenerResponseHttpOutput.ListenerResponseOutputStream.BeginWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state)
   at System.ServiceModel.Channels.BufferedOutputAsyncStream.Close()
   at System.ServiceModel.Channels.HttpOutput.Close()
   at System.ServiceModel.Channels.HttpRequestContext.ListenerHttpContext.OnClose(TimeSpan timeout)
   at System.ServiceModel.Channels.RequestContextBase.Close(TimeSpan timeout)
   at System.Web.Http.SelfHost.HttpSelfHostServer.ReplyContext.Dispose(Boolean disposing)
   at System.Web.Http.SelfHost.HttpSelfHostServer.ReplyComplete(IAsyncResult result)
   at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
   --- End of inner exception stack trace ---
   at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
   at System.ServiceModel.Channels.HttpRequestContext.ReplyAsyncResult.OnSendResponseCompletedCallback(IAsyncResult result)
   at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
   at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
   at System.ServiceModel.Channels.HttpOutput.SendAsyncResult.OnWriteStreamedMessage(Object state)
   at System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
   at System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

and
System.Runtime.CallbackException: An AsyncCallback threw an exception. ---> System.ObjectDisposedException: Cannot access a disposed object.
   at System.Threading.ReaderWriterLockSlim.TryEnterReadLockCore(Int32 millisecondsTimeout)
   at System.Threading.ReaderWriterLockSlim.TryEnterReadLock(Int32 millisecondsTimeout)
   at System.Web.Http.Services.DefaultServices.GetService(Type serviceType)
   at System.Web.Http.ServicesExtensions.GetService[TService](ServicesContainer services)
   at System.Net.Http.HttpRequestMessageExtensions.CreateResponse[T](HttpRequestMessage request, HttpStatusCode statusCode, T value, HttpConfiguration configuration)
   at System.Net.Http.HttpRequestMessageExtensions.CreateErrorResponse(HttpRequestMessage request, HttpStatusCode statusCode, Func`2 errorCreator)
   at System.Net.Http.HttpRequestMessageExtensions.CreateErrorResponse(HttpRequestMessage request, HttpStatusCode statusCode, Exception exception)
   at System.Web.Http.SelfHost.HttpSelfHostServer.ProcessRequestContext(ChannelContext channelContext, RequestContext requestContext)
   at System.Web.Http.SelfHost.HttpSelfHostServer.ReceiveRequestContextComplete(IAsyncResult result)
   at System.Web.Http.SelfHost.HttpSelfHostServer.OnReceiveRequestContextComplete(IAsyncResult result)
   at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
   --- End of inner exception stack trace ---
   at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
   at System.Runtime.InputQueue`1.AsyncQueueReader.Set(Item item)
   at System.Runtime.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)
   at System.Runtime.InputQueue`1.EnqueueAndDispatch(T item, Action dequeuedCallback, Boolean canDispatchOnThisThread)
   at System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, Action dequeuedCallback, Boolean canDispatchOnThisThread)
   at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, Action callback)
   at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContextCore(IAsyncResult result)
   at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContext(IAsyncResult result)
   at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
   at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
   at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
   at System.Net.ListenerAsyncResult.WaitCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
Coordinator
Jun 28, 2012 at 4:04 PM

Is the server perhaps getting disposed before the request is completed?

Daniel Roth

Jul 2, 2012 at 12:18 PM
Edited Jul 2, 2012 at 12:19 PM

I was investigating that issue in direction of too early disposed server according to your advice, but I didn't determine any place in my tests where it would be possible (I always wait for the response). However removal of those two lines of code:

server.Dispose();
config.Dispose();

from Dispose method of my tests, makes the xUnit console runner does not fail because of ObjectDisposedException. I still sometime get CommunicationObjectAbortedException but only for a few tests which use SignalR to receive some notification from the server. My question is: do we need to explicitly call Dispose() of HttpSelfHostServer? I saw that WebApi internal tests do not call it.