1

I need to upload quite large files to my app in Azure (Linux, .Net Core 3.1, but also .Net 5.0 fails), but when uploading a file takes more than 2 minutes I get a TCP reset, showing the following error:

Unhandled exception. System.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host..

I looked through many posts with this error, but I do not see this scenario where the limit is 2 minutes and caused by the actual reading of the body stream. I already configured timeout - options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(20); - but it is not the request duration, it is when reading the stream in HttpProtocol takes longer than 2 minutes. In that case the middle ware is not even reached.

To reproduce I have a console app:

namespace TestUpload
{
public class DurationStream : Stream
{
    public DurationStream(TimeSpan duration)
    {
        // times 512 to prevent being kicked out because of too slow data
        Length = 512 * (int) duration.TotalSeconds;
        CanRead = true;
        CanWrite = false;
        CanSeek = false;
    }
    public override bool CanRead { get; }
    public override bool CanSeek { get; }
    public override bool CanWrite { get; }
    public override long Length { get; }
    public override long Position { get; set; }

    public override void Flush() { }

    public override int Read(byte[] buffer, int offset, int count)
    {
        var toCopy = Math.Min((int) (Length - Position), count);
        for (var i = 0; i < toCopy; ++i)
        {
            if (i % 512 == 0)
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
            }
            Position++;
        }
        return toCopy;
    }
    public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); }
    public override void SetLength(long value) { throw new NotImplementedException(); }
    public override void Write(byte[] buffer, int offset, int count) { throw new NotImplementedException(); }
}

internal static class Program
{
    private static async Task Main()
    {
        var httpClient = new HttpClient();
        var request = new HttpRequestMessage(HttpMethod.Post, new Uri("http://[myazuresite.com]/upload"))
            {
                Content = new StreamContent(new DurationStream(TimeSpan.FromSeconds(130)))
            };
        var start = DateTime.Now;
        Console.WriteLine($"Started at {start}");
        try
        {
            await httpClient.SendAsync(request);
        }
        finally
        {
            Console.WriteLine($"Finished in {DateTime.Now - start}");
        }
    }
}

And then as controller:

[HttpPost]
public async Task Upload()
{
    await Request.Body.CopyToAsync(Stream.Null);            
}

Locally there is no problem running it against Kestrel, and also if I limit the upload to 2 minutes and after that sleep for a minute in the controller it works fine. The request fails after however long the upload is - so if the stream takes 5 minutes it fails after 5 minutes - and never reaches the middle ware. It fails with both http and https.

My hunch is that there is a network component in front of it in Azure that needs some acknowledgement within 2 minutes, but as it reads the whole stream before getting in the middle ware I do not see how to change the behavior. I did not see an easy way to inject a component in the internals of the Kestrel web server.

Suggestions anyone?

Cloghead
  • 19
  • 4
  • Don't upload a large file in a single request. Split it, then upload the pieces and rejoin them. Or just use Blob Storage. – Ian Kemp Aug 13 '21 at 08:48
  • Thanks, I thought of uploading chunks but it adds extra complexity for all clients of the API (and the server as well), the Blob storage lacks the more fine grained authorization that I enforce in the API. – Cloghead Aug 13 '21 at 08:57
  • Can you check TLS VERSION of azure app, hope this helps [Link](https://stackoverflow.com/questions/5420656/unable-to-read-data-from-the-transport-connection-an-existing-connection-was-f) – Sarang Kulkarni Aug 13 '21 at 09:35
  • The issue is both with http and https, so I guess it is not relevant, but minimum TLS is set to 1.2 – Cloghead Aug 13 '21 at 09:47
  • Does [How do I set the request timeout for one controller action in an asp.net mvc application](https://stackoverflow.com/questions/579523/how-do-i-set-the-request-timeout-for-one-controller-action-in-an-asp-net-mvc-app) help? – Steeeve Aug 13 '21 at 11:28
  • No, I would have to be in my controller to do that, but it already gets interrupted before the middleware, so I do not get into the controller. Thanks for thinking along though! – Cloghead Aug 13 '21 at 12:54

0 Answers0