2

I could not figure out why Kestrel keeps closing the connection when I try uploading large files to it, as soon as it gets over 30MB, to be precise.

I am aware Kestrel has a default setting of 30MB in terms of maximum request size, and I have tried increasing the maximum request size to 100MB in the web.config file:

<security>
  <requestFiltering allowDoubleEscaping="true">
    <!-- This will handle requests up to 100MB -->
    <requestLimits maxAllowedContentLength="104857600" />
  </requestFiltering>
</security>

I have tried configuring it when bootstrapping the server as well:

WebHost.UseKestrel(options => options.Limits.MaxRequestBodySize = 104857600) // 100MB

[...]

services.Configure<FormOptions>(options => options.MultipartBodyLengthLimit = 104857600); // 100MB

I have tried setting it in the middleware:

var maxRequestBodySizeFeature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
maxRequestBodySizeFeature.MaxRequestBodySize = 104857600;

I have tried everything I could find, for example what is suggested here:

Increase upload file size in Asp.Net core

And here:

https://www.talkingdotnet.com/how-to-increase-file-upload-size-asp-net-core/

But as soon as my POST request containing several files that together go over the 30MB limit, when the following line executes:

foreach (var entry in httpContext.Request.Form /* Exception is thrown by Form property */)
{
}

I get the following exception:

[Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ConnectionResetException] - Error -4077 ECONNRESET connection reset by peer
   at Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines.PipeCompletion.ThrowFailed()
   at Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines.Pipe.GetResult(ReadResult& result)
   at Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines.Pipe.Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines.IReadableBufferAwaiter.GetResult()
   at Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines.ReadableBufferAwaitable.GetResult()
   at Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.LibuvOutputConsumer.<WriteOutputAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.WebUtilities.BufferedReadStream.<EnsureBufferedAsync>d__37.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.WebUtilities.MultipartReaderStream.<ReadAsync>d__36.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.WebUtilities.StreamHelperExtensions.<DrainAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.WebUtilities.MultipartReader.<ReadNextSectionAsync>d__20.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Http.Features.FormFeature.<InnerReadFormAsync>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Http.Features.FormFeature.ReadForm()
[...]

As far as I know, this is not the browser closing the connection. In the browser, I just select a bunch of files and upload them, and immediately in Visual Studio, the exception is thrown. Everything is done in localhost.

sboisse
  • 4,860
  • 3
  • 37
  • 48

1 Answers1

1

I was successful providing 11 7mb files using the following settings:

Initial setup:

WebHost.CreateDefaultBuilder(args)
    .ConfigureKestrel(options =>
    {
        options.Limits.MaxRequestBodySize = 104857600;
    })

In Startup.cs set the MultipartBodyLengthLimit:

services.Configure<FormOptions>(options => options.MultipartBodyLengthLimit = 104857600);

In the RunExtensions.Run:

Microsoft.AspNetCore.Builder.RunExtensions.Run(app, request =>
{
    if (request.Request.Path.Value == "/test")
    {
        foreach (var file in request.Request.Form.Files)
        {
            // No exceptions
        }
    }
    return Task.CompletedTask;
});
nelsontruran
  • 514
  • 4
  • 18
  • Unfortunately we are not using the MVC controllers, so things have to be done using what's in HttpContext. – sboisse May 28 '19 at 19:23
  • If you're not using the controllers how are you getting the HttpContext? – nelsontruran May 28 '19 at 19:26
  • We process requests directly from the app using Microsoft.AspNetCore.Builder.RunExtensions.Run(this IApplicationBuilder app, RequestDelegate handler) – sboisse May 28 '19 at 19:37
  • @sboisse try my updated code. Let me know if it works. – nelsontruran May 28 '19 at 19:58
  • thanks, I will try that when I get the chance, but meanwhile, do you mean you also get the exception if you iterate on `httpContext.Request.Form`, but not if you iterate on `httpContext.Request.Form.Files` ? – sboisse May 29 '19 at 15:59
  • I got the exception the first time I ran it on Form, but ran it 4 more times successfully. Ran it 5 times successfully on Files. Called the endpoint using curl – nelsontruran May 29 '19 at 19:14
  • after investigating more on the issue, the conclusion is accessing `Form.Files` instead of `Form` did not bring any difference in the outcome. We still get the ConnectionResetException. That said, it wouldn't have been a viable option for us as we still need to get access to the other form fields of the request, not just the files. – sboisse Jun 06 '19 at 16:50