Buffered Stream - Synchronous operations are disallowed in ASP.NET Core 3.0

Viewed 2655

I had an REST API targeting AspNetCore 2.2 with an endpoint that allows download of some big json files. After migrating to AspNetCore 3.1 this code stopped working:

    try
    {
        HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
        HttpContext.Response.Headers.Add("Content-Type", "application/json");

        using (var bufferedOutput = new BufferedStream(HttpContext.Response.Body, bufferSize: 4 * 1024 * 1024))
        {
            await _downloadService.Download(_applicationId, bufferedOutput);
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, ex.Message);                
    }

This is the download method, that created the json that I want to return on the HttpContext.Response.Body:

    public async Task Download(string applicationId, Stream output, CancellationToken cancellationToken = default(CancellationToken))
    {       
        using (var textWriter = new StreamWriter(output, Constants.Utf8))
        {
            using (var jsonWriter = new JsonTextWriter(textWriter))
            {
                jsonWriter.Formatting = Formatting.None;
                await jsonWriter.WriteStartArrayAsync(cancellationToken);

                //write json...
                await jsonWriter.WritePropertyNameAsync("Status", cancellationToken);
                await jsonWriter.WriteValueAsync(someStatus, cancellationToken); 

                await jsonWriter.WriteEndArrayAsync(cancellationToken);
            }
        }
    }

Now I get this exception: "Synchronous operations are disallowed in ASP.NET Core 3.0" How can I change this code to work without using AllowSynchronousIO = true;

2 Answers

AllowSynchronousIO option is disabled by default from .Net core 3.0.0-preview3 in (Kestrel, HttpSys, IIS in-process, TestServer) because these APIs are source of thread starvation and application hangs.

The option can be overridden on a per request for a temporary migration :

var allowSynchronousIoOption = HttpContext.Features.Get<IHttpBodyControlFeature>();
if (allowSynchronousIoOption != null)
{
    allowSynchronousIoOption.AllowSynchronousIO = true;
}

You can find more info and follow the ASP.NET Core Issue Tracker : AllowSynchronousIO disabled in all servers

By calling FlushAsync and DisposeAsync before exiting using clause, you can stop synchronous operations that happen during writer dispositions. BufferedStream seems to have the synchronous write issue, how about control the buffer size in StreamWriter.

using (var streamWriter = new StreamWriter(context.Response.Body, Encoding.UTF8, 1024))
using (var jsonWriter = new JsonTextWriter(streamWriter))
{
    jsonWriter.Formatting = Formatting.None;
    await jsonWriter.WriteStartObjectAsync();
    await jsonWriter.WritePropertyNameAsync("test");
    await jsonWriter.WriteValueAsync("value " + new string('a', 1024 * 65));
    await jsonWriter.WriteEndObjectAsync();

    await jsonWriter.FlushAsync();
    await streamWriter.FlushAsync();
    await streamWriter.DisposeAsync();
}

In this way, you can make your code work with a small change without AllowSynchronousIO = true

Related