Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The client is disconnected because the underlying request has been completed. There is no longer an HttpContext available

We are intermittently getting a The client is disconnected because the underlying request has been completed. There is no longer an HttpContext available exception in our WebApi endpoint.

Does anyone know what is causing this error, or how it can be reproduced in a consistent way? Currently it happens in 150 out of 100,000 requests.

The exception is thrown by XmlDeserialize() in this part of the code:

[HttpPost]
public async Task<IHttpActionResult> Process()
{
    using (var requestStream = await Request.Content.ReadAsStreamAsync())
    {
        XmlSerializer serializer = new XmlSerializer(typeof(MyEntity));

        // !! The below line throws the exception
        MyEntity entity = serializer.Deserialize(requestStream) as MyEntity;
    }
    ...
}

This is the full exception message:

  "ExceptionType": "HttpException",
  "Message": "The client is disconnected because the underlying request has been completed.  There is no longer an HttpContext available.",
  "StackTrace": [
    "HttpBufferlessInputStream.Read;0",
    "SeekableBufferedRequestStream.Read;0",
    "XmlTextReaderImpl.ReadData;0",
    ...

Edit: I found this in the source code for .NET. System.Web.txt:

HttpBufferlessInputStream_ClientDisconnected=The client is disconnected because the underlying request has been completed. There is no longer an HttpContext available.

https://referencesource.microsoft.com/#system.web/HttpBufferlessInputStream.cs,332ecbede7a1f12a:

// We are done if the count == 0 or there is no more content
while (count > 0 && _remainingBytes != 0) {
    // Do the actual read
    bytesRead = wr.ReadEntityBody(buffer, offset, count);
    if (bytesRead <= 0) {
        if (!_context.Response.IsClientConnected) {
            if (_persistEntityBody) {
                SetRawContentOnce();
            }
            throw new HttpException(SR.GetString(SR.HttpBufferlessInputStream_ClientDisconnected));

So the exception is thrown by HttpBufferlessInputStream.Read(), after wr.ReadEntityBody(buffer, offset, count) returns 0 or negative, and _context.Response.IsClientConnected is false.

This would mean that the client became disconnected while XmlDeserialize() was still running. But from my limited experiments with netcat, .NET waits until the entire XML request has been uploaded before even calling the controller method. So we aren't uploading the request while we are still deserializing.

like image 904
sashoalm Avatar asked Jul 01 '19 09:07

sashoalm


1 Answers

Like it's explained here you should avoid capturing the synchronization context and use .ConfigureAwait(false);

[HttpPost]
public async Task<IHttpActionResult> Process()
{
    using (var requestStream = await Request.Content.ReadAsStreamAsync().ConfigureAwait(false))
    {
        XmlSerializer serializer = new XmlSerializer(typeof(MyEntity));
        MyEntity entity = serializer.Deserialize(requestStream) as MyEntity;
    }
    ...
}

As noticed by Thowk, SynchronizationContext has been removed from ASP.NET Core.

like image 136
Orace Avatar answered Oct 03 '22 00:10

Orace