This project is read-only.
1
Vote

MultipartStreamProvider loses HttpContext after async work

description

I have implemented a custom subclass of MultipartStreamProvider in order to write uploaded file data to a custom stream. After writing to the stream, the HttpContext is sometimes null. The code below is a simplified repro that demonstrates the problem. If the custom stream does not actually do any asynchronous work in WriteAsync (i.e. if it stays on the same thread), then everything works as you'd expect. However, as soon as we introduce some actual async work into WriteAsync (simulated here by Task.Delay), then the HttpContext is (usually) lost.

Am I doing something incorrectly? Or is this a bug in the framework? We are using version 5.2.3.0.
public class TestApiController : ApiController
{
    public class CustomMultipartStreamProvider : MultipartStreamProvider
    {
        public CustomMultipartStreamProvider()
        {
        }

        public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
        {
            return new CustomStream();
        }
    }

    class CustomStream : MemoryStream
    {
        public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            await Task.Delay(100, cancellationToken); // simulate async work (if this line is commented out, everything works correctly)
            await base.WriteAsync(buffer, offset, count, cancellationToken);
        }
    }

    [Route("api/test/multipart")]
    public async Task<string> PutMultiPart()
    {
        // Check if the request contains multipart/mixed content
        if (!Request.Content.IsMimeMultipartContent("mixed"))
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);

        // Read the multipart data
        var multipartStreamProvider = new CustomMultipartStreamProvider();
        await Request.Content.ReadAsMultipartAsync(multipartStreamProvider);

        if (HttpContext.Current != null)
        {
            return "good";
        }
        else
        {
            return "bad";
        }
    }
}
For testing purposes, the request I am sending is essentially this:
Content-Type: multipart/mixed; boundary=boundary42
--boundary42 
Content-Type: application/json

{
  Description: "test file"
}
--boundary42
Content-Type: application/octet-stream 
Content-Disposition: inline; filename=hello.txt

Hello world.
--boundary42--

comments

jr76 wrote Oct 4, 2016 at 2:41 AM

Figured out what the issue was - posted here